/* 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 "packet-tcp.h"
#include "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"

/* Header Length */
#define SAPHDB_HEADER_LEN 32

/* 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_const(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_const(segmentkind, saphdb_segment_segmentkind_vals, "Unknown"));
	proto_item_append_text(segment_item, ", %s", val_to_str_const(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_const(message_type, saphdb_segment_messagetype_vals, "Unknown"));
			proto_item_append_text(segment_item, ", %s", val_to_str_const(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_const(function_code, saphdb_segment_functioncode_vals, "Unknown"));
			proto_item_append_text(segment_item, ", %s", val_to_str_const(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) >= SAPHDB_HEADER_LEN) {

		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) >= SAPHDB_HEADER_LEN) {

			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, SAPHDB_HEADER_LEN, 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 + SAPHDB_HEADER_LEN;
}

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
	{
		/* Header must be present */
		if (!tvb_bytes_exist(tvb, 0, SAPHDB_HEADER_LEN))
			return 0;

		/* Filter on reserved bytes */
		if(tvb_get_guint8(tvb, 23) || tvb_get_guint32(tvb, 28, ENC_BIG_ENDIAN))
			return 0;

		tcp_dissect_pdus(tvb, pinfo, tree, TRUE, SAPHDB_HEADER_LEN, 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_with_description("saphdb_tls", "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:
 */
