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 = buffer.dup
  @endianness = 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 69
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 53
def unmarshall(signature, len = nil)
  if !len.nil?
    if @buffy.bytesize < @idx + len
      raise IncompleteBufferException
    end
  end
  sigtree = Type::Parser.new(signature).parse
  ret = []
  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 127
def do_parse(signature)
  packet = nil
  case signature.sigtype
  when Type::BYTE
    packet = read(1).unpack("C")[0]
  when Type::UINT16
    align(2)
    packet = read(2).unpack(@uint16)[0]
  when Type::INT16
    align(4)
    packet = read(4).unpack(@uint16)[0]
    if (packet & 0x8000) != 0
      packet -= 0x10000
    end
  when Type::UINT32, Type::UNIX_FD
    align(4)
    packet = read(4).unpack(@uint32)[0]
  when Type::INT32
    align(4)
    packet = read(4).unpack(@uint32)[0]
    if (packet & 0x80000000) != 0
      packet -= 0x100000000
    end
  when Type::UINT64
    align(8)
    packet_l = read(4).unpack(@uint32)[0]
    packet_h = read(4).unpack(@uint32)[0]
    packet = if @endianness == LIL_END
               packet_l + packet_h * 2**32
             else
               packet_l * 2**32 + packet_h
             end
  when Type::INT64
    align(8)
    packet_l = read(4).unpack(@uint32)[0]
    packet_h = read(4).unpack(@uint32)[0]
    packet = if @endianness == LIL_END
               packet_l + packet_h * 2**32
             else
               packet_l * 2**32 + packet_h
             end
    if (packet & 0x8000000000000000) != 0
      packet -= 0x10000000000000000
    end
  when Type::DOUBLE
    align(8)
    packet = read(8).unpack(@double)[0]
  when Type::BOOLEAN
    align(4)
    v = read(4).unpack(@uint32)[0]
    raise InvalidPacketException if ![0, 1].member?(v)
    packet = (v == 1)
  when Type::ARRAY
    align(4)
    # checks please
    array_sz = read(4).unpack(@uint32)[0]
    raise InvalidPacketException if array_sz > 67_108_864

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

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

    if signature.child.sigtype == Type::DICT_ENTRY
      packet = Hash[packet]
    end
  when Type::STRUCT
    align(8)
    packet = []
    signature.members.each do |elem|
      packet << do_parse(elem)
    end
  when Type::VARIANT
    string = read_signature
    # error checking please
    sig = Type::Parser.new(string).parse[0]
    align(sig.alignment)
    packet = do_parse(sig)
  when Type::OBJECT_PATH
    packet = read_string
  when Type::STRING
    packet = read_string
    packet.force_encoding("UTF-8")
  when Type::SIGNATURE
    packet = read_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
read(nbytes) click to toggle source

Retrieve the next nbytes number of bytes from the buffer.

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

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

# File lib/dbus/marshall.rb, line 112
def read_signature
  str_sz = read(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
read_string() click to toggle source

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

# File lib/dbus/marshall.rb, line 96
def read_string
  align(4)
  str_sz = read(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