net-ssh-backports 6.3.0.backports

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 (111) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +93 -0
  3. data/.gitignore +13 -0
  4. data/.rubocop.yml +21 -0
  5. data/.rubocop_todo.yml +1074 -0
  6. data/.travis.yml +51 -0
  7. data/CHANGES.txt +698 -0
  8. data/Gemfile +13 -0
  9. data/Gemfile.noed25519 +12 -0
  10. data/ISSUE_TEMPLATE.md +30 -0
  11. data/LICENSE.txt +19 -0
  12. data/Manifest +132 -0
  13. data/README.md +287 -0
  14. data/Rakefile +105 -0
  15. data/THANKS.txt +110 -0
  16. data/appveyor.yml +58 -0
  17. data/lib/net/ssh/authentication/agent.rb +284 -0
  18. data/lib/net/ssh/authentication/certificate.rb +183 -0
  19. data/lib/net/ssh/authentication/constants.rb +20 -0
  20. data/lib/net/ssh/authentication/ed25519.rb +185 -0
  21. data/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  22. data/lib/net/ssh/authentication/key_manager.rb +297 -0
  23. data/lib/net/ssh/authentication/methods/abstract.rb +69 -0
  24. data/lib/net/ssh/authentication/methods/hostbased.rb +72 -0
  25. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +77 -0
  26. data/lib/net/ssh/authentication/methods/none.rb +34 -0
  27. data/lib/net/ssh/authentication/methods/password.rb +80 -0
  28. data/lib/net/ssh/authentication/methods/publickey.rb +95 -0
  29. data/lib/net/ssh/authentication/pageant.rb +497 -0
  30. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  31. data/lib/net/ssh/authentication/session.rb +163 -0
  32. data/lib/net/ssh/buffer.rb +434 -0
  33. data/lib/net/ssh/buffered_io.rb +202 -0
  34. data/lib/net/ssh/config.rb +406 -0
  35. data/lib/net/ssh/connection/channel.rb +695 -0
  36. data/lib/net/ssh/connection/constants.rb +33 -0
  37. data/lib/net/ssh/connection/event_loop.rb +123 -0
  38. data/lib/net/ssh/connection/keepalive.rb +59 -0
  39. data/lib/net/ssh/connection/session.rb +712 -0
  40. data/lib/net/ssh/connection/term.rb +180 -0
  41. data/lib/net/ssh/errors.rb +106 -0
  42. data/lib/net/ssh/key_factory.rb +218 -0
  43. data/lib/net/ssh/known_hosts.rb +264 -0
  44. data/lib/net/ssh/loggable.rb +62 -0
  45. data/lib/net/ssh/packet.rb +106 -0
  46. data/lib/net/ssh/prompt.rb +62 -0
  47. data/lib/net/ssh/proxy/command.rb +123 -0
  48. data/lib/net/ssh/proxy/errors.rb +16 -0
  49. data/lib/net/ssh/proxy/http.rb +98 -0
  50. data/lib/net/ssh/proxy/https.rb +50 -0
  51. data/lib/net/ssh/proxy/jump.rb +54 -0
  52. data/lib/net/ssh/proxy/socks4.rb +67 -0
  53. data/lib/net/ssh/proxy/socks5.rb +140 -0
  54. data/lib/net/ssh/service/forward.rb +426 -0
  55. data/lib/net/ssh/test/channel.rb +147 -0
  56. data/lib/net/ssh/test/extensions.rb +173 -0
  57. data/lib/net/ssh/test/kex.rb +46 -0
  58. data/lib/net/ssh/test/local_packet.rb +53 -0
  59. data/lib/net/ssh/test/packet.rb +101 -0
  60. data/lib/net/ssh/test/remote_packet.rb +40 -0
  61. data/lib/net/ssh/test/script.rb +180 -0
  62. data/lib/net/ssh/test/socket.rb +65 -0
  63. data/lib/net/ssh/test.rb +94 -0
  64. data/lib/net/ssh/transport/algorithms.rb +502 -0
  65. data/lib/net/ssh/transport/cipher_factory.rb +103 -0
  66. data/lib/net/ssh/transport/constants.rb +40 -0
  67. data/lib/net/ssh/transport/ctr.rb +115 -0
  68. data/lib/net/ssh/transport/hmac/abstract.rb +97 -0
  69. data/lib/net/ssh/transport/hmac/md5.rb +10 -0
  70. data/lib/net/ssh/transport/hmac/md5_96.rb +9 -0
  71. data/lib/net/ssh/transport/hmac/none.rb +13 -0
  72. data/lib/net/ssh/transport/hmac/ripemd160.rb +11 -0
  73. data/lib/net/ssh/transport/hmac/sha1.rb +11 -0
  74. data/lib/net/ssh/transport/hmac/sha1_96.rb +9 -0
  75. data/lib/net/ssh/transport/hmac/sha2_256.rb +11 -0
  76. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +9 -0
  77. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  78. data/lib/net/ssh/transport/hmac/sha2_512.rb +11 -0
  79. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +9 -0
  80. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  81. data/lib/net/ssh/transport/hmac.rb +47 -0
  82. data/lib/net/ssh/transport/identity_cipher.rb +57 -0
  83. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  84. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  85. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  86. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  87. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +37 -0
  88. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  89. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +122 -0
  90. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +72 -0
  91. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +11 -0
  92. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +39 -0
  93. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +21 -0
  94. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +21 -0
  95. data/lib/net/ssh/transport/kex.rb +31 -0
  96. data/lib/net/ssh/transport/key_expander.rb +30 -0
  97. data/lib/net/ssh/transport/openssl.rb +253 -0
  98. data/lib/net/ssh/transport/packet_stream.rb +280 -0
  99. data/lib/net/ssh/transport/server_version.rb +77 -0
  100. data/lib/net/ssh/transport/session.rb +354 -0
  101. data/lib/net/ssh/transport/state.rb +208 -0
  102. data/lib/net/ssh/verifiers/accept_new.rb +33 -0
  103. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  104. data/lib/net/ssh/verifiers/always.rb +58 -0
  105. data/lib/net/ssh/verifiers/never.rb +19 -0
  106. data/lib/net/ssh/version.rb +68 -0
  107. data/lib/net/ssh.rb +330 -0
  108. data/net-ssh-public_cert.pem +20 -0
  109. data/net-ssh.gemspec +44 -0
  110. data/support/ssh_tunnel_bug.rb +65 -0
  111. metadata +271 -0
