module Net::BER::BERParser

This module is for mixing into IO and IO-like objects.

Constants

BuiltinSyntax
TagClasses

The order of these follows the class-codes in BER. Maybe this should have been a hash.

Public Instance Methods

read_ber(syntax=nil) click to toggle source

read_ber TODO: clean this up so it works properly with partial packets coming from streams that don’t block when we ask for more data (like StringIOs). At it is, this can throw TypeErrors and other nasties.

# File lib/net/ber.rb, line 70
def read_ber syntax=nil
  return nil if eof?

  id = getc  # don't trash this value, we'll use it later
  tag = id & 31
  tag < 31 or raise BerError.new( "unsupported tag encoding: #{id}" )
  tagclass = TagClasses[ id >> 6 ]
  encoding = (id & 0x20 != 0) ? :constructed : :primitive

  n = getc
  lengthlength,contentlength = if n <= 127
    [1,n]
  else
    j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getc}
    [1 + (n & 127), j]
  end

  newobj = read contentlength

  objtype = nil
  [syntax, BuiltinSyntax].each {|syn|
    if syn && (ot = syn[tagclass]) && (ot = ot[encoding]) && ot[tag]
      objtype = ot[tag]
      break
    end
  }
  
  obj = case objtype
  when :boolean
    newobj != "\000"
  when :string
    (newobj || "").dup
  when :integer
    j = 0
    newobj.each_byte {|b| j = (j << 8) + b}
    j
  when :array
    seq = []
    sio = StringIO.new( newobj || "" )
    # Interpret the subobject, but note how the loop
    # is built: nil ends the loop, but false (a valid
    # BER value) does not!
    while (e = sio.read_ber(syntax)) != nil
      seq << e
    end
    seq
  else
    raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" )
  end

  # Add the identifier bits into the object if it's a String or an Array.
  # We can't add extra stuff to Fixnums and booleans, not that it makes much sense anyway.
  obj and ([String,Array].include? obj.class) and obj.instance_eval "def ber_identifier; #{id}; end"
  obj

end