diff options
author | Jamis Buck <jamis@37signals.com> | 2007-07-24 15:41:14 +0000 |
---|---|---|
committer | Jamis Buck <jamis@37signals.com> | 2007-07-24 15:41:14 +0000 |
commit | 50253e85175da17e4b179bf4d8906e640ca5f187 (patch) | |
tree | eb6b8603a110a5d6b8d1f5b5762b1f006547efac /lib/net/ssh/buffer.rb | |
download | net-ssh-50253e85175da17e4b179bf4d8906e640ca5f187.tar.gz |
Add v2 branch
git-svn-id: http://svn.jamisbuck.org/net-ssh/branches/v2@117 1d2a57f2-1ded-0310-ad52-83097a15a5de
Diffstat (limited to 'lib/net/ssh/buffer.rb')
-rw-r--r-- | lib/net/ssh/buffer.rb | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/lib/net/ssh/buffer.rb b/lib/net/ssh/buffer.rb new file mode 100644 index 0000000..d2ec2b8 --- /dev/null +++ b/lib/net/ssh/buffer.rb @@ -0,0 +1,239 @@ +module Net; module SSH + class Buffer + 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) + else + buffer.send("write_#{type}", value) + end + end + + buffer + end + + # exposes the raw content of the buffer + attr_reader :content + + # the current position of the pointer in the buffer + attr_reader :position + + # creates a new buffer + def initialize(content="") + @content = content + @position = 0 + end + + # the length of the buffer's content. + def length + @content.length + end + + # the number of bytes available to be read. + def available + length - position + end + + # returns a copy of the buffer's content. + def to_s + (@content || "").dup + end + + # Compares the contents of the two buffers. + def ==(buffer) + to_s == buffer.to_s + end + + # Resets the buffer, making it empty. + def clear! + @content = "" + @position = 0 + end + + # Consume's n bytes from the buffer, where n is the current position + # unless otherwise specified. + def consume!(n=position) + if n > 0 + @content = @content[n..-1] || "" + @position -= n + @position = 0 if @position < 0 + end + self + end + + # Appends the given text to the end of the buffer. + def append(text) + @content << text + self + end + + # Returns all text from the current pointer to the end of the buffer as + # a new buffer as the same class as the receiver. + def remainder_as_buffer + self.class.new(@content[@position..-1]) + end + + # Reads +count+ bytes from the buffer. If +count+ is +nil+, this will + # return all remaining text in the buffer. This method will increment + # the pointer. + def read(count=nil) + count = length - @position unless count + return nil if @position + count > length + + @position += count + @content[@position-count, count] + end + + # Return the next 8 bytes as a 64-bit integer (in network byte order). + def read_int64 + hi = read_long + lo = read_long + return (hi << 32) + lo + end + + # Return the next four bytes as a long integer (in network byte order). + def read_long + b = read(4) or return nil + b.unpack("N").first + end + + # Read the next two bytes as a short integer (in network byte order). + def read_short + b = read(2) or return nil + b.unpack("n").first + end + + # Read and return the next byte in the buffer. + def read_byte + b = read(1) or return nil + b[0] + 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. + def read_string + length = read_long or return nil + read(length) + end + + # Read a single byte and convert it into a boolean, using 'C' rules + # (i.e., zero is false, non-zero is true). + def read_bool + b = read(1) or return nil + b[0] != 0 + 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. + def read_bignum + data = read_string + return unless data + OpenSSL::BN.new(data, 2) + 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. + def read_key + type = read_string + return (type ? read_keyblob(type) : nil) + end + + # Read a keyblob of the given type from the buffer, and return it as + # a key. Only RSA and DSA keys are supported. + 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 + + # Resets the pointer to the start of the buffer. + def reset! + @position = 0 + end + + # Returns true if the pointer is at the end of the buffer. + def eof? + @position >= length + end + + # Writes the given data literally into the string. + def write(*data) + @content << data.join + end + + # Writes each argument to the buffer as a network-byte-order-encoded + # 64-bit integer (8 bytes). + def write_int64(*n) + n.each do |i| + hi = (i >> 32) & 0xFFFFFFFF + lo = i & 0xFFFFFFFF + @content << [hi, lo].pack("N2") + end + end + + # Writes each argument to the buffer as a network-byte-order-encoded + # long (4-byte) integer. + def write_long(*n) + @content << n.pack("N*") + end + + # Writes each argument to the buffer as a network-byte-order-encoded + # short (2-byte) integer. + def write_short(*n) + @content << n.pack("n*") + end + + # Writes each argument to the buffer as a byte. + def write_byte(*n) + n.each { |b| @content << b.chr } + 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. + def write_string(*text) + text.each do |string| + write_long(string.length) + write(string) + end + end + + # Writes each argument to the buffer as a (C-style) boolean, with 1 + # meaning true, and 0 meaning false. + def write_bool(*b) + b.each { |v| @content << (v ? "\1" : "\0") } + 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. + def write_bignum(*n) + @content << n.map { |b| b.to_ssh }.join + end + + # Writes the given arguments to the buffer as SSH2-encoded keys. + def write_key(*key) + key.each { |k| append(k.to_blob) } + end + end +end; end;
\ No newline at end of file |