Class: Orthrus::SSH::Buffer
- Inherits:
-
Object
- Object
- Orthrus::SSH::Buffer
- Defined in:
- lib/orthrus/ssh/buffer.rb
Overview
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.
Instance Attribute Summary collapse
-
#content ⇒ Object
readonly
exposes the raw content of the buffer.
-
#position ⇒ Object
the current position of the pointer in the buffer.
Class Method Summary collapse
-
.from(*args) ⇒ Object
This is a convenience method for creating and populating a new buffer from a single command.
Instance Method Summary collapse
-
#==(buffer) ⇒ Object
Compares the contents of the two buffers, returning
true
only if they are identical in size and content. -
#append(text) ⇒ Object
Appends the given text to the end of the buffer.
-
#available ⇒ Object
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).
-
#clear! ⇒ Object
Resets the buffer, making it empty.
-
#consume!(n = position) ⇒ Object
Consumes n bytes from the buffer, where n is the current position unless otherwise specified.
-
#empty? ⇒ Boolean
Returns
true
if the buffer contains no data (e.g., it is of zero length). -
#eof? ⇒ Boolean
Returns true if the pointer is at the end of the buffer.
-
#initialize(content = "") ⇒ Buffer
constructor
Creates a new buffer, initialized to the given content.
- #inspect ⇒ Object
-
#length ⇒ Object
Returns the length of the buffer’s content.
-
#read(count = nil) ⇒ Object
Reads and returns the next
count
bytes from the buffer, starting from the read position. -
#read!(count = nil) ⇒ Object
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.
-
#read_bignum ⇒ Object
Read a bignum (OpenSSL::BN) from the buffer, in SSH2 format.
-
#read_bool ⇒ Object
Read a single byte and convert it into a boolean, using ‘C’ rules (i.e., zero is false, non-zero is true).
-
#read_buffer ⇒ Object
Reads the next string from the buffer, and returns a new Buffer object that wraps it.
-
#read_byte ⇒ Object
Read and return the next byte in the buffer.
-
#read_int64 ⇒ Object
Return the next 8 bytes as a 64-bit integer (in network byte order).
-
#read_key ⇒ Object
Read a key from the buffer.
-
#read_keyblob(type) ⇒ Object
Read a keyblob of the given type from the buffer, and return it as a key.
-
#read_long ⇒ Object
Return the next four bytes as a long integer (in network byte order).
-
#read_string ⇒ Object
Read and return an SSH2-encoded string.
-
#read_to(pattern) ⇒ Object
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.
-
#remainder_as_buffer ⇒ Object
Returns all text from the current pointer to the end of the buffer as a new Net::SSH::Buffer object.
-
#reset! ⇒ Object
Resets the pointer to the start of the buffer.
-
#to_s ⇒ Object
Returns a copy of the buffer’s content.
-
#write(*data) ⇒ Object
Writes the given data literally into the string.
-
#write_bignum(*n) ⇒ Object
Writes each argument to the buffer as a bignum (SSH2-style).
-
#write_bool(*b) ⇒ Object
Writes each argument to the buffer as a (C-style) boolean, with 1 meaning true, and 0 meaning false.
-
#write_byte(*n) ⇒ Object
Writes each argument to the buffer as a byte.
-
#write_int64(*n) ⇒ Object
Writes each argument to the buffer as a network-byte-order-encoded 64-bit integer (8 bytes).
-
#write_key(*key) ⇒ Object
Writes the given arguments to the buffer as SSH2-encoded keys.
-
#write_long(*n) ⇒ Object
Writes each argument to the buffer as a network-byte-order-encoded long (4-byte) integer.
-
#write_string(*text) ⇒ Object
Writes each argument to the buffer as an SSH2-encoded string.
Constructor Details
#initialize(content = "") ⇒ Buffer
Creates a new buffer, initialized to the given content. The position is initialized to the beginning of the buffer.
73 74 75 76 77 |
# File 'lib/orthrus/ssh/buffer.rb', line 73 def initialize(content="") @content = content.to_s @content.force_encoding "ASCII-8BIT" if @content.respond_to? :force_encoding @position = 0 end |
Instance Attribute Details
#content ⇒ Object (readonly)
exposes the raw content of the buffer
66 67 68 |
# File 'lib/orthrus/ssh/buffer.rb', line 66 def content @content end |
#position ⇒ Object
the current position of the pointer in the buffer
69 70 71 |
# File 'lib/orthrus/ssh/buffer.rb', line 69 def position @position end |
Class Method Details
.from(*args) ⇒ Object
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:
-
:raw => write the next value verbatim (#write)
-
:int64 => write an 8-byte integer (#write_int64)
-
:long => write a 4-byte integer (#write_long)
-
:byte => write a single byte (#write_byte)
-
:string => write a 4-byte length followed by character data (#write_string)
-
:bool => write a single byte, interpreted as a boolean (#write_bool)
-
:bignum => write an SSH-encoded bignum (#write_bignum)
-
:key => write an SSH-encoded key value (#write_key)
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.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/orthrus/ssh/buffer.rb', line 46 def self.from(*args) raise ArgumentError, "odd number of arguments given" unless args.length % 2 == 0 buffer = new 0.step(args.length-1, 2) do |index| type = args[index] value = args[index+1] if type == :raw buffer.append(value.to_s) elsif Array === value buffer.send("write_#{type}", *value) else buffer.send("write_#{type}", value) end end buffer end |
Instance Method Details
#==(buffer) ⇒ Object
Compares the contents of the two buffers, returning true
only if they are identical in size and content.
101 102 103 |
# File 'lib/orthrus/ssh/buffer.rb', line 101 def ==(buffer) to_s == buffer.to_s end |
#append(text) ⇒ Object
Appends the given text to the end of the buffer. Does not alter the read position. Returns the buffer object itself.
150 151 152 153 |
# File 'lib/orthrus/ssh/buffer.rb', line 150 def append(text) @content << text self end |
#available ⇒ Object
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).
86 87 88 |
# File 'lib/orthrus/ssh/buffer.rb', line 86 def available length - position end |
#clear! ⇒ Object
Resets the buffer, making it empty. Also, resets the read position to 0.
124 125 126 127 |
# File 'lib/orthrus/ssh/buffer.rb', line 124 def clear! @content = "" @position = 0 end |
#consume!(n = position) ⇒ Object
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.
136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/orthrus/ssh/buffer.rb', line 136 def consume!(n=position) if n >= length # optimize for a fairly common case clear! elsif n > 0 @content = @content[n..-1] || "" @position -= n @position = 0 if @position < 0 end self end |
#empty? ⇒ Boolean
Returns true
if the buffer contains no data (e.g., it is of zero length).
106 107 108 |
# File 'lib/orthrus/ssh/buffer.rb', line 106 def empty? @content.empty? end |
#eof? ⇒ Boolean
Returns true if the pointer is at the end of the buffer. Subsequent reads will return nil, in this case.
118 119 120 |
# File 'lib/orthrus/ssh/buffer.rb', line 118 def eof? @position >= length end |
#inspect ⇒ Object
95 96 97 |
# File 'lib/orthrus/ssh/buffer.rb', line 95 def inspect "#<#{self.class} #{@content.size} bytes>" end |
#length ⇒ Object
Returns the length of the buffer’s content.
80 81 82 |
# File 'lib/orthrus/ssh/buffer.rb', line 80 def length @content.length end |
#read(count = nil) ⇒ Object
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.
179 180 181 182 183 184 |
# File 'lib/orthrus/ssh/buffer.rb', line 179 def read(count=nil) count ||= length count = length - @position if @position + count > length @position += count @content[@position-count, count] end |
#read!(count = nil) ⇒ Object
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.
188 189 190 191 192 |
# File 'lib/orthrus/ssh/buffer.rb', line 188 def read!(count=nil) data = read(count) consume! data end |
#read_bignum ⇒ Object
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.
236 237 238 239 240 |
# File 'lib/orthrus/ssh/buffer.rb', line 236 def read_bignum data = read_string return unless data OpenSSL::BN.new(data, 2) end |
#read_bool ⇒ Object
Read a single byte and convert it into a boolean, using ‘C’ rules (i.e., zero is false, non-zero is true).
228 229 230 231 |
# File 'lib/orthrus/ssh/buffer.rb', line 228 def read_bool b = read_byte or return nil b != 0 end |
#read_buffer ⇒ Object
Reads the next string from the buffer, and returns a new Buffer object that wraps it.
275 276 277 |
# File 'lib/orthrus/ssh/buffer.rb', line 275 def read_buffer Buffer.new(read_string) end |
#read_byte ⇒ Object
Read and return the next byte in the buffer. Returns nil if called at the end of the buffer.
213 214 215 216 |
# File 'lib/orthrus/ssh/buffer.rb', line 213 def read_byte b = read(1) or return nil b.getbyte(0) end |
#read_int64 ⇒ Object
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.
197 198 199 200 201 |
# File 'lib/orthrus/ssh/buffer.rb', line 197 def read_int64 hi = read_long or return nil lo = read_long or return nil return (hi << 32) + lo end |
#read_key ⇒ Object
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.
245 246 247 248 |
# File 'lib/orthrus/ssh/buffer.rb', line 245 def read_key type = read_string return (type ? read_keyblob(type) : nil) end |
#read_keyblob(type) ⇒ Object
Read a keyblob of the given type from the buffer, and return it as a key. Only RSA and DSA keys are supported.
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/orthrus/ssh/buffer.rb', line 252 def read_keyblob(type) case type when "ssh-dss" key = OpenSSL::PKey::DSA.new key.p = read_bignum key.q = read_bignum key.g = read_bignum key.pub_key = read_bignum when "ssh-rsa" key = OpenSSL::PKey::RSA.new key.e = read_bignum key.n = read_bignum else raise NotImplementedError, "unsupported key type `#{type}'" end return key end |
#read_long ⇒ Object
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.
206 207 208 209 |
# File 'lib/orthrus/ssh/buffer.rb', line 206 def read_long b = read(4) or return nil b.unpack("N").first end |
#read_string ⇒ Object
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.
221 222 223 224 |
# File 'lib/orthrus/ssh/buffer.rb', line 221 def read_string length = read_long or return nil read(length) end |
#read_to(pattern) ⇒ Object
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.
166 167 168 169 170 171 172 173 174 |
# File 'lib/orthrus/ssh/buffer.rb', line 166 def read_to(pattern) index = @content.index(pattern, @position) or return nil length = case pattern when String then pattern.length when Fixnum then 1 when Regexp then $&.length end index && read(index+length) end |
#remainder_as_buffer ⇒ Object
Returns all text from the current pointer to the end of the buffer as a new Net::SSH::Buffer object.
157 158 159 |
# File 'lib/orthrus/ssh/buffer.rb', line 157 def remainder_as_buffer Buffer.new(@content[@position..-1]) end |
#reset! ⇒ Object
Resets the pointer to the start of the buffer. Subsequent reads will begin at position 0.
112 113 114 |
# File 'lib/orthrus/ssh/buffer.rb', line 112 def reset! @position = 0 end |
#to_s ⇒ Object
Returns a copy of the buffer’s content.
91 92 93 |
# File 'lib/orthrus/ssh/buffer.rb', line 91 def to_s (@content || "").dup end |
#write(*data) ⇒ Object
Writes the given data literally into the string. Does not alter the read position. Returns the buffer object.
281 282 283 284 |
# File 'lib/orthrus/ssh/buffer.rb', line 281 def write(*data) data.each { |datum| @content << datum } self end |
#write_bignum(*n) ⇒ Object
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.
336 337 338 339 |
# File 'lib/orthrus/ssh/buffer.rb', line 336 def write_bignum(*n) @content << n.map { |b| Utils.write_bignum(b) }.join self end |
#write_bool(*b) ⇒ Object
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.
328 329 330 331 |
# File 'lib/orthrus/ssh/buffer.rb', line 328 def write_bool(*b) b.each { |v| @content << (v ? "\1" : "\0") } self end |
#write_byte(*n) ⇒ Object
Writes each argument to the buffer as a byte. Does not alter the read position. Returns the buffer object.
308 309 310 311 |
# File 'lib/orthrus/ssh/buffer.rb', line 308 def write_byte(*n) n.each { |b| @content << b.chr } self end |
#write_int64(*n) ⇒ Object
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.
289 290 291 292 293 294 295 296 |
# File 'lib/orthrus/ssh/buffer.rb', line 289 def write_int64(*n) n.each do |i| hi = (i >> 32) & 0xFFFFFFFF lo = i & 0xFFFFFFFF @content << [hi, lo].pack("N2") end self end |
#write_key(*key) ⇒ Object
Writes the given arguments to the buffer as SSH2-encoded keys. Does not alter the read position. Returns the buffer object.
343 344 345 346 |
# File 'lib/orthrus/ssh/buffer.rb', line 343 def write_key(*key) key.each { |k| append(k.public_identity(false)) } self end |
#write_long(*n) ⇒ Object
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.
301 302 303 304 |
# File 'lib/orthrus/ssh/buffer.rb', line 301 def write_long(*n) @content << n.pack("N*") self end |
#write_string(*text) ⇒ Object
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.
316 317 318 319 320 321 322 323 |
# File 'lib/orthrus/ssh/buffer.rb', line 316 def write_string(*text) text.each do |string| s = string.to_s write_long(s.length) write(s) end self end |