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.
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 |
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.
# 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.
# 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
Compares the contents of the two buffers, returning true only if they are identical in size and content.
# 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.
# File lib/net/ssh/buffer.rb, line 92 92: def ==(buffer) 93: to_s == buffer.to_s 94: 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.
# 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.
# 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 pointer is at the end of the buffer. Subsequent reads will return nil, in this case.
# 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.
# File lib/net/ssh/buffer.rb, line 109 109: def eof? 110: @position >= length 111: 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.
# 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.
# 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
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.
# 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.
# 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).
# 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).
# 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 and return the next byte in the buffer. Returns nil if called at the end of the buffer.
# 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.
# 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.
# 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.
# 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 keyblob of the given type from the buffer, and return it as a key. Only RSA and DSA keys are supported.
# 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.
# 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 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.
# 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.
# 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.
# 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.
# 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.
# 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.
# 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.
# 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.
# File lib/net/ssh/buffer.rb, line 103 103: def reset! 104: @position = 0 105: end
Returns a copy of the buffer‘s content.
# 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.
# File lib/net/ssh/buffer.rb, line 86 86: def to_s 87: (@content || "").dup 88: 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.
# 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.
# 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.
# 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.
# 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 network-byte-order-encoded 64-bit integer (8 bytes). Does not alter the read position. Returns the buffer object.
# 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.
# 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 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.
# 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.
# 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