Moved core extensions to Net::BER. Documented.

This commit is contained in:
Austin Ziegler 2010-03-20 00:18:10 -04:00 committed by Kaspar Schiess
parent e31af4bead
commit c913bc6fb9
17 changed files with 310 additions and 248 deletions

View file

@ -0,0 +1,79 @@
module Net::BER::Extensions::Array
##
# Converts an Array to a BER sequence. All values in the Array are
# expected to be in BER format prior to calling this method.
def to_ber(id = 0)
# The universal sequence tag 0x30 is composed of the base tag value
# (0x10) and the constructed flag (0x20).
to_ber_seq_internal(0x30 + id)
end
alias_method :to_ber_sequence, :to_ber
##
# Converts an Array to a BER set. All values in the Array are expected to
# be in BER format prior to calling this method.
def to_ber_set(id = 0)
# The universal set tag 0x31 is composed of the base tag value (0x11)
# and the constructed flag (0x20).
to_ber_seq_internal(0x31 + id)
end
##
# Converts an Array to an application-specific sequence, assigned a tag
# value that is meaningful to the particular protocol being used. All
# values in the Array are expected to be in BER format pr prior to calling
# this method.
#--
# Implementor's note 20100320(AZ): RFC 4511 (the LDAPv3 protocol) as well
# as earlier RFCs 1777 and 2559 seem to indicate that LDAP only has
# application constructed sequences (0x60). However, ldapsearch sends some
# context-specific constructed sequences (0xA0); other clients may do the
# same. This behaviour appears to violate the RFCs. In real-world
# practice, we may need to change calls of #to_ber_appsequence to
# #to_ber_contextspecific for full LDAP server compatibility.
#
# This note probably belongs elsewhere.
#++
def to_ber_appsequence(id = 0)
# The application sequence tag always starts from the application flag
# (0x40) and the constructed flag (0x20).
to_ber_seq_internal(0x60 + id)
end
##
# Converts an Array to a context-specific sequence, assigned a tag value
# that is meaningful to the particular context of the particular protocol
# being used. All values in the Array are expected to be in BER format
# prior to calling this method.
def to_ber_contextspecific(id = 0)
# The application sequence tag always starts from the context flag
# (0x80) and the constructed flag (0x20).
to_ber_seq_internal(0xa0 + id)
end
##
# The internal sequence packing routine. All values in the Array are
# expected to be in BER format prior to calling this method.
def to_ber_seq_internal(code)
s = self.join
[code].pack('C') + s.length.to_ber_length_encoding + s
end
private :to_ber_seq_internal
##
# SNMP Object Identifiers (OID) are special arrays
#--
# 20100320 AZ: I do not think that this method should be in BER, since
# this appears to be SNMP-specific. This should probably be subsumed by a
# proper SNMP OID object.
#++
def to_ber_oid
ary = self.dup
first = ary.shift
raise Net::BER::BerError, "Invalid OID" unless [0, 1, 2].include?(first)
first = first * 40 + ary.shift
ary.unshift first
oid = ary.pack("w*")
[6, oid.length].pack("CC") + oid
end
end

View file

@ -0,0 +1,19 @@
module Net::BER::Extensions::Bignum
##
# Converts a Bignum to an uncompressed BER integer.
def to_ber
result = []
# NOTE: Array#pack's 'w' is a BER _compressed_ integer. We need
# uncompressed BER integers, so we're not using that. See also:
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/228864
n = self
while n > 0
b = n & 0xff
result << b
n = n >> 8
end
"\002" + ([result.size] + result.reverse).pack('C*')
end
end

View file

@ -0,0 +1,7 @@
module Net::BER::Extensions::FalseClass
##
# Converts +false+ to the BER wireline representation of +false+.
def to_ber
"\001\001\000"
end
end

View file

@ -0,0 +1,63 @@
module Net::BER::Extensions::Fixnum
##
# Converts the fixnum to BER format.
def to_ber
"\002#{to_ber_internal}"
end
##
# Converts the fixnum to BER enumerated format.
def to_ber_enumerated
"\012#{to_ber_internal}"
end
##
# Converts the fixnum to BER length encodining format.
def to_ber_length_encoding
if self <= 127
[self].pack('C')
else
i = [self].pack('N').sub(/^[\0]+/,"")
[0x80 + i.length].pack('C') + i
end
end
##
# Generate a BER-encoding for an application-defined INTEGER. Examples of
# such integers are SNMP's Counter, Gauge, and TimeTick types.
def to_ber_application(tag)
[0x40 + tag].pack("C") + to_ber_internal
end
##
# Used to BER-encode the length and content bytes of a Fixnum. Callers
# must prepend the tag byte for the contained value.
def to_ber_internal
# CAUTION: Bit twiddling ahead. You might want to shield your eyes or
# something.
# Looks for the first byte in the fixnum that is not all zeroes. It does
# this by masking one byte after another, checking the result for bits
# that are left on.
size = Net::BER::MAX_FIXNUM_SIZE
while size > 1
break if (self & (0xff << (size - 1) * 8)) > 0
size -= 1
end
# Store the size of the fixnum in the result
result = [size]
# Appends bytes to result, starting with higher orders first. Extraction
# of bytes is done by right shifting the original fixnum by an amount
# and then masking that with 0xff.
while size > 0
# right shift size - 1 bytes, mask with 0xff
result << ((self >> ((size - 1) * 8)) & 0xff)
size -= 1
end
result.pack('C*')
end
private :to_ber_internal
end

View file

@ -0,0 +1,51 @@
require 'stringio'
module Net::BER::Extensions::String
##
# Converts a string to a BER string. Universal octet-strings are tagged
# with 0x04, but other values are possible depending on the context, so we
# let the caller give us one.
#
# User code should call either #to_ber_application_string or
# #to_ber_contextspecific.
def to_ber(code = 0x04)
[code].pack('C') + length.to_ber_length_encoding + self
end
##
# Creates an application-specific BER string encoded value with the
# provided syntax code value.
def to_ber_application_string(code)
to_ber(0x40 + code)
end
##
# Creates a context-specific BER string encoded value with the provided
# syntax code value.
def to_ber_contextspecific(code)
to_ber(0x80 + code)
end
##
# Nondestructively reads a BER object from this string.
def read_ber(syntax = nil)
StringIO.new(self).read_ber(syntax)
end
=begin
# 20100319 AZ I've kept this here because I'm not yet sure if it's
# necessary.
##
# Destructively reads a BER object from this string.
def read_ber!(syntax = nil)
obj, consumed = read_ber_from_string(self, syntax)
if consumed
self.slice!(0...consumed)
obj
else
nil
end
end
=end
end

View file

@ -0,0 +1,9 @@
module Net::BER::Extensions::TrueClass
##
# Converts +true+ to the BER wireline representation of +true+.
def to_ber
# 20100319 AZ: Note that this may not be the completely correct value,
# per some test documentation. We need to determine the truth of this.
"\001\001\001"
end
end