Class Net::SSH::Buffer
In: lib/net/ssh/buffer.rb
lib/net/ssh/buffer.rb
Parent: Object

Net::SSH::Buffer is a flexible class for building and parsing binary data packets. It provides a stream-like interface for sequentially reading data items from the buffer, as well as a useful helper method for building binary packets given a signature.

Writing to a buffer always appends to the end, regardless of where the read cursor is. Reading, on the other hand, always begins at the first byte of the buffer and increments the read cursor, with subsequent reads taking up where the last left off.

As a consumer of the Net::SSH library, you will rarely come into contact with these buffer objects directly, but it could happen. Also, if you are ever implementing a protocol on top of SSH (e.g. SFTP), this buffer class can be quite handy.

Methods

Attributes

content  [R]  exposes the raw content of the buffer
content  [R]  exposes the raw content of the buffer
position  [RW]  the current position of the pointer in the buffer
position  [RW]  the current position of the pointer in the buffer

Public Class methods

This is a convenience method for creating and populating a new buffer from a single command. The arguments must be even in length, with the first of each pair of arguments being a symbol naming the type of the data that follows. If the type is :raw, the value is written directly to the hash.

  b = Buffer.from(:byte, 1, :string, "hello", :raw, "\1\2\3\4")
  #-> "\1\0\0\0\5hello\1\2\3\4"

The supported data types are:

Any of these, except for :raw, accepts an Array argument, to make it easier to write multiple values of the same type in a briefer manner.

[Source]

    # File lib/net/ssh/buffer.rb, line 42
42:     def self.from(*args)
43:       raise ArgumentError, "odd number of arguments given" unless args.length % 2 == 0
44: 
45:       buffer = new
46:       0.step(args.length-1, 2) do |index|
47:         type = args[index]
48:         value = args[index+1]
49:         if type == :raw
50:           buffer.append(value.to_s)
51:         elsif Array === value
52:           buffer.send("write_#{type}", *value)
53:         else
54:           buffer.send("write_#{type}", value)
55:         end
56:       end
57: 
58:       buffer
59:     end

This is a convenience method for creating and populating a new buffer from a single command. The arguments must be even in length, with the first of each pair of arguments being a symbol naming the type of the data that follows. If the type is :raw, the value is written directly to the hash.

  b = Buffer.from(:byte, 1, :string, "hello", :raw, "\1\2\3\4")
  #-> "\1\0\0\0\5hello\1\2\3\4"

The supported data types are:

Any of these, except for :raw, accepts an Array argument, to make it easier to write multiple values of the same type in a briefer manner.

[Source]

    # File lib/net/ssh/buffer.rb, line 42
42:     def self.from(*args)
43:       raise ArgumentError, "odd number of arguments given" unless args.length % 2 == 0
44: 
45:       buffer = new
46:       0.step(args.length-1, 2) do |index|
47:         type = args[index]
48:         value = args[index+1]
49:         if type == :raw
50:           buffer.append(value.to_s)
51:         elsif Array === value
52:           buffer.send("write_#{type}", *value)
53:         else
54:           buffer.send("write_#{type}", value)
55:         end
56:       end
57: 
58:       buffer
59:     end

Creates a new buffer, initialized to the given content. The position is initialized to the beginning of the buffer.

[Source]

    # File lib/net/ssh/buffer.rb, line 69
69:     def initialize(content="")
70:       @content = content.to_s
71:       @position = 0
72:     end

Creates a new buffer, initialized to the given content. The position is initialized to the beginning of the buffer.

[Source]

    # File lib/net/ssh/buffer.rb, line 69
69:     def initialize(content="")
70:       @content = content.to_s
71:       @position = 0
72:     end

Public Instance methods

Compares the contents of the two buffers, returning true only if they are identical in size and content.

[Source]

    # File lib/net/ssh/buffer.rb, line 92
92:     def ==(buffer)
93:       to_s == buffer.to_s
94:     end

Compares the contents of the two buffers, returning true only if they are identical in size and content.

[Source]

    # File lib/net/ssh/buffer.rb, line 92
92:     def ==(buffer)
93:       to_s == buffer.to_s
94:     end

Appends the given text to the end of the buffer. Does not alter the read position. Returns the buffer object itself.

[Source]

     # File lib/net/ssh/buffer.rb, line 141