@@ -0,0 +1,434 @@
1
+ require 'net/ssh/transport/openssl'
2
+
3
+ require 'net/ssh/authentication/certificate'
4
+ require 'net/ssh/authentication/ed25519_loader'
5
+
6
+ module Net
7
+ module SSH
8
+ # Net::SSH::Buffer is a flexible class for building and parsing binary
9
+ # data packets. It provides a stream-like interface for sequentially
10
+ # reading data items from the buffer, as well as a useful helper method
11
+ # for building binary packets given a signature.
12
+ #
13
+ # Writing to a buffer always appends to the end, regardless of where the
14
+ # read cursor is. Reading, on the other hand, always begins at the first
15
+ # byte of the buffer and increments the read cursor, with subsequent reads
16
+ # taking up where the last left off.
17
+ #
18
+ # As a consumer of the Net::SSH library, you will rarely come into contact
19
+ # with these buffer objects directly, but it could happen. Also, if you
20
+ # are ever implementing a protocol on top of SSH (e.g. SFTP), this buffer
21
+ # class can be quite handy.
22
+ class Buffer
23
+ # This is a convenience method for creating and populating a new buffer
24
+ # from a single command. The arguments must be even in length, with the
25
+ # first of each pair of arguments being a symbol naming the type of the
26
+ # data that follows. If the type is :raw, the value is written directly
27
+ # to the hash.
28
+ #
29
+ # b = Buffer.from(:byte, 1, :string, "hello", :raw, "\1\2\3\4")
30
+ # #-> "\1\0\0\0\5hello\1\2\3\4"
31
+ #
32
+ # The supported data types are:
33
+ #
34
+ # * :raw => write the next value verbatim (#write)
35
+ # * :int64 => write an 8-byte integer (#write_int64)
36
+ # * :long => write a 4-byte integer (#write_long)
37
+ # * :byte => write a single byte (#write_byte)
38
+ # * :string => write a 4-byte length followed by character data (#write_string)
39
+ # * :mstring => same as string, but caller cannot resuse the string, avoids potential duplication (#write_moved)
40
+ # * :bool => write a single byte, interpreted as a boolean (#write_bool)
41
+ # * :bignum => write an SSH-encoded bignum (#write_bignum)
42
+ # * :key => write an SSH-encoded key value (#write_key)
43
+ #
44
+ # Any of these, except for :raw, accepts an Array argument, to make it
45
+ # easier to write multiple values of the same type in a briefer manner.
46
+ def self.from(*args)
47
+ raise ArgumentError, "odd number of arguments given" unless args.length % 2 == 0
48
+
49
+ buffer = new
50
+ 0.step(args.length - 1, 2) do |index|
51
+ type = args[index]
52
+ value = args[index + 1]
53
+ if type == :raw
54
+ buffer.append(value.to_s)
55
+ elsif Array === value
56
+ buffer.send("write_#{type}", *value)
57
+ else
58
+ buffer.send("write_#{type}", value)
59
+ end
60
+ end
61
+
62
+ buffer
63
+ end
64
+
65
+ # exposes the raw content of the buffer
66
+ attr_reader :content
67
+
68
+ # the current position of the pointer in the buffer
69
+ attr_accessor :position
70
+
71
+ # Creates a new buffer, initialized to the given content. The position
72
+ # is initialized to the beginning of the buffer.
73
+ def initialize(content=String.new)
74
+ @content = content.to_s
75
+ @position = 0
76
+ end
77
+
78
+ # Returns the length of the buffer's content.
79
+ def length
80
+ @content.length
81
+ end
82
+
83
+ # Returns the number of bytes available to be read (e.g., how many bytes
84
+ # remain between the current position and the end of the buffer).
85
+ def available
86
+ length - position
87
+ end
88
+
89
+ # Returns a copy of the buffer's content.
90
+ def to_s
91
+ (@content || "").dup
92
+ end
93
+
94
+ # Compares the contents of the two buffers, returning +true+ only if they
95
+ # are identical in size and content.
96
+ def ==(buffer)
97
+ to_s == buffer.to_s
98
+ end
99
+
100
+ # Returns +true+ if the buffer contains no data (e.g., it is of zero length).
101
+ def empty?
102
+ @content.empty?
103
+ end
104
+
105
+ # Resets the pointer to the start of the buffer. Subsequent reads will
106
+ # begin at position 0.
107
+ def reset!
108
+ @position = 0
109
+ end
110
+
111
+ # Returns true if the pointer is at the end of the buffer. Subsequent
112
+ # reads will return nil, in this case.
113
+ def eof?
114
+ @position >= length
115
+ end
116
+
117
+ # Resets the buffer, making it empty. Also, resets the read position to
118
+ # 0.
119
+ def clear!
120
+ @content = String.new
121
+ @position = 0
122
+ end
123
+
124
+ # Consumes n bytes from the buffer, where n is the current position
125
+ # unless otherwise specified. This is useful for removing data from the
126
+ # buffer that has previously been read, when you are expecting more data
127
+ # to be appended. It helps to keep the size of buffers down when they
128
+ # would otherwise tend to grow without bound.
129
+ #
130
+ # Returns the buffer object itself.
131
+ def consume!(n=position)
132
+ if n >= length
133
+ # optimize for a fairly common case
134
+ clear!
135
+ elsif n > 0
136
+ @content = @content[n..-1] || String.new
137
+ @position -= n
138
+ @position = 0 if @position < 0
139
+ end
140
+ self
141
+ end
142
+
143
+ # Appends the given text to the end of the buffer. Does not alter the
144
+ # read position. Returns the buffer object itself.
145
+ def append(text)
146
+ @content << text
147
+ self
148
+ end
149
+
150
+ # Returns all text from the current pointer to the end of the buffer as
151
+ # a new Net::SSH::Buffer object.
152
+ def remainder_as_buffer
153
+ Buffer.new(@content[@position..-1])
154
+ end
155
+
156
+ # Reads all data up to and including the given pattern, which may be a
157
+ # String, Fixnum, or Regexp and is interpreted exactly as String#index
158
+ # does. Returns nil if nothing matches. Increments the position to point
159
+ # immediately after the pattern, if it does match. Returns all data up to
160
+ # and including the text that matched the pattern.
161
+ def read_to(pattern)
162
+ index = @content.index(pattern, @position) or return nil
163
+ length = case pattern
164
+ when String then pattern.length
165
+ when Integer then 1
166
+ when Regexp then $&.length
167
+ end
168
+ index && read(index + length)
169
+ end
170
+
171
+ # Reads and returns the next +count+ bytes from the buffer, starting from
172
+ # the read position. If +count+ is +nil+, this will return all remaining
173
+ # text in the buffer. This method will increment the pointer.
174
+ def read(count=nil)
175
+ count ||= length
176
+ count = length - @position if @position + count > length
177
+ @position += count
178
+ @content[@position - count, count]
179
+ end
180
+
181
+ # Reads (as #read) and returns the given number of bytes from the buffer,
182
+ # and then consumes (as #consume!) all data up to the new read position.
183
+ def read!(count=nil)
184
+ data = read(count)
185
+ consume!
186
+ data
187
+ end
188
+
189
+ # Calls block(self) until the buffer is empty, and returns all results.
190
+ def read_all(&block)
191
+ Enumerator.new { |e| e << yield(self) until eof? }.to_a
192
+ end
193
+
194
+ # Return the next 8 bytes as a 64-bit integer (in network byte order).
195
+ # Returns nil if there are less than 8 bytes remaining to be read in the
196
+ # buffer.
197
+ def read_int64
198
+ hi = read_long or return nil
199
+ lo = read_long or return nil
200
+ return (hi << 32) + lo
201
+ end
202
+
203
+ # Return the next four bytes as a long integer (in network byte order).
204
+ # Returns nil if there are less than 4 bytes remaining to be read in the
205
+ # buffer.
206
+ def read_long
207
+ b = read(4) or return nil
208
+ b.unpack("N").first
209
+ end
210
+
211
+ # Read and return the next byte in the buffer. Returns nil if called at
212
+ # the end of the buffer.
213
+ def read_byte
214
+ b = read(1) or return nil
215
+ b.getbyte(0)
216
+ end
217
+
218
+ # Read and return an SSH2-encoded string. The string starts with a long
219
+ # integer that describes the number of bytes remaining in the string.
220
+ # Returns nil if there are not enough bytes to satisfy the request.
221
+ def read_string
222
+ length = read_long or return nil
223
+ read(length)
224
+ end
225
+
226
+ # Read a single byte and convert it into a boolean, using 'C' rules
227
+ # (i.e., zero is false, non-zero is true).
228
+ def read_bool
229
+ b = read_byte or return nil
230
+ b != 0
231
+ end
232
+
233
+ # Read a bignum (OpenSSL::BN) from the buffer, in SSH2 format. It is
234
+ # essentially just a string, which is reinterpreted to be a bignum in
235
+ # binary format.
236
+ def read_bignum
237
+ data = read_string
238
+ return unless data
239
+
240
+ OpenSSL::BN.new(data, 2)
241
+ end
242
+
243
+ # Read a key from the buffer. The key will start with a string
244
+ # describing its type. The remainder of the key is defined by the
245
+ # type that was read.
246
+ def read_key
247
+ type = read_string
248
+ return (type ? read_keyblob(type) : nil)
249
+ end
250
+
251
+ def read_private_keyblob(type)
252
+ case type
253
+ when /^ssh-rsa$/
254
+ key = OpenSSL::PKey::RSA.new
255
+ n = read_bignum
256
+ e = read_bignum
257
+ d = read_bignum
258
+ iqmp = read_bignum
259
+ p = read_bignum
260
+ q = read_bignum
261
+ _unkown1 = read_bignum
262
+ _unkown2 = read_bignum
263
+ dmp1 = d % (p - 1)
264
+ dmq1 = d % (q - 1)
265
+ if key.respond_to?(:set_key)
266
+ key.set_key(n, e, d)
267
+ else
268
+ key.e = e
269
+ key.n = n
270
+ key.d = d
271
+ end
272
+ if key.respond_to?(:set_factors)
273
+ key.set_factors(p, q)
274
+ else
275
+ key.p = p
276
+ key.q = q
277
+ end
278
+ if key.respond_to?(:set_crt_params)
279
+ key.set_crt_params(dmp1, dmq1, iqmp)
280
+ else
281
+ key.dmp1 = dmp1
282
+ key.dmq1 = dmq1
283
+ key.iqmp = iqmp
284
+ end
285
+ key
286
+ else
287
+ raise Exception, "Cannot decode private key of type #{type}"
288
+ end
289
+ end
290
+
291
+ # Read a keyblob of the given type from the buffer, and return it as
292
+ # a key. Only RSA, DSA, and ECDSA keys are supported.
293
+ def read_keyblob(type)
294
+ case type
295
+ when /^(.*)-cert-v01@openssh\.com$/
296
+ key = Net::SSH::Authentication::Certificate.read_certblob(self, $1)
297
+ when /^ssh-dss$/
298
+ key = OpenSSL::PKey::DSA.new
299
+ if key.respond_to?(:set_pqg)
300
+ key.set_pqg(read_bignum, read_bignum, read_bignum)
301
+ else
302
+ key.p = read_bignum
303
+ key.q = read_bignum
304
+ key.g = read_bignum
305
+ end
306
+ if key.respond_to?(:set_key)
307
+ key.set_key(read_bignum, nil)
308
+ else
309
+ key.pub_key = read_bignum
310
+ end
311
+ when /^ssh-rsa$/
312
+ key = OpenSSL::PKey::RSA.new
313
+ if key.respond_to?(:set_key)
314
+ e = read_bignum
315
+ n = read_bignum
316
+ key.set_key(n, e, nil)
317
+ else
318
+ key.e = read_bignum
319
+ key.n = read_bignum
320
+ end
321
+ when /^ssh-ed25519$/
322
+ Net::SSH::Authentication::ED25519Loader.raiseUnlessLoaded("unsupported key type `#{type}'")
323
+ key = Net::SSH::Authentication::ED25519::PubKey.read_keyblob(self)
324
+ when /^ecdsa\-sha2\-(\w*)$/
325
+ key = OpenSSL::PKey::EC.read_keyblob($1, self)
326
+ else
327
+ raise NotImplementedError, "unsupported key type `#{type}'"
328
+ end
329
+
330
+ return key
331
+ end
332
+
333
+ # Reads the next string from the buffer, and returns a new Buffer
334
+ # object that wraps it.
335
+ def read_buffer
336
+ Buffer.new(read_string)
337
+ end
338
+
339
+ # Writes the given data literally into the string. Does not alter the
340
+ # read position. Returns the buffer object.
341
+ def write(*data)
342
+ data.each { |datum| @content << datum.dup.force_encoding('BINARY') }
343
+ self
344
+ end
345
+
346
+ # Optimized version of write where the caller gives up ownership of string
347
+ # to the method. This way we can mutate the string.
348
+ def write_moved(string)
349
+ @content <<
350
+ if string.frozen?
351
+ string.dup.force_encoding('BINARY')
352
+ else
353
+ string.force_encoding('BINARY')
354
+ end
355
+ self
356
+ end
357
+
358
+ # Writes each argument to the buffer as a network-byte-order-encoded
359
+ # 64-bit integer (8 bytes). Does not alter the read position. Returns the
360
+ # buffer object.
361
+ def write_int64(*n)
362
+ n.each do |i|
363
+ hi = (i >> 32) & 0xFFFFFFFF
364
+ lo = i & 0xFFFFFFFF
365
+ @content << [hi, lo].pack("N2")
366
+ end
367
+ self
368
+ end
369
+
370
+ # Writes each argument to the buffer as a network-byte-order-encoded
371
+ # long (4-byte) integer. Does not alter the read position. Returns the
372
+ # buffer object.
373
+ def write_long(*n)
374
+ @content << n.pack("N*")
375
+ self
376
+ end
377
+
378
+ # Writes each argument to the buffer as a byte. Does not alter the read
379
+ # position. Returns the buffer object.
380
+ def write_byte(*n)
381
+ n.each { |b| @content << b.chr }
382
+ self
383
+ end
384
+
385
+ # Writes each argument to the buffer as an SSH2-encoded string. Each
386
+ # string is prefixed by its length, encoded as a 4-byte long integer.
387
+ # Does not alter the read position. Returns the buffer object.
388
+ def write_string(*text)
389
+ text.each do |string|
390
+ s = string.to_s
391
+ write_long(s.bytesize)
392
+ write(s)
393
+ end
394
+ self
395
+ end
396
+
397
+ # Writes each argument to the buffer as an SSH2-encoded string. Each
398
+ # string is prefixed by its length, encoded as a 4-byte long integer.
399
+ # Does not alter the read position. Returns the buffer object.
400
+ # Might alter arguments see write_moved
401
+ def write_mstring(*text)
402
+ text.each do |string|
403
+ s = string.to_s
404
+ write_long(s.bytesize)
405
+ write_moved(s)
406
+ end
407
+ self
408
+ end
409
+
410
+ # Writes each argument to the buffer as a (C-style) boolean, with 1
411
+ # meaning true, and 0 meaning false. Does not alter the read position.
412
+ # Returns the buffer object.
413
+ def write_bool(*b)
414
+ b.each { |v| @content << (v ? "\1" : "\0") }
415
+ self
416
+ end
417
+
418
+ # Writes each argument to the buffer as a bignum (SSH2-style). No
419
+ # checking is done to ensure that the arguments are, in fact, bignums.
420
+ # Does not alter the read position. Returns the buffer object.
421
+ def write_bignum(*n)
422
+ @content << n.map { |b| b.to_ssh }.join
423
+ self
424
+ end
425
+
426
+ # Writes the given arguments to the buffer as SSH2-encoded keys. Does not
427
+ # alter the read position. Returns the buffer object.
428
+ def write_key(*key)
429
+ key.each { |k| append(k.to_blob) }
430
+ self
431
+ end
432
+ end
433
+ end
434
+ end;
@@ -0,0 +1,202 @@
1
+ require 'net/ssh/buffer'
2
+ require 'net/ssh/loggable'
3
+
4
+ module Net
5
+ module SSH
6
+ # This module is used to extend sockets and other IO objects, to allow
7
+ # them to be buffered for both read and write. This abstraction makes it
8
+ # quite easy to write a select-based event loop
9
+ # (see Net::SSH::Connection::Session#listen_to).
10
+ #
11
+ # The general idea is that instead of calling #read directly on an IO that
12
+ # has been extended with this module, you call #fill (to add pending input
13
+ # to the internal read buffer), and then #read_available (to read from that
14
+ # buffer). Likewise, you don't call #write directly, you call #enqueue to
15
+ # add data to the write buffer, and then #send_pending or #wait_for_pending_sends
16
+ # to actually send the data across the wire.
17
+ #
18
+ # In this way you can easily use the object as an argument to IO.select,
19
+ # calling #fill when it is available for read, or #send_pending when it is
20
+ # available for write, and then call #enqueue and #read_available during
21
+ # the idle times.
22
+ #
23
+ # socket = TCPSocket.new(address, port)
24
+ # socket.extend(Net::SSH::BufferedIo)
25
+ #
26
+ # ssh.listen_to(socket)
27
+ #
28
+ # ssh.loop do
29
+ # if socket.available > 0
30
+ # puts socket.read_available
31
+ # socket.enqueue("response\n")
32
+ # end
33
+ # end
34
+ #
35
+ # Note that this module must be used to extend an instance, and should not
36
+ # be included in a class. If you do want to use it via an include, then you
37
+ # must make sure to invoke the private #initialize_buffered_io method in
38
+ # your class' #initialize method:
39
+ #
40
+ # class Foo < IO
41
+ # include Net::SSH::BufferedIo
42
+ #
43
+ # def initialize
44
+ # initialize_buffered_io
45
+ # # ...
46
+ # end
47
+ # end
48
+ module BufferedIo
49
+ include Loggable
50
+
51
+ # Called when the #extend is called on an object, with this module as the
52
+ # argument. It ensures that the modules instance variables are all properly
53
+ # initialized.
54
+ def self.extended(object) #:nodoc:
55
+ # need to use __send__ because #send is overridden in Socket
56
+ object.__send__(:initialize_buffered_io)
57
+ end
58
+
59
+ # Tries to read up to +n+ bytes of data from the remote end, and appends
60
+ # the data to the input buffer. It returns the number of bytes read, or 0
61
+ # if no data was available to be read.
62
+ def fill(n=8192)
63
+ input.consume!
64
+ data = recv(n)
65
+ debug { "read #{data.length} bytes" }
66
+ input.append(data)
67
+ return data.length
68
+ rescue EOFError => e
69
+ @input_errors << e
70
+ return 0
71
+ end
72
+
73
+ # Read up to +length+ bytes from the input buffer. If +length+ is nil,
74
+ # all available data is read from the buffer. (See #available.)
75
+ def read_available(length=nil)
76
+ input.read(length || available)
77
+ end
78
+
79
+ # Returns the number of bytes available to be read from the input buffer.
80
+ # (See #read_available.)
81
+ def available
82
+ input.available
83
+ end
84
+
85
+ # Enqueues data in the output buffer, to be written when #send_pending
86
+ # is called. Note that the data is _not_ sent immediately by this method!
87
+ def enqueue(data)
88
+ output.append(data)
89
+ end
90
+
91
+ # Returns +true+ if there is data waiting in the output buffer, and
92
+ # +false+ otherwise.
93
+ def pending_write?
94
+ output.length > 0
95
+ end
96
+
97
+ # Sends as much of the pending output as possible. Returns +true+ if any
98
+ # data was sent, and +false+ otherwise.
99
+ def send_pending
100
+ if output.length > 0
101
+ sent = send(output.to_s, 0)
102
+ debug { "sent #{sent} bytes" }
103
+ output.consume!(sent)
104
+ return sent > 0
105
+ else
106
+ return false
107
+ end
108
+ end
109
+
110
+ # Calls #send_pending repeatedly, if necessary, blocking until the output
111
+ # buffer is empty.
112
+ def wait_for_pending_sends
113
+ send_pending
114
+ while output.length > 0
115
+ result = IO.select(nil, [self]) or next
116
+ next unless result[1].any?
117
+
118
+ send_pending
119
+ end
120
+ end
121
+
122
+ public # these methods are primarily for use in tests
123
+
124
+ def write_buffer #:nodoc:
125
+ output.to_s
126
+ end
127
+
128
+ def read_buffer #:nodoc:
129
+ input.to_s
130
+ end
131
+
132
+ private
133
+
134
+ #--
135
+ # Can't use attr_reader here (after +private+) without incurring the
136
+ # wrath of "ruby -w". We hates it.
137
+ #++
138
+
139
+ def input; @input; end
140
+
141
+ def output; @output; end
142
+
143
+ # Initializes the intput and output buffers for this object. This method
144
+ # is called automatically when the module is mixed into an object via
145
+ # Object#extend (see Net::SSH::BufferedIo.extended), but must be called
146
+ # explicitly in the +initialize+ method of any class that uses
147
+ # Module#include to add this module.
148
+ def initialize_buffered_io
149
+ @input = Net::SSH::Buffer.new
150
+ @input_errors = []
151
+ @output = Net::SSH::Buffer.new
152
+ @output_errors = []
153
+ end
154
+ end
155
+
156
+ # Fixes for two issues by Miklós Fazekas:
157
+ #
158
+ # * if client closes a forwarded connection, but the server is
159
+ # reading, net-ssh terminates with IOError socket closed.
160
+ # * if client force closes (RST) a forwarded connection, but
161
+ # server is reading, net-ssh terminates with [an exception]
162
+ #
163
+ # See:
164
+ #
165
+ # http://net-ssh.lighthouseapp.com/projects/36253/tickets/7
166
+ # http://github.com/net-ssh/net-ssh/tree/portfwfix
167
+ #
168
+ module ForwardedBufferedIo
169
+ def fill(n=8192)
170
+ begin
171
+ super(n)
172
+ rescue Errno::ECONNRESET => e
173
+ debug { "connection was reset => shallowing exception:#{e}" }
174
+ return 0
175
+ rescue IOError => e
176
+ if e.message =~ /closed/ then
177
+ debug { "connection was reset => shallowing exception:#{e}" }
178
+ return 0
179
+ else
180
+ raise
181
+ end
182
+ end
183
+ end
184
+
185
+ def send_pending
186
+ begin
187
+ super
188
+ rescue Errno::ECONNRESET => e
189
+ debug { "connection was reset => shallowing exception:#{e}" }
190
+ return 0
191
+ rescue IOError => e
192
+ if e.message =~ /closed/ then
193
+ debug { "connection was reset => shallowing exception:#{e}" }
194
+ return 0
195
+ else
196
+ raise
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end