class DBus::PacketUnmarshaller

D-Bus packet unmarshaller class

Class that handles the conversion (unmarshalling) of payload data to Array.

Attributes

idx[R]

Index pointer that points to the byte in the data that is currently being processed.

Used to kown what part of the buffer has been consumed by unmarshalling. FIXME: Maybe should be accessed with a “consumed_size” method.

Public Class Methods

new(buffer, endianness) click to toggle source

Create a new unmarshaller for the given data buffer and endianness.

# File lib/dbus/marshall.rb, line 34
def initialize(buffer, endianness)
  @buffy, @endianness = buffer.dup, endianness
  if @endianness == BIG_END
    @uint32 = "N"
    @uint16 = "n"
    @double = "G"
  elsif @endianness == LIL_END
    @uint32 = "V"
    @uint16 = "v"
    @double = "E"
  else
    raise InvalidPacketException, "Incorrect endianness #{@endianness}"
  end
  @idx = 0
end

Public Instance Methods

align(a) click to toggle source

Align the pointer index on a byte index of a, where a must be 1, 2, 4 or 8.

# File lib/dbus/marshall.rb, line 68
def align(a)
  case a
  when 1
  when 2, 4, 8
    bits = a - 1
    @idx = @idx + bits & ~bits
    raise IncompleteBufferException if @idx > @buffy.bytesize
  else
    raise "Unsupported alignment #{a}"
  end
end
unmarshall(signature, len = nil) click to toggle source

Unmarshall the buffer for a given signature and length len. Return an array of unmarshalled objects

# File lib/dbus/marshall.rb, line 52
def unmarshall(signature, len = nil)
  if len != nil
    if @buffy.bytesize < @idx + len
      raise IncompleteBufferException
    end
  end
  sigtree = Type::Parser.new(signature).parse
  ret = Array.new
  sigtree.each do |elem|
    ret << do_parse(elem)
  end
  ret
end

Private Instance Methods

do_parse(signature) click to toggle source

Based on the signature type, retrieve a packet from the buffer and return it.

# File lib/dbus/marshall.rb, line 126
def do_parse(signature)
  packet = nil
  case signature.sigtype
  when Type::BYTE
    packet = get(1).unpack("C")[0]
  when Type::UINT16
    align(2)
    packet = get(2).unpack(@uint16)[0]
  when Type::INT16
    align(4)
    packet = get(4).unpack(@uint16)[0]
    if (packet & 0x8000) != 0
      packet -= 0x10000
    end
  when Type::UINT32, Type::UNIX_FD
    align(4)
    packet = get(4).unpack(@uint32)[0]
  when Type::INT32
    align(4)
    packet = get(4).unpack(@uint32)[0]
    if (packet & 0x80000000) != 0
      packet -= 0x100000000
    end
  when Type::UINT64
    align(8)
    packet_l = get(4).unpack(@uint32)[0]
    packet_h = get(4).unpack(@uint32)[0]
    if @endianness == LIL_END
      packet = packet_l + packet_h * 2**32
    else
      packet = packet_l * 2**32 + packet_h
    end
  when Type::INT64
    align(8)
    packet_l = get(4).unpack(@uint32)[0]
    packet_h = get(4).unpack(@uint32)[0]
    if @endianness == LIL_END
      packet = packet_l + packet_h * 2**32
    else
      packet = packet_l * 2**32 + packet_h
    end
    if (packet & 0x8000000000000000) != 0
      packet -= 0x10000000000000000
    end
  when Type::DOUBLE
    align(8)
    packet = get(8).unpack(@double)[0]
  when Type::BOOLEAN
    align(4)
    v = get(4).unpack(@uint32)[0]
    raise InvalidPacketException if not [0, 1].member?(v)
    packet = (v == 1)
  when Type::ARRAY
    align(4)
    # checks please
    array_sz = get(4).unpack(@uint32)[0]
    raise InvalidPacketException if array_sz > 67108864

    align(signature.child.alignment)
    raise IncompleteBufferException if @idx + array_sz > @buffy.bytesize

    packet = Array.new
    start_idx = @idx
    while @idx - start_idx < array_sz
      packet << do_parse(signature.child)
    end

    if signature.child.sigtype == Type::DICT_ENTRY then
      packet = packet.inject(Hash.new) do |hash, pair|
        hash[pair[0]] = pair[1]
        hash
      end
    end
  when Type::STRUCT
    align(8)
    packet = Array.new
    signature.members.each do |elem|
      packet << do_parse(elem)
    end
  when Type::VARIANT
    string = get_signature
    # error checking please
    sig = Type::Parser.new(string).parse[0]
    align(sig.alignment)
    packet = do_parse(sig)
  when Type::OBJECT_PATH
    packet = get_string
  when Type::STRING
    packet = get_string
    packet.force_encoding('UTF-8')
  when Type::SIGNATURE
    packet = get_signature
  when Type::DICT_ENTRY
    align(8)
    key = do_parse(signature.members[0])
    value = do_parse(signature.members[1])
    packet = [key, value]
  else
    raise NotImplementedError,
      "sigtype: #{signature.sigtype} (#{signature.sigtype.chr})"
  end
  packet
end
get(nbytes) click to toggle source

Retrieve the next nbytes number of bytes from the buffer.

# File lib/dbus/marshall.rb, line 86
def get(nbytes)
  raise IncompleteBufferException if @idx + nbytes > @buffy.bytesize
  ret = @buffy.slice(@idx, nbytes)
  @idx += nbytes
  ret
end
get_signature() click to toggle source

Get the signature length and signature itself from the buffer. Return the signature.

# File lib/dbus/marshall.rb, line 111
def get_signature
  str_sz = get(1).unpack('C')[0]
  ret = @buffy.slice(@idx, str_sz)
  raise IncompleteBufferException if @idx + str_sz + 1 >= @buffy.bytesize
  @idx += str_sz
  if @buffy[@idx].ord != 0
    raise InvalidPacketException, "Type is not nul-terminated"
  end
  @idx += 1
  # no exception, see check above
  ret
end
get_string() click to toggle source

Get the string length and string itself from the buffer. Return the string.

# File lib/dbus/marshall.rb, line 95
def get_string
  align(4)
  str_sz = get(4).unpack(@uint32)[0]
  ret = @buffy.slice(@idx, str_sz)
  raise IncompleteBufferException if @idx + str_sz + 1 > @buffy.bytesize
  @idx += str_sz
  if @buffy[@idx].ord != 0
    raise InvalidPacketException, "String is not nul-terminated"
  end
  @idx += 1
  # no exception, see check above
  ret
end