141:     def append(text)
142:       @content << text
143:       self
144:     end

Appends the given text to the end of the buffer. Does not alter the read position. Returns the buffer object itself.

[Source]

     # File lib/net/ssh/buffer.rb, line 141
141:     def append(text)
142:       @content << text
143:       self
144:     end

Returns the number of bytes available to be read (e.g., how many bytes remain between the current position and the end of the buffer).

[Source]

    # File lib/net/ssh/buffer.rb, line 81
81:     def available
82:       length - position
83:     end

Returns the number of bytes available to be read (e.g., how many bytes remain between the current position and the end of the buffer).

[Source]

    # File lib/net/ssh/buffer.rb, line 81
81:     def available
82:       length - position
83:     end

Resets the buffer, making it empty. Also, resets the read position to 0.

[Source]

     # File lib/net/ssh/buffer.rb, line 115
115:     def clear!
116:       @content = ""
117:       @position = 0
118:     end

Resets the buffer, making it empty. Also, resets the read position to 0.

[Source]

     # File lib/net/ssh/buffer.rb, line 115
115:     def clear!
116:       @content = ""
117:       @position = 0
118:     end

Consumes n bytes from the buffer, where n is the current position unless otherwise specified. This is useful for removing data from the buffer that has previously been read, when you are expecting more data to be appended. It helps to keep the size of buffers down when they would otherwise tend to grow without bound.

Returns the buffer object itself.

[Source]

     # File lib/net/ssh/buffer.rb, line 127
127:     def consume!(n=position)
128:       if n >= length
129:         # optimize for a fairly common case
130:         clear!
131:       elsif n > 0
132:         @content = @content[n..-1] || ""
133:         @position -= n
134:         @position = 0 if @position < 0
135:       end
136:       self
137:     end

Consumes n bytes from the buffer, where n is the current position unless otherwise specified. This is useful for removing data from the buffer that has previously been read, when you are expecting more data to be appended. It helps to keep the size of buffers down when they would otherwise tend to grow without bound.

Returns the buffer object itself.

[Source]

     # File lib/net/ssh/buffer.rb, line 127
127:     def consume!(n=position)
128:       if n >= length
129:         # optimize for a fairly common case
130:         clear!
131:       elsif n > 0
132:         @content = @content[n..-1] || ""
133:         @position -= n
134:         @position = 0 if @position < 0
135:       end
136:       self
137:     end

Returns true if the buffer contains no data (e.g., it is of zero length).

[Source]

    # File lib/net/ssh/buffer.rb, line 97
97:     def empty?
98:       @content.empty?
99:     end

Returns true if the buffer contains no data (e.g., it is of zero length).

[Source]

    # File lib/net/ssh/buffer.rb, line 97
97:     def empty?
98:       @content.empty?
99:     end

Returns true if the pointer is at the end of the buffer. Subsequent reads will return nil, in this case.

[Source]

     # File lib/net/ssh/buffer.rb, line 109
109:     def eof?
110:       @position >= length
111:     end

Returns true if the pointer is at the end of the buffer. Subsequent reads will return nil, in this case.

[Source]

     # File lib/net/ssh/buffer.rb, line 109
109:     def eof?
110:       @position >= length
111:     end

Returns the length of the buffer‘s content.

[Source]

    # File lib/net/ssh/buffer.rb, line 75
75:     def length
76:       @content.length
77:     end

Returns the length of the buffer‘s content.

[Source]

    # File lib/net/ssh/buffer.rb, line 75
75:     def length
76:       @content.length
77:     end

Reads and returns the next count bytes from the buffer, starting from the read position. If count is nil, this will return all remaining text in the buffer. This method will increment the pointer.

[Source]

     # File lib/net/ssh/buffer.rb, line 170
170:     def read(count=nil)
171:       count ||= length
172:       count = length - @position if @position + count > length
173:       @position += count
174:       @content[@position-count, count]
175:     end

Reads and returns the next count bytes from the buffer, starting from the read position. If count is nil, this will return all remaining text in the buffer. This method will increment the pointer.

[Source]

     # File lib/net/ssh/buffer.rb, line 170
170:     def read(count=nil)
171:       count ||= length
172:       count = length - @position if @position + count > length
173:       @position += count
174:       @content[@position-count, count]
175:     end

