wireshark/epan/dissectors/packet-saphdb.c

1507 lines
57 KiB
C

/* packet-saphdb.c
* Routines for SAP HDB (HANA SQL Command Network Protocol) dissection
* Copyright 2022, Martin Gallo <martin.gallo [AT] gmail.com>
* Code contributed by SecureAuth Corp.
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
/*
* This is a dissector that partially implements the HDB protocol. Reference of the protocol can be found in SAP's official documentation:
* https://help.sap.com/viewer/7e4aba181371442d9e4395e7ff71b777/2.0.03/en-US/d5b80175490741adbf1a1ba5ec8f2695.html
*
* and the blog series SecureAuth published around the topic "Exploring the SAP HANA SQL Command Network Protocol":
* - Protocol Basics and Authentication: https://www.secureauth.com/blog/exploring-sap-hana-sql-command-network-protocol-protocol-basics-and-authentication/
* - Password-based Authentication and TLS: https://www.secureauth.com/blog/exploring-sap-hana-sql-command-network-protocol-password-based-authentication-and-tls/
* - Federated Authentication: https://www.secureauth.com/blog/exploring-the-sap-hana-sql-command-network-protocol-federated-authentication/
*/
#include <config.h>
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/expert.h>
#include <wsutil/wmem/wmem.h>
#include <epan/wmem_scopes.h>
#include <epan/dissectors/packet-tcp.h>
#include <epan/dissectors/packet-tls.h>
/*
* Define default ports. The right range should be 3NN13 and 3NN15, but as port numbers are proprietary and not
* IANA assigned, we leave only the ones corresponding to the instance 00.
*/
#define SAPHDB_PORT_RANGE "30013,30015"
/* SAP HDB Packet Options values */
static const value_string saphdb_message_header_packetoptions_vals[] = {
{ 0, "Uncompressed" },
{ 2, "Compressed" },
/* NULL */
{ 0x00, NULL }
};
/* SAP HDB Segment Kind values */
static const value_string saphdb_segment_segmentkind_vals[] = {
{ 0, "Invalid" },
{ 1, "Request" },
{ 2, "Reply" },
{ 5, "Error" },
/* NULL */
{ 0x00, NULL }
};
/* SAP HDB Segment Message Type values */
static const value_string saphdb_segment_messagetype_vals[] = {
{ 0, "NIL" },
{ 2, "EXECUTEDIRECT" },
{ 3, "PREPARE" },
{ 4, "ABAPSTREAM" },
{ 5, "XA_START" },
{ 6, "XA_JOIN" },
{ 7, "XA_COMMIT" },
{ 13, "EXECUTE" },
{ 16, "READLOB" },
{ 17, "WRITELOB" },
{ 18, "FINDLOB" },
{ 25, "PING" },
{ 65, "AUTHENTICATE" },
{ 66, "CONNECT" },
{ 67, "COMMIT" },
{ 68, "ROLLBACK" },
{ 69, "CLOSERESULTSET" },
{ 70, "DROPSTATEMENTID" },
{ 71, "FETCHNEXT" },
{ 72, "FETCHABSOLUTE" },
{ 73, "FETCHRELATIVE" },
{ 74, "FETCHFIRST" },
{ 75, "FETCHLAST" },
{ 77, "DISCONNECT" },
{ 78, "EXECUTEITAB" },
{ 79, "FETCHNEXTITAB" },
{ 80, "INSERTNEXTITAB" },
{ 81, "BATCHPREPARE" },
{ 82, "DBCONNECTINFO" },
{ 83, "XOPEN_XASTART" },
{ 84, "XOPEN_XAEND" },
{ 85, "XOPEN_XAPREPARE" },
{ 86, "XOPEN_XACOMMIT" },
{ 87, "XOPEN_XAROLLBACK" },
{ 88, "XOPEN_XARECOVER" },
{ 89, "XOPEN_XAFORGET" },
/* NULL */
{ 0x00, NULL }
};
/* SAP HDB Segment Function Code values */
static const value_string saphdb_segment_functioncode_vals[] = {
{ 0, "NIL" },
{ 1, "DDL" },
{ 2, "INSERT" },
{ 3, "UPDATE" },
{ 4, "DELETE" },
{ 5, "SELECT" },
{ 6, "SELECTFORUPDATE" },
{ 7, "EXPLAIN" },
{ 8, "DBPROCEDURECALL" },
{ 9, "DBPROCEDURECALLWITHRESULT" },
{ 10, "FETCH" },
{ 11, "COMMIT" },
{ 12, "ROLLBACK" },
{ 13, "SAVEPOINT" },
{ 14, "CONNECT" },
{ 15, "WRITELOB" },
{ 16, "READLOB" },
{ 17, "PING" },
{ 18, "DISCONNECT" },
{ 19, "CLOSECURSOR" },
{ 20, "FINDLOB" },
{ 21, "ABAPSTREAM" },
{ 22, "XASTART" },
{ 23, "XAJOIN" },
{ 24, "ITABWRITE" },
{ 25, "XOPEN_XACONTROL" },
{ 26, "XOPEN_XAPREPARE" },
{ 27, "XOPEN_XARECOVER" },
/* NULL */
{ 0x00, NULL }
};
/* SAP HDB Part Kind values */
static const value_string saphdb_part_partkind_vals[] = {
{ 0, "NIL" },
{ 3, "COMMAND" },
{ 5, "RESULTSET" },
{ 6, "ERROR" },
{ 10, "STATEMENTID" },
{ 11, "TRANSACTIONID" },
{ 12, "ROWSAFFECTED" },
{ 13, "RESULTSETID" },
{ 15, "TOPOLOGYINFORMATION" },
{ 16, "TABLELOCATION" },
{ 17, "READLOBREQUEST" },
{ 18, "READLOBREPLY" },
{ 25, "ABAPISTREAM" },
{ 26, "ABAPOSTREAM" },
{ 27, "COMMANDINFO" },
{ 28, "WRITELOBREQUEST" },
{ 29, "CLIENTCONTEXT" },
{ 30, "WRITELOBREPLY" },
{ 32, "PARAMETERS" },
{ 33, "AUTHENTICATION" },
{ 34, "SESSIONCONTEXT" },
{ 35, "CLIENTID" },
{ 38, "PROFILE" },
{ 39, "STATEMENTCONTEXT" },
{ 40, "PARTITIONINFORMATION" },
{ 41, "OUTPUTPARAMETERS" },
{ 42, "CONNECTOPTIONS" },
{ 43, "COMMITOPTIONS" },
{ 44, "FETCHOPTIONS" },
{ 45, "FETCHSIZE" },
{ 47, "PARAMETERMETADATA" },
{ 48, "RESULTSETMETADATA" },
{ 49, "FINDLOBREQUEST" },
{ 50, "FINDLOBREPLY" },
{ 51, "ITABSHM" },
{ 53, "ITABCHUNKMETADATA" },
{ 55, "ITABMETADATA" },
{ 56, "ITABRESULTCHUNK" },
{ 57, "CLIENTINFO" },
{ 58, "STREAMDATA" },
{ 59, "OSTREAMRESULT" },
{ 60, "FDAREQUESTMETADATA" },
{ 61, "FDAREPLYMETADATA" },
{ 62, "BATCHPREPARE" },
{ 63, "BATCHEXECUTE" },
{ 64, "TRANSACTIONFLAGS" },
{ 65, "ROWSLOTIMAGEPARAMMETADATA" },
{ 66, "ROWSLOTIMAGERESULTSET" },
{ 67, "DBCONNECTINFO" },
{ 68, "LOBFLAGS" },
{ 69, "RESULTSETOPTIONS" },
{ 70, "XATRANSACTIONINFO" },
{ 71, "SESSIONVARIABLE" },
{ 72, "WORKLOADREPLAYCONTEXT" },
{ 73, "SQLREPLYOTIONS" },
/* NULL */
{ 0x00, NULL }
};
/* SAP HDB Type values */
static const value_string saphdb_part_type_vals[] = {
{ 0, "NULL" },
{ 1, "TINYINT" },
{ 2, "SMALLINT" },
{ 3, "INT" },
{ 4, "BIGINT" },
{ 5, "DECIMAL" },
{ 6, "REAL" },
{ 7, "DOUBLE" },
{ 8, "CHAR" },
{ 9, "VARCHAR1" },
{ 10, "NCHAR" },
{ 11, "NVARCHAR" },
{ 12, "BINARY" },
{ 13, "VARBINARY" },
{ 14, "DATE" },
{ 15, "TIME" },
{ 16, "TIMESTAMP" },
{ 17, "TIME_TZ" },
{ 18, "TIME_LTZ" },
{ 19, "TIMESTAMP_TZ" },
{ 20, "TIMESTAMP_LTZ" },
{ 21, "INTERVAL_YM" },
{ 22, "INTERVAL_DS" },
{ 23, "ROWID" },
{ 24, "UROWID" },
{ 25, "CLOB" },
{ 26, "NCLOB" },
{ 27, "BLOB" },
{ 28, "BOOLEAN" },
{ 29, "STRING" },
{ 30, "NSTRING" },
{ 31, "LOCATOR" },
{ 32, "NLOCATOR" },
{ 33, "BSTRING" },
{ 34, "DECIMAL_DIGIT_ARRAY" },
{ 35, "VARCHAR2" },
{ 36, "VARCHAR3" },
{ 37, "NVARCHAR3" },
{ 38, "VARBINARY3" },
{ 39, "VARGROUP" },
{ 40, "TINYINT_NOTNULL" },
{ 41, "SMALLINT_NOTNULL" },
{ 42, "INT_NOTNULL" },
{ 43, "BIGINT_NOTNULL" },
{ 44, "ARGUMENT" },
{ 45, "TABLE" },
{ 46, "CURSOR" },
{ 47, "SMALLDECIMAL" },
{ 48, "ABAPSTREAM" },
{ 49, "ABAPSTRUCT" },
{ 50, "ARRAY" },
{ 51, "TEXT" },
{ 52, "SHORTTEXT" },
{ 53, "FIXEDSTRING" },
{ 54, "FIXEDPOINTDECIMAL" },
{ 55, "ALPHANUM" },
{ 56, "TLOCATOR" },
{ 61, "LONGDATE" },
{ 62, "SECONDDATE" },
{ 63, "DAYDATE" },
{ 64, "SECONDTIME" },
{ 65, "CSDATE" },
{ 66, "CSTIME" },
{ 71, "BLOB_DISK" },
{ 72, "CLOB_DISK" },
{ 73, "NCLOB_DISK" },
{ 74, "GEOMETRY" },
{ 75, "POINT" },
{ 76, "FIXED16" },
{ 77, "BLOB_HYBRID" },
{ 78, "CLOB_HYBRID" },
{ 79, "NCLOB_HYBRID" },
{ 80, "POINTZ" },
/* NULL */
{ 0x00, NULL }
};
/* SAP HDB Error Level values */
static const value_string saphdb_error_level_vals[] = {
{ 0, "WARNING" },
{ 1, "ERROR" },
{ 2, "FATALERROR" },
/* NULL */
{ 0x00, NULL }
};
/* Structure to define Option Parts */
typedef struct _option_part_definition {
gint8 value;
const gchar *identifier_strptr;
gint8 type;
} option_part_definition;
static const option_part_definition saphdb_part_connect_options_vals[] = {
{ 1, "Connection ID", 3 },
{ 2, "Complete Array Execution", 28 },
{ 3, "Client Locale", 29 },
{ 4, "Supports Large Bulk Operations", 28 },
{ 5, "Distribution Enabled", 28 },
{ 6, "Primary Connection ID", 0 },
{ 7, "Primary Connection Host", 0 },
{ 8, "Primary Connection Port", 0 },
{ 9, "Complete Data Type Support", 0 },
{ 10, "Large Number of Parameters Support", 28 },
{ 11, "System ID", 29 },
{ 12, "Data Format Version", 3 },
{ 13, "ABAP VARCHAR Mode", 28 },
{ 14, "Select for Update Supported", 28 },
{ 15, "Client Distribution Mode", 3 },
{ 16, "Engine Data Format Version", 3 },
{ 17, "Distribution Protocol Version", 3 },
{ 18, "Split Batch Commands", 28 },
{ 19, "Use Transaction Flags Only", 28 },
{ 20, "Row and Column Optimized Format", 28 },
{ 21, "Ignore Unknown Parts", 3 },
{ 22, "Table Output Parameter", 28 },
{ 23, "Data Format Version 2", 3 },
{ 24, "ITAB Parameter", 28 },
{ 25, "Describe Table Output Parameter", 28 },
{ 26, "Columnar Result Set", 0 }, /* This is BITVECTOR type ??? */
{ 27, "Scrollable Result Set", 3 },
{ 28, "Client Info NULL Value Supported", 28 },
{ 29, "Associated Connection ID", 3 },
{ 30, "Non-Transactional Prepare", 28 },
{ 31, "Fast Data Access Enabled", 28 },
{ 32, "OS User", 29 },
{ 33, "Row Slot Image Result", 0 }, /* This is BITVECTOR type ??? */
{ 34, "Endianness", 3 },
{ 35, "Update Topology Anywhere", 28 },
{ 36, "Enable Array Type", 28 },
{ 37, "Implicit LOB Streaming", 28 },
{ 38, "Cached View Property", 28 },
{ 39, "X OpenXA Protocol Supported", 28 },
{ 40, "Master Commit Redirection Supported", 28 },
{ 41, "Active/Active Protocol Version", 3 },
{ 42, "Active/Active Connection Origin Site", 3 },
{ 43, "Query Timeout Supported", 28 },
{ 44, "Full Version String", 29 },
{ 45, "Database Name", 29 },
{ 46, "Build Platform", 3 },
{ 47, "Implicit XA Session Supported", 28 },
{ 48, "Client Side Column Encryption Version", 3 },
{ 49, "Compression Level And Flags", 3 },
{ 50, "Client Side Re-Execution Supported", 28 },
{ 51, "Client Reconnect Wait Timeout", 3 },
{ 52, "Original Anchor Connection ID", 3 },
{ 53, "Flag Set 1", 3 },
{ 54, "Topology Network Group", 28 },
{ 55, "IP Address", 29 },
{ 56, "LRR Ping Time", 3 },
/* NULL */
{ 0x00, NULL, 0x00 }
};
static const option_part_definition saphdb_part_commit_options_vals[] = {
{ 1, "Hold Cursors Over Commit", 28 },
/* NULL */
{ 0x00, NULL, 0x00 }
};
static const option_part_definition saphdb_part_fetch_options_vals[] = {
{ 1, "Result Set Pos", 3 },
/* NULL */
{ 0x00, NULL, 0x00 }
};
static const option_part_definition saphdb_part_transaction_flags_vals[] = {
{ 0, "Rolled Back", 28 },
{ 1, "Commited", 28 },
{ 2, "New Isolation Level", 3 },
{ 3, "DDL Commit Mode Changed", 28 },
{ 4, "Write Transaction Started", 28 },
{ 5, "No Write Transaction Started", 28 },
{ 6, "Session Closing Transaction Error", 28 },
/* NULL */
{ 0x00, NULL, 0x00}
};
static const option_part_definition saphdb_part_topology_info_vals[] = {
{ 1, "Host Name", 29 },
{ 2, "Host Port Number", 3 },
{ 3, "Tenant Name", 29 },
{ 4, "Load Factor", 7 },
{ 5, "Site Volume ID", 3 },
{ 6, "Is Master", 28 },
{ 7, "Is Current Session", 28 },
{ 8, "Service Type", 3 },
{ 9, "Network Domain", 29 },
{ 10, "Is Stand-By", 28 },
{ 11, "All IP Addresses", 29 },
{ 12, "All Host Names", 29 },
{ 13, "Site Type", 3 },
/* NULL */
{ 0x00, NULL, 0x00 }
};
static const option_part_definition saphdb_part_command_info_vals[] = {
{ 1, "Line Number", 3 },
{ 2, "Source Module", 29 },
/* NULL */
{ 0x00, NULL, 0x00 }
};
static const option_part_definition saphdb_part_client_context_vals[] = {
{ 1, "Client Version", 29 },
{ 2, "Client Type", 29 },
{ 3, "Application Name", 29 },
/* NULL */
{ 0x00, NULL, 0x00 }
};
static const option_part_definition saphdb_part_session_context_vals[] = {
{ 1, "Primary Connection ID", 3 },
{ 2, "Primary Host Name", 29 },
{ 3, "Primary Host Port Number", 3 },
{ 4, "Master Connection ID", 3 },
{ 5, "Master Host Name", 29 },
{ 6, "Master Host Port Number", 3 },
/* NULL */
{ 0x00, NULL, 0x00 }
};
static const option_part_definition saphdb_part_statement_context_vals[] = {
{ 1, "Statement Sequence Info", 33 },
{ 2, "Server Processing Time", 4 },
{ 3, "Schema Name", 29 },
{ 4, "Flag Set", 8 },
{ 5, "Query Time Out", 4 },
{ 6, "Client Reconnection Wait Timeout", 3 },
{ 7, "Server CPU Time", 4 },
{ 8, "Server Memory Usage", 4 },
/* NULL */
{ 0x00, NULL, 0x00 }
};
static const option_part_definition saphdb_part_dbconnect_info_flags_vals[] = {
{ 1, "Database Name", 29 },
{ 2, "Host", 29 },
{ 3, "Port", 3 },
{ 4, "Is Connected", 28 },
/* NULL */
{ 0x00, NULL, 0x00 }
};
static const option_part_definition saphdb_part_lob_flags_vals[] = {
{ 0, "Implicit Streaming", 28 },
/* NULL */
{ 0x00, NULL, 0x00 }
};
static int proto_saphdb = -1;
/* SAP HDB Initialization items */
static int hf_saphdb_initialization_request = -1;
static int hf_saphdb_initialization_reply = -1;
static int hf_saphdb_initialization_reply_product_version_major = -1;
static int hf_saphdb_initialization_reply_product_version_minor = -1;
static int hf_saphdb_initialization_reply_protocol_version_major = -1;
static int hf_saphdb_initialization_reply_protocol_version_minor = -1;
/* SAP HDB Message Header items */
static int hf_saphdb_message_header = -1;
static int hf_saphdb_message_header_sessionid = -1;
static int hf_saphdb_message_header_packetcount = -1;
static int hf_saphdb_message_header_varpartlength = -1;
static int hf_saphdb_message_header_varpartsize = -1;
static int hf_saphdb_message_header_noofsegm = -1;
static int hf_saphdb_message_header_packetoptions = -1;
static int hf_saphdb_message_header_compressionvarpartlength = -1;
static int hf_saphdb_message_header_reserved = -1;
/* SAP HDB Message Buffer items */
static int hf_saphdb_message_buffer = -1;
static int hf_saphdb_compressed_buffer = -1;
/* SAP HDB Segment items */
static int hf_saphdb_segment = -1;
static int hf_saphdb_segment_segmentlength = -1;
static int hf_saphdb_segment_segmentofs = -1;
static int hf_saphdb_segment_noofparts = -1;
static int hf_saphdb_segment_segmentno = -1;
static int hf_saphdb_segment_segmentkind = -1;
static int hf_saphdb_segment_messagetype = -1;
static int hf_saphdb_segment_commit = -1;
static int hf_saphdb_segment_commandoptions = -1;
static int hf_saphdb_segment_functioncode = -1;
static int hf_saphdb_segment_reserved = -1;
/* SAP HDB Segment Buffer items */
static int hf_saphdb_segment_buffer = -1;
/* SAP HDB Part items */
static int hf_saphdb_part = -1;
static int hf_saphdb_part_partkind = -1;
static int hf_saphdb_part_partattributes = -1;
static int hf_saphdb_part_argumentcount = -1;
static int hf_saphdb_part_bigargumentcount = -1;
static int hf_saphdb_part_bufferlength = -1;
static int hf_saphdb_part_buffersize = -1;
/* SAP HDB Part Buffer items */
static int hf_saphdb_part_buffer = -1;
/* SAP HDB Part Buffer Option Part Data items */
static int hf_saphdb_part_option_argcount = -1;
static int hf_saphdb_part_option_name = -1;
static int hf_saphdb_part_option_type = -1;
static int hf_saphdb_part_option_length = -1;
static int hf_saphdb_part_option_value = -1;
static int hf_saphdb_part_option_value_bool = -1;
static int hf_saphdb_part_option_value_byte = -1;
static int hf_saphdb_part_option_value_short = -1;
static int hf_saphdb_part_option_value_int = -1;
static int hf_saphdb_part_option_value_bigint = -1;
static int hf_saphdb_part_option_value_string = -1;
static int hf_saphdb_part_option_value_double = -1;
/* SAP HDB Part Buffer COMMAND items */
static int hf_saphdb_part_command = -1;
/* SAP HDB Part Buffer ERROR items */
static int hf_saphdb_part_error_code = -1;
static int hf_saphdb_part_error_position = -1;
static int hf_saphdb_part_error_text_length = -1;
static int hf_saphdb_part_error_level = -1;
static int hf_saphdb_part_error_sqlstate = -1;
static int hf_saphdb_part_error_text = -1;
/* SAP HDB Part Buffer AUTHENTICATE items */
static int hf_saphdb_part_authentication_field_count = -1;
static int hf_saphdb_part_authentication_field_length = -1;
static int hf_saphdb_part_authentication_field_value = -1;
/* SAP HDB Part Buffer CLIENTID items */
static int hf_saphdb_part_clientid = -1;
static gint ett_saphdb = -1;
/* Global port preference */
static range_t *global_saphdb_port_range;
/* Expert info */
static expert_field ei_saphdb_compressed_unknown = EI_INIT;
static expert_field ei_saphdb_option_part_unknown = EI_INIT;
static expert_field ei_saphdb_segments_incorrect_order = EI_INIT;
static expert_field ei_saphdb_segments_number_incorrect = EI_INIT;
static expert_field ei_saphdb_segment_length = EI_INIT;
static expert_field ei_saphdb_buffer_length = EI_INIT;
static expert_field ei_saphdb_parts_number_incorrect = EI_INIT;
static expert_field ei_saphdb_varpartlenght_incorrect = EI_INIT;
/* Global highlight preference */
static gboolean global_saphdb_highlight_items = TRUE;
/* Protocol handle */
static dissector_handle_t saphdb_handle;
static dissector_handle_t saphdb_handle_tls;
static dissector_handle_t gssapi_handle;
void proto_reg_handoff_saphdb(void);
void proto_register_saphdb(void);
/* Option Part Value to Option Part Identifier */
static const gchar *
opv_to_opi(const gint8 value, const option_part_definition *opd, const char *unknown_str)
{
gint i = 0;
if (opd) {
while (opd[i].identifier_strptr) {
if (opd[i].value == value) {
return(opd[i].identifier_strptr);
}
i++;
}
}
return unknown_str;
}
/* Option Part Value to Option Part Type */
static gint8
opv_to_opt(const gint8 value, const option_part_definition *opd)
{
gint i = 0;
if (opd) {
while (opd[i].identifier_strptr) {
if (opd[i].value == value) {
return(opd[i].type);
}
i++;
}
}
return 0;
}
static int
dissect_saphdb_part_options_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, gint16 argcount, guint8 partkind, const option_part_definition *definition)
{
guint32 parsed_length = 0;
while (argcount > 0 && tvb_reported_length_remaining(tvb, offset + parsed_length) > 2) {
gint8 option_key = 0, option_type = 0;
gint16 option_length = 0;
gint8 option_value_byte = 0;
proto_item *option_type_item = NULL;
option_key = tvb_get_gint8(tvb, offset + parsed_length);
proto_tree_add_int_format(tree, hf_saphdb_part_option_name, tvb, offset + parsed_length, 1, option_key,
"Option Name: %s (%d)", opv_to_opi(option_key, definition, "Unknown"), option_key);
parsed_length += 1;
option_type = tvb_get_gint8(tvb, offset + parsed_length);
option_type_item = proto_tree_add_item(tree, hf_saphdb_part_option_type, tvb, offset + parsed_length, 1, ENC_NA);
parsed_length += 1;
if (option_type != opv_to_opt(option_key, definition)) {
if (global_saphdb_highlight_items){
expert_add_info_format(pinfo, option_type_item, &ei_saphdb_option_part_unknown, "Option Type for key %d in part kind %d doesn't match! (expected %d, obtained %d)", option_key, partkind, opv_to_opt(option_key, definition), option_type);
}
}
switch (option_type) {
case 1: // TINYINT
proto_tree_add_item(tree, hf_saphdb_part_option_value_byte, tvb, offset + parsed_length, 1, ENC_NA);
parsed_length += 1;
break;
case 2: // SMALLINT
proto_tree_add_item(tree, hf_saphdb_part_option_value_short, tvb, offset + parsed_length, 2, ENC_LITTLE_ENDIAN);
parsed_length += 2;
break;
case 3: // INT
proto_tree_add_item(tree, hf_saphdb_part_option_value_int, tvb, offset + parsed_length, 4, ENC_LITTLE_ENDIAN);
parsed_length += 4;
break;
case 4: // BIGINT
proto_tree_add_item(tree, hf_saphdb_part_option_value_bigint, tvb, offset + parsed_length, 8, ENC_LITTLE_ENDIAN);
parsed_length += 8;
break;
case 7: // DOUBLE
proto_tree_add_item(tree, hf_saphdb_part_option_value_double, tvb, offset + parsed_length, 8, ENC_LITTLE_ENDIAN);
parsed_length += 8;
break;
case 28: // BOOLEAN
option_value_byte = tvb_get_gint8(tvb, offset + parsed_length);
proto_tree_add_boolean(tree, hf_saphdb_part_option_value_bool, tvb, offset + parsed_length, 1, option_value_byte);
parsed_length += 1;
break;
case 29: // STRING
case 30: // NSTRING
case 33: // BSTRING
option_length = tvb_get_gint16(tvb, offset + parsed_length, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_saphdb_part_option_length, tvb, offset + parsed_length, 2, ENC_LITTLE_ENDIAN);
parsed_length += 2;
if (tvb_reported_length_remaining(tvb, offset + parsed_length) >= option_length) {
if (option_type == 29) {
/* TODO: This need to be CESU-8 decoded */
proto_tree_add_item(tree, hf_saphdb_part_option_value_string, tvb, offset + parsed_length, option_length, ENC_UTF_8);
parsed_length += option_length;
} else if (option_type == 30) {
proto_tree_add_item(tree, hf_saphdb_part_option_value_string, tvb, offset + parsed_length, option_length, ENC_UTF_8);
parsed_length += option_length;
} else if (option_type == 33) {
/* This is binary data, not rendering it as a string */
proto_tree_add_item(tree, hf_saphdb_part_option_value, tvb, offset + parsed_length, option_length, ENC_NA);
parsed_length += option_length;
}
}
break;
default: // Unknown type, we don't know the length nor how to parse it
if (global_saphdb_highlight_items){
expert_add_info_format(pinfo, option_type_item, &ei_saphdb_option_part_unknown, "Option Type %d length unknown", option_type);
}
break;
}
argcount--;
}
return parsed_length;
}
static int
dissect_saphdb_part_multi_line_options_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, gint16 rowcount, guint8 partkind, const option_part_definition *definition)
{
guint32 parsed_length = 0;
/* In Multi-line Option Part, the part's argcount is the number of rows. For each row we need to parse the options. */
while (rowcount > 0 && tvb_reported_length_remaining(tvb, offset + parsed_length) > 2) {
gint16 argcount = 0;
/* First we read the amount of arguments in this row */
argcount = tvb_get_gint16(tvb, offset + parsed_length, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_saphdb_part_option_argcount, tvb, offset + parsed_length, 2, ENC_LITTLE_ENDIAN);
parsed_length += 2;
/* Now parse the options in the row if there are*/
if (argcount > 0) {
parsed_length += dissect_saphdb_part_options_data(tvb, pinfo, tree, offset + parsed_length, argcount, partkind, definition);
}
rowcount--;
}
return parsed_length;
}
static void
dissect_saphdb_gss_authentication_fields(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
{
guint8 field_short_length, commtype = 0;
guint16 field_count = 0, field_length;
/* Parse the field count */
field_count = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_saphdb_part_authentication_field_count, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
for (guint16 field = 0; field < field_count; field++) {
/* Parse the field length. If the first byte is 0xFF, the length is contained in the next 2 bytes */
field_short_length = tvb_get_guint8(tvb, offset);
if (field_short_length == 0xff) {
offset += 1;
field_length = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN);
proto_tree_add_item(tree, hf_saphdb_part_authentication_field_length, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
} else {
proto_tree_add_item(tree, hf_saphdb_part_authentication_field_length, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
field_length = field_short_length;
}
/* We try then to see if we're dealing with the commtype field (second field and with length 1)
* and extract it
*/
if ((field == 1) && (field_length == 1))
{
commtype = tvb_get_guint8(tvb, offset);
}
/* If this is the last value of a three field packet, and is one of the commtypes that carries an
* SPNEGO structure, we call the GSSAPI dissector. The Kerberos data is extracted in a new TVB.
*/
if (((commtype == 3) || (commtype == 6)) && (field_count == 3) && (field == 2)) {
tvbuff_t *kerberos_tvb;
kerberos_tvb = tvb_new_subset_length(tvb, offset, field_length);
add_new_data_source(pinfo, kerberos_tvb, "Kerberos Data");
call_dissector(gssapi_handle, kerberos_tvb, pinfo, tree);
}
else
/* If not we add the field value in plain */
{
proto_tree_add_item(tree, hf_saphdb_part_authentication_field_value, tvb, offset, field_length, ENC_NA);
}
offset += field_length;
}
}
static int
dissect_saphdb_part_authentication_fields(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
{
guint8 field_short_length;
guint16 field_count = 0, field_length;
guint32 parsed_length = 0;
proto_item *gss_item = NULL;
proto_tree *gss_tree = NULL;
gboolean is_gss = FALSE;
/* Parse the field count */ /* TODO: Should this match with argcount? */
field_count = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_saphdb_part_authentication_field_count, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
parsed_length += 2;
for (guint16 field = 0; field < field_count; field++) {
/* Parse the field length. If the first byte is 0xFF, the length is contained in the next 2 bytes */
field_short_length = tvb_get_guint8(tvb, offset);
if (field_short_length == 0xff) {
offset += 1;
parsed_length += 1;
field_length = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN);
proto_tree_add_item(tree, hf_saphdb_part_authentication_field_length, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
parsed_length += 2;
} else {
proto_tree_add_item(tree, hf_saphdb_part_authentication_field_length, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
parsed_length += 1;
field_length = field_short_length;
}
/* Add the field value */
gss_item = proto_tree_add_item(tree, hf_saphdb_part_authentication_field_value, tvb, offset, field_length, ENC_NA);
/* Check if this is a GSS field so we can parse the remaining fields */
if ((((field_count == 2) && (field == 0)) || ((field_count == 3) && (field == 1))) &&
(field_length == 3) && (tvb_strneql(tvb, offset, "GSS", 3) != -1)) {
is_gss = TRUE;
}
/* If the method is GSS, and this is the last value, we add a new tree and parse the value */
if (is_gss && field == field_count - 1) {
proto_item_append_text(gss_item, ": GSS Token");
gss_tree = proto_item_add_subtree(gss_item, ett_saphdb);
dissect_saphdb_gss_authentication_fields(tvb, pinfo, gss_tree, offset);
}
offset += field_length; parsed_length += field_length;
}
return parsed_length;
}
static int
dissect_saphdb_part_buffer(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, guint32 length, gint16 argcount, guint8 partkind, proto_item *partkind_item)
{
gint32 error_text_length = 0;
switch (partkind) {
case 3: // COMMAND
if ((length > 0) && ((guint32)tvb_reported_length_remaining(tvb, offset) >= length)) {
proto_tree_add_item(tree, hf_saphdb_part_command, tvb, offset, length, ENC_ASCII);
length = 0;
}
break;
case 6: // ERROR
proto_tree_add_item(tree, hf_saphdb_part_error_code, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
length -= 4;
proto_tree_add_item(tree, hf_saphdb_part_error_position, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
length -= 4;
proto_tree_add_item_ret_int(tree, hf_saphdb_part_error_text_length, tvb, offset, 4, ENC_LITTLE_ENDIAN, &error_text_length);
offset += 4;
length -= 4;
proto_tree_add_item(tree, hf_saphdb_part_error_level, tvb, offset, 1, ENC_NA);
offset += 1;
length -= 1;
proto_tree_add_item(tree, hf_saphdb_part_error_sqlstate, tvb, offset, 5, ENC_ASCII);
offset += 5;
length -= 5;
if ((error_text_length > 0) && (tvb_reported_length_remaining(tvb, offset) >= error_text_length)) {
proto_tree_add_item(tree, hf_saphdb_part_error_text, tvb, offset, error_text_length, ENC_ASCII);
length -= error_text_length;
/* Align the error text length to 8 */
if ((error_text_length % 8) != 0) {
length += 8 - (error_text_length % 8);
}
}
break;
case 33: // AUTHENTICATION
dissect_saphdb_part_authentication_fields(tvb, pinfo, tree, offset);
break;
case 35: // CLIENTID
if ((length > 0) && ((guint32)tvb_reported_length_remaining(tvb, offset) >= length)) {
proto_tree_add_item(tree, hf_saphdb_part_clientid, tvb, offset, length, ENC_ASCII);
length = 0;
}
break;
// Multi-line Option Parts
case 15: // TOPOLOGYINFORMATION
dissect_saphdb_part_multi_line_options_data(tvb, pinfo, tree, offset, argcount, partkind, saphdb_part_topology_info_vals);
break;
// Option Parts
case 27: // COMMANDINFO
dissect_saphdb_part_options_data(tvb, pinfo, tree, offset, argcount, partkind, saphdb_part_command_info_vals);
break;
case 29: // CLIENTCONTEXT
dissect_saphdb_part_options_data(tvb, pinfo, tree, offset, argcount, partkind, saphdb_part_client_context_vals);
break;
case 34: // SESSIONCONTEXT
dissect_saphdb_part_options_data(tvb, pinfo, tree, offset, argcount, partkind, saphdb_part_session_context_vals);
break;
case 39: // STATEMENTCONTEXT
dissect_saphdb_part_options_data(tvb, pinfo, tree, offset, argcount, partkind, saphdb_part_statement_context_vals);
break;
case 42: // CONNECTOPTIONS
dissect_saphdb_part_options_data(tvb, pinfo, tree, offset, argcount, partkind, saphdb_part_connect_options_vals);
break;
case 43: // COMMITOPTIONS
dissect_saphdb_part_options_data(tvb, pinfo, tree, offset, argcount, partkind, saphdb_part_commit_options_vals);
break;
case 44: // FETCHOPTIONS
dissect_saphdb_part_options_data(tvb, pinfo, tree, offset, argcount, partkind, saphdb_part_fetch_options_vals);
break;
case 64: // TRANSACTIONFLAGS
dissect_saphdb_part_options_data(tvb, pinfo, tree, offset, argcount, partkind, saphdb_part_transaction_flags_vals);
break;
case 67: // DBCONNECTINFO
dissect_saphdb_part_options_data(tvb, pinfo, tree, offset, argcount, partkind, saphdb_part_dbconnect_info_flags_vals);
break;
case 68: // LOBFLAGS
dissect_saphdb_part_options_data(tvb, pinfo, tree, offset, argcount, partkind, saphdb_part_lob_flags_vals);
break;
default:
if (global_saphdb_highlight_items){
expert_add_info_format(pinfo, partkind_item, &ei_saphdb_option_part_unknown, "Part Kind %d unknown", partkind);
}
break;
}
return length;
}
static int
dissect_saphdb_part(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_, guint32 offset, gint16 number_of_parts, guint16 part_number)
{
gint8 partkind = 0;
gint16 argcount = 0;
gint32 bufferlength = 0;
guint32 length = 0;
proto_item *part_item = NULL, *partkind_item = NULL, *part_buffer_length_item = NULL, *part_buffer_item = NULL;
proto_tree *part_tree = NULL, *part_buffer_tree = NULL;
/* Add the Part subtree */
part_item = proto_tree_add_item(tree, hf_saphdb_part, tvb, offset, 16, ENC_NA);
part_tree = proto_item_add_subtree(part_item, ett_saphdb);
proto_item_append_text(part_item, " (%d/%d)", part_number, number_of_parts);
/* Add the Part fields */
partkind = tvb_get_gint8(tvb, offset);
proto_item_append_text(part_item, ", %s", val_to_str(partkind, saphdb_part_partkind_vals, "Unknown"));
partkind_item = proto_tree_add_item(part_tree, hf_saphdb_part_partkind, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
length += 1;
proto_tree_add_item(part_tree, hf_saphdb_part_partattributes, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
length += 1;
argcount = tvb_get_gint16(tvb, offset, ENC_LITTLE_ENDIAN);
proto_tree_add_item(part_tree, hf_saphdb_part_argumentcount, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
length += 2;
proto_tree_add_item(part_tree, hf_saphdb_part_bigargumentcount, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
length += 4;
part_buffer_length_item = proto_tree_add_item_ret_int(part_tree, hf_saphdb_part_bufferlength, tvb, offset, 4, ENC_LITTLE_ENDIAN, &bufferlength);
offset += 4;
length += 4;
proto_tree_add_item(part_tree, hf_saphdb_part_buffersize, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
length += 4;
/* Check the length */
if (bufferlength < 0) {
expert_add_info_format(pinfo, part_buffer_length_item, &ei_saphdb_buffer_length, "Part Buffer length %d is invalid", bufferlength);
}
/* Align the buffer length to 8 */
if (bufferlength % 8 != 0) {
bufferlength += 8 - bufferlength % 8;
}
/* Adjust the length */
if (bufferlength < 0 || tvb_reported_length_remaining(tvb, offset) < bufferlength) {
bufferlength = tvb_reported_length_remaining(tvb, offset);
}
/* Add the part buffer tree and dissect it */
if (argcount > 0) {
part_buffer_item = proto_tree_add_item(part_tree, hf_saphdb_part_buffer, tvb, offset, bufferlength, ENC_NA);
part_buffer_tree = proto_item_add_subtree(part_buffer_item, ett_saphdb);
dissect_saphdb_part_buffer(tvb, pinfo, part_buffer_tree, offset, bufferlength, argcount, partkind, partkind_item);
length += bufferlength;
}
/* Adjust the item tree length */
proto_item_set_len(part_tree, length);
return length;
}
static int
dissect_saphdb_segment(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_, guint32 offset, gint16 number_of_segments, guint16 nosegment, gboolean compressed)
{
gint8 segmentkind = 0, message_type = 0;
gint16 number_of_parts = 0, segment_number = 0, function_code = 0;
guint32 length = 0, part_length = 0;
gint32 segmentlength = 0;
proto_item *segment_item = NULL, *segmentlength_item = NULL, *number_of_parts_item = NULL, *segment_number_item = NULL, *segment_buffer_item = NULL;
proto_tree *segment_tree = NULL, *segment_buffer_tree = NULL;
/* Add the Segment subtree */
segment_item = proto_tree_add_item(tree, hf_saphdb_segment, tvb, offset, 13, ENC_NA);
segment_tree = proto_item_add_subtree(segment_item, ett_saphdb);
proto_item_append_text(segment_item, " (%d/%d)", nosegment, number_of_segments);
/* Add the Segment fields */
segmentlength = tvb_get_gint32(tvb, offset, ENC_LITTLE_ENDIAN);
segmentlength_item = proto_tree_add_item(segment_tree, hf_saphdb_segment_segmentlength, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
length += 4;
proto_tree_add_item(segment_tree, hf_saphdb_segment_segmentofs, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
length += 4;
number_of_parts = tvb_get_gint16(tvb, offset, ENC_LITTLE_ENDIAN);
number_of_parts_item = proto_tree_add_item(segment_tree, hf_saphdb_segment_noofparts, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
length += 2;
segment_number = tvb_get_gint16(tvb, offset, ENC_LITTLE_ENDIAN);
segment_number_item = proto_tree_add_item(segment_tree, hf_saphdb_segment_segmentno, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
length += 2;
segmentkind = tvb_get_gint8(tvb, offset);
proto_tree_add_item(segment_tree, hf_saphdb_segment_segmentkind, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
length += 1;
col_append_fstr(pinfo->cinfo, COL_INFO, "Segment %s (", val_to_str(segmentkind, saphdb_segment_segmentkind_vals, "Unknown"));
proto_item_append_text(segment_item, ", %s", val_to_str(segmentkind, saphdb_segment_segmentkind_vals, "Unknown"));
/* Check a couple of fields */
if (segmentlength < 13) {
expert_add_info_format(pinfo, segmentlength_item, &ei_saphdb_segment_length, "Segment length %d is invalid", segmentlength);
}
if (number_of_parts < 0) {
expert_add_info_format(pinfo, number_of_parts_item, &ei_saphdb_parts_number_incorrect, "Number of parts %d is invalid", number_of_parts);
}
if (segment_number < 0 || nosegment != segment_number) {
expert_add_info_format(pinfo, segment_number_item, &ei_saphdb_segments_incorrect_order, "Segment number %d is invalid (expected %d)", segment_number, nosegment);
}
/* Add additional fields according to the segment kind*/
switch (segmentkind) {
case 1: /* Request */
message_type = tvb_get_gint8(tvb, offset);
col_append_fstr(pinfo->cinfo, COL_INFO, "%s)", val_to_str(message_type, saphdb_segment_messagetype_vals, "Unknown"));
proto_item_append_text(segment_item, ", %s", val_to_str(message_type, saphdb_segment_messagetype_vals, "Unknown"));
proto_tree_add_item(segment_tree, hf_saphdb_segment_messagetype, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
length += 1;
proto_tree_add_item(segment_tree, hf_saphdb_segment_commit, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
length += 1;
proto_tree_add_item(segment_tree, hf_saphdb_segment_commandoptions, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
length += 1;
proto_tree_add_item(segment_tree, hf_saphdb_segment_reserved, tvb, offset, 8, ENC_NA);
offset += 8;
length += 8;
break;
case 2: /* Reply */
proto_tree_add_item(segment_tree, hf_saphdb_segment_reserved, tvb, offset, 1, ENC_NA);
offset += 1;
length += 1;
function_code = tvb_get_gint16(tvb, offset, ENC_LITTLE_ENDIAN);
col_append_fstr(pinfo->cinfo, COL_INFO, "%s)", val_to_str(function_code, saphdb_segment_functioncode_vals, "Unknown"));
proto_item_append_text(segment_item, ", %s", val_to_str(function_code, saphdb_segment_functioncode_vals, "Unknown"));
proto_tree_add_item(segment_tree, hf_saphdb_segment_functioncode, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
length += 2;
proto_tree_add_item(segment_tree, hf_saphdb_segment_reserved, tvb, offset, 8, ENC_NA);
offset += 8;
length += 8;
break;
default: /* Error and other types */
proto_tree_add_item(segment_tree, hf_saphdb_segment_reserved, tvb, offset, 11, ENC_NA);
offset += 11;
length += 11;
col_append_fstr(pinfo->cinfo, COL_INFO, ")");
break;
}
/* If the packet is compressed, compression will apply from here on. As we don't support compression yet, we stop dissecting here. */
if (compressed) {
return length;
}
/* Add the Segment Buffer subtree */
if (((guint32)segmentlength > length) && (number_of_parts > 0)) {
segment_buffer_item = proto_tree_add_item(segment_tree, hf_saphdb_segment_buffer, tvb, offset, segmentlength - length, ENC_NA);
segment_buffer_tree = proto_item_add_subtree(segment_buffer_item, ett_saphdb);
/* Iterate over the parts and dissect them */
for (guint16 part_number = 1; part_number <= number_of_parts && tvb_reported_length_remaining(tvb, offset) >= 16; part_number++) {
part_length = dissect_saphdb_part(tvb, pinfo, segment_buffer_tree, NULL, offset, number_of_parts, part_number);
offset += part_length;
length += part_length;
}
}
/* Adjust the item tree length */
proto_item_set_len(segment_tree, length);
return length;
}
static int
dissect_saphdb_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
guint32 offset = 0;
/* Add the protocol to the column */
col_add_str(pinfo->cinfo, COL_PROTOCOL, "SAPHDB");
/* Clear out stuff in the info column */
col_clear(pinfo->cinfo,COL_INFO);
/* we are being asked for details */
if (tvb_reported_length(tvb) == 8 || tvb_reported_length(tvb) == 14 || tvb_reported_length(tvb) >= 32) {
proto_item *ti = NULL;
proto_tree *saphdb_tree = NULL;
/* Add the main saphdb subtree */
ti = proto_tree_add_item(tree, proto_saphdb, tvb, offset, -1, ENC_NA);
saphdb_tree = proto_item_add_subtree(ti, ett_saphdb);
/* Initialization Request message */
if (tvb_reported_length(tvb) == 14) {
proto_tree_add_item(saphdb_tree, hf_saphdb_initialization_request, tvb, offset, 14, ENC_NA);
offset += 14;
col_add_str(pinfo->cinfo, COL_INFO, "Initialization Request");
/* Initialization Reply message */
} else if (tvb_reported_length(tvb) == 8) {
proto_item *initialization_reply = NULL;
proto_tree *initialization_reply_tree = NULL;
/* Add the Initialization Reply subtree */
initialization_reply = proto_tree_add_item(saphdb_tree, hf_saphdb_initialization_reply, tvb, offset, 8, ENC_NA);
initialization_reply_tree = proto_item_add_subtree(initialization_reply, ett_saphdb);
proto_tree_add_item(initialization_reply_tree, hf_saphdb_initialization_reply_product_version_major, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
proto_tree_add_item(initialization_reply_tree, hf_saphdb_initialization_reply_product_version_minor, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(initialization_reply_tree, hf_saphdb_initialization_reply_protocol_version_major, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
proto_tree_add_item(initialization_reply_tree, hf_saphdb_initialization_reply_protocol_version_minor, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
col_add_str(pinfo->cinfo, COL_INFO, "Initialization Reply");
/* All other message types */
} else if (tvb_reported_length(tvb) >= 32) {
gboolean compressed = FALSE;
gint16 number_of_segments = 0;
guint32 varpartlength = 0;
proto_item *message_header_item = NULL, *varpartlength_item = NULL, *number_of_segments_item = NULL, *message_buffer_item = NULL, *compressed_buffer_item = NULL;
proto_tree *message_header_tree = NULL, *message_buffer_tree = NULL;
/* Add the Message Header subtree */
message_header_item = proto_tree_add_item(saphdb_tree, hf_saphdb_message_header, tvb, offset, 32, ENC_NA);
message_header_tree = proto_item_add_subtree(message_header_item, ett_saphdb);
/* Add the Message Header fields */
proto_tree_add_item(message_header_tree, hf_saphdb_message_header_sessionid, tvb, offset, 8, ENC_LITTLE_ENDIAN);
offset += 8;
proto_tree_add_item(message_header_tree, hf_saphdb_message_header_packetcount, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
varpartlength_item = proto_tree_add_item_ret_uint(message_header_tree, hf_saphdb_message_header_varpartlength, tvb, offset, 4, ENC_LITTLE_ENDIAN, &varpartlength);
offset += 4;
proto_tree_add_item(message_header_tree, hf_saphdb_message_header_varpartsize, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
number_of_segments = tvb_get_gint16(tvb, offset, ENC_LITTLE_ENDIAN);
number_of_segments_item = proto_tree_add_item(message_header_tree, hf_saphdb_message_header_noofsegm, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
compressed = tvb_get_gint8(tvb, offset) == 2;
proto_tree_add_item(message_header_tree, hf_saphdb_message_header_packetoptions, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
proto_tree_add_item(message_header_tree, hf_saphdb_message_header_reserved, tvb, offset, 1, ENC_NA);
offset += 1;
proto_tree_add_item(message_header_tree, hf_saphdb_message_header_compressionvarpartlength, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
proto_tree_add_item(message_header_tree, hf_saphdb_message_header_reserved, tvb, offset, 4, ENC_NA);
offset += 4;
/* Check the length of the variable part against the remaining packet */
if ((guint32)tvb_reported_length_remaining(tvb, offset) != varpartlength) {
expert_add_info_format(pinfo, varpartlength_item, &ei_saphdb_varpartlenght_incorrect, "Length of variable part %d is invalid", varpartlength);
varpartlength = tvb_reported_length_remaining(tvb, offset);
}
/* Add the Message Buffer subtree */
if (varpartlength > 0 && number_of_segments > 0) {
message_buffer_item = proto_tree_add_item(saphdb_tree, hf_saphdb_message_buffer, tvb, offset, varpartlength, ENC_NA);
message_buffer_tree = proto_item_add_subtree(message_buffer_item, ett_saphdb);
/* If the packet is compressed, the message header and the first segment header is sent uncompressed. We dissect the
* first segment only and add a new item with the compressed buffer. Adding an expert warning as well. */
if (compressed) {
offset += dissect_saphdb_segment(tvb, pinfo, message_buffer_tree, NULL, offset, number_of_segments, 1, compressed);
compressed_buffer_item = proto_tree_add_item(message_buffer_tree, hf_saphdb_compressed_buffer, tvb, offset, varpartlength, ENC_NA);
if (global_saphdb_highlight_items){
expert_add_info_format(pinfo, compressed_buffer_item, &ei_saphdb_compressed_unknown, "Packet is compressed and decompression is not supported");
}
} else {
/* Iterate over the segments and dissect them */
for (guint16 segment_number = 1; segment_number <= number_of_segments && tvb_reported_length_remaining(tvb, offset) >= 13; segment_number++) {
offset += dissect_saphdb_segment(tvb, pinfo, message_buffer_tree, NULL, offset, number_of_segments, segment_number, compressed);
}
}
} else {
expert_add_info_format(pinfo, number_of_segments_item, &ei_saphdb_segments_number_incorrect, "Number of segments %d is invalid", number_of_segments);
}
}
}
return offset;
}
static guint
get_saphdb_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
{
/* Entire HDB packets are of 32-bytes header plus the value in varpartlength field */
guint32 varpartlength = tvb_get_guint32(tvb, offset + 12, ENC_LITTLE_ENDIAN);
return varpartlength + 32;
}
static int
dissect_saphdb_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
return dissect_saphdb_message(tvb, pinfo, tree, FALSE);
}
static int
dissect_saphdb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
if (tvb_reported_length(tvb) == 14 || tvb_reported_length(tvb) == 8) {
return dissect_saphdb_tcp(tvb, pinfo, tree, data);
}
else
{
tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 32, get_saphdb_pdu_len, dissect_saphdb_tcp, data);
}
return tvb_reported_length(tvb);
}
void
proto_register_saphdb(void)
{
static hf_register_info hf[] = {
/* Initialization items */
{ &hf_saphdb_initialization_request,
{ "Initialization Request", "saphdb.init_request", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_initialization_reply,
{ "Initialization Reply", "saphdb.init_reply", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_initialization_reply_product_version_major,
{ "Product Version Major", "saphdb.init_reply.product_version.major", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_initialization_reply_product_version_minor,
{ "Product Version Minor", "saphdb.init_reply.product_version.minor", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_initialization_reply_protocol_version_major,
{ "Protocol Version Major", "saphdb.init_reply.protocol_version.major", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_initialization_reply_protocol_version_minor,
{ "Protocol Version Minor", "saphdb.init_reply.protocol_version.minor", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
/* Message Header items */
{ &hf_saphdb_message_header,
{ "Message Header", "saphdb.message_header", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_message_header_sessionid,
{ "Session ID", "saphdb.sessionid", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_message_header_packetcount,
{ "Packet Count", "saphdb.packetcount", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_message_header_varpartlength,
{ "Var Part Length", "saphdb.varpartlength", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_message_header_varpartsize,
{ "Var Part Size", "saphdb.varpartsize", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_message_header_noofsegm,
{ "Number of Segments", "saphdb.noofsegm", FT_INT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_message_header_packetoptions,
{ "Packet Options", "saphdb.packetoptions", FT_INT8, BASE_DEC, VALS(saphdb_message_header_packetoptions_vals), 0x0, NULL, HFILL }},
{ &hf_saphdb_message_header_compressionvarpartlength,
{ "Compression Var Part Length", "saphdb.compressionvarpartlength", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_message_header_reserved,
{ "Reserved", "saphdb.reserved", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/* Message Buffer items */
{ &hf_saphdb_message_buffer,
{ "Message Buffer", "saphdb.messagebuffer", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_compressed_buffer,
{ "Compressed Buffer", "saphdb.compressedbuffer", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/* Segment items */
{ &hf_saphdb_segment,
{ "Segment", "saphdb.segment", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_segment_segmentlength,
{ "Segment Length", "saphdb.segment.length", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_segment_segmentofs,
{ "Segment Offset", "saphdb.segment.offset", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_segment_noofparts,
{ "Number of Parts", "saphdb.segment.noofparts", FT_INT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_segment_segmentno,
{ "Segment Number", "saphdb.segment.segmentno", FT_INT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_segment_segmentkind,
{ "Segment Kind", "saphdb.segment.kind", FT_INT8, BASE_DEC, VALS(saphdb_segment_segmentkind_vals), 0x0, NULL, HFILL }},
{ &hf_saphdb_segment_messagetype,
{ "Message Type", "saphdb.segment.messagetype", FT_INT8, BASE_DEC, VALS(saphdb_segment_messagetype_vals), 0x0, NULL, HFILL }},
{ &hf_saphdb_segment_commit,
{ "Commit", "saphdb.segment.commit", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_segment_commandoptions,
{ "Command Options", "saphdb.segment.commandoptions", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_segment_functioncode,
{ "Function Code", "saphdb.segment.functioncode", FT_INT16, BASE_DEC, VALS(saphdb_segment_functioncode_vals), 0x0, NULL, HFILL }},
{ &hf_saphdb_segment_reserved,
{ "Reserved", "saphdb.segment.reserved", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/* Segment Buffer items */
{ &hf_saphdb_segment_buffer,
{ "Segment Buffer", "saphdb.segment.buffer", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/* Part items */
{ &hf_saphdb_part,
{ "Part", "saphdb.segment.part", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_partkind,
{ "Part Kind", "saphdb.segment.part.partkind", FT_INT8, BASE_DEC, VALS(saphdb_part_partkind_vals), 0x0, NULL, HFILL }},
{ &hf_saphdb_part_partattributes,
{ "Part Attributes", "saphdb.segment.part.partattributes", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_argumentcount,
{ "Argument Count", "saphdb.segment.part.argumentcount", FT_INT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_bigargumentcount,
{ "Big Argument Count", "saphdb.segment.part.bigargumentcount", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_bufferlength,
{ "Buffer Length", "saphdb.segment.part.bufferlength", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_buffersize,
{ "Buffer Size", "saphdb.segment.part.buffersize", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
/* Part Buffer items */
{ &hf_saphdb_part_buffer,
{ "Part Buffer", "saphdb.segment.part.buffer", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/* Part Buffer Option Part Data items */
{ &hf_saphdb_part_option_argcount,
{ "Argument Row Count", "saphdb.segment.part.option.argcount", FT_INT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_option_name,
{ "Option Name", "saphdb.segment.part.option.name", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_option_type,
{ "Option Type", "saphdb.segment.part.option.type", FT_INT8, BASE_DEC, VALS(saphdb_part_type_vals), 0x0, NULL, HFILL }},
{ &hf_saphdb_part_option_length,
{ "Option Length", "saphdb.segment.part.option.length", FT_INT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_option_value,
{ "Option Value", "saphdb.segment.part.option.value", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_option_value_bool,
{ "Option Value", "saphdb.segment.part.option.value.bool", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_option_value_byte,
{ "Option Value", "saphdb.segment.part.option.value.byte", FT_INT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_option_value_short,
{ "Option Value", "saphdb.segment.part.option.value.short", FT_INT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_option_value_int,
{ "Option Value", "saphdb.segment.part.option.value.int", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_option_value_bigint,
{ "Option Value", "saphdb.segment.part.option.value.bigint", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_option_value_string,
{ "Option Value", "saphdb.segment.part.option.value.string", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_option_value_double,
{ "Option Value", "saphdb.segment.part.option.value.double", FT_DOUBLE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/* SAP HDB Part Buffer COMMAND items */
{ &hf_saphdb_part_command,
{ "Command", "saphdb.segment.part.command", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/* SAP HDB Part Buffer ERROR items */
{ &hf_saphdb_part_error_code,
{ "Error Code", "saphdb.segment.part.error.code", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_error_position,
{ "Error Position", "saphdb.segment.part.error.position", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_error_text_length,
{ "Error Text Length", "saphdb.segment.part.error.text_length", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_error_level,
{ "Error Level", "saphdb.segment.part.error.level", FT_INT8, BASE_DEC, VALS(saphdb_error_level_vals), 0x0, NULL, HFILL }},
{ &hf_saphdb_part_error_sqlstate,
{ "SQL State", "saphdb.segment.part.error.sqlstate", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_error_text,
{ "Error Text", "saphdb.segment.part.error.text", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/* Part Buffer AUTHENTICATION items */
{ &hf_saphdb_part_authentication_field_count,
{ "Field Count", "saphdb.segment.part.authentication.fieldcount", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_authentication_field_length,
{ "Field Length", "saphdb.segment.part.authentication.fieldlength", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saphdb_part_authentication_field_value,
{ "Field Value", "saphdb.segment.part.authentication.fieldvalue", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/* SAP HDB Part Buffer CLIENTID items */
{ &hf_saphdb_part_clientid,
{ "Client ID", "saphdb.segment.part.clientid", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
};
/* Setup protocol subtree array */
static gint *ett[] = {
&ett_saphdb
};
/* Register the expert info */
static ei_register_info ei[] = {
{ &ei_saphdb_compressed_unknown, { "saphdb.compressed", PI_UNDECODED, PI_WARN, "The packet is compressed, and decompression is not supported", EXPFILL }},
{ &ei_saphdb_option_part_unknown, { "saphdb.segment.part.option.unknown", PI_UNDECODED, PI_WARN, "The Option Part has a unknown type that is not dissected", EXPFILL }},
{ &ei_saphdb_segments_incorrect_order, { "saphdb.segment.segmentno.invalid", PI_MALFORMED, PI_ERROR, "The segments are in incorrect order or are invalid", EXPFILL }},
{ &ei_saphdb_segments_number_incorrect, { "saphdb.noofsegm.invalid", PI_MALFORMED, PI_ERROR, "The number of segments is incorrect", EXPFILL }},
{ &ei_saphdb_segment_length, { "saphdb.segment.segmentlength.invalid", PI_MALFORMED, PI_ERROR, "The segment length is incorrect", EXPFILL }},
{ &ei_saphdb_buffer_length, { "saphdb.segment.part.bufferlength.invalid", PI_MALFORMED, PI_ERROR, "The part buffer length is incorrect", EXPFILL }},
{ &ei_saphdb_parts_number_incorrect, { "saphdb.segment.noofparts.invalid", PI_MALFORMED, PI_ERROR, "The number of parts is incorrect", EXPFILL }},
{ &ei_saphdb_varpartlenght_incorrect, { "saphdb.varpartlength.invalid", PI_MALFORMED, PI_ERROR, "The length is incorrect", EXPFILL }},
};
module_t *saphdb_module;
expert_module_t* saphdb_expert;
/* Register the protocol */
proto_saphdb = proto_register_protocol("SAP HANA SQL Command Network Protocol", "SAPHDB", "saphdb");
saphdb_expert = expert_register_protocol(proto_saphdb);
expert_register_field_array(saphdb_expert, ei, array_length(ei));
saphdb_handle = register_dissector("saphdb", dissect_saphdb, proto_saphdb);
proto_register_field_array(proto_saphdb, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
/* Register the preferences */
saphdb_module = prefs_register_protocol(proto_saphdb, proto_reg_handoff_saphdb);
range_convert_str(wmem_epan_scope(), &global_saphdb_port_range, SAPHDB_PORT_RANGE, MAX_TCP_PORT);
prefs_register_range_preference(saphdb_module, "tcp_ports", "SAP HANA SQL Command Network Protocol port numbers", "Port numbers used for SAP HANA SQL Command Network Protocol (default " SAPHDB_PORT_RANGE ")", &global_saphdb_port_range, MAX_TCP_PORT);
prefs_register_bool_preference(saphdb_module, "highlight_unknown_items", "Highlight unknown SAP HANA HDB items", "Whether the SAP HANA HDB Protocol dissector should highlight unknown items (might be noise and generate a lot of expert warnings)", &global_saphdb_highlight_items);
}
/**
* Helpers for dealing with the port range
*/
static void range_delete_callback (guint32 port, gpointer ptr _U_)
{
dissector_delete_uint("tcp.port", port, saphdb_handle);
}
static void range_add_callback (guint32 port, gpointer ptr _U_)
{
dissector_add_uint("tcp.port", port, saphdb_handle);
}
/**
* Register Hand off for the SAP HDB Protocol
*/
void
proto_reg_handoff_saphdb(void)
{
static gboolean initialized = FALSE;
static range_t *saphdb_port_range;
if (!initialized) {
saphdb_handle = create_dissector_handle(dissect_saphdb, proto_saphdb);
saphdb_handle_tls = register_dissector("SAPHDB over TLS", dissect_saphdb, proto_saphdb);
initialized = TRUE;
} else {
range_foreach(saphdb_port_range, range_delete_callback, NULL);
wmem_free(wmem_epan_scope(), saphdb_port_range);
}
saphdb_port_range = range_copy(wmem_epan_scope(), global_saphdb_port_range);
range_foreach(saphdb_port_range, range_add_callback, NULL);
ssl_dissector_add(0, saphdb_handle_tls);
gssapi_handle = find_dissector_add_dependency("gssapi", proto_saphdb);
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: t
* End:
*
* vi: set shiftwidth=8 tabstop=8 noexpandtab:
* :indentSize=8:tabSize=8:noTabs=false:
*/