chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,325 @@
|
||||
--
|
||||
-- Licensed to the Apache Software Foundation (ASF) under one
|
||||
-- or more contributor license agreements. See the NOTICE file
|
||||
-- distributed with this work for additional information
|
||||
-- regarding copyright ownership. The ASF licenses this file
|
||||
-- to you under the Apache License, Version 2.0 (the
|
||||
-- "License"); you may not use this file except in compliance
|
||||
-- with the License. You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing,
|
||||
-- software distributed under the License is distributed on an
|
||||
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
-- KIND, either express or implied. See the License for the
|
||||
-- specific language governing permissions and limitations
|
||||
-- under the License.
|
||||
--
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- function
|
||||
-- Proto.new(name, desc)
|
||||
-- proto.dissector
|
||||
-- proto.fields
|
||||
-- proto.prefs
|
||||
-- proto.prefs_changed
|
||||
-- proto.init
|
||||
-- proto.name
|
||||
-- proto.description
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local plugin_info = {
|
||||
name = "baidu",
|
||||
version = "0.1.0",
|
||||
description = "baidu_std"
|
||||
}
|
||||
|
||||
local major, minor, patch = get_version():match("(%d+)%.(%d+)%.(%d+)")
|
||||
local version_code = tonumber(major) * 1000 * 1000 + tonumber(minor) * 1000 + tonumber(patch)
|
||||
|
||||
local proto = Proto.new(plugin_info.name, plugin_info.description)
|
||||
|
||||
local LM_DBG = function(fmt, ...)
|
||||
if (tonumber(major) < 3) then
|
||||
critical(table.concat({ plugin_info.name:upper() .. ":", fmt }, ' '):format(...))
|
||||
else
|
||||
print (table.concat({ plugin_info.name:upper() .. ":", fmt }, ' '):format(...))
|
||||
end
|
||||
end
|
||||
|
||||
LM_DBG("Wireshark version =", get_version())
|
||||
LM_DBG("Lua version =", _VERSION)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local MAGIC_CODE_PRPC = "PRPC"
|
||||
local MAGIC_CODE_STRM = "STRM"
|
||||
|
||||
local PROTO_HEADER_LENGTH = 12
|
||||
|
||||
|
||||
local pf_magic = ProtoField.string(plugin_info.name .. ".magic",
|
||||
"Magic Code", BASE_NONE)
|
||||
local pf_body_size = ProtoField.uint32(plugin_info.name .. ".body_size",
|
||||
"Body Size", BASE_DEC)
|
||||
local pf_meta_size = ProtoField.uint32(plugin_info.name .. ".meta_size",
|
||||
"Meta Size", BASE_DEC)
|
||||
|
||||
-- used to customize tree item
|
||||
local pf_any = ProtoField.bytes (plugin_info.name .. ".any",
|
||||
"Any", BASE_NONE)
|
||||
|
||||
proto.fields = {
|
||||
pf_magic,
|
||||
pf_body_size,
|
||||
pf_meta_size,
|
||||
pf_any
|
||||
}
|
||||
|
||||
----------------------------------------
|
||||
-- ref fields
|
||||
local proto_f_magic_code = Field.new(plugin_info.name .. ".magic")
|
||||
local proto_f_body_size = Field.new(plugin_info.name .. ".body_size")
|
||||
local proto_f_meta_size = Field.new(plugin_info.name .. ".meta_size")
|
||||
|
||||
local proto_f_protobuf_field_name = Field.new("protobuf.field.name")
|
||||
local proto_f_protobuf_field_value = Field.new("protobuf.field.value")
|
||||
|
||||
----------------------------------------
|
||||
-- protobuf dissector
|
||||
-- Note:
|
||||
-- options.proto, baidu_rpc_meta.proto and streaming_rpc_meta.proto
|
||||
-- should be put in Wireshark Protobuf Search Paths.
|
||||
local protobuf_dissector = Dissector.get("protobuf")
|
||||
|
||||
----------------------------------------
|
||||
-- declare functions
|
||||
|
||||
local check_length = function() end
|
||||
local dissect_proto = function() end
|
||||
|
||||
----------------------------------------
|
||||
-- main dissector
|
||||
function proto.dissector(tvbuf, pktinfo, root)
|
||||
local pktlen = tvbuf:len()
|
||||
|
||||
local bytes_consumed = 0
|
||||
|
||||
while bytes_consumed < pktlen do
|
||||
local result = dissect_proto(tvbuf, pktinfo, root, bytes_consumed)
|
||||
|
||||
if result > 0 then
|
||||
bytes_consumed = bytes_consumed + result
|
||||
elseif result == 0 then
|
||||
-- hit an error
|
||||
return 0
|
||||
else
|
||||
pktinfo.desegment_offset = bytes_consumed
|
||||
-- require more bytes
|
||||
pktinfo.desegment_len = -result
|
||||
|
||||
return pktlen
|
||||
end
|
||||
end
|
||||
|
||||
return bytes_consumed
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- heuristic
|
||||
local function heur_dissect_proto(tvbuf, pktinfo, root)
|
||||
LM_DBG("heur brpc: pkg number: " .. pktinfo.number)
|
||||
if (tvbuf:len() < PROTO_HEADER_LENGTH) then
|
||||
LM_DBG("too short: pkg number: " .. pktinfo.number)
|
||||
return false
|
||||
end
|
||||
|
||||
local magic = tvbuf:range(0, 4):string()
|
||||
-- for range dissectors
|
||||
if magic ~= MAGIC_CODE_PRPC and maigc ~= MAGIC_CODE_STRM then
|
||||
LM_DBG("invalid magic code: pkg number: " .. pktinfo.number)
|
||||
return false
|
||||
end
|
||||
|
||||
proto.dissector(tvbuf, pktinfo, root)
|
||||
|
||||
pktinfo.conversation = proto
|
||||
|
||||
return true
|
||||
end
|
||||
proto:register_heuristic("tcp", heur_dissect_proto)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local correlation_method_map = {}
|
||||
|
||||
local store_method = function(correlation_id, method)
|
||||
-- TODO: pop items
|
||||
correlation_method_map[correlation_id] = method
|
||||
end
|
||||
|
||||
local load_method = function(correlation_id)
|
||||
return correlation_method_map[correlation_id]
|
||||
end
|
||||
|
||||
-- check packet length, return length of packet if valid
|
||||
check_length = function(tvbuf, offset)
|
||||
local msglen = tvbuf:len() - offset
|
||||
|
||||
if msglen ~= tvbuf:reported_length_remaining(offset) then
|
||||
-- captured packets are being sliced/cut-off, so don't try to desegment/reassemble
|
||||
LM_WARN("Captured packet was shorter than original, can't reassemble")
|
||||
return 0
|
||||
end
|
||||
|
||||
if msglen < PROTO_HEADER_LENGTH then
|
||||
-- we need more bytes, so tell the main dissector function that we
|
||||
-- didn't dissect anything, and we need an unknown number of more
|
||||
-- bytes (which is what "DESEGMENT_ONE_MORE_SEGMENT" is used for)
|
||||
return -DESEGMENT_ONE_MORE_SEGMENT
|
||||
end
|
||||
|
||||
-- if we got here, then we know we have enough bytes in the Tvb buffer
|
||||
-- to at least figure out whether this is valid baidu_std packet
|
||||
|
||||
local magic = tvbuf:range(offset, 4):string()
|
||||
if magic ~= MAGIC_CODE_PRPC and magic ~= MAGIC_CODE_STRM then
|
||||
return 0
|
||||
end
|
||||
|
||||
-- big endian
|
||||
local packet_size = tvbuf:range(offset+4, 4):uint() + PROTO_HEADER_LENGTH
|
||||
if msglen < packet_size then
|
||||
LM_DBG("Need more bytes to desegment full baidu_std packet")
|
||||
return -(packet_size - msglen)
|
||||
end
|
||||
|
||||
return packet_size
|
||||
end
|
||||
|
||||
-- convert uint32 integer to byte array
|
||||
function uint32_to_byte_array(value)
|
||||
local bytes = {}
|
||||
while value > 0 do
|
||||
table.insert(bytes, bit32.band(value, 0xFF))
|
||||
value = bit32.rshift(value, 8)
|
||||
end
|
||||
return bytes
|
||||
end
|
||||
|
||||
-- varint decoding function
|
||||
function decode_varint(value)
|
||||
local bytes = uint32_to_byte_array(value)
|
||||
local result = 0
|
||||
local shift = 0
|
||||
|
||||
for _, byte in ipairs(bytes) do
|
||||
result = bit32.bor(result, bit32.lshift(bit32.band(byte, 0x7F), shift))
|
||||
if bit32.band(byte, 0x80) == 0 then
|
||||
return result
|
||||
end
|
||||
shift = shift + 7
|
||||
end
|
||||
|
||||
LM_DBG("Malformed Varint encoding")
|
||||
end
|
||||
|
||||
----------------------------------------
|
||||
dissect_proto = function(tvbuf, pktinfo, root, offset)
|
||||
local len = check_length(tvbuf, offset)
|
||||
if len <= 0 then
|
||||
return len
|
||||
end
|
||||
|
||||
local tree = root:add(proto, tvbuf:range(offset, len))
|
||||
|
||||
tree:add(pf_magic, tvbuf:range(offset, 4))
|
||||
tree:add(pf_body_size, tvbuf:range(offset+4, 4))
|
||||
tree:add(pf_meta_size, tvbuf:range(offset+8, 4))
|
||||
|
||||
local magic_code = tvbuf:range(offset, 4):string()
|
||||
local meta_size = tvbuf:range(offset + 8, 4):uint()
|
||||
local body_size = tvbuf:range(offset + 4, 4):uint()
|
||||
local tvb_meta = tvbuf:range(offset + PROTO_HEADER_LENGTH, meta_size):tvb()
|
||||
if magic_code == MAGIC_CODE_PRPC then
|
||||
-- update 'Protocol' field
|
||||
if offset == 0 then
|
||||
pktinfo.cols.protocol:set("ProtoBuf")
|
||||
end
|
||||
-- dissect rpc meta fields
|
||||
pktinfo.private["pb_msg_type"] = "message,brpc.policy.RpcMeta"
|
||||
|
||||
-- solve the problem of parsing errors when multiple RPCs are included in a packet
|
||||
local protobuf_field_names = { proto_f_protobuf_field_name() }
|
||||
local pre_field_nums = #protobuf_field_names
|
||||
protobuf_dissector:call(tvb_meta, pktinfo, tree)
|
||||
-- https://ask.wireshark.org/question/31800/protobuf-dissector-with-nested-structures/?answer=31924#post-id-31924
|
||||
protobuf_field_names = { proto_f_protobuf_field_name() }
|
||||
local cur_field_nums = #protobuf_field_names
|
||||
local protobuf_field_values = { proto_f_protobuf_field_value() }
|
||||
|
||||
local direction, method
|
||||
local service_name, method_name, correlation_id, attachment_size
|
||||
|
||||
-- only process new fields added after the previous call to protobuf_dissector
|
||||
for k = pre_field_nums + 1, cur_field_nums do
|
||||
local v = protobuf_field_names[k]
|
||||
if v.value == "request" then
|
||||
direction = "request"
|
||||
elseif v.value == "response" then
|
||||
direction = "response"
|
||||
elseif v.value == "service_name" then
|
||||
service_name = protobuf_field_values[k].range:string(ENC_UTF8)
|
||||
elseif v.value == "method_name" then
|
||||
method_name = protobuf_field_values[k].range:string(ENC_UTF8)
|
||||
elseif v.value == "correlation_id" then
|
||||
correlation_id = protobuf_field_values[k].range:uint64()
|
||||
elseif v.value == "attachment_size" then
|
||||
attachment_size = protobuf_field_values[k].range:le_uint()
|
||||
end
|
||||
end
|
||||
|
||||
if direction == "request" then
|
||||
method = service_name .. "/" .. method_name
|
||||
-- NOTE: convert uint64 to string to be used as key of table
|
||||
store_method(tostring(correlation_id), method)
|
||||
elseif direction == "response" then
|
||||
method = load_method(tostring(correlation_id))
|
||||
end
|
||||
|
||||
if attachment_size then
|
||||
attachment_size = decode_varint(attachment_size)
|
||||
else
|
||||
attachment_size = 0
|
||||
end
|
||||
|
||||
if method ~= nil then
|
||||
-- dissect rpc body
|
||||
local tvb_body = tvbuf:range(offset + PROTO_HEADER_LENGTH + meta_size, body_size - meta_size - attachment_size):tvb()
|
||||
pktinfo.private["pb_msg_type"] = "application/grpc,/" .. method .. "," .. direction
|
||||
protobuf_dissector:call(tvb_body, pktinfo, tree)
|
||||
end
|
||||
elseif magic_code == MAGIC_CODE_STRM then
|
||||
-- dissect streaming meta fields
|
||||
pktinfo.private["pb_msg_type"] = "message,brpc.StreamFrameMeta"
|
||||
protobuf_dissector:call(tvb_meta, pktinfo, tree)
|
||||
end
|
||||
|
||||
-- body fields are business related, customized here
|
||||
|
||||
return len
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Editor modelines
|
||||
--
|
||||
-- Local variables:
|
||||
-- c-basic-offset: 4
|
||||
-- tab-width: 4
|
||||
-- indent-tab-mode: nil
|
||||
-- End:
|
||||
--
|
||||
-- kate: indent-width 4; tab-width 4;
|
||||
-- vim: tabstop=4:softtabstop=4:shiftwidth=4:expandtab
|
||||
-- :indentSize=4:tabSize=4:noTabs=true
|
||||
Reference in New Issue
Block a user