class DBus::PacketUnmarshaller
D-Bus packet unmarshaller class¶ ↑
Class
that handles the conversion (unmarshalling) of payload data to Array.
Attributes
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
Create a new unmarshaller for the given data buffer and endianness.
# File lib/dbus/marshall.rb 34 def initialize(buffer, endianness) 35 @buffy = buffer.dup 36 @endianness = endianness 37 if @endianness == BIG_END 38 @uint32 = "N" 39 @uint16 = "n" 40 @double = "G" 41 elsif @endianness == LIL_END 42 @uint32 = "V" 43 @uint16 = "v" 44 @double = "E" 45 else 46 raise InvalidPacketException, "Incorrect endianness #{@endianness}" 47 end 48 @idx = 0 49 end
Public Instance Methods
Align the pointer index on a byte index of a, where a must be 1, 2, 4 or 8.
# File lib/dbus/marshall.rb 69 def align(a) 70 case a 71 when 1 72 nil 73 when 2, 4, 8 74 bits = a - 1 75 @idx = @idx + bits & ~bits 76 raise IncompleteBufferException if @idx > @buffy.bytesize 77 else 78 raise "Unsupported alignment #{a}" 79 end 80 end
Unmarshall the buffer for a given signature and length len. Return an array of unmarshalled objects
# File lib/dbus/marshall.rb 53 def unmarshall(signature, len = nil) 54 if !len.nil? 55 if @buffy.bytesize < @idx + len 56 raise IncompleteBufferException 57 end 58 end 59 sigtree = Type::Parser.new(signature).parse 60 ret = [] 61 sigtree.each do |elem| 62 ret << do_parse(elem) 63 end 64 ret 65 end
Private Instance Methods
Based on the signature type, retrieve a packet from the buffer and return it.
# File lib/dbus/marshall.rb 128 def do_parse(signature) 129 packet = nil 130 case signature.sigtype 131 when Type::BYTE 132 packet = read(1).unpack("C")[0] 133 when Type::UINT16 134 align(2) 135 packet = read(2).unpack(@uint16)[0] 136 when Type::INT16 137 align(2) 138 packet = read(2).unpack(@uint16)[0] 139 if (packet & 0x8000) != 0 140 packet -= 0x10000 141 end 142 when Type::UINT32, Type::UNIX_FD 143 align(4) 144 packet = read(4).unpack(@uint32)[0] 145 when Type::INT32 146 align(4) 147 packet = read(4).unpack(@uint32)[0] 148 if (packet & 0x80000000) != 0 149 packet -= 0x100000000 150 end 151 when Type::UINT64 152 align(8) 153 packet_l = read(4).unpack(@uint32)[0] 154 packet_h = read(4).unpack(@uint32)[0] 155 packet = if @endianness == LIL_END 156 packet_l + packet_h * 2**32 157 else 158 packet_l * 2**32 + packet_h 159 end 160 when Type::INT64 161 align(8) 162 packet_l = read(4).unpack(@uint32)[0] 163 packet_h = read(4).unpack(@uint32)[0] 164 packet = if @endianness == LIL_END 165 packet_l + packet_h * 2**32 166 else 167 packet_l * 2**32 + packet_h 168 end 169 if (packet & 0x8000000000000000) != 0 170 packet -= 0x10000000000000000 171 end 172 when Type::DOUBLE 173 align(8) 174 packet = read(8).unpack(@double)[0] 175 when Type::BOOLEAN 176 align(4) 177 v = read(4).unpack(@uint32)[0] 178 raise InvalidPacketException if ![0, 1].member?(v) 179 packet = (v == 1) 180 when Type::ARRAY 181 align(4) 182 # checks please 183 array_sz = read(4).unpack(@uint32)[0] 184 raise InvalidPacketException if array_sz > 67_108_864 185 186 align(signature.child.alignment) 187 raise IncompleteBufferException if @idx + array_sz > @buffy.bytesize 188 189 packet = [] 190 start_idx = @idx 191 while @idx - start_idx < array_sz 192 packet << do_parse(signature.child) 193 end 194 195 if signature.child.sigtype == Type::DICT_ENTRY 196 packet = Hash[packet] 197 end 198 when Type::STRUCT 199 align(8) 200 packet = [] 201 signature.members.each do |elem| 202 packet << do_parse(elem) 203 end 204 when Type::VARIANT 205 string = read_signature 206 # error checking please 207 sig = Type::Parser.new(string).parse[0] 208 align(sig.alignment) 209 packet = do_parse(sig) 210 when Type::OBJECT_PATH 211 packet = read_string 212 when Type::STRING 213 packet = read_string 214 packet.force_encoding("UTF-8") 215 when Type::SIGNATURE 216 packet = read_signature 217 when Type::DICT_ENTRY 218 align(8) 219 key = do_parse(signature.members[0]) 220 value = do_parse(signature.members[1]) 221 packet = [key, value] 222 else 223 raise NotImplementedError, 224 "sigtype: #{signature.sigtype} (#{signature.sigtype.chr})" 225 end 226 packet 227 end
Retrieve the next nbytes number of bytes from the buffer.
# File lib/dbus/marshall.rb 88 def read(nbytes) 89 raise IncompleteBufferException if @idx + nbytes > @buffy.bytesize 90 ret = @buffy.slice(@idx, nbytes) 91 @idx += nbytes 92 ret 93 end
Read the signature length and signature itself from the buffer. Return the signature.
# File lib/dbus/marshall.rb 113 def read_signature 114 str_sz = read(1).unpack("C")[0] 115 ret = @buffy.slice(@idx, str_sz) 116 raise IncompleteBufferException if @idx + str_sz + 1 >= @buffy.bytesize 117 @idx += str_sz 118 if @buffy[@idx].ord != 0 119 raise InvalidPacketException, "Type is not nul-terminated" 120 end 121 @idx += 1 122 # no exception, see check above 123 ret 124 end
Read the string length and string itself from the buffer. Return the string.
# File lib/dbus/marshall.rb 97 def read_string 98 align(4) 99 str_sz = read(4).unpack(@uint32)[0] 100 ret = @buffy.slice(@idx, str_sz) 101 raise IncompleteBufferException if @idx + str_sz + 1 > @buffy.bytesize 102 @idx += str_sz 103 if @buffy[@idx].ord != 0 104 raise InvalidPacketException, "String is not nul-terminated" 105 end 106 @idx += 1 107 # no exception, see check above 108 ret 109 end