2018-10-17 01:39:35 +00:00
/* packet-blip.c
*
* BLIP protocol for Couchbase Lite < - > Sync Gateway 2.0 + Replication
*
* Spec : https : //github.com/couchbaselabs/BLIP-Cpp/blob/master/docs/BLIP%20Protocol.md
*
* Copyright 2018 , Traun Leyden < traun @ couchbase . com >
* Copyright 2018 , Jim Borden < jim . borden @ couchbase . com >
*
* Wireshark - Network traffic analyzer
* By Gerald Combs < gerald @ wireshark . org >
* Copyright 1998 Gerald Combs
*
* SPDX - License - Identifier : GPL - 2.0 - or - later
*/
# include "config.h"
# include <epan/packet.h>
# include <epan/conversation.h>
2020-09-23 05:16:27 +00:00
# include <epan/prefs.h>
# include <epan/expert.h>
2019-07-03 08:12:21 +00:00
# include "proto_data.h"
2018-10-17 01:39:35 +00:00
2018-11-14 11:48:06 +00:00
# ifdef HAVE_ZLIB
2018-10-17 01:39:35 +00:00
# include <zlib.h>
2018-11-14 11:48:06 +00:00
# endif
2018-10-17 01:39:35 +00:00
2018-12-25 16:54:54 +00:00
void proto_reg_handoff_blip ( void ) ;
void proto_register_blip ( void ) ;
2018-10-17 01:39:35 +00:00
# define BLIP_BODY_CHECKSUM_SIZE 4
// blip_conversation_entry_t is metadata that the blip dissector associates w/ each wireshark conversation
typedef struct {
// Keep track of the largest frame number seen. This is useful for determining whether
// this is the first frame in a request message or not.
2019-07-03 08:12:21 +00:00
// key: msgtype:srcport:destport:messagenumber -> value: frame number for the _first_ frame in this request message
// Example: "MSG:23243:4984:56" -> 12
// which means: "the first frame for blip message number 56, originating from source port 23243,
// ... and going to port 4984 for message type = MSG occurred in wireshark packet #12"
2018-10-17 01:39:35 +00:00
wmem_map_t * blip_requests ;
2019-07-03 08:12:21 +00:00
# ifdef HAVE_ZLIB
// The streams used to decode a particular connection. These are per direction and per connection.
wmem_map_t * decompress_streams ;
# endif
2018-10-17 01:39:35 +00:00
} blip_conversation_entry_t ;
2019-07-03 08:12:21 +00:00
# ifdef HAVE_ZLIB
2020-09-23 05:16:27 +00:00
typedef enum
{
no_error = 0 ,
zlib_error ,
overflow_error
} decompress_error_t ;
2019-07-03 08:12:21 +00:00
typedef struct
{
2020-09-23 05:16:27 +00:00
decompress_error_t domain ;
int code ;
2019-07-03 08:12:21 +00:00
size_t size ;
void * buf ;
2020-09-23 05:16:27 +00:00
} decompress_result_t ;
2019-07-03 08:12:21 +00:00
# endif
2018-10-17 01:39:35 +00:00
// File level variables
static dissector_handle_t blip_handle ;
static int proto_blip = - 1 ;
static int hf_blip_message_number = - 1 ;
static int hf_blip_frame_flags = - 1 ;
static int hf_blip_properties_length = - 1 ;
static int hf_blip_properties = - 1 ;
static int hf_blip_message_body = - 1 ;
static int hf_blip_ack_size = - 1 ;
static int hf_blip_checksum = - 1 ;
2020-09-23 05:16:27 +00:00
2018-10-17 01:39:35 +00:00
static gint ett_blip = - 1 ;
2020-09-23 19:44:30 +00:00
static expert_field ei_blip_decompress_buffer_error = EI_INIT ;
2020-09-23 05:16:27 +00:00
2018-10-17 01:39:35 +00:00
// Compressed = 0x08
// Urgent = 0x10
// NoReply = 0x20
// MoreComing = 0x40
// In ascending order so that a binary search will be used as per the
// README.dissector
static const value_string flag_combos [ ] = {
{ 0x00 , " None " } ,
{ 0x08 , " Compressed " } ,
{ 0x10 , " Urgent " } ,
{ 0x10 | 0x08 , " Compressed|Urgent " } ,
{ 0x20 , " NoReply " } ,
{ 0x20 | 0x08 , " Compressed|NoReply " } ,
{ 0x20 | 0x10 , " Urgent|NoReply " } ,
{ 0x20 | 0x10 | 0x08 , " Compressed|Urgent|NoReply " } ,
{ 0x40 , " MoreComing " } ,
{ 0x40 | 0x08 , " Compressed|MoreComing " } ,
{ 0x40 | 0x10 , " Urgent|MoreComing " } ,
{ 0x40 | 0x10 | 0x08 , " Compressed|Urgent|MoreComing " } ,
{ 0x40 | 0x20 , " NoReply|MoreComing " } ,
{ 0x40 | 0x20 | 0x08 , " Compressed|NoReply|MoreComing " } ,
{ 0x40 | 0x20 | 0x10 , " Urgent|NoReply|MoreComing " } ,
{ 0x40 | 0x20 | 0x10 | 0x08 , " Compressed|Urgent|NoReply|MoreComing " } ,
{ 0 , NULL }
} ;
static value_string_ext flag_combos_ext = VALUE_STRING_EXT_INIT ( flag_combos ) ;
static const val64_string msg_types [ ] = {
{ 0x00ll , " MSG " } ,
{ 0x01ll , " RPY " } ,
{ 0x02ll , " ERR " } ,
{ 0x04ll , " ACKMSG " } ,
{ 0x05ll , " ACKRPY " } ,
{ 0 , NULL }
} ;
2020-09-23 05:16:27 +00:00
// Preferences
# ifdef HAVE_ZLIB
static guint max_uncompressed_size = 64 ; // Max uncompressed body size in Kb
# endif
2018-10-17 01:39:35 +00:00
// MSG = 0x00
// RPY = 0x01
// ERR = 0x02
// ACKMSG = 0x04
// ACKRPY = 0x05
static const gchar *
get_message_type ( guint64 value_frame_flags )
{
// Mask out the least significant bits: 0000 0111
guint64 type_mask_val = ( 0x07ll & value_frame_flags ) ;
return val64_to_str_const ( type_mask_val , msg_types , " ??? " ) ;
}
static gboolean
is_ack_message ( guint64 value_frame_flags ) {
2019-07-03 08:12:21 +00:00
// Note, even though this is a 64-bit int, only the least significant byte has meaningful information,
2018-10-17 01:39:35 +00:00
// since frame flags all fit into one byte at the time this code was written.
// Mask out the least significant bits: 0000 0111
guint64 type_mask_val = ( 0x07ll & value_frame_flags ) ;
// ACKMSG
if ( type_mask_val = = 0x04ll ) {
return TRUE ;
}
// ACKRPY
if ( type_mask_val = = 0x05ll ) {
return TRUE ;
}
return FALSE ;
}
static gboolean
is_compressed ( guint64 value_frame_flags )
{
// Note, even though this is a 64-bit int, only the least significant byte has meaningful information,
// since frame flags all fit into one byte at the time this code was written.
if ( ( 0x08ll & value_frame_flags ) = = 0x08ll ) {
return TRUE ;
}
return FALSE ;
}
static gchar *
message_hash_key_convo ( packet_info * pinfo ,
guint64 value_frame_flags ,
guint64 value_message_num )
{
// Derive the hash key to use
// msgtype:srcport:destport:messagenum
const gchar * msg_type = get_message_type ( value_frame_flags ) ;
2021-07-16 15:36:34 +00:00
gchar * hash_key = wmem_strdup_printf ( pinfo - > pool , " %s:%u:%u:% " G_GINT64_MODIFIER " u " ,
2018-10-17 01:39:35 +00:00
msg_type , pinfo - > srcport , pinfo - > destport , value_message_num ) ;
return hash_key ;
}
// Finds out whether this is the first blip frame in the blip message (which can consist of a series of frames).
// If it is, updates the conversation_entry_ptr->blip_requests hash to record the pinfo->num (wireshark packet number)
static gboolean
is_first_frame_in_msg ( blip_conversation_entry_t * conversation_entry_ptr , packet_info * pinfo ,
guint64 value_frame_flags , guint64 value_message_num ) {
gboolean first_frame_in_msg = TRUE ;
// Temporary pool for the lookup hash_key. Will get duplicated on the file_scope() pool if needed to be
// stored in the hashtable.
gchar * hash_key = message_hash_key_convo ( pinfo , value_frame_flags , value_message_num ) ;
guint * first_frame_number_for_msg = ( guint * ) wmem_map_lookup ( conversation_entry_ptr - > blip_requests , ( void * ) hash_key ) ;
if ( first_frame_number_for_msg ! = NULL ) {
if ( GPOINTER_TO_UINT ( first_frame_number_for_msg ) ! = pinfo - > num ) {
first_frame_in_msg = FALSE ;
}
} else {
// If storing the key in the hashmap, re-allocate it with the file_scope() allocator
gchar * hash_key_copy = wmem_strdup ( wmem_file_scope ( ) , hash_key ) ;
wmem_map_insert ( conversation_entry_ptr - > blip_requests , ( void * ) hash_key_copy , GUINT_TO_POINTER ( pinfo - > num ) ) ;
}
return first_frame_in_msg ;
}
static int
blip: fix used-but-marked-unused warnings
blip.c:195:4: error: 'offset' was marked unused but was used
offset,
^
blip.c:200:22: error: 'blip_tree' was marked unused but was used
proto_tree_add_item(blip_tree, hf_blip_ack_size, tvb, offset, varint_ack_size_length, ENC_VARINT_PROTOBUF);
^
blip.c:200:56: error: 'offset' was marked unused but was used
proto_tree_add_item(blip_tree, hf_blip_ack_size, tvb, offset, varint_ack_size_length, ENC_VARINT_PROTOBUF);
^
blip.c:202:2: error: 'offset' was marked unused but was used
offset += varint_ack_size_length;
^
blip.c:284:14: error: 'pinfo' was marked unused but was used
col_set_str(pinfo->cinfo, COL_PROTOCOL, "BLIP");
^
blip.c:286:12: error: 'pinfo' was marked unused but was used
col_clear(pinfo->cinfo,COL_INFO);
^
blip.c:333:14: error: 'pinfo' was marked unused but was used
col_add_str(pinfo->cinfo, COL_INFO, col_info);
^
blip.c:337:34: error: 'pinfo' was marked unused but was used
return handle_ack_message(tvb, pinfo, blip_tree, offset, value_frame_flags);
^
blip.c:346:45: error: 'pinfo' was marked unused but was used
conversation = find_or_create_conversation(pinfo);
^
blip.c:361:4: error: 'pinfo' was marked unused but was used
pinfo,
^
blip.c:380:27: error: 'pinfo' was marked unused but was used
tvb_to_use = decompress(pinfo, tvb, offset, tvb_reported_length_remaining(tvb, offset) - BLIP_BODY_CHECKSUM_SIZE);
Change-Id: I9de1a78942469cc16011fd1a21d93b81820bee80
Reviewed-on: https://code.wireshark.org/review/33373
Reviewed-by: Martin Kaiser <wireshark@kaiser.cx>
Petri-Dish: Martin Kaiser <wireshark@kaiser.cx>
Petri-Dish: Anders Broman <a.broman58@gmail.com>
Tested-by: Petri Dish Buildbot
Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
2019-05-26 16:39:54 +00:00
handle_ack_message ( tvbuff_t * tvb , _U_ packet_info * pinfo , proto_tree * blip_tree , gint offset , _U_ guint64 value_frame_flags )
2018-10-17 01:39:35 +00:00
{
// This gets the number of ack bytes received as a var int in order to find out how much to bump
// the offset for the next proto_tree item
guint64 value_ack_size ;
guint varint_ack_size_length = tvb_get_varint (
tvb ,
offset ,
FT_VARINT_MAX_LEN ,
& value_ack_size ,
ENC_VARINT_PROTOBUF ) ;
proto_tree_add_item ( blip_tree , hf_blip_ack_size , tvb , offset , varint_ack_size_length , ENC_VARINT_PROTOBUF ) ;
return tvb_captured_length ( tvb ) ;
}
2019-07-03 08:12:21 +00:00
static blip_conversation_entry_t *
get_blip_conversation ( packet_info * pinfo )
{
// Create a new conversation if needed and associate the blip_conversation_entry_t with it
// Adapted from sample code in doc/README.dissector
conversation_t * conversation ;
conversation = find_or_create_conversation ( pinfo ) ;
blip_conversation_entry_t * conversation_entry_ptr = ( blip_conversation_entry_t * ) conversation_get_proto_data ( conversation , proto_blip ) ;
if ( conversation_entry_ptr = = NULL ) {
// create a new blip_conversation_entry_t
conversation_entry_ptr = wmem_new ( wmem_file_scope ( ) , blip_conversation_entry_t ) ;
// create a new hash map and save a reference in blip_conversation_entry_t
conversation_entry_ptr - > blip_requests = wmem_map_new ( wmem_file_scope ( ) , g_str_hash , g_str_equal ) ;
# ifdef HAVE_ZLIB
conversation_entry_ptr - > decompress_streams = wmem_map_new ( wmem_file_scope ( ) , g_direct_hash , g_direct_equal ) ;
# endif
conversation_add_proto_data ( conversation , proto_blip , conversation_entry_ptr ) ;
}
return conversation_entry_ptr ;
}
2018-11-14 11:48:06 +00:00
# ifdef HAVE_ZLIB
2019-07-03 08:12:21 +00:00
2018-11-14 11:48:06 +00:00
static gboolean
z_stream_destroy_cb ( wmem_allocator_t * allocator _U_ , wmem_cb_event_t event _U_ , void * user_data )
2018-10-17 01:39:35 +00:00
{
2018-11-14 11:48:06 +00:00
z_stream * decompress_stream = ( z_stream * ) user_data ;
inflateEnd ( decompress_stream ) ;
return FALSE ;
2018-10-17 01:39:35 +00:00
}
2019-07-03 08:12:21 +00:00
static z_stream *
get_decompress_stream ( packet_info * pinfo )
2018-10-17 01:39:35 +00:00
{
2019-07-03 08:12:21 +00:00
const blip_conversation_entry_t * blip_convo = get_blip_conversation ( pinfo ) ;
2018-11-14 11:48:06 +00:00
// Store compression state per srcport/destport.
guint32 hash_key = ( pinfo - > srcport < < 16 ) | pinfo - > destport ;
2019-07-03 08:12:21 +00:00
z_stream * decompress_stream = ( z_stream * ) wmem_map_lookup ( blip_convo - > decompress_streams , GUINT_TO_POINTER ( hash_key ) ) ;
2018-10-17 01:39:35 +00:00
if ( decompress_stream ) {
return decompress_stream ;
}
decompress_stream = wmem_new0 ( wmem_file_scope ( ) , z_stream ) ;
2019-07-03 08:12:21 +00:00
wmem_map_insert ( blip_convo - > decompress_streams , GUINT_TO_POINTER ( hash_key ) , decompress_stream ) ;
2018-11-14 11:48:06 +00:00
wmem_register_callback ( wmem_file_scope ( ) , z_stream_destroy_cb , decompress_stream ) ;
2019-07-03 08:12:21 +00:00
2018-10-17 01:39:35 +00:00
return decompress_stream ;
}
static tvbuff_t *
2020-09-23 05:16:27 +00:00
decompress ( packet_info * pinfo , proto_tree * tree , tvbuff_t * tvb , gint offset , gint length )
2018-10-17 01:39:35 +00:00
{
2019-07-03 08:12:21 +00:00
if ( PINFO_FD_VISITED ( pinfo ) ) {
2020-09-23 05:16:27 +00:00
const decompress_result_t * saved_data = ( decompress_result_t * ) p_get_proto_data ( wmem_file_scope ( ) , pinfo , proto_blip , 0 ) ;
if ( ! saved_data ) {
proto_tree_add_string ( tree , hf_blip_message_body , tvb , offset , tvb_reported_length_remaining ( tvb , offset ) , " <Error decompressing data> " ) ;
return NULL ;
}
if ( saved_data - > domain ) {
proto_item * field = proto_tree_add_string ( tree , hf_blip_message_body , tvb , offset , tvb_reported_length_remaining ( tvb , offset ) , " <Error decompressing data> " ) ;
if ( saved_data - > domain = = zlib_error ) {
2020-09-23 19:44:30 +00:00
expert_add_info_format ( pinfo , field , & ei_blip_decompress_buffer_error , " Unable to decompress message, got zlib error %d " , saved_data - > code ) ;
2020-09-23 05:16:27 +00:00
} else {
2020-09-23 19:44:30 +00:00
expert_add_info_format ( pinfo , field , & ei_blip_decompress_buffer_error , " Unable to decompress message, buffer too small (%u Kb). Please adjust in settings. " , max_uncompressed_size ) ;
2020-09-23 05:16:27 +00:00
}
return NULL ;
} else {
tvbuff_t * decompressedChild = tvb_new_child_real_data ( tvb , ( guint8 * ) saved_data - > buf ,
( gint ) saved_data - > size , ( gint ) saved_data - > size ) ;
add_new_data_source ( pinfo , decompressedChild , " Decompressed Payload " ) ;
return decompressedChild ;
}
2019-07-03 08:12:21 +00:00
}
2020-09-23 05:16:27 +00:00
static gboolean size_overflow = FALSE ;
2019-07-03 08:12:21 +00:00
const guint8 * buf = tvb_get_ptr ( tvb , offset , length ) ;
2018-10-17 01:39:35 +00:00
z_stream * decompress_stream = get_decompress_stream ( pinfo ) ;
static Byte trailer [ 4 ] = { 0x00 , 0x00 , 0xff , 0xff } ;
if ( ! decompress_stream - > next_out ) {
decompress_stream - > zalloc = 0 ;
decompress_stream - > zfree = 0 ;
decompress_stream - > opaque = 0 ;
int err = inflateInit2 ( decompress_stream , - MAX_WBITS ) ;
if ( err ! = Z_OK ) {
decompress_stream - > next_out = 0 ;
REPORT_DISSECTOR_BUG ( " Unable to create INFLATE context to decompress messages " ) ;
return NULL ;
}
}
2020-09-23 05:16:27 +00:00
// Create a temporary buffer of the maximum size, which will get cleaned up later
// when the packet scope is freed
uInt buffer_size = max_uncompressed_size * 1024 ;
2021-07-16 15:36:34 +00:00
Bytef * decompress_buffer = ( Bytef * ) wmem_alloc ( pinfo - > pool , buffer_size ) ;
2018-10-17 01:39:35 +00:00
decompress_stream - > next_in = ( Bytef * ) buf ;
decompress_stream - > avail_in = length ;
decompress_stream - > next_out = decompress_buffer ;
2020-09-23 05:16:27 +00:00
decompress_stream - > avail_out = buffer_size ;
2018-10-17 01:39:35 +00:00
uLong start = decompress_stream - > total_out ;
int err = inflate ( decompress_stream , Z_NO_FLUSH ) ;
2020-09-23 05:16:27 +00:00
if ( err ! = Z_OK ) {
proto_item * field = proto_tree_add_string ( tree , hf_blip_message_body , tvb , offset , tvb_reported_length_remaining ( tvb , offset ) , " <Error decompressing data> " ) ;
decompress_result_t * data_to_save = wmem_new0 ( wmem_file_scope ( ) , decompress_result_t ) ;
if ( size_overflow & & err = = Z_DATA_ERROR ) {
data_to_save - > domain = overflow_error ;
2020-09-23 19:44:30 +00:00
expert_add_info_format ( pinfo , field , & ei_blip_decompress_buffer_error , " Unable to decompress message, buffer too small (%u Kb). Please adjust in settings. " , max_uncompressed_size ) ;
2020-09-23 05:16:27 +00:00
} else {
data_to_save - > domain = zlib_error ;
data_to_save - > code = err ;
2020-09-23 19:44:30 +00:00
expert_add_info_format ( pinfo , field , & ei_blip_decompress_buffer_error , " Unable to decompress message, got zlib error %d " , err ) ;
2020-09-23 05:16:27 +00:00
}
p_add_proto_data ( wmem_file_scope ( ) , pinfo , proto_blip , 0 , data_to_save ) ;
2018-10-17 01:39:35 +00:00
return NULL ;
}
decompress_stream - > next_in = trailer ;
decompress_stream - > avail_in = 4 ;
err = inflate ( decompress_stream , Z_SYNC_FLUSH ) ;
2020-09-23 05:16:27 +00:00
if ( err ! = Z_OK ) {
proto_item * field = proto_tree_add_string ( tree , hf_blip_message_body , tvb , offset , tvb_reported_length_remaining ( tvb , offset ) , " <Error decompressing data> " ) ;
decompress_result_t * data_to_save = wmem_new0 ( wmem_file_scope ( ) , decompress_result_t ) ;
if ( err = = Z_BUF_ERROR ) {
data_to_save - > domain = overflow_error ;
size_overflow = TRUE ;
2020-09-23 19:44:30 +00:00
expert_add_info_format ( pinfo , field , & ei_blip_decompress_buffer_error , " Unable to decompress message, buffer too small (%u Kb). Please adjust in settings. " , max_uncompressed_size ) ;
2020-09-23 05:16:27 +00:00
} else {
data_to_save - > domain = zlib_error ;
data_to_save - > code = err ;
2020-09-23 19:44:30 +00:00
expert_add_info_format ( pinfo , field , & ei_blip_decompress_buffer_error , " Unable to decompress message, got zlib error %d " , err ) ;
2020-09-23 05:16:27 +00:00
}
p_add_proto_data ( wmem_file_scope ( ) , pinfo , proto_blip , 0 , data_to_save ) ;
2018-10-17 01:39:35 +00:00
return NULL ;
}
2020-09-23 05:16:27 +00:00
// Shrink the buffer so that there is not wasted space on the end of it since
// it will be long lived in the file scope
2018-10-17 01:39:35 +00:00
uLong bodyLength = decompress_stream - > total_out - start ;
2020-09-23 05:16:27 +00:00
Bytef * shortened_buffer = ( Bytef * ) wmem_memdup ( wmem_file_scope ( ) , decompress_buffer , bodyLength ) ;
tvbuff_t * decompressedChild = tvb_new_child_real_data ( tvb , shortened_buffer , ( guint ) bodyLength , ( gint ) bodyLength ) ;
2018-10-17 01:39:35 +00:00
add_new_data_source ( pinfo , decompressedChild , " Decompressed Payload " ) ;
2020-09-23 05:16:27 +00:00
decompress_result_t * data_to_save = wmem_new0 ( wmem_file_scope ( ) , decompress_result_t ) ;
2019-07-03 08:12:21 +00:00
data_to_save - > size = ( size_t ) bodyLength ;
2020-09-23 05:16:27 +00:00
data_to_save - > buf = shortened_buffer ;
2019-07-03 08:12:21 +00:00
p_add_proto_data ( wmem_file_scope ( ) , pinfo , proto_blip , 0 , data_to_save ) ;
2018-10-17 01:39:35 +00:00
return decompressedChild ;
2018-11-14 11:48:06 +00:00
}
# endif /* HAVE_ZLIB */
2018-10-17 01:39:35 +00:00
static int
blip: fix used-but-marked-unused warnings
blip.c:195:4: error: 'offset' was marked unused but was used
offset,
^
blip.c:200:22: error: 'blip_tree' was marked unused but was used
proto_tree_add_item(blip_tree, hf_blip_ack_size, tvb, offset, varint_ack_size_length, ENC_VARINT_PROTOBUF);
^
blip.c:200:56: error: 'offset' was marked unused but was used
proto_tree_add_item(blip_tree, hf_blip_ack_size, tvb, offset, varint_ack_size_length, ENC_VARINT_PROTOBUF);
^
blip.c:202:2: error: 'offset' was marked unused but was used
offset += varint_ack_size_length;
^
blip.c:284:14: error: 'pinfo' was marked unused but was used
col_set_str(pinfo->cinfo, COL_PROTOCOL, "BLIP");
^
blip.c:286:12: error: 'pinfo' was marked unused but was used
col_clear(pinfo->cinfo,COL_INFO);
^
blip.c:333:14: error: 'pinfo' was marked unused but was used
col_add_str(pinfo->cinfo, COL_INFO, col_info);
^
blip.c:337:34: error: 'pinfo' was marked unused but was used
return handle_ack_message(tvb, pinfo, blip_tree, offset, value_frame_flags);
^
blip.c:346:45: error: 'pinfo' was marked unused but was used
conversation = find_or_create_conversation(pinfo);
^
blip.c:361:4: error: 'pinfo' was marked unused but was used
pinfo,
^
blip.c:380:27: error: 'pinfo' was marked unused but was used
tvb_to_use = decompress(pinfo, tvb, offset, tvb_reported_length_remaining(tvb, offset) - BLIP_BODY_CHECKSUM_SIZE);
Change-Id: I9de1a78942469cc16011fd1a21d93b81820bee80
Reviewed-on: https://code.wireshark.org/review/33373
Reviewed-by: Martin Kaiser <wireshark@kaiser.cx>
Petri-Dish: Martin Kaiser <wireshark@kaiser.cx>
Petri-Dish: Anders Broman <a.broman58@gmail.com>
Tested-by: Petri Dish Buildbot
Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
2019-05-26 16:39:54 +00:00
dissect_blip ( tvbuff_t * tvb , packet_info * pinfo , proto_tree * tree , _U_ void * data )
2018-10-17 01:39:35 +00:00
{
proto_tree * blip_tree ;
2019-07-03 08:12:21 +00:00
gint offset = 0 ;
2018-10-17 01:39:35 +00:00
/* Set the protcol column to say BLIP */
col_set_str ( pinfo - > cinfo , COL_PROTOCOL , " BLIP " ) ;
/* Clear out stuff in the info column */
col_clear ( pinfo - > cinfo , COL_INFO ) ;
// ------------------------------------- Setup BLIP tree -----------------------------------------------------------
/* Add a subtree to dissection. See WSDG 9.2.2. Dissecting the details of the protocol */
proto_item * blip_item = proto_tree_add_item ( tree , proto_blip , tvb , offset , - 1 , ENC_NA ) ;
blip_tree = proto_item_add_subtree ( blip_item , ett_blip ) ;
// ------------------------ BLIP Frame Header: Message Number VarInt -----------------------------------------------
// This gets the message number as a var int in order to find out how much to bump
// the offset for the next proto_tree item
guint64 value_message_num ;
guint varint_message_num_length = tvb_get_varint (
tvb ,
offset ,
FT_VARINT_MAX_LEN ,
& value_message_num ,
ENC_VARINT_PROTOBUF ) ;
proto_tree_add_item ( blip_tree , hf_blip_message_number , tvb , offset , varint_message_num_length , ENC_VARINT_PROTOBUF ) ;
offset + = varint_message_num_length ;
// ------------------------ BLIP Frame Header: Frame Flags VarInt --------------------------------------------------
// This gets the message number as a var int in order to find out how much to bump
// the offset for the next proto_tree item
guint64 value_frame_flags ;
guint varint_frame_flags_length = tvb_get_varint (
tvb ,
offset ,
FT_VARINT_MAX_LEN ,
& value_frame_flags ,
ENC_VARINT_PROTOBUF ) ;
guint64 masked = value_frame_flags & ~ 0x07 ;
proto_tree_add_uint ( blip_tree , hf_blip_frame_flags , tvb , offset , varint_frame_flags_length , ( guint8 ) masked ) ;
offset + = varint_frame_flags_length ;
const gchar * msg_type = get_message_type ( value_frame_flags ) ;
2021-07-16 15:36:34 +00:00
gchar * msg_num = wmem_strdup_printf ( pinfo - > pool , " #% " G_GUINT64_FORMAT , value_message_num ) ;
gchar * col_info = wmem_strconcat ( pinfo - > pool , msg_type , msg_num , NULL ) ;
2018-10-17 01:39:35 +00:00
col_add_str ( pinfo - > cinfo , COL_INFO , col_info ) ;
// If it's an ACK message, handle that separately, since there are no properties etc.
if ( is_ack_message ( value_frame_flags ) = = TRUE ) {
return handle_ack_message ( tvb , pinfo , blip_tree , offset , value_frame_flags ) ;
}
// ------------------------------------- Conversation Tracking -----------------------------------------------------
2019-07-03 08:12:21 +00:00
blip_conversation_entry_t * conversation_entry_ptr = get_blip_conversation ( pinfo ) ;
2018-10-17 01:39:35 +00:00
// Is this the first frame in a blip message with multiple frames?
gboolean first_frame_in_msg = is_first_frame_in_msg (
conversation_entry_ptr ,
pinfo ,
value_frame_flags ,
value_message_num
) ;
tvbuff_t * tvb_to_use = tvb ;
gboolean compressed = is_compressed ( value_frame_flags ) ;
2020-09-23 05:16:27 +00:00
2018-10-17 01:39:35 +00:00
if ( compressed ) {
2018-11-14 11:48:06 +00:00
# ifdef HAVE_ZLIB
2020-09-23 05:16:27 +00:00
tvb_to_use = decompress ( pinfo , blip_tree , tvb , offset , tvb_reported_length_remaining ( tvb , offset ) - BLIP_BODY_CHECKSUM_SIZE ) ;
2018-10-17 01:39:35 +00:00
if ( ! tvb_to_use ) {
return tvb_reported_length ( tvb ) ;
}
2018-11-14 11:48:06 +00:00
# else /* ! HAVE_ZLIB */
2020-09-23 05:16:27 +00:00
proto_tree_add_string ( tree , hf_blip_message_body , tvb , offset , tvb_reported_length_remaining ( tvb , offset ) , " <decompression support is not available> " ) ;
2018-11-14 11:48:06 +00:00
return tvb_reported_length ( tvb ) ;
# endif /* ! HAVE_ZLIB */
2018-10-17 01:39:35 +00:00
offset = 0 ;
}
// Is this the first frame in a message?
if ( first_frame_in_msg = = TRUE ) {
// ------------------------ BLIP Frame Header: Properties Length VarInt --------------------------------------------------
// WARNING: this only works because this code assumes that ALL MESSAGES FIT INTO ONE FRAME, which is absolutely not true.
// In other words, as soon as there is a message that spans two frames, this code will break.
guint64 value_properties_length ;
guint value_properties_length_varint_length = tvb_get_varint (
tvb_to_use ,
offset ,
FT_VARINT_MAX_LEN ,
& value_properties_length ,
ENC_VARINT_PROTOBUF ) ;
proto_tree_add_item ( blip_tree , hf_blip_properties_length , tvb_to_use , offset , value_properties_length_varint_length , ENC_VARINT_PROTOBUF ) ;
offset + = value_properties_length_varint_length ;
// ------------------------ BLIP Frame: Properties --------------------------------------------------
// WARNING: this only works because this code assumes that ALL MESSAGES FIT INTO ONE FRAME, which is absolutely not true.
// In other words, as soon as there is a message that spans two frames, this code will break.
// At this point, the length of the properties is known and is stored in value_properties_length.
// This reads the entire properties out of the tvb and into a buffer (buf).
2021-07-16 15:36:34 +00:00
guint8 * buf = tvb_get_string_enc ( pinfo - > pool , tvb_to_use , offset , ( gint ) value_properties_length , ENC_UTF_8 ) ;
2018-10-17 01:39:35 +00:00
// "Profile\0subChanges\0continuous\0true\0foo\0bar" -> "Profile:subChanges:continuous:true:foo:bar"
// Iterate over buf and change all the \0 null characters to ':', since otherwise trying to set a header
// field to this buffer via proto_tree_add_item() will end up only printing it up to the first null character,
// for example "Profile", even though there are many more properties that follow.
for ( int i = 0 ; i < ( int ) value_properties_length ; i + + ) {
if ( i < ( int ) ( value_properties_length - 1 ) ) {
if ( buf [ i ] = = ' \0 ' ) { // TODO: I don't even know if this is actually a safe assumption in a UTF-8 encoded string
buf [ i ] = ' : ' ;
}
}
}
if ( value_properties_length > 0 ) {
proto_tree_add_string ( blip_tree , hf_blip_properties , tvb_to_use , offset , ( int ) value_properties_length , ( const char * ) buf ) ;
}
// Bump the offset by the length of the properties
offset + = ( gint ) value_properties_length ;
}
// ------------------------ BLIP Frame: Message Body --------------------------------------------------
// WS_DLL_PUBLIC gint tvb_reported_length_remaining(const tvbuff_t *tvb, const gint offset);
gint reported_length_remaining = tvb_reported_length_remaining ( tvb_to_use , offset ) ;
// Don't read in the trailing checksum at the end
if ( ! compressed & & reported_length_remaining > = BLIP_BODY_CHECKSUM_SIZE ) {
reported_length_remaining - = BLIP_BODY_CHECKSUM_SIZE ;
}
if ( reported_length_remaining > 0 ) {
proto_tree_add_item ( blip_tree , hf_blip_message_body , tvb_to_use , offset , reported_length_remaining , ENC_UTF_8 | ENC_NA ) ;
}
proto_tree_add_item ( blip_tree , hf_blip_checksum , tvb , tvb_reported_length ( tvb ) - BLIP_BODY_CHECKSUM_SIZE , BLIP_BODY_CHECKSUM_SIZE , ENC_BIG_ENDIAN ) ;
// -------------------------------------------- Etc ----------------------------------------------------------------
return tvb_captured_length ( tvb ) ;
}
void
proto_register_blip ( void )
{
static hf_register_info hf [ ] = {
{ & hf_blip_message_number ,
{ " Message Number " , " blip.messagenum " , FT_UINT64 , BASE_DEC ,
NULL , 0x0 , NULL , HFILL }
} ,
{ & hf_blip_frame_flags ,
{ " Frame Flags " , " blip.frameflags " , FT_UINT8 , BASE_HEX | BASE_EXT_STRING ,
& flag_combos_ext , 0x0 , NULL , HFILL }
} ,
{ & hf_blip_properties_length ,
{ " Properties Length " , " blip.propslength " , FT_UINT64 , BASE_DEC ,
NULL , 0x0 , NULL , HFILL }
} ,
{ & hf_blip_properties ,
{ " Properties " , " blip.props " , FT_STRING , STR_UNICODE ,
NULL , 0x0 , NULL , HFILL }
} ,
{ & hf_blip_message_body ,
{ " Message Body " , " blip.messagebody " , FT_STRING , STR_UNICODE ,
NULL , 0x0 , NULL , HFILL }
} ,
{ & hf_blip_ack_size ,
{ " ACK num bytes " , " blip.numackbytes " , FT_UINT64 , BASE_DEC ,
NULL , 0x0 , NULL , HFILL }
} ,
{ & hf_blip_checksum ,
{ " Checksum " , " blip.checksum " , FT_UINT32 , BASE_DEC ,
NULL , 0x0 , NULL , HFILL }
}
} ;
/* Setup protocol subtree array */
static gint * ett [ ] = {
& ett_blip
} ;
2020-09-23 05:16:27 +00:00
/* Expert Infos */
static ei_register_info ei [ ] = {
2020-09-23 19:44:30 +00:00
{ & ei_blip_decompress_buffer_error , { " blip.decompress_buffer_error " , PI_UNDECODED , PI_WARN , " Decompression error " , EXPFILL } }
2020-09-23 05:16:27 +00:00
} ;
2018-10-17 01:39:35 +00:00
proto_blip = proto_register_protocol ( " BLIP Couchbase Mobile " , " BLIP " , " blip " ) ;
2020-09-23 05:16:27 +00:00
expert_module_t * expert_blip = expert_register_protocol ( proto_blip ) ;
2018-10-17 01:39:35 +00:00
proto_register_field_array ( proto_blip , hf , array_length ( hf ) ) ;
proto_register_subtree_array ( ett , array_length ( ett ) ) ;
2020-09-23 05:16:27 +00:00
expert_register_field_array ( expert_blip , ei , array_length ( ei ) ) ;
2018-10-17 01:39:35 +00:00
blip_handle = register_dissector ( " blip " , dissect_blip , proto_blip ) ;
2020-09-23 05:16:27 +00:00
# ifdef HAVE_ZLIB
module_t * blip_module = prefs_register_protocol ( proto_blip , NULL ) ;
prefs_register_uint_preference ( blip_module , " max_uncompressed_size " ,
" Maximum uncompressed message size (Kb) " ,
" The maximum size of the buffer for uncompressed messages. "
" If a message is larger than this, then the packet containing "
" the message, as well as subsequent packets, will fail to "
" decompress " , 10 , & max_uncompressed_size ) ;
# endif
2018-10-17 01:39:35 +00:00
}
void
proto_reg_handoff_blip ( void )
{
// Register the blip dissector as a subprotocol dissector of "ws.protocol",
// matching any packets with a Web-Sec-Protocol header of "BLIP_3+CBMobile_2".
//
// See https://github.com/couchbase/sync_gateway/issues/3356#issuecomment-370958321 for
// more notes on how the websocket dissector routes packets down to subprotocol handlers.
dissector_add_string ( " ws.protocol " , " BLIP_3+CBMobile_2 " , blip_handle ) ;
}
/*
2019-07-26 18:43:17 +00:00
* Editor modelines - https : //www.wireshark.org/tools/modelines.html
2018-10-17 01:39:35 +00:00
*
* Local Variables :
2018-11-14 11:48:06 +00:00
* c - basic - offset : 8
2018-10-17 01:39:35 +00:00
* tab - width : 8
2018-11-14 11:48:06 +00:00
* indent - tabs - mode : t
2018-10-17 01:39:35 +00:00
* End :
*
2018-11-14 11:48:06 +00:00
* ex : set shiftwidth = 8 tabstop = 8 noexpandtab :
* : indentSize = 8 : tabSize = 8 : noTabs = false :
2018-10-17 01:39:35 +00:00
*/