Reads (as read) and returns the given number of bytes from the buffer, and then consumes (as consume!) all data up to the new read position.

[Source]

     # File lib/net/ssh/buffer.rb, line 179
179:     def read!(count=nil)
180:       data = read(count)
181:       consume!
182:       data
183:     end

Reads (as read) and returns the given number of bytes from the buffer, and then consumes (as consume!) all data up to the new read position.

[Source]

     # File lib/net/ssh/buffer.rb, line 179
179:     def read!(count=nil)
180:       data = read(count)
181:       consume!
182:       data
183:     end

Read a bignum (OpenSSL::BN) from the buffer, in SSH2 format. It is essentially just a string, which is reinterpreted to be a bignum in binary format.

[Source]

     # File lib/net/ssh/buffer.rb, line 227
227:     def read_bignum
228:       data = read_string
229:       return unless data
230:       OpenSSL::BN.new(data, 2)
231:     end

Read a bignum (OpenSSL::BN) from the buffer, in SSH2 format. It is essentially just a string, which is reinterpreted to be a bignum in binary format.

[Source]

     # File lib/net/ssh/buffer.rb, line 227
227:     def read_bignum
228:       data = read_string
229:       return unless data
230:       OpenSSL::BN.new(data, 2)
231:     end

Read a single byte and convert it into a boolean, using ‘C’ rules (i.e., zero is false, non-zero is true).

[Source]

     # File lib/net/ssh/buffer.rb, line 219
219:     def read_bool
220:       b = read_byte or return nil
221:       b != 0
222:     end

Read a single byte and convert it into a boolean, using ‘C’ rules (i.e., zero is false, non-zero is true).

[Source]

     # File lib/net/ssh/buffer.rb, line 219
219:     def read_bool
220:       b = read_byte or return nil
221:       b != 0
222:     end

Reads the next string from the buffer, and returns a new Buffer object that wraps it.

[Source]

     # File lib/net/ssh/buffer.rb, line 266
266:     def read_buffer
267:       Buffer.new(read_string)
268:     end

Reads the next string from the buffer, and returns a new Buffer object that wraps it.

[Source]

     # File lib/net/ssh/buffer.rb, line 266
266:     def read_buffer
267:       Buffer.new(read_string)
268:     end

Read and return the next byte in the buffer. Returns nil if called at the end of the buffer.

[Source]

     # File lib/net/ssh/buffer.rb, line 204
204:     def read_byte
205:       b = read(1) or return nil
206:       b[0]
207:     end

Read and return the next byte in the buffer. Returns nil if called at the end of the buffer.

[Source]

     # File lib/net/ssh/buffer.rb, line 204
204:     def read_byte
205:       b = read(1) or return nil
206:       b[0]
207:     end

Return the next 8 bytes as a 64-bit integer (in network byte order). Returns nil if there are less than 8 bytes remaining to be read in the buffer.

[Source]

     # File lib/net/ssh/buffer.rb, line 188
188:     def read_int64
189:       hi = read_long or return nil
190:       lo = read_long or return nil
191:       return (hi << 32) + lo
192:     end

Return the next 8 bytes as a 64-bit integer (in network byte order). Returns nil if there are less than 8 bytes remaining to be read in the buffer.

[Source]

     # File lib/net/ssh/buffer.rb, line 188
188:     def read_int64
189:       hi = read_long or return nil
190:       lo = read_long or return nil
191:       return (hi << 32) + lo
192:     end

Read a key from the buffer. The key will start with a string describing its type. The remainder of the key is defined by the type that was read.

[Source]

     # File lib/net/ssh/buffer.rb, line 236
236:     def read_key
237:       type = read_string
238:       return (type ? read_keyblob(type) : nil)
239:     end

Read a key from the buffer. The key will start with a string describing its type. The remainder of the key is defined by the type that was read.

[Source]

     # File lib/net/ssh/buffer.rb, line 236
236:     def read_key
237:       type = read_string
238:       return (type ? read_keyblob(type) : nil)
239:     end

Read a keyblob of the given type from the buffer, and return it as a key. Only RSA and DSA keys are supported.

[Source]

     # File lib/net/ssh/buffer.rb, line 243
243:     def read_keyblob(type)
244:       case type
245:         when "ssh-dss"
246:           key = OpenSSL::PKey::DSA.new
247:           key.p = read_bignum
248:           key.q = read_bignum
249:           key.g = read_bignum
250:           key.pub_key = read_bignum
251: 
252:         when "ssh-rsa"
253:           key = OpenSSL::PKey::RSA.new
254:           key.e = read_bignum
255:           key.n = read_bignum
256: 
257:         else
258:           raise NotImplementedError, "unsupported key type `#{type}'"
259:       end
260: 
261:       return key
262:     end

Read a keyblob of the given type from the buffer, and return it as a key. Only RSA and DSA keys are supported.

[Source]

     # File lib/net/ssh/buffer.rb, line 243
243:     def read_keyblob(type)
244:       case type
245:         when "ssh-dss"
246:           key = OpenSSL::PKey::DSA.new
247:           key.p = read_bignum
248:           key.q = read_bignum
249:           key.g = read_bignum
250:           key.pub_key = read_bignum
251: 
252:         when "ssh-rsa"
253:           key = OpenSSL::PKey::RSA.new
254:           key.e = read_bignum
255:           key.n = read_bignum
256: 
257:         else
258:           raise NotImplementedError, "unsupported key type `#{type}'"
259:       end
260: 
261:       return key
262:     end

Return the next four bytes as a long integer (in network byte order). Returns nil if there are less than 4 bytes remaining to be read in the buffer.

[Source]

     # File lib/net/ssh/buffer.rb, line 197
197:     def read_long
198:       b = read(4) or return nil
199:       b.unpack("N").first
200:     end

Return the next four bytes as a long integer (in network byte order). Returns nil if there are less than 4 bytes remaining to be read in the buffer.

[Source]

     # File lib/net/ssh/buffer.rb, line 197
197:     def read_long
198:       b = read(4) or return nil
199:       b.unpack("N").first
200:     end

Read and return an SSH2-encoded string. The string starts with a long integer that describes the number of bytes remaining in the string. Returns nil if there are not enough bytes to satisfy the request.

[Source]

     # File lib/net/ssh/buffer.rb, line 212
212:     def read_string
213:       length = read_long or return nil
214:       read(length)
215:     end

Read and return an SSH2-encoded string. The string starts with a long integer that describes the number of bytes remaining in the string. Returns nil if there are not enough bytes to satisfy the request.

[Source]

     # File lib/net/ssh/buffer.rb, line 212
212:     def read_string
213:       length = read_long or return nil
214:       read(length)
215:     end

Reads all data up to and including the given pattern, which may be a String, Fixnum, or Regexp and is interpreted exactly as String#index does. Returns nil if nothing matches. Increments the position to point immediately after the pattern, if it does match. Returns all data up to and including the text that matched the pattern.

[Source]

     # File lib/net/ssh/buffer.rb, line 157
157:     def read_to(pattern)
158:       index = @content.index(pattern, @position) or return nil
159:       length = case pattern
160:         when String then pattern.length
161:         when Fixnum then 1
162:         when Regexp then $&.length
163:       end
164:       index && read(index+length)
165:     end

Reads all data up to and including the given pattern, which may be a String, Fixnum, or Regexp and is interpreted exactly as String#index does. Returns nil if nothing matches. Increments the position to point immediately after the pattern, if it does match. Returns all data up to and including the text that matched the pattern.

[Source]

     # File lib/net/ssh/buffer.rb, line 157
157:     def read_to(pattern)
158:       index = @content.index(pattern, @position) or return nil
159:       length = case pattern
160:         when String then pattern.length
161:         when Fixnum then 1
162:         when Regexp then $&.length
163:       end
164:       index && read(index+length)
165:     end

Returns all text from the current pointer to the end of the buffer as a new Net::SSH::Buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 148
148:     def remainder_as_buffer
149:       Buffer.new(@content[@position..-1])
150:     end

Returns all text from the current pointer to the end of the buffer as a new Net::SSH::Buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 148
148:     def remainder_as_buffer
149:       Buffer.new(@content[@position..-1])
150:     end

Resets the pointer to the start of the buffer. Subsequent reads will begin at position 0.

[Source]

     # File lib/net/ssh/buffer.rb, line 103
103:     def reset!
104:       @position = 0
105:     end

Resets the pointer to the start of the buffer. Subsequent reads will begin at position 0.

