2 * Routines for GRPC dissection
3 * Copyright 2017,2022 Huang Qiangxiong <qiangxiong.huang@qq.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
13 * The information used comes from:
14 * https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
15 * https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md
17 * This GRPC dissector must be invoked by HTTP2 or HTTP dissector.
18 * The native GRPC is always over HTTP2, the GRPC-Web is over either HTTP2 or HTTP.
20 * The main task of GRPC dissector for native GRPC includes:
22 * 1. Parse grpc message header first, if header shows message is compressed,
23 * it will find grpc-encoding http2 header by invoking http2_get_header_value()
24 * and uncompress the following message body according to the value of
25 * grpc-encoding header. After that grpc dissector call subdissector
26 * to dissect the (uncompressed) data of message body.
28 * 2. GRPC dissector will create and maintain a new dissector table named
29 * 'grpc_message_type'. It allows dissection of a grpc message body.
30 * The pattern format used by this table has two levels:
32 * 1) Request/Response level pattern, which includes request
33 * grpc-method-path (equals to http2 ':path' header value) and
34 * direction (request or response), the format:
35 * http2-content-type "," http2-path "," direction
36 * direction = "request" / "response", for example:
37 * "application/grpc,/helloworld.Greeter/SayHello,request"
38 * The "helloworld.Greeter" is grpc_package "." grpc_service
40 * 2) Content-type level pattern, which just takes http2-content-type
41 * as pattern (for example, "application/grpc",
42 * "application/grpc+proto" and "application/grpc+json").
44 * GRPC dissector will try to call request/response message level
45 * subdissector first. If not found, then try content-type level
46 * dissectors. grpc dissector will always transmit grpc message
47 * information - (http2-content-type "," http2-path "," direction ) to
48 * subdissector in (void *data) parameter of dissect handler.
49 * Content-type level subdissector can use this information to locate
50 * the request/response message type.
52 * For GRPC-WEB, the ways to get information like content-type, path (request uri)
53 * are different. And for GRPC-WEB-TEXT, the dissector will first decode the base64
54 * payload and then dissect the data as GRPC-WEB.
59 #include <epan/conversation.h>
60 #include <epan/proto_data.h>
61 #include <epan/packet.h>
62 #include <epan/expert.h>
63 #include <epan/prefs.h>
64 #include <epan/strutil.h>
65 #include <epan/proto_data.h>
66 #include "packet-http.h"
67 #include "packet-http2.h"
68 #include "packet-media-type.h"
70 #include <wsutil/array.h>
72 #define GRPC_MESSAGE_HEAD_LEN 5
74 /* calculate the size of a bytes after decoding as base64 */
75 #define BASE64_ENCODE_SIZE(len) ((len) / 3 * 4 + ((len) % 3 == 0 ? 0 : 4))
78 * Decompression of zlib encoded entities.
80 #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
81 static bool grpc_decompress_body
= true;
83 static bool grpc_decompress_body
;
86 /* detect json automatically */
87 static bool grpc_detect_json_automatically
= true;
88 /* whether embed GRPC messages under HTTP2 (or other) protocol tree items */
89 static bool grpc_embedded_under_http2
;
91 void proto_register_grpc(void);
92 void proto_reg_handoff_grpc(void);
94 static int proto_grpc
;
95 static int proto_http
;
98 static int hf_grpc_frame_type
;
99 static int hf_grpc_compressed_flag
;
100 static int hf_grpc_message_length
;
102 static int hf_grpc_message_data
;
104 /* grpc protocol type */
105 #define grpc_protocol_type_vals_VALUE_STRING_LIST(XXX) \
106 XXX(GRPC_PTYPE_GRPC, 0, "GRPC") \
107 XXX(GRPC_PTYPE_GRPC_WEB, 1, "GRPC-Web") \
108 XXX(GRPC_PTYPE_GRPC_WEB_TEXT, 2, "GRPC-Web-Text")
110 typedef VALUE_STRING_ENUM(grpc_protocol_type_vals
) grpc_protocol_type_t
;
111 VALUE_STRING_ARRAY(grpc_protocol_type_vals
);
113 /* grpc frame type (grpc-web extension) */
114 #define grpc_frame_type_vals_VALUE_STRING_LIST(XXX) \
115 XXX(GRPC_FRAME_TYPE_DATA, 0, "Data") \
116 XXX(GRPC_FRAME_TYPE_TRAILER, 1, "Trailer")
118 VALUE_STRING_ENUM(grpc_frame_type_vals
);
119 VALUE_STRING_ARRAY(grpc_frame_type_vals
);
121 /* compressed flag vals */
122 #define grpc_compressed_flag_vals_VALUE_STRING_LIST(XXX) \
123 XXX(GRPC_NOT_COMPRESSED, 0, "Not Compressed") \
124 XXX(GRPC_COMPRESSED, 1, "Compressed")
126 VALUE_STRING_ENUM(grpc_compressed_flag_vals
);
127 VALUE_STRING_ARRAY(grpc_compressed_flag_vals
);
130 static expert_field ei_grpc_body_decompression_failed
;
131 static expert_field ei_grpc_body_malformed
;
135 static int ett_grpc_message
;
136 static int ett_grpc_encoded_entity
;
138 static dissector_handle_t grpc_handle
;
139 static dissector_handle_t grpc_web_handle
;
140 static dissector_handle_t grpc_web_text_handle
;
141 static dissector_handle_t data_text_lines_handle
;
143 /* the information used during dissecting a grpc message */
145 bool is_request
; /* is request or response message */
146 grpc_protocol_type_t proto_type
;
147 const char* path
; /* is http2 ":path" or http request_uri, format: "/" Service-Name "/" {method name} */
148 const char* content_type
; /* is http2 or http content-type, like: application/grpc */
149 const char* encoding
; /* is grpc-encoding header containing compressed method, for example "gzip" */
150 } grpc_context_info_t
;
152 /* GRPC message type dissector table list.
153 * Dissectors can register themselves in this table as grpc message data dissectors.
154 * Dissectors registered in this table may use pattern that
155 * contains content-type,grpc-method-path(http2_path),request/response info, like:
156 * application/grpc,/helloworld.Greeter/SayHello,request
157 * or just contains content-type:
159 * application/grpc+proto
160 * application/grpc+json
162 static dissector_table_t grpc_message_type_subdissector_table
;
164 /* Try to dissect grpc message according to grpc message info or http2 content_type. */
166 dissect_body_data(proto_tree
*grpc_tree
, packet_info
*pinfo
, tvbuff_t
*tvb
, const int offset
,
167 int length
, bool continue_dissect
,
168 uint32_t frame_type
, grpc_context_info_t
*grpc_ctx
)
170 const char *http2_content_type
= grpc_ctx
->content_type
;
171 char *grpc_message_info
;
174 proto_tree
*parent_tree
;
176 proto_tree_add_bytes_format_value(grpc_tree
, hf_grpc_message_data
, tvb
, offset
, length
, NULL
, "%u bytes", length
);
178 if (frame_type
== GRPC_FRAME_TYPE_TRAILER
) {
179 call_dissector(data_text_lines_handle
, tvb_new_subset_length(tvb
, offset
, length
), pinfo
, grpc_tree
);
183 if (!continue_dissect
) {
184 return; /* if uncompress failed, we don't continue dissecting. */
187 if (http2_content_type
== NULL
|| grpc_ctx
->path
== NULL
) {
188 return; /* not continue if there is not enough grpc information */
191 next_tvb
= tvb_new_subset_length(tvb
, offset
, length
);
193 /* Try to detect body as json first.
194 * Current grpc-java version sends json on grpc with content-type = application/grpc
195 * insteadof application/grpc+json, so we may detect to dissect message with default
196 * content-type application/grpc by json dissector insteadof protobuf dissector.
198 if (grpc_detect_json_automatically
&& length
> 3
199 && tvb_get_uint8(next_tvb
, 0) == '{') /* start with '{' */
201 uint8_t end_bytes
[3];
202 tvb_memcpy(next_tvb
, end_bytes
, length
- 3, 3);
203 if (end_bytes
[2] == '}' /* end with '}' */
204 || end_bytes
[1] == '}' /* or "}\n" */
205 || end_bytes
[0] == '}') /* or "}\n\r" or " }\r\n" */
207 /* We just replace content-type with "application/grpc+json" insteadof calling
208 JSON dissector directly. Because someone may want to use his own dissector to
209 parse json insteadof default json dissector. */
210 http2_content_type
= "application/grpc+json";
214 /* Since message data (like protobuf) may be not a self-describing protocol, we need
215 * provide grpc service-name, method-name and request or response type to subdissector.
216 * According to these information, subdissector may find correct message definition
217 * from IDL file like ".proto".
219 * We define a string format to carry these information. The benefit using string is
220 * the grpc message information might be used by the other Lua dissector in the future.
221 * The grpc message information format is:
222 * http2_content_type "," http2_path "," ("request" / "response")
223 * According to grpc wire format guide, it will be:
224 * "application/grpc" [("+proto" / "+json" / {custom})] "," "/" service-name "/" method-name "/" "," ("request" / "response")
226 * application/grpc,/helloworld.Greeter/SayHello,request
228 grpc_message_info
= wmem_strconcat(pinfo
->pool
, http2_content_type
, ",",
229 grpc_ctx
->path
, ",", (grpc_ctx
->is_request
? "request" : "response"), NULL
);
231 parent_tree
= proto_tree_get_parent_tree(grpc_tree
);
233 /* Protobuf dissector may be implemented that each request or response message
234 * of a method is defined as an individual dissector, so we try dissect using
235 * grpc_message_info first.
237 dissected
= dissector_try_string_with_data(grpc_message_type_subdissector_table
, grpc_message_info
,
238 next_tvb
, pinfo
, parent_tree
, true, grpc_message_info
);
240 if (dissected
== 0) {
241 /* not dissected yet, we try common subdissector again. */
242 dissector_try_string_with_data(grpc_message_type_subdissector_table
, http2_content_type
,
243 next_tvb
, pinfo
, parent_tree
, true, grpc_message_info
);
248 can_uncompress_body(const char *grpc_encoding
)
250 /* check http2 have a grpc-encoding header appropriate */
251 return grpc_decompress_body
252 && grpc_encoding
!= NULL
253 && (strcmp(grpc_encoding
, "gzip") == 0 || strcmp(grpc_encoding
, "deflate") == 0);
256 /* Dissect a grpc message. The caller needs to guarantee that the length is equal
257 to 5 + message_length according to grpc wire format definition. */
259 dissect_grpc_message(tvbuff_t
*tvb
, unsigned offset
, unsigned length
, packet_info
*pinfo
, proto_tree
*grpc_tree
,
260 grpc_context_info_t
* grpc_ctx
)
262 uint32_t frame_type
, compressed_flag
, message_length
;
263 const char *compression_method
= grpc_ctx
->encoding
;
265 /* GRPC message format:
266 Delimited-Message -> Compressed-Flag Message-Length Message
267 Compressed-Flag -> 0 / 1 # encoded as 1 byte unsigned integer
268 Message-Length -> {length of Message} # encoded as 4 byte unsigned integer
269 Message -> *{binary octet} (may be protobuf or json)
271 Note: GRPC-WEB extend the MSB of Compressed-Flag as frame type (0-data, 1-trailer)
273 proto_tree_add_item_ret_uint(grpc_tree
, hf_grpc_frame_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
, &frame_type
);
274 proto_tree_add_item_ret_uint(grpc_tree
, hf_grpc_compressed_flag
, tvb
, offset
, 1, ENC_BIG_ENDIAN
, &compressed_flag
);
277 if (frame_type
== GRPC_FRAME_TYPE_TRAILER
) {
278 proto_item_append_text(proto_tree_get_parent(grpc_tree
), " (Trailer)");
281 proto_tree_add_item(grpc_tree
, hf_grpc_message_length
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
282 message_length
= length
- 5; /* should be equal to tvb_get_ntohl(tvb, offset) */
285 if (message_length
== 0) {
289 /* uncompressed message data if compressed_flag is set */
290 if (compressed_flag
& GRPC_COMPRESSED
) {
291 if (can_uncompress_body(compression_method
)) {
292 proto_item
*compressed_proto_item
= NULL
;
293 tvbuff_t
*uncompressed_tvb
= tvb_child_uncompress_zlib(tvb
, tvb
, offset
, message_length
);
295 proto_tree
*compressed_entity_tree
= proto_tree_add_subtree_format(
296 grpc_tree
, tvb
, offset
, message_length
, ett_grpc_encoded_entity
,
297 &compressed_proto_item
, "Message-encoded entity body (%s): %u bytes",
298 compression_method
== NULL
? "unknown" : compression_method
, message_length
301 if (uncompressed_tvb
!= NULL
) {
302 unsigned uncompressed_length
= tvb_captured_length(uncompressed_tvb
);
303 add_new_data_source(pinfo
, uncompressed_tvb
, "Uncompressed entity body");
304 proto_item_append_text(compressed_proto_item
, " -> %u bytes", uncompressed_length
);
305 dissect_body_data(grpc_tree
, pinfo
, uncompressed_tvb
, 0, uncompressed_length
, true, frame_type
, grpc_ctx
);
307 proto_tree_add_expert(compressed_entity_tree
, pinfo
, &ei_grpc_body_decompression_failed
,
308 tvb
, offset
, message_length
);
309 dissect_body_data(grpc_tree
, pinfo
, tvb
, offset
, message_length
, false, frame_type
, grpc_ctx
);
311 } else { /* compressed flag is set, but we can not uncompressed */
312 dissect_body_data(grpc_tree
, pinfo
, tvb
, offset
, message_length
, false, frame_type
, grpc_ctx
);
315 dissect_body_data(grpc_tree
, pinfo
, tvb
, offset
, message_length
, true, frame_type
, grpc_ctx
);
318 return offset
+ message_length
;
322 dissect_grpc_common(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, grpc_context_info_t
*grpc_ctx
)
325 proto_tree
*grpc_tree
;
326 uint32_t message_length
;
328 unsigned tvb_len
= tvb_reported_length(tvb
);
329 const char* proto_name
;
331 DISSECTOR_ASSERT(grpc_ctx
!= NULL
);
333 proto_name
= val_to_str_const(grpc_ctx
->proto_type
, grpc_protocol_type_vals
, "GRPC");
335 if (!grpc_embedded_under_http2
&& proto_tree_get_parent_tree(tree
)) {
336 tree
= proto_tree_get_parent_tree(tree
);
339 /* http2 had reassembled the http2.data.data, so we need not reassemble again.
340 reassembled http2.data.data may contain one or more grpc messages. */
341 while (offset
< tvb_len
)
343 if (tvb_len
- offset
< GRPC_MESSAGE_HEAD_LEN
) {
344 /* need at least 5 bytes for dissecting a grpc message */
345 if (pinfo
->can_desegment
) {
346 pinfo
->desegment_offset
= offset
;
347 pinfo
->desegment_len
= GRPC_MESSAGE_HEAD_LEN
- (tvb_len
- offset
);
350 proto_tree_add_expert_format(tree
, pinfo
, &ei_grpc_body_malformed
, tvb
, offset
, -1,
351 "GRPC Malformed message data: only %u bytes left, need at least %u bytes.", tvb_len
- offset
, GRPC_MESSAGE_HEAD_LEN
);
355 message_length
= tvb_get_ntohl(tvb
, offset
+ 1);
356 if (tvb_len
- offset
< GRPC_MESSAGE_HEAD_LEN
+ message_length
) {
357 /* remaining bytes are not enough for dissecting the message body */
358 if (pinfo
->can_desegment
) {
359 pinfo
->desegment_offset
= offset
;
360 pinfo
->desegment_len
= GRPC_MESSAGE_HEAD_LEN
+ message_length
- (tvb_len
- offset
);
363 proto_tree_add_expert_format(tree
, pinfo
, &ei_grpc_body_malformed
, tvb
, offset
, -1,
364 "GRPC Malformed message data: only %u bytes left, need at least %u bytes.", tvb_len
- offset
, GRPC_MESSAGE_HEAD_LEN
+ message_length
);
367 /* ready to add information into protocol columns and tree */
368 if (offset
== 0) { /* change columns only when there is at least one grpc message will be parsed */
369 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, proto_name
);
370 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " (%s)", proto_name
);
371 col_set_fence(pinfo
->cinfo
, COL_PROTOCOL
);
373 ti
= proto_tree_add_item(tree
, proto_grpc
, tvb
, offset
, message_length
+ GRPC_MESSAGE_HEAD_LEN
, ENC_NA
);
374 grpc_tree
= proto_item_add_subtree(ti
, ett_grpc_message
);
375 proto_item_set_text(ti
, "%s Message", proto_name
);
377 if (grpc_ctx
->path
) {
378 proto_item_append_text(ti
, ": %s, %s", grpc_ctx
->path
, (grpc_ctx
->is_request
? "Request" : "Response"));
381 offset
= dissect_grpc_message(tvb
, offset
, GRPC_MESSAGE_HEAD_LEN
+ message_length
, pinfo
, grpc_tree
, grpc_ctx
);
384 return tvb_captured_length(tvb
);
387 static grpc_context_info_t
*
388 get_grpc_context(packet_info
*pinfo
, const media_content_info_t
*content_info
)
390 http_req_res_t
* curr_req_res
;
391 grpc_context_info_t
* grpc_ctx
= wmem_new0(pinfo
->pool
, grpc_context_info_t
);
393 if (proto_is_frame_protocol(pinfo
->layers
, "http2")) {
394 grpc_ctx
->path
= http2_get_header_value(pinfo
, HTTP2_HEADER_PATH
, false);
395 grpc_ctx
->is_request
= (grpc_ctx
->path
!= NULL
);
396 if (grpc_ctx
->path
== NULL
) {
397 /* this must be response, so we get it from http2 request stream */
398 /* XXX - Not necessarily true if the data has errors. */
399 grpc_ctx
->path
= http2_get_header_value(pinfo
, HTTP2_HEADER_PATH
, true);
401 grpc_ctx
->content_type
= http2_get_header_value(pinfo
, HTTP2_HEADER_CONTENT_TYPE
, false);
402 grpc_ctx
->encoding
= http2_get_header_value(pinfo
, HTTP2_HEADER_GRPC_ENCODING
, false);
404 else if (proto_is_frame_protocol(pinfo
->layers
, "http")) {
405 curr_req_res
= (http_req_res_t
*)p_get_proto_data(wmem_file_scope(), pinfo
, proto_http
, HTTP_PROTO_DATA_REQRES
);
406 DISSECTOR_ASSERT_HINT(curr_req_res
&& content_info
, "Unexpected error: HTTP request/reply or HTTP message info not available.");
407 grpc_ctx
->is_request
= (content_info
->type
== MEDIA_CONTAINER_HTTP_REQUEST
);
408 grpc_ctx
->path
= curr_req_res
->request_uri
;
409 grpc_ctx
->content_type
= pinfo
->match_string
; /* only for grpc-web(-text) over http1.1 */
410 if (content_info
->data
) {
411 grpc_ctx
->encoding
= (const char*)wmem_map_lookup((wmem_map_t
*)content_info
->data
, HTTP2_HEADER_GRPC_ENCODING
);
415 /* unexpected protocol error */
416 DISSECTOR_ASSERT_NOT_REACHED();
423 dissect_grpc(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, void* data
)
425 grpc_context_info_t
* grpc_ctx
;
426 media_content_info_t
* content_info
= (media_content_info_t
*)data
;
428 grpc_ctx
= get_grpc_context(pinfo
, content_info
);
429 grpc_ctx
->proto_type
= GRPC_PTYPE_GRPC
;
431 return dissect_grpc_common(tvb
, pinfo
, tree
, grpc_ctx
);
435 dissect_grpc_web(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, void* data
)
437 grpc_context_info_t
* grpc_ctx
;
438 media_content_info_t
* content_info
= (media_content_info_t
*)data
;
440 grpc_ctx
= get_grpc_context(pinfo
, content_info
);
441 grpc_ctx
->proto_type
= GRPC_PTYPE_GRPC_WEB
;
443 return dissect_grpc_common(tvb
, pinfo
, tree
, grpc_ctx
);
447 dissect_grpc_web_text(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, void* data
)
450 tvbuff_t
* real_data_tvb
;
451 grpc_context_info_t
* grpc_ctx
;
452 media_content_info_t
* content_info
= (media_content_info_t
*)data
;
454 real_data_tvb
= base64_tvb_to_new_tvb(tvb
, 0, tvb_reported_length(tvb
));
455 add_new_data_source(pinfo
, real_data_tvb
, "Decoded base64 body");
457 grpc_ctx
= get_grpc_context(pinfo
, content_info
);
458 grpc_ctx
->proto_type
= GRPC_PTYPE_GRPC_WEB_TEXT
;
460 ret
= dissect_grpc_common(real_data_tvb
, pinfo
, tree
, grpc_ctx
);
462 /* convert reassembly the lengths of offset and remaining bytes back to the base64 lengths */
463 pinfo
->desegment_offset
= BASE64_ENCODE_SIZE(pinfo
->desegment_offset
);
464 pinfo
->desegment_len
= BASE64_ENCODE_SIZE(pinfo
->desegment_len
);
470 proto_register_grpc(void)
473 static hf_register_info hf
[] = {
474 { &hf_grpc_frame_type
,
475 { "Frame Type", "grpc.frame_type",
476 FT_UINT8
, BASE_DEC
, VALS(grpc_frame_type_vals
), 0x80,
477 "The frame type of this grpc message (GRPC-WEB extension)", HFILL
}
479 { &hf_grpc_compressed_flag
,
480 { "Compressed Flag", "grpc.compressed_flag",
481 FT_UINT8
, BASE_DEC
, VALS(grpc_compressed_flag_vals
), 0x01,
482 "Compressed-Flag value of 1 indicates that the binary octet sequence of Message is compressed", HFILL
}
484 { &hf_grpc_message_length
,
485 { "Message Length", "grpc.message_length",
486 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
487 "The length (32 bits) of message payload (not include itself)", HFILL
}
489 { &hf_grpc_message_data
,
490 { "Message Data", "grpc.message_data",
491 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
496 static int *ett
[] = {
499 &ett_grpc_encoded_entity
502 /* Setup protocol expert items */
503 static ei_register_info ei
[] = {
504 { &ei_grpc_body_decompression_failed
,
505 { "grpc.body_decompression_failed", PI_UNDECODED
, PI_WARN
,
506 "Body decompression failed", EXPFILL
}
508 { &ei_grpc_body_malformed
,
509 { "grpc.body_malformed", PI_UNDECODED
, PI_WARN
,
510 "Malformed message data", EXPFILL
}
514 module_t
*grpc_module
;
515 expert_module_t
*expert_grpc
;
517 proto_grpc
= proto_register_protocol("GRPC Message", "GRPC", "grpc");
519 proto_register_field_array(proto_grpc
, hf
, array_length(hf
));
520 proto_register_subtree_array(ett
, array_length(ett
));
522 grpc_module
= prefs_register_protocol(proto_grpc
, NULL
);
524 prefs_register_bool_preference(grpc_module
, "detect_json_automatically",
525 "Always check whether the message is JSON regardless of content-type.",
526 "Normally application/grpc message is protobuf, "
527 "but sometime the true message is json. "
528 "If this option in on, we always check whether the message is JSON "
529 "(body starts with '{' and ends with '}') regardless of "
530 "grpc_message_type_subdissector_table settings (which dissect grpc "
531 "message according to content-type).",
532 &grpc_detect_json_automatically
);
534 prefs_register_bool_preference(grpc_module
, "embedded_under_http2",
535 "Embed gRPC messages under HTTP2 (or other) protocol tree items.",
536 "Embed gRPC messages under HTTP2 (or other) protocol tree items.",
537 &grpc_embedded_under_http2
);
539 prefs_register_static_text_preference(grpc_module
, "service_definition",
540 "Please refer to preferences of Protobuf for specifying gRPC Service Definitions (*.proto).",
541 "Including specifying .proto files search paths, etc.");
543 expert_grpc
= expert_register_protocol(proto_grpc
);
544 expert_register_field_array(expert_grpc
, ei
, array_length(ei
));
546 grpc_handle
= register_dissector_with_description("grpc", "gRPC", dissect_grpc
, proto_grpc
);
547 grpc_web_handle
= register_dissector_with_description("grpc_web", "gRPC Web", dissect_grpc_web
, proto_grpc
);
548 grpc_web_text_handle
= register_dissector_with_description("grpc_web_text", "gRPC Web Text", dissect_grpc_web_text
, proto_grpc
);
551 * Dissectors can register themselves in this table as grpc message
552 * subdissector. Default it support json, protobuf.
554 grpc_message_type_subdissector_table
=
555 register_dissector_table("grpc_message_type",
556 "GRPC message type", proto_grpc
, FT_STRING
, STRING_CASE_SENSITIVE
);
560 proto_reg_handoff_grpc(void)
564 char *content_types
[] = {
566 "application/grpc+proto",
567 "application/grpc+json",
571 char *content_types_web
[] = {
572 "application/grpc-web",
573 "application/grpc-web+proto",
577 char *content_types_web_text
[] = {
578 "application/grpc-web-text",
579 "application/grpc-web-text+proto",
583 /* register native grpc handler */
584 for (i
= 0; content_types
[i
]; i
++) {
585 dissector_add_string("streaming_content_type", content_types
[i
], grpc_handle
);
586 dissector_add_string("media_type", content_types
[i
], grpc_handle
);
589 /* register gRPC Web handler */
590 for (i
= 0; content_types_web
[i
]; i
++) {
591 dissector_add_string("streaming_content_type", content_types_web
[i
], grpc_web_handle
);
592 dissector_add_string("media_type", content_types_web
[i
], grpc_web_handle
);
595 /* register gRPC Web Text handler */
596 for (i
= 0; content_types_web_text
[i
]; i
++) {
597 dissector_add_string("streaming_content_type", content_types_web_text
[i
], grpc_web_text_handle
);
598 dissector_add_string("media_type", content_types_web_text
[i
], grpc_web_text_handle
);
601 proto_http
= proto_get_id_by_filter_name("http");
602 data_text_lines_handle
= find_dissector_add_dependency("data-text-lines", proto_grpc
);
606 * Editor modelines - https://www.wireshark.org/tools/modelines.html
611 * indent-tabs-mode: nil
614 * vi: set shiftwidth=4 tabstop=8 expandtab:
615 * :indentSize=4:tabSize=8:noTabs=true: