1require ("bit") 2 3p_astron = Proto ("astron", "Astron (MD)") 4 5local f_length = ProtoField.uint16("astron.length", "Message length", base.DEC) 6local f_recipient = ProtoField.uint64("astron.recipient", "Recipient channel", base.HEX) 7local f_sender = ProtoField.uint64("astron.sender", "Sender channel", base.HEX) 8local f_msgtype = ProtoField.uint16("astron.msgtype", "Message type", base.DEC) 9 10local f_doid = ProtoField.uint32("astron.doid", "DistributedObject ID", base.DEC) 11local f_field = ProtoField.uint16("astron.field", "Field ID", base.DEC) 12local f_field_count = ProtoField.uint16("astron.field_count", "Field count", base.DEC) 13 14local f_object_count = ProtoField.uint32("astron.object_count", "Object count", base.DEC) 15 16local f_parent = ProtoField.uint32("astron.parent", "Parent ID", base.DEC) 17local f_zone = ProtoField.uint32("astron.zone", "Zone ID", base.DEC) 18local f_zone_count = ProtoField.uint16("astron.zone_count", "Zone count", base.DEC) 19 20local f_context = ProtoField.uint32("astron.context", "Request context", base.DEC) 21 22p_astron.fields = { 23 f_length, f_recipient, f_sender, f_msgtype, 24 25 f_doid, f_field, f_field_count, 26 27 f_object_count, 28 29 f_parent, f_zone, f_zone_count, 30 31 f_context, 32} 33 34-- Helpers that, maybe someday, will actually get info from a .dc file? 35function dclass_by_field (field) 36 return "DistributedObject" 37end 38 39function dclass_by_index (index) 40 return "DistributedObject" 41end 42 43function field_by_index (index) 44 return "FIELD_" .. index 45end 46 47function decode_field (index, buf) 48 return "<" .. buf .. ">" 49end 50 51local message_table = { 52 [1011] = { 53 name="CLIENTAGENT_UNDECLARE_OBJECT", 54 dissector=function(buf, root) 55 local doid = buf(0,4):le_uint() 56 root:add_le(f_doid, buf(0,4)) 57 58 return tostring(doid) 59 end 60 }, 61 [2020] = { 62 name="STATESERVER_OBJECT_SET_FIELD", 63 dissector=function(buf, root) 64 local doid = buf(0,4):le_uint() 65 root:add_le(f_doid, buf(0,4)) 66 67 local field = buf(4,2):le_uint() 68 root:add_le(f_field, buf(0,2), field, 69 "Field: " .. field_by_index(field).."("..field..")") 70 71 local decoded = "" 72 if buf:len() > 6 then 73 local data = buf(6) 74 decoded = decode_field(field, data) 75 root:add(data, "Field data: "..decoded) 76 end 77 78 return string.format("%s(%d).%s(%s)", 79 dclass_by_field(field), doid, 80 field_by_index(field), decoded) 81 end 82 }, 83 [2100] = { 84 name="STATESERVER_OBJECT_GET_ZONE_OBJECTS", 85 dissector=function(buf, root) 86 local context = buf(0,4):le_uint() 87 root:add_le(f_context, buf(0,4)) 88 89 local parent = buf(4,4):le_uint() 90 root:add_le(f_parent, buf(4,4)) 91 92 local zone = buf(8,4):le_uint() 93 root:add_le(f_zone, buf(8,4)) 94 95 return string.format("%d: %d, %d", context, parent, zone) 96 end 97 }, 98 [2102] = { 99 name="STATESERVER_OBJECT_GET_ZONES_OBJECTS", 100 dissector=function(buf, root) 101 local context = buf(0,4):le_uint() 102 root:add_le(f_context, buf(0,4)) 103 104 local parent = buf(4,4):le_uint() 105 root:add_le(f_parent, buf(4,4)) 106 107 local zone_count = buf(8,2):le_uint() 108 local count_item = root:add_le(f_zone_count, buf(8,2)) 109 110 local zones = buf(10) 111 local zone_strings = {} 112 for i = 0, zone_count-1 do 113 table.insert(zone_strings, tostring(zones(i*4,4):le_uint())) 114 count_item:add_le(f_zone, zones(i*4,4)) 115 end 116 117 return string.format("%d: %d(%s)", context, parent, 118 table.concat(zone_strings, "&")) 119 end 120 }, 121 [2110] = { 122 name="STATESERVER_OBJECT_GET_ZONE_COUNT", 123 dissector=function(buf, root) 124 local context = buf(0,4):le_uint() 125 root:add_le(f_context, buf(0,4)) 126 127 local parent = buf(4,4):le_uint() 128 root:add_le(f_parent, buf(4,4)) 129 130 local zone = buf(8,4):le_uint() 131 root:add_le(f_zone, buf(8,4)) 132 133 return string.format("%d: %d, %d", context, parent, zone) 134 end 135 }, 136 [2111] = { 137 name="STATESERVER_OBJECT_GET_ZONE_COUNT_RESP", 138 dissector=function(buf, root) 139 local context = buf(0,4):le_uint() 140 root:add_le(f_context, buf(0,4)) 141 142 local object_count = buf(4,4):le_uint() 143 root:add_le(f_object_count, buf(4,4)) 144 145 return string.format("%d: %d", context, object_count) 146 end 147 }, 148 [2112] = { 149 name="STATESERVER_OBJECT_GET_ZONES_COUNT", 150 dissector=function(buf, root) 151 local context = buf(0,4):le_uint() 152 root:add_le(f_context, buf(0,4)) 153 154 local parent = buf(4,4):le_uint() 155 root:add_le(f_parent, buf(4,4)) 156 157 local zone_count = buf(8,2):le_uint() 158 local count_item = root:add_le(f_zone_count, buf(8,2)) 159 160 local zones = buf(10) 161 local zone_strings = {} 162 for i = 0, zone_count-1 do 163 table.insert(zone_strings, tostring(zones(i*4,4):le_uint())) 164 count_item:add_le(f_zone, zones(i*4,4)) 165 end 166 167 return string.format("%d: %d(%s)", context, parent, 168 table.concat(zone_strings, "&")) 169 end 170 }, 171 [2113] = { 172 name="STATESERVER_OBJECT_GET_ZONES_COUNT_RESP", 173 dissector=function(buf, root) 174 local context = buf(0,4):le_uint() 175 root:add_le(f_context, buf(0,4)) 176 177 local object_count = buf(4,4):le_uint() 178 root:add_le(f_object_count, buf(4,4)) 179 180 return string.format("%d: %d", context, object_count) 181 end 182 }, 183 [3012] = { 184 name="DBSERVER_OBJECT_GET_FIELDS", 185 dissector=function(buf, root) 186 local context = buf(0,4):le_uint() 187 root:add_le(f_context, buf(0,4)) 188 189 local doid = buf(4,4):le_uint() 190 root:add_le(f_doid, buf(4,4)) 191 192 local field_count = buf(8,2):le_uint() 193 local count_item = root:add_le(f_field_count, buf(8,2)) 194 195 local fields = buf(10) 196 local field_names = {} 197 for i = 0, field_count-1 do 198 local field_name = field_by_index(fields(i*2,2):le_uint()) 199 table.insert(field_names, field_name) 200 201 local field_item = count_item:add_le(f_field, fields(i*2,2)) 202 field_item:set_text(field_name) 203 end 204 205 return string.format("%d[%s]", doid, 206 table.concat(field_names, "&")) 207 end 208 }, 209} 210 211function pretty_msgtype (msgtype) 212 local msg_entry = message_table[msgtype] or {} 213 214 local msgname = msg_entry.name or "UNKNOWN" 215 return string.format("%s(%d)", msgname, msgtype) 216end 217 218function pretty_channel (channel) 219 local hex = channel:tohex() 220 221 local high = tostring(channel:rshift(32):band(0xFFFFFFFF)) 222 local low = tostring(channel:band(0xFFFFFFFF)) 223 224 return string.format("0x%s(%s, %s)", hex, high, low) 225end 226 227function dissect_one (buf, root, packet_descriptions) 228 local length = buf(0,2):le_uint() 229 local recip_count = buf(2, 1):uint() 230 231 -- Perform several sanity checks to see if we can even begin decode: 232 if length < 11 then return 0 end 233 if recip_count*8 > buf:len() then return 0 end 234 if recip_count == 0 then return 0 end 235 if length > 16384 then return 0 end -- Remove this if you have huge messages 236 if recip_count > 16 then return 0 end -- Remove this if you have many recipients 237 238 -- Make sure we have enough bytes, ask for more if not: 239 if buf:len() < length+2 then return buf:len() - length-2 end 240 buf = buf(0, length+2) 241 242 local subtree = root:add(buf, "Message") 243 244 local header_offset = buf:offset() 245 local header = subtree:add(buf, "Header") 246 header:add_le(f_length, buf(0, 2)) 247 buf = buf(2) 248 249 local recipients = header:add(buf, "Recipient(s): ") 250 local recipient_channel 251 for i=0,recip_count-1 do 252 local channel_tvb = buf(1+i*8, 8) 253 recipients:add_le(f_recipient, channel_tvb) 254 if i ~= 0 then recipients:append_text(", ") end 255 recipient_channel = channel_tvb:le_uint64() 256 recipients:append_text(pretty_channel(recipient_channel)) 257 end 258 recipients:set_len(1 + recip_count*8) 259 buf = buf(1 + recip_count*8) 260 261 if(recip_count ~= 1 or tostring(recipient_channel) ~= "1") then 262 local sender_channel = buf(0, 8):le_uint64() 263 header:add_le(f_sender, buf(0, 8), sender_channel, 264 "Sender channel: " .. pretty_channel(sender_channel)) 265 buf = buf(8) 266 end 267 268 local msgtype = buf(0, 2):le_uint() 269 header:add_le(f_msgtype, buf(0, 2), msgtype, "Message type: " .. pretty_msgtype(msgtype)) 270 if buf:len() > 2 then 271 buf = buf(2) 272 end 273 274 header:set_len(buf:offset() - header_offset) 275 276 -- Invoke the sub-dissector, if available. 277 local full_description = pretty_msgtype(msgtype) 278 local dissector = (message_table[msgtype] or {}).dissector 279 if dissector ~= nil then 280 local description = dissector(buf, subtree) 281 if description ~= nil then 282 full_description = full_description .. ": " .. description 283 end 284 else 285 subtree:add(buf, "Payload") 286 end 287 288 subtree:set_text(full_description) 289 table.insert(packet_descriptions, full_description) 290 291 return length + 2 292end 293 294function p_astron.dissector (buf, pinfo, root) 295 if buf:len() < 2 then return end 296 297 local root = root:add(p_astron, buf) 298 299 local descriptions = {} 300 local message_count = 0 301 local offset = 0 302 while offset < buf:len() do 303 local len = dissect_one(buf(offset), root, descriptions) 304 305 if len < 0 then 306 pinfo.desegment_offset = offset 307 pinfo.desegment_len = -len 308 return 309 elseif len == 0 then 310 return 0 311 else 312 offset = offset + len 313 message_count = message_count + 1 314 end 315 end 316 317 if message_count > 0 then 318 pinfo.cols.protocol = p_astron.name 319 pinfo.cols.info = table.concat(descriptions, "; ") 320 end 321 322 return offset 323end 324 325function p_astron.init() 326 local tcp_dissector_table = DissectorTable.get("tcp.port") 327 328 tcp_dissector_table:add(7199, p_astron) 329end 330