[Source]

     # File lib/net/ssh/buffer.rb, line 103
103:     def reset!
104:       @position = 0
105:     end

Returns a copy of the buffer‘s content.

[Source]

    # File lib/net/ssh/buffer.rb, line 86
86:     def to_s
87:       (@content || "").dup
88:     end

Returns a copy of the buffer‘s content.

[Source]

    # File lib/net/ssh/buffer.rb, line 86
86:     def to_s
87:       (@content || "").dup
88:     end

Writes the given data literally into the string. Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 272
272:     def write(*data)
273:       data.each { |datum| @content << datum }
274:       self
275:     end

Writes the given data literally into the string. Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 272
272:     def write(*data)
273:       data.each { |datum| @content << datum }
274:       self
275:     end

Writes each argument to the buffer as a bignum (SSH2-style). No checking is done to ensure that the arguments are, in fact, bignums. Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 327
327:     def write_bignum(*n)
328:       @content << n.map { |b| b.to_ssh }.join
329:       self
330:     end

Writes each argument to the buffer as a bignum (SSH2-style). No checking is done to ensure that the arguments are, in fact, bignums. Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 327
327:     def write_bignum(*n)
328:       @content << n.map { |b| b.to_ssh }.join
329:       self
330:     end

Writes each argument to the buffer as a (C-style) boolean, with 1 meaning true, and 0 meaning false. Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 319
319:     def write_bool(*b)
320:       b.each { |v| @content << (v ? "\1" : "\0") }
321:       self
322:     end

Writes each argument to the buffer as a (C-style) boolean, with 1 meaning true, and 0 meaning false. Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 319
319:     def write_bool(*b)
320:       b.each { |v| @content << (v ? "\1" : "\0") }
321:       self
322:     end

Writes each argument to the buffer as a byte. Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 299
299:     def write_byte(*n)
300:       n.each { |b| @content << b.chr }
301:       self
302:     end

Writes each argument to the buffer as a byte. Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 299
299:     def write_byte(*n)
300:       n.each { |b| @content << b.chr }
301:       self
302:     end

Writes each argument to the buffer as a network-byte-order-encoded 64-bit integer (8 bytes). Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 280
280:     def write_int64(*n)
281:       n.each do |i|
282:         hi = (i >> 32) & 0xFFFFFFFF
283:         lo = i & 0xFFFFFFFF
284:         @content << [hi, lo].pack("N2")
285:       end
286:       self
287:     end

Writes each argument to the buffer as a network-byte-order-encoded 64-bit integer (8 bytes). Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 280
280:     def write_int64(*n)
281:       n.each do |i|
282:         hi = (i >> 32) & 0xFFFFFFFF
283:         lo = i & 0xFFFFFFFF
284:         @content << [hi, lo].pack("N2")
285:       end
286:       self
287:     end

Writes the given arguments to the buffer as SSH2-encoded keys. Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 334
334:     def write_key(*key)
335:       key.each { |k| append(k.to_blob) }
336:       self
337:     end

Writes the given arguments to the buffer as SSH2-encoded keys. Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 334
334:     def write_key(*key)
335:       key.each { |k| append(k.to_blob) }
336:       self
337:     end

Writes each argument to the buffer as a network-byte-order-encoded long (4-byte) integer. Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 292
292:     def write_long(*n)
293:       @content << n.pack("N*")
294:       self
295:     end

Writes each argument to the buffer as a network-byte-order-encoded long (4-byte) integer. Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 292
292:     def write_long(*n)
293:       @content << n.pack("N*")
294:       self
295:     end

Writes each argument to the buffer as an SSH2-encoded string. Each string is prefixed by its length, encoded as a 4-byte long integer. Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 307
307:     def write_string(*text)
308:       text.each do |string|
309:         s = string.to_s
310:         write_long(s.length)
311:         write(s)
312:       end
313:       self
314:     end

Writes each argument to the buffer as an SSH2-encoded string. Each string is prefixed by its length, encoded as a 4-byte long integer. Does not alter the read position. Returns the buffer object.

[Source]

     # File lib/net/ssh/buffer.rb, line 307
307:     def write_string(*text)
308:       text.each do |string|
309:         s = string.to_s
310:         write_long(s.length)
311:         write(s)
312:       end
313:       self
314:     end

[Validate]