1
+ --
2
+ --
3
+ -- Tencent is pleased to support the open source community by making tRPC available.
4
+ --
5
+ -- Copyright (C) 2024 THL A29 Limited, a Tencent company.
6
+ -- All rights reserved.
7
+ --
8
+ -- If you have downloaded a copy of the tRPC source code from Tencent,
9
+ -- please note that tRPC source code is licensed under the Apache 2.0 License,
10
+ -- A copy of the Apache 2.0 License is included in this file.
11
+ --
12
+ --
13
+ -- tRPC is licensed under the Apache 2.0 License, and includes source codes from
14
+ -- the following components:
15
+ -- 1. incubator-brpc
16
+ -- Copyright (C) 2019 The Apache Software Foundation
17
+ -- incubator-brpc is licensed under the Apache 2.0 License.
18
+ --
19
+ --
20
+
21
+ local protocol_name = " trpc"
22
+ local trpc_proto = Proto (protocol_name , " tRPC Protocol Dissector" )
23
+
24
+ local field_magic = ProtoField .uint16 (protocol_name .. " .magic" , " Magic" , base .HEX )
25
+ local field_type = ProtoField .uint8 (protocol_name .. " .type" , " Packet Type" , base .DEC )
26
+ local field_stream = ProtoField .uint8 (protocol_name .. " .stream" , " Stream Type" , base .DEC )
27
+ local field_total_size = ProtoField .uint32 (protocol_name .. " .total_size" , " Total Size" , base .DEC )
28
+ local field_header_size = ProtoField .uint16 (protocol_name .. " .header_size" , " Header Size" , base .DEC )
29
+ local field_unique_id = ProtoField .uint32 (protocol_name .. " .unique_id" , " Unique ID" , base .DEC )
30
+ local field_version = ProtoField .uint8 (protocol_name .. " .version" , " Version" , base .DEC )
31
+ local field_reserved = ProtoField .uint8 (protocol_name .. " .reserved" , " Reserved" , base .DEC )
32
+ trpc_proto .fields = {field_magic , field_type , field_stream , field_total_size , field_header_size , field_unique_id , field_version , field_reserved }
33
+
34
+ local MAGIC_CODE_TRPC = " 0930"
35
+ local PROTO_HEADER_LENGTH = 16
36
+
37
+ local tcp_src_port = Field .new (" tcp.srcport" )
38
+ local tcp_dst_port = Field .new (" tcp.dstport" )
39
+ local tcp_stream = Field .new (" tcp.stream" )
40
+
41
+ local proto_f_protobuf_field_name = Field .new (" protobuf.field.name" )
42
+ local proto_f_protobuf_field_value = Field .new (" protobuf.field.value" )
43
+
44
+ local data_dissector = Dissector .get (" data" )
45
+ local protobuf_dissector = Dissector .get (" protobuf" )
46
+
47
+ ---- ------------------------------------
48
+ -- declare functions
49
+ local check_length = function () end
50
+ local dissect_proto = function () end
51
+
52
+ ---- ------------------------------------
53
+ -- main dissector
54
+ function trpc_proto .dissector (tvbuf , pktinfo , root )
55
+ local pktlen = tvbuf :len ()
56
+
57
+ local bytes_consumed = 0
58
+
59
+ while bytes_consumed < pktlen do
60
+ local result = dissect_proto (tvbuf , pktinfo , root , bytes_consumed )
61
+
62
+ if result > 0 then
63
+ bytes_consumed = bytes_consumed + result
64
+ elseif result == 0 then
65
+ -- hit an error
66
+ return 0
67
+ else
68
+ pktinfo .desegment_offset = bytes_consumed
69
+ -- require more bytes
70
+ pktinfo .desegment_len = - result
71
+
72
+ return pktlen
73
+ end
74
+ end
75
+
76
+ return bytes_consumed
77
+ end
78
+
79
+ ---- ----------------------------------------------------------------------------
80
+ -- heuristic
81
+ -- tcp_stream_id <-> {client_port, server_port, {request_id<->method_name}}
82
+ local stream_map = {}
83
+ local function heur_dissect_proto (tvbuf , pktinfo , root )
84
+ -- dynmaic decide client or server data
85
+ -- by first tcp syn frame
86
+ local f_src_port = tcp_src_port ()()
87
+ local f_dst_port = tcp_dst_port ()()
88
+ local stream_n = tcp_stream ().value
89
+ if stream_map [stream_n ] == nil then
90
+ stream_map [stream_n ] = {f_src_port , f_dst_port , {}}
91
+ end
92
+
93
+ if (tvbuf :len () < PROTO_HEADER_LENGTH ) then
94
+ return false
95
+ end
96
+
97
+ local magic = tvbuf :range (0 , 2 ):bytes ():tohex ()
98
+ -- for range dissectors
99
+ if magic ~= MAGIC_CODE_TRPC then
100
+ return false
101
+ end
102
+
103
+ trpc_proto .dissector (tvbuf , pktinfo , root )
104
+
105
+ pktinfo .conversation = trpc_proto
106
+
107
+ return true
108
+ end
109
+
110
+ trpc_proto :register_heuristic (" tcp" , heur_dissect_proto )
111
+
112
+ ---- ----------------------------------------------------------------------------
113
+
114
+ -- check packet length, return length of packet if valid
115
+ check_length = function (tvbuf , offset )
116
+ local msglen = tvbuf :len () - offset
117
+
118
+ if msglen ~= tvbuf :reported_length_remaining (offset ) then
119
+ -- captured packets are being sliced/cut-off, so don't try to desegment/reassemble
120
+ LM_WARN (" Captured packet was shorter than original, can't reassemble" )
121
+ return 0
122
+ end
123
+
124
+ if msglen < PROTO_HEADER_LENGTH then
125
+ -- we need more bytes, so tell the main dissector function that we
126
+ -- didn't dissect anything, and we need an unknown number of more
127
+ -- bytes (which is what "DESEGMENT_ONE_MORE_SEGMENT" is used for)
128
+ return - DESEGMENT_ONE_MORE_SEGMENT
129
+ end
130
+
131
+ -- if we got here, then we know we have enough bytes in the Tvb buffer
132
+ -- to at least figure out whether this is valid trpc packet
133
+
134
+ local magic = tvbuf :range (offset , 2 ):bytes ():tohex ()
135
+ if magic ~= MAGIC_CODE_TRPC then
136
+ return 0
137
+ end
138
+
139
+ local packet_size = tvbuf :range (offset + 4 , 4 ):uint ()
140
+ if msglen < packet_size then
141
+ -- Need more bytes to desegment full trpc packet
142
+ return - (packet_size - msglen )
143
+ end
144
+
145
+ return packet_size
146
+ end
147
+
148
+ ---- ----------------------------------------------------------------------------
149
+
150
+ dissect_proto = function (tvbuf , pktinfo , root , offset )
151
+ local len = check_length (tvbuf , offset )
152
+ if len <= 0 then
153
+ return len
154
+ end
155
+
156
+ -- update 'Protocol' field
157
+ if offset == 0 then
158
+ pktinfo .cols .protocol :set (" tRPC" )
159
+ end
160
+
161
+ local f_src_port = tcp_src_port ()()
162
+ local f_dst_port = tcp_dst_port ()()
163
+
164
+ local direction
165
+ local stream_n = tcp_stream ().value
166
+ if f_src_port == stream_map [stream_n ][1 ] then
167
+ pktinfo .private [" pb_msg_type" ] = " message,trpc.RequestProtocol"
168
+ direction = " request"
169
+ end
170
+ if f_src_port == stream_map [stream_n ][2 ] then
171
+ pktinfo .private [" pb_msg_type" ] = " message,trpc.ResponseProtocol"
172
+ direction = " response"
173
+ end
174
+
175
+ -- check packet length,
176
+ local magic_value = tvbuf (offset , 2 )
177
+ local type_value = tvbuf (offset + 2 , 1 )
178
+ local stream_value = tvbuf (offset + 3 , 1 )
179
+ local total_size_value = tvbuf (offset + 4 , 4 )
180
+ local header_size_value = tvbuf (offset + 8 , 2 )
181
+ local unique_id_value = tvbuf (offset + 10 , 4 )
182
+ local version_value = tvbuf (offset + 14 , 1 )
183
+ local reserved_value = tvbuf (offset + 15 , 1 )
184
+
185
+ local header_length = header_size_value :uint ()
186
+ local total_length = total_size_value :uint ()
187
+ local tree = root :add (trpc_proto , tvbuf :range (offset , len ), " tRPC Protocol Data" )
188
+
189
+ data_dissector :call (tvbuf , pktinfo , tree )
190
+
191
+ local t = tree :add (trpc_proto , tvbuf )
192
+ t :add (field_magic , magic_value )
193
+ t :add (field_type , type_value )
194
+ t :add (field_stream , stream_value )
195
+ t :add (field_total_size , total_size_value )
196
+ t :add (field_header_size , header_size_value )
197
+ t :add (field_unique_id , unique_id_value )
198
+ t :add (field_version , version_value )
199
+ t :add (field_reserved , reserved_value )
200
+
201
+ -- solve the problem of parsing errors when multiple RPCs are included in a packet
202
+ local protobuf_field_names = { proto_f_protobuf_field_name () }
203
+ local pre_field_nums = # protobuf_field_names
204
+ pcall (Dissector .call , protobuf_dissector , tvbuf (offset + 16 , header_length ):tvb (), pktinfo , tree )
205
+
206
+ -- Add bussiness rpc pb
207
+ -- Get invoke rpc method name from trpc.RequestProtocol
208
+ protobuf_field_names = { proto_f_protobuf_field_name () }
209
+ local cur_field_nums = # protobuf_field_names
210
+ local protobuf_field_values = { proto_f_protobuf_field_value () }
211
+ local method
212
+ -- default request id
213
+ local request_id = 0
214
+ for k = pre_field_nums + 1 , cur_field_nums do
215
+ local v = protobuf_field_names [k ]
216
+ if v .value == " func" then
217
+ method = protobuf_field_values [k ].range :string (ENC_UTF8 )
218
+ elseif v .value == " request_id" then
219
+ request_id = protobuf_field_values [k ].range :uint ()
220
+ end
221
+ end
222
+
223
+ local tvb_body = tvbuf :range (offset + 16 + header_length , total_length - header_length - 16 ):tvb ()
224
+ if method ~= nil then
225
+ -- only req contains method, correlate it with request id so that response protocol can use.
226
+ stream_map [stream_n ][3 ][request_id ] = method
227
+ pktinfo .private [" pb_msg_type" ] = " application/trpc," .. method .. " ," .. direction
228
+ else
229
+ -- get method for the same request id
230
+ method = stream_map [stream_n ][3 ][request_id ]
231
+ pktinfo .private [" pb_msg_type" ] = " application/trpc," .. method .. " ," .. direction
232
+ end
233
+ pcall (Dissector .call , protobuf_dissector , tvb_body , pktinfo , tree )
234
+
235
+ return total_length
236
+ end
0 commit comments