forked from osmocom/wireshark
From Bill Meier:
1. Add Preferences: a. To allow specification of a hint as to TDS protocol being decoded (Unspecified/TDS4/TDS5/TDS7/TDS8); Default: 'unspecified' The 'hint' is used only when needed to do a correct decode. If the protocol is unspecified, the decode is as previous. b. To allow specification of 'ranges' of TCP ports to be treated as 'TDS tcp ports'; i.e. if the source or destination port of a tcp connection matches a specified range, then the connection should be considered to be TDS. c. To allow specification of a hint as to whether TDS being decoded is 'little-endian' or 'big-endian'. Default: 'little-endian'. A hint is just that; E.G. if TDS7+ packets are encountered the decode is always 'little-endian'. 2, Register tcp MS SQL default ports (1433, 2433) as TDS ports ('dissector_add'). This also enables TDS as a choice for 'decode as'. 3. 'netlib_check_login_pkt' changed to check 'TDS tcp port' range(s) as entered in preferences; 4. Change 'dissect_tds_query_packet' to handle TDS4 ascii in addition to TDS7/8 UCS-16. 5. Change 'dissect_tds_rpc' to: a. handle TDS4 ascii RPC in addition to TDS7/8 UCS-16 RPC; b. handle Microsoft 'encoded' rpc_name; c. fix memory leak (not freeing memory obtained using 'tvb_fake_unicode'); 6. Change 'dissect_tds_response' to: a. handle tds4 tokens 'tds_col_name' and 'tds_col_info'; b. dissect tokens 'tds_doneinproc' and tds 'doneproc' similarly to 'tds_done' c. reclaim memory allocated for 'tds_col' structures when finished processing response (Additional memory was being allocated each time a tokenized tds5 response was processed) 7. New function 'dissect_tds_col_info_token' (similar to 'read_results_tds5') associated with handling TDS4 responses. 8. New functions 'dissect_tds_query5_packet', 'dissect_tds5_lang_token' 9. Rework TDS token size calculation; Some TDS tokens have a length field of other than 2 bytes. (e.g.: the length field for TDS_LANG_TOKEN is 4 bytes) 10. Update token definitions and usages; a. Update based upon info from current version of FreeTDS 'tds.h' as well as info from Sybase TDS5 document; example: TDS_124_TOKEN renamed to TDS_PROCID_TOKEN b. TDS_124_TOKEN [TDS_PROCID] was incorrectly not considered a 'fixed-size' token in function 'tds_is_fixed_token' svn path=/trunk/; revision=12566
This commit is contained in:
parent
81a790ca83
commit
4956f5fba1
3
AUTHORS
3
AUTHORS
|
@ -2229,6 +2229,9 @@ Francesco Fondelli <fondelli.francesco [AT] tiscali.it> {
|
|||
ICE protocol support
|
||||
}
|
||||
|
||||
Bill Meier <wmeier [AT] newsguy.com> {
|
||||
TDS4/TDS5 enhancements
|
||||
}
|
||||
|
||||
And assorted fixes and enhancements by the people listed above
|
||||
and by:
|
||||
|
|
|
@ -181,27 +181,35 @@
|
|||
#define is_valid_tds_type(x) ((x) >= TDS_QUERY_PKT && (x) <= TDS_XXX7_PKT)
|
||||
|
||||
/* The following constants are imported more or less directly from FreeTDS */
|
||||
/* TODO Update from current version of FreeTDS tds.h */
|
||||
|
||||
#define TDS5_DYN_TOKEN 231 /* 0xE7 TDS 5.0 only */
|
||||
#define TDS5_DYNRES_TOKEN 236 /* 0xEC TDS 5.0 only */
|
||||
#define TDS5_DYN3_TOKEN 215 /* 0xD7 TDS 5.0 only */
|
||||
#define TDS5_PARAMS_TOKEN 215 /* 0xD7 TDS 5.0 only */
|
||||
#define TDS5_DYNAMIC_TOKEN 231 /* 0xE7 TDS 5.0 only */
|
||||
#define TDS5_PARAMFMT_TOKEN 236 /* 0xEC TDS 5.0 only */
|
||||
#define TDS5_PARAMFMT2_TOKEN 32 /* 0x20 TDS 5.0 only */
|
||||
#define TDS_LANG_TOKEN 33 /* 0x21 TDS 5.0 only */
|
||||
#define TDS_CLOSE_TOKEN 113 /* 0x71 TDS 5.0 only? ct_close() */
|
||||
#define TDS5_ORDERBY2_TOKEN 34 /* 0x22 TDS 5.0 only */
|
||||
#define TDS5_CURDECLARE2_TOKEN 35 /* 0x23 TDS 5.0 only */
|
||||
#define TDS5_ROWFMT2_TOKEN 97 /* 0x61 TDS 5.0 only */
|
||||
#define TDS5_MSG_TOKEN 101 /* 0x65 TDS 5.0 only */
|
||||
#define TDS_LOGOUT_TOKEN 113 /* 0x71 TDS 5.0 only? ct_close() */
|
||||
#define TDS_RET_STAT_TOKEN 121 /* 0x79 */
|
||||
#define TDS_124_TOKEN 124 /* 0x7C TDS 4.2 only - TDS_PROCID */
|
||||
#define TDS_PROCID_TOKEN 124 /* 0x7C TDS 4.2 only - TDS_PROCID */
|
||||
#define TDS7_RESULT_TOKEN 129 /* 0x81 TDS 7.0 only */
|
||||
#define TDS_COL_NAME_TOKEN 160 /* 0xA0 TDS 4.2 only */
|
||||
#define TDS_COL_INFO_TOKEN 161 /* 0xA1 TDS 4.2 only - TDS_COLFMT */
|
||||
#define TDS5_DYNAMIC2_TOKEN 163 /* 0xA3 TDS 5.0 only */
|
||||
/*#define TDS_TABNAME 164 */
|
||||
/*#define TDS_COL_INFO 165 */
|
||||
#define TDS_167_TOKEN 167 /* 0xA7 */
|
||||
#define TDS_168_TOKEN 168 /* 0xA8 */
|
||||
#define TDS_COMPUTE_NAMES_TOKEN 167 /* 0xA7 */
|
||||
#define TDS_COMPUTE_RESULT_TOKEN 168 /* 0xA8 */
|
||||
#define TDS_ORDER_BY_TOKEN 169 /* 0xA9 TDS_ORDER */
|
||||
#define TDS_ERR_TOKEN 170 /* 0xAA */
|
||||
#define TDS_MSG_TOKEN 171 /* 0xAB */
|
||||
#define TDS_PARAM_TOKEN 172 /* 0xAC RETURNVALUE? */
|
||||
#define TDS_LOGIN_ACK_TOKEN 173 /* 0xAD */
|
||||
#define TDS_174_TOKEN 174 /* 0xAE TDS_CONTROL */
|
||||
#define TDS_CONTROL_TOKEN 174 /* 0xAE TDS_CONTROL */
|
||||
#define TDS_KEY_TOKEN 202 /* 0xCA */
|
||||
#define TDS_ROW_TOKEN 209 /* 0xD1 */
|
||||
#define TDS_CMP_ROW_TOKEN 211 /* 0xD3 */
|
||||
#define TDS_CAP_TOKEN 226 /* 0xE2 */
|
||||
|
@ -213,6 +221,26 @@
|
|||
#define TDS_DONEPROC_TOKEN 254 /* 0xFE TDS_DONEPROC */
|
||||
#define TDS_DONEINPROC_TOKEN 255 /* 0xFF TDS_DONEINPROC */
|
||||
|
||||
/* Microsoft internal stored procedure id's */
|
||||
|
||||
#define TDS_SP_CURSOR 1
|
||||
#define TDS_SP_CURSOROPEN 2
|
||||
#define TDS_SP_CURSORPREPARE 3
|
||||
#define TDS_SP_CURSOREXECUTE 4
|
||||
#define TDS_SP_CURSORPREPEXEC 5
|
||||
#define TDS_SP_CURSORUNPREPARE 6
|
||||
#define TDS_SP_CURSORFETCH 7
|
||||
#define TDS_SP_CURSOROPTION 8
|
||||
#define TDS_SP_CURSORCLOSE 9
|
||||
#define TDS_SP_EXECUTESQL 10
|
||||
#define TDS_SP_PREPARE 11
|
||||
#define TDS_SP_EXECUTE 12
|
||||
#define TDS_SP_PREPEXEC 13
|
||||
#define TDS_SP_PREPEXECRPC 14
|
||||
#define TDS_SP_UNPREPARE 15
|
||||
|
||||
/* Sybase Data Types */
|
||||
|
||||
#define SYBCHAR 47 /* 0x2F */
|
||||
#define SYBVARCHAR 39 /* 0x27 */
|
||||
#define SYBINTN 38 /* 0x26 */
|
||||
|
@ -328,6 +356,55 @@ static dissector_handle_t tds_tcp_handle;
|
|||
static dissector_handle_t ntlmssp_handle;
|
||||
static dissector_handle_t data_handle;
|
||||
|
||||
/* TDS protocol type preference */
|
||||
/* XXX: This preference is used as a 'hint' for cases where interpretation is ambiguous */
|
||||
/* Currently the hint is global */
|
||||
/* TODO: Consider storing protocol type with each conversation */
|
||||
/* (when type is determined and using the preference as a default) ?? */
|
||||
|
||||
#define TDS_PROTOCOL_NOT_SPECIFIED 0
|
||||
#define TDS_PROTOCOL_4 4
|
||||
#define TDS_PROTOCOL_5 5
|
||||
#define TDS_PROTOCOL_7 7
|
||||
#define TDS_PROTOCOL_8 8
|
||||
|
||||
static gint tds_protocol_type = TDS_PROTOCOL_NOT_SPECIFIED;
|
||||
|
||||
const enum_val_t tds_protocol_type_options[] = {
|
||||
{"not_specified", "Not Specified", TDS_PROTOCOL_NOT_SPECIFIED},
|
||||
{"tds4", "TDS 4", TDS_PROTOCOL_4}, /* TDS 4.2 and TDS 4.6 */
|
||||
{"tds5", "TDS 5", TDS_PROTOCOL_5},
|
||||
{"tds7", "TDS 7", TDS_PROTOCOL_7},
|
||||
{"tds8", "TDS 8", TDS_PROTOCOL_8},
|
||||
{NULL, NULL, -1}
|
||||
};
|
||||
|
||||
#define TDS_PROTO_PREF_NOT_SPECIFIED (tds_protocol_type == TDS_NOT_SPECIFIED)
|
||||
#define TDS_PROTO_PREF_TDS4 (tds_protocol_type == TDS_PROTOCOL_4)
|
||||
#define TDS_PROTO_PREF_TDS5 (tds_protocol_type == TDS_PROTOCOL_5)
|
||||
#define TDS_PROTO_PREF_TDS7 (tds_protocol_type == TDS_PROTOCOL_7)
|
||||
#define TDS_PROTO_PREF_TDS8 (tds_protocol_type == TDS_PROTOCOL_8)
|
||||
#define TDS_PROTO_PREF_TDS7_TDS8 ( TDS_PROTO_PREF_TDS7 || TDS_PROTO_PREF_TDS8 )
|
||||
|
||||
/* TDS "endian type" */
|
||||
/* XXX: Assumption is that all TDS conversations being decoded in a particular capture */
|
||||
/* have the same endian type */
|
||||
/* TODO: consider storing endian type with each conversation */
|
||||
/* (using pref as the default) */
|
||||
|
||||
static gint tds_little_endian = TRUE;
|
||||
|
||||
const enum_val_t tds_endian_type_options[] = {
|
||||
{"little_endian", "Little Endian", TRUE},
|
||||
{"big_endian" , "Big Endian" , FALSE},
|
||||
{NULL, NULL, -1}
|
||||
};
|
||||
|
||||
|
||||
/* TCP port preferences for TDS decode */
|
||||
|
||||
static range_t *tds_tcp_ports = NULL;
|
||||
|
||||
/* These correspond to the netlib packet type field */
|
||||
static const value_string packet_type_names[] = {
|
||||
{TDS_QUERY_PKT, "Query Packet"},
|
||||
|
@ -363,24 +440,26 @@ static const value_string status_names[] = {
|
|||
|
||||
/* The one byte token at the start of each TDS PDU */
|
||||
static const value_string token_names[] = {
|
||||
{TDS5_DYN_TOKEN, "Dynamic SQL"},
|
||||
{TDS5_DYNRES_TOKEN, "Dynamic Results"},
|
||||
{TDS5_DYN3_TOKEN, "Dynamic (Unknown)"},
|
||||
{TDS5_DYNAMIC_TOKEN, "TDS5 Dynamic SQL"},
|
||||
{TDS5_PARAMFMT_TOKEN, "TDS5 Parameter Format"},
|
||||
{TDS5_PARAMFMT2_TOKEN, "TDS5 Parameter2 Format"},
|
||||
{TDS5_PARAMS_TOKEN, "TDS5 Parameters"},
|
||||
{TDS_LANG_TOKEN, "Language"},
|
||||
{TDS_CLOSE_TOKEN, "Close Connection"},
|
||||
{TDS_LOGOUT_TOKEN, "Logout"},
|
||||
{TDS_RET_STAT_TOKEN, "Return Status"},
|
||||
{TDS_124_TOKEN, "Proc ID"},
|
||||
{TDS_PROCID_TOKEN, "Proc ID"},
|
||||
{TDS7_RESULT_TOKEN, "TDS7+ Results"},
|
||||
{TDS_COL_NAME_TOKEN, "Column Names"},
|
||||
{TDS_COL_INFO_TOKEN, "Column Info"},
|
||||
{TDS_167_TOKEN, "Unknown (167)"},
|
||||
{TDS_168_TOKEN, "Unknown (168)"},
|
||||
{TDS_COMPUTE_NAMES_TOKEN, "Compute Names"},
|
||||
{TDS_COMPUTE_RESULT_TOKEN, "Compute Results"},
|
||||
{TDS_ORDER_BY_TOKEN, "Order By"},
|
||||
{TDS_ERR_TOKEN, "Error Message"},
|
||||
{TDS_MSG_TOKEN, "Info Message"},
|
||||
{TDS_PARAM_TOKEN, "Paramater"},
|
||||
{TDS_LOGIN_ACK_TOKEN, "Login Acknowledgement"},
|
||||
{TDS_174_TOKEN, "Unknown (174)"},
|
||||
{TDS_CONTROL_TOKEN, "TDS Control"},
|
||||
{TDS_KEY_TOKEN, "TDS Key"},
|
||||
{TDS_ROW_TOKEN, "Row"},
|
||||
{TDS_CMP_ROW_TOKEN, "Compute Row"},
|
||||
{TDS_CAP_TOKEN, "Capabilities"},
|
||||
|
@ -391,9 +470,34 @@ static const value_string token_names[] = {
|
|||
{TDS_DONE_TOKEN, "Done"},
|
||||
{TDS_DONEPROC_TOKEN, "Done Proc"},
|
||||
{TDS_DONEINPROC_TOKEN, "Done In Proc"},
|
||||
{TDS5_DYNAMIC2_TOKEN, "TDS5 Dynamic2"},
|
||||
{TDS5_ORDERBY2_TOKEN, "TDS5 OrderBy2"},
|
||||
{TDS5_CURDECLARE2_TOKEN, "TDS5 CurDeclare2"},
|
||||
{TDS5_ROWFMT2_TOKEN, "TDS5 RowFmt2"},
|
||||
{TDS5_MSG_TOKEN, "TDS5 Msg"},
|
||||
{0, NULL},
|
||||
};
|
||||
|
||||
|
||||
static const value_string internal_stored_proc_id_names[] = {
|
||||
{TDS_SP_CURSOR, "sp_cursor" },
|
||||
{TDS_SP_CURSOROPEN, "sp_cursoropen" },
|
||||
{TDS_SP_CURSORPREPARE, "sp_cursorprepare" },
|
||||
{TDS_SP_CURSOREXECUTE, "sp_cursorexecute" },
|
||||
{TDS_SP_CURSORPREPEXEC, "sp_cursorprepexec" },
|
||||
{TDS_SP_CURSORUNPREPARE, "sp_cursorunprepare"},
|
||||
{TDS_SP_CURSORFETCH, "sp_cursorfetch" },
|
||||
{TDS_SP_CURSOROPTION, "sp_cursoroption" },
|
||||
{TDS_SP_CURSORCLOSE, "sp_cursorclose" },
|
||||
{TDS_SP_EXECUTESQL, "sp_executesql" },
|
||||
{TDS_SP_PREPARE, "sp_prepare" },
|
||||
{TDS_SP_EXECUTE, "sp_execute" },
|
||||
{TDS_SP_PREPEXEC, "sp_prepexec" },
|
||||
{TDS_SP_PREPEXECRPC, "sp_prepexecrpc" },
|
||||
{TDS_SP_UNPREPARE, "sp_unprepare" },
|
||||
{0, NULL },
|
||||
};
|
||||
|
||||
static const value_string env_chg_names[] = {
|
||||
{1, "Database"},
|
||||
{2, "Language"},
|
||||
|
@ -401,7 +505,7 @@ static const value_string env_chg_names[] = {
|
|||
{4, "Blocksize"},
|
||||
{5, "Unicode Locale ID"},
|
||||
{6, "Unicode Comparison Style"},
|
||||
{7, "Collation Info"},
|
||||
{7, "Collation Info"},
|
||||
{0, NULL},
|
||||
};
|
||||
|
||||
|
@ -454,11 +558,12 @@ struct tds7_login_packet_hdr {
|
|||
|
||||
/* all the standard memory management stuff */
|
||||
#define tds_column_length (sizeof(struct _tds_col))
|
||||
#define tds_column_init_count 10
|
||||
#define tds_column_init_count 40
|
||||
|
||||
static GMemChunk *tds_column = NULL;
|
||||
|
||||
/* support routines */
|
||||
|
||||
static void
|
||||
dissect_tds_ntlmssp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
||||
guint offset, guint length)
|
||||
|
@ -469,6 +574,84 @@ dissect_tds_ntlmssp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
|
|||
call_dissector(ntlmssp_handle, ntlmssp_tvb, pinfo, tree);
|
||||
}
|
||||
|
||||
/* */
|
||||
|
||||
static guint16
|
||||
tds_tvb_get_xxtohs(tvbuff_t *tvb, gint offset, gint tds_little_endian) {
|
||||
if (tds_little_endian)
|
||||
return tvb_get_letohs(tvb, offset);
|
||||
else
|
||||
return tvb_get_ntohs(tvb, offset);
|
||||
}
|
||||
|
||||
static guint32
|
||||
tds_tvb_get_xxtohl(tvbuff_t *tvb, gint offset, gint tds_little_endian) {
|
||||
if (tds_little_endian)
|
||||
return tvb_get_letohl(tvb, offset);
|
||||
else
|
||||
return tvb_get_ntohl(tvb, offset);
|
||||
}
|
||||
|
||||
|
||||
static int tds_token_is_fixed_size(guint8 token)
|
||||
{
|
||||
switch (token) {
|
||||
case TDS_DONE_TOKEN:
|
||||
case TDS_DONEPROC_TOKEN:
|
||||
case TDS_DONEINPROC_TOKEN:
|
||||
case TDS_RET_STAT_TOKEN:
|
||||
case TDS7_RESULT_TOKEN:
|
||||
case TDS_PROCID_TOKEN:
|
||||
case TDS_LOGOUT_TOKEN:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int tds_get_fixed_token_size(guint8 token)
|
||||
{
|
||||
switch(token) {
|
||||
case TDS_DONE_TOKEN:
|
||||
case TDS_DONEPROC_TOKEN:
|
||||
case TDS_DONEINPROC_TOKEN:
|
||||
case TDS_PROCID_TOKEN:
|
||||
return 8;
|
||||
case TDS_RET_STAT_TOKEN:
|
||||
return 4;
|
||||
case TDS_LOGOUT_TOKEN:
|
||||
return 1;
|
||||
case TDS7_RESULT_TOKEN:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static guint tds_get_variable_token_size(tvbuff_t *tvb, gint offset, guint8 token, guint *len_field_size_p)
|
||||
{
|
||||
switch(token) {
|
||||
/* some tokens have a 4 byte length field */
|
||||
case TDS5_PARAMFMT2_TOKEN:
|
||||
case TDS_LANG_TOKEN:
|
||||
case TDS5_ORDERBY2_TOKEN:
|
||||
case TDS5_CURDECLARE2_TOKEN:
|
||||
case TDS5_ROWFMT2_TOKEN:
|
||||
case TDS5_DYNAMIC2_TOKEN:
|
||||
*len_field_size_p = 4;
|
||||
return tds_tvb_get_xxtohl(tvb, offset, tds_little_endian) + 5;
|
||||
/* some have a 1 byte length field */
|
||||
case TDS5_MSG_TOKEN:
|
||||
*len_field_size_p = 1;
|
||||
return tvb_get_guint8(tvb, offset) +2;
|
||||
/* and most have a 2 byte length field */
|
||||
default:
|
||||
*len_field_size_p = 2;
|
||||
return tds_tvb_get_xxtohs(tvb, offset, tds_little_endian) + 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dissect_tds_query_packet(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
|
||||
{
|
||||
|
@ -483,17 +666,97 @@ dissect_tds_query_packet(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree
|
|||
query_hdr = proto_tree_add_text(tree, tvb, offset, -1, "TDS Query Packet");
|
||||
query_tree = proto_item_add_subtree(query_hdr, ett_tds7_query);
|
||||
len = tvb_reported_length_remaining(tvb, offset);
|
||||
if((len < 2) || tvb_get_guint8(tvb, offset+1) !=0)
|
||||
|
||||
if (TDS_PROTO_PREF_TDS4 ||
|
||||
(!TDS_PROTO_PREF_TDS7_TDS8 &&
|
||||
((len < 2) || tvb_get_guint8(tvb, offset+1) != 0)))
|
||||
is_unicode = FALSE;
|
||||
|
||||
if (is_unicode) {
|
||||
if (is_unicode)
|
||||
msg = tvb_fake_unicode(tvb, offset, len/2, TRUE);
|
||||
proto_tree_add_text(query_tree, tvb, offset, len, "Query: %s", msg);
|
||||
g_free(msg);
|
||||
offset += len;
|
||||
}
|
||||
else
|
||||
msg = tvb_get_string(tvb, offset, len);
|
||||
|
||||
proto_tree_add_text(query_tree, tvb, offset, len, "Query: %s", msg);
|
||||
g_free(msg);
|
||||
offset += len;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dissect_tds5_lang_token(tvbuff_t *tvb, guint offset, guint len, proto_tree *tree) {
|
||||
gboolean is_unicode = FALSE;
|
||||
char *msg;
|
||||
|
||||
proto_tree_add_text(tree, tvb, offset, 1 , "Status: %u", tvb_get_guint8(tvb, offset));
|
||||
offset += 1;
|
||||
len -= 1;
|
||||
|
||||
if (is_unicode)
|
||||
msg = tvb_fake_unicode(tvb, offset, (len)/2, TRUE);
|
||||
else
|
||||
msg = tvb_get_string(tvb, offset, len);
|
||||
|
||||
proto_tree_add_text(tree, tvb, offset, len, "Language text: %s", msg);
|
||||
g_free(msg);
|
||||
}
|
||||
|
||||
static void
|
||||
dissect_tds_query5_packet(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
|
||||
{
|
||||
guint offset;
|
||||
guint pos;
|
||||
guint token_len_field_size = 2;
|
||||
guint8 token;
|
||||
guint token_sz;
|
||||
proto_item *query_hdr;
|
||||
proto_tree *query_tree;
|
||||
proto_item *token_item;
|
||||
proto_tree *token_tree;
|
||||
|
||||
offset = 0;
|
||||
query_hdr = proto_tree_add_text(tree, tvb, offset, -1, "TDS5 Query Packet");
|
||||
query_tree = proto_item_add_subtree(query_hdr, ett_tds7_query);
|
||||
|
||||
/*
|
||||
* Until we reach the end of the packet, read tokens.
|
||||
*/
|
||||
pos = offset;
|
||||
while (tvb_reported_length_remaining(tvb, pos) > 0) {
|
||||
|
||||
/* our token */
|
||||
token = tvb_get_guint8(tvb, pos);
|
||||
if (tds_token_is_fixed_size(token))
|
||||
token_sz = tds_get_fixed_token_size(token) + 1;
|
||||
else
|
||||
token_sz = tds_get_variable_token_size(tvb, pos+1, token, &token_len_field_size);
|
||||
|
||||
token_item = proto_tree_add_text(tree, tvb, pos, token_sz,
|
||||
"Token 0x%02x %s", token,
|
||||
val_to_str(token, token_names, "Unknown Token Type"));
|
||||
token_tree = proto_item_add_subtree(token_item, ett_tds_token);
|
||||
|
||||
/*
|
||||
* If it's a variable token, put the length field in here
|
||||
* instead of replicating this for each token subdissector.
|
||||
*/
|
||||
if (!tds_token_is_fixed_size(token))
|
||||
proto_tree_add_text(token_tree, tvb, pos+1, token_len_field_size, "Length: %u", token_sz);
|
||||
|
||||
switch (token) {
|
||||
case TDS_LANG_TOKEN:
|
||||
dissect_tds5_lang_token(tvb, pos + 5, token_sz -5, token_tree);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pos += token_sz;
|
||||
|
||||
} /* while */
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dissect_tds7_login(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
||||
{
|
||||
|
@ -520,7 +783,8 @@ dissect_tds7_login(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|||
header_tree = proto_item_add_subtree(header_hdr, ett_tds7_hdr);
|
||||
|
||||
td7hdr.total_packet_size = tvb_get_letohl(tvb, offset);
|
||||
proto_tree_add_uint(header_tree, hf_tds7_login_total_size, tvb, offset, sizeof(td7hdr.total_packet_size), td7hdr.total_packet_size);
|
||||
proto_tree_add_uint(header_tree, hf_tds7_login_total_size, tvb, offset,
|
||||
sizeof(td7hdr.total_packet_size), td7hdr.total_packet_size);
|
||||
offset += sizeof(td7hdr.total_packet_size);
|
||||
|
||||
td7hdr.tds_version = tvb_get_ntohl(tvb, offset);
|
||||
|
@ -629,35 +893,6 @@ static int get_size_by_coltype(int servertype)
|
|||
default: return -1; break;
|
||||
}
|
||||
}
|
||||
static int tds_is_fixed_token(int token)
|
||||
{
|
||||
switch (token) {
|
||||
case TDS_DONE_TOKEN:
|
||||
case TDS_DONEPROC_TOKEN:
|
||||
case TDS_DONEINPROC_TOKEN:
|
||||
case TDS_RET_STAT_TOKEN:
|
||||
case TDS7_RESULT_TOKEN:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
static int tds_get_token_size(int token)
|
||||
{
|
||||
switch(token) {
|
||||
case TDS_DONE_TOKEN:
|
||||
case TDS_DONEPROC_TOKEN:
|
||||
case TDS_DONEINPROC_TOKEN:
|
||||
return 8;
|
||||
case TDS_RET_STAT_TOKEN:
|
||||
return 4;
|
||||
case TDS_124_TOKEN:
|
||||
return 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
# if 0
|
||||
/*
|
||||
* data_to_string should take column data and turn it into something we can
|
||||
|
@ -714,24 +949,79 @@ tds_get_row_size(tvbuff_t *tvb, struct _netlib_data *nl_data, guint offset)
|
|||
}
|
||||
|
||||
/*
|
||||
* Read the results token and store the relevant information in the
|
||||
* _netlib_data structure for later use (see tds_get_row_size).
|
||||
* Process TDS 4 "COL_INFO" token and store relevant information in the
|
||||
* _netlib_data structure for later use (see tds_get_row_size)
|
||||
*
|
||||
* XXX Can TDS 4 be "big-endian" ? we'll assume yes.
|
||||
*
|
||||
*/
|
||||
static gboolean
|
||||
read_results_tds5(tvbuff_t *tvb, struct _netlib_data *nl_data, guint offset)
|
||||
dissect_tds_col_info_token(tvbuff_t *tvb, struct _netlib_data *nl_data, guint offset)
|
||||
{
|
||||
guint len, name_len;
|
||||
guint next, cur;
|
||||
guint col;
|
||||
|
||||
next = offset + tds_tvb_get_xxtohs(tvb, offset+1, tds_little_endian) + 3;
|
||||
cur = offset + 3;
|
||||
|
||||
col = 0;
|
||||
while (cur < next) {
|
||||
|
||||
if (col >= MAX_COLUMNS) {
|
||||
nl_data->num_cols = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
nl_data->columns[col] = g_mem_chunk_alloc(tds_column);
|
||||
|
||||
nl_data->columns[col]->name[0] ='\0';
|
||||
|
||||
nl_data->columns[col]->utype = tds_tvb_get_xxtohs(tvb, cur, tds_little_endian);
|
||||
cur += 2;
|
||||
|
||||
cur += 2; /* unknown */
|
||||
|
||||
nl_data->columns[col]->ctype = tvb_get_guint8(tvb,cur);
|
||||
cur++;
|
||||
|
||||
if (!is_fixed_coltype(nl_data->columns[col]->ctype)) {
|
||||
nl_data->columns[col]->csize = tvb_get_guint8(tvb,cur);
|
||||
cur ++;
|
||||
} else {
|
||||
nl_data->columns[col]->csize =
|
||||
get_size_by_coltype(nl_data->columns[col]->ctype);
|
||||
}
|
||||
|
||||
col += 1;
|
||||
|
||||
} /* while */
|
||||
|
||||
nl_data->num_cols = col;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read the results token and store the relevant information in the
|
||||
* _netlib_data structure for later use (see tds_get_row_size).
|
||||
*
|
||||
* TODO: check we don't go past end of the token
|
||||
*/
|
||||
static gboolean
|
||||
read_results_tds5(tvbuff_t *tvb, struct _netlib_data *nl_data, guint offset, guint len)
|
||||
{
|
||||
guint name_len;
|
||||
guint cur;
|
||||
guint i;
|
||||
|
||||
len = tvb_get_letohs(tvb, offset+1);
|
||||
cur = offset + 3;
|
||||
cur = offset;
|
||||
|
||||
/*
|
||||
* This would be the logical place to check for little/big endianess
|
||||
* if we didn't see the login packet.
|
||||
* XXX: We'll take a hint
|
||||
*/
|
||||
nl_data->num_cols = tvb_get_letohs(tvb, cur);
|
||||
nl_data->num_cols = tds_tvb_get_xxtohs(tvb, cur, tds_little_endian);
|
||||
if (nl_data->num_cols > MAX_COLUMNS) {
|
||||
nl_data->num_cols = 0;
|
||||
return FALSE;
|
||||
|
@ -747,7 +1037,7 @@ read_results_tds5(tvbuff_t *tvb, struct _netlib_data *nl_data, guint offset)
|
|||
|
||||
cur++; /* unknown */
|
||||
|
||||
nl_data->columns[i]->utype = tvb_get_letohs(tvb, cur);
|
||||
nl_data->columns[i]->utype = tds_tvb_get_xxtohs(tvb, cur, tds_little_endian);
|
||||
cur += 2;
|
||||
|
||||
cur += 2; /* unknown */
|
||||
|
@ -807,13 +1097,16 @@ netlib_check_login_pkt(tvbuff_t *tvb, guint offset, packet_info *pinfo, guint8 t
|
|||
if (tvb_get_guint8(tvb, 8) != TDS_LANG_TOKEN) {
|
||||
return FALSE;
|
||||
}
|
||||
/* check if it is MS SQL default port */
|
||||
} else if ((pinfo->srcport != 1433 &&
|
||||
pinfo->destport != 1433) && (pinfo->srcport != 2433 && pinfo->destport != 2433)) {
|
||||
/* otherwise, we can not ensure this is netlib */
|
||||
/* beyond a reasonable doubt. */
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* See if either tcp.destport or tcp.srcport is specified
|
||||
* in the preferences as being a TDS port.
|
||||
*/
|
||||
else if (!value_is_in_range(tds_tcp_ports, pinfo->srcport) &&
|
||||
!value_is_in_range(tds_tcp_ports, pinfo->destport)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -900,14 +1193,14 @@ dissect_tds_err_token(tvbuff_t *tvb, guint offset, guint token_sz, proto_tree *t
|
|||
char *msg;
|
||||
gboolean is_unicode = FALSE;
|
||||
|
||||
proto_tree_add_text(tree, tvb, offset, 4, "SQL Error Number: %d", tvb_get_letohl(tvb, offset));
|
||||
proto_tree_add_text(tree, tvb, offset, 4, "SQL Error Number: %d", tds_tvb_get_xxtohl(tvb, offset, tds_little_endian));
|
||||
offset += 4;
|
||||
proto_tree_add_text(tree, tvb, offset, 1, "State: %u", tvb_get_guint8(tvb, offset));
|
||||
offset +=1;
|
||||
proto_tree_add_text(tree, tvb, offset, 1, "Severity Level: %u", tvb_get_guint8(tvb, offset));
|
||||
offset +=1;
|
||||
|
||||
msg_len = tvb_get_letohs(tvb, offset);
|
||||
msg_len = tds_tvb_get_xxtohs(tvb, offset, tds_little_endian);
|
||||
proto_tree_add_text(tree, tvb, offset, 1, "Error message length: %u characters", msg_len);
|
||||
offset +=2;
|
||||
|
||||
|
@ -956,7 +1249,7 @@ dissect_tds_err_token(tvbuff_t *tvb, guint offset, guint token_sz, proto_tree *t
|
|||
g_free(msg);
|
||||
}
|
||||
|
||||
proto_tree_add_text(tree, tvb, offset, 2, "line number: %d", tvb_get_letohs(tvb, offset));
|
||||
proto_tree_add_text(tree, tvb, offset, 2, "line number: %d", tds_tvb_get_xxtohs(tvb, offset, tds_little_endian));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1080,7 +1373,7 @@ dissect_tds_done_token(tvbuff_t *tvb, guint offset, proto_tree *tree)
|
|||
offset += 2;
|
||||
proto_tree_add_text(tree, tvb, offset, 2, "Operation");
|
||||
offset += 2;
|
||||
proto_tree_add_text(tree, tvb, offset, 4, "row count: %u", tvb_get_letohl(tvb, offset));
|
||||
proto_tree_add_text(tree, tvb, offset, 4, "row count: %u", tds_tvb_get_xxtohl(tvb, offset, tds_little_endian));
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
|
@ -1089,24 +1382,46 @@ dissect_tds_rpc(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
|
|||
{
|
||||
int offset = 0;
|
||||
guint len;
|
||||
const char *val;
|
||||
guint16 sp_id;
|
||||
char *val;
|
||||
|
||||
/*
|
||||
* RPC name.
|
||||
* XXX - how can we determine whether this is ASCII or Unicode?
|
||||
*/
|
||||
len = tvb_get_letohs(tvb, offset);
|
||||
proto_tree_add_text(tree, tvb, offset, 2, "RPC Name Length: %u", len);
|
||||
offset += 2;
|
||||
if (len != 0) {
|
||||
val = tvb_fake_unicode(tvb, offset, len, TRUE);
|
||||
len *= 2;
|
||||
proto_tree_add_text(tree, tvb, offset, len, "RPC Name: %s",
|
||||
val);
|
||||
offset += len;
|
||||
switch(tds_protocol_type) {
|
||||
case TDS_PROTOCOL_4:
|
||||
len = tvb_get_guint8(tvb, offset);
|
||||
proto_tree_add_text(tree, tvb, offset, 1, "RPC Name Length: %u", len);
|
||||
offset += 1;
|
||||
val = tvb_get_string(tvb, offset, len);
|
||||
proto_tree_add_text(tree, tvb, offset, len, "RPC Name: %s", val);
|
||||
g_free(val);
|
||||
offset += len;
|
||||
break;
|
||||
|
||||
case TDS_PROTOCOL_7:
|
||||
case TDS_PROTOCOL_8:
|
||||
default: /* unspecified: try as if TDS7/TDS8 */
|
||||
len = tvb_get_letohs(tvb, offset);
|
||||
proto_tree_add_text(tree, tvb, offset, 2, "RPC Name Length: %u", len);
|
||||
offset += 2;
|
||||
if (len == 0xFFFF) {
|
||||
sp_id = tvb_get_letohs(tvb, offset);
|
||||
proto_tree_add_text(tree, tvb, offset, 2, "RPC Stored Proc ID: %u (%s)",
|
||||
sp_id,
|
||||
val_to_str(sp_id, internal_stored_proc_id_names, "Unknown"));
|
||||
offset += 2;
|
||||
}
|
||||
else if (len != 0) {
|
||||
val = tvb_fake_unicode(tvb, offset, len, TRUE);
|
||||
len *= 2;
|
||||
proto_tree_add_text(tree, tvb, offset, len, "RPC Name: %s", val);
|
||||
g_free(val);
|
||||
offset += len;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
proto_tree_add_text(tree, tvb, offset, -1, "Unknown data");
|
||||
proto_tree_add_text(tree, tvb, offset, -1, "Params (not dissected)");
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1116,10 +1431,13 @@ dissect_tds_resp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|||
proto_item *token_item;
|
||||
proto_tree *token_tree;
|
||||
guint pos, token_sz = 0;
|
||||
guint token_len_field_size = 2;
|
||||
guint8 token;
|
||||
struct _netlib_data nl_data;
|
||||
gint length_remaining;
|
||||
|
||||
g_mem_chunk_reset(tds_column); /* in case exception thrown the previous time thru this code */
|
||||
|
||||
memset(&nl_data, '\0', sizeof nl_data);
|
||||
|
||||
/*
|
||||
|
@ -1130,8 +1448,9 @@ dissect_tds_resp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|||
/* our token */
|
||||
token = tvb_get_guint8(tvb, pos);
|
||||
|
||||
if (tds_is_fixed_token(token)) {
|
||||
token_sz = tds_get_token_size(token) + 1;
|
||||
/* TODO Handle TDS_PARAMFMT, TDS_PARAMS [similar to TDS_RESULTS, TDS_ROW] */
|
||||
if (tds_token_is_fixed_size(token)) {
|
||||
token_sz = tds_get_fixed_token_size(token) + 1;
|
||||
} else if (token == TDS_ROW_TOKEN) {
|
||||
/*
|
||||
* Rows are special; they have no size field and
|
||||
|
@ -1139,11 +1458,9 @@ dissect_tds_resp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|||
*/
|
||||
token_sz = tds_get_row_size(tvb, &nl_data, pos + 1);
|
||||
} else
|
||||
token_sz = tvb_get_letohs(tvb, pos + 1) + 3;
|
||||
token_sz = tds_get_variable_token_size(tvb, pos+1, token, &token_len_field_size);
|
||||
|
||||
length_remaining = tvb_ensure_length_remaining(tvb, pos);
|
||||
if (token_sz > (guint)length_remaining)
|
||||
token_sz = (guint)length_remaining;
|
||||
|
||||
token_item = proto_tree_add_text(tree, tvb, pos, token_sz,
|
||||
"Token 0x%02x %s", token,
|
||||
|
@ -1154,35 +1471,52 @@ dissect_tds_resp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|||
* If it's a variable token, put the length field in here
|
||||
* instead of replicating this for each token subdissector.
|
||||
*/
|
||||
if (!tds_is_fixed_token(token) && token != TDS_ROW_TOKEN) {
|
||||
proto_tree_add_text(token_tree, tvb, pos+1, 2,
|
||||
"Length: %u", tvb_get_letohs(tvb, pos+1));
|
||||
if (!tds_token_is_fixed_size(token) && token != TDS_ROW_TOKEN) {
|
||||
proto_tree_add_text(token_tree, tvb, pos+1, token_len_field_size, "Length: %u", token_sz);
|
||||
}
|
||||
|
||||
if (token_sz > (guint)length_remaining)
|
||||
token_sz = (guint)length_remaining;
|
||||
|
||||
switch (token) {
|
||||
|
||||
case TDS_COL_NAME_TOKEN:
|
||||
/*
|
||||
* TDS 4.2
|
||||
* TODO dissect token to get "column names" to fill in _netlib_data
|
||||
*/
|
||||
break;
|
||||
|
||||
case TDS_COL_INFO_TOKEN:
|
||||
/*
|
||||
* TDS 4.2: get the column info
|
||||
*/
|
||||
dissect_tds_col_info_token(tvb, &nl_data, pos);
|
||||
break;
|
||||
|
||||
case TDS_RESULT_TOKEN:
|
||||
/*
|
||||
* If it's a result token, we need to stash the
|
||||
* column info.
|
||||
*/
|
||||
read_results_tds5(tvb, &nl_data, pos);
|
||||
read_results_tds5(tvb, &nl_data, pos + 3, token_sz - 3);
|
||||
break;
|
||||
|
||||
case TDS_ENV_CHG_TOKEN:
|
||||
dissect_tds_env_chg(tvb, pos + 3, token_sz - 3,
|
||||
token_tree);
|
||||
dissect_tds_env_chg(tvb, pos + 3, token_sz - 3, token_tree);
|
||||
break;
|
||||
|
||||
case TDS_AUTH_TOKEN:
|
||||
dissect_tds_ntlmssp(tvb, pinfo, token_tree, pos + 3,
|
||||
token_sz - 3);
|
||||
dissect_tds_ntlmssp(tvb, pinfo, token_tree, pos + 3, token_sz - 3);
|
||||
break;
|
||||
case TDS_ERR_TOKEN:
|
||||
case TDS_MSG_TOKEN:
|
||||
dissect_tds_err_token(tvb, pos + 3, token_sz - 3, token_tree);
|
||||
break;
|
||||
|
||||
case TDS_DONE_TOKEN:
|
||||
case TDS_DONEPROC_TOKEN:
|
||||
case TDS_DONEINPROC_TOKEN:
|
||||
dissect_tds_done_token(tvb, pos + 1, token_tree);
|
||||
break;
|
||||
case TDS_LOGIN_ACK_TOKEN:
|
||||
|
@ -1196,6 +1530,9 @@ dissect_tds_resp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|||
/* and step to the end of the token, rinse, lather, repeat */
|
||||
pos += token_sz;
|
||||
}
|
||||
/* reset tds_column mem_chunk since finished processing response */
|
||||
/* (pointers to atoms in nl_data being discarded) */
|
||||
g_mem_chunk_reset(tds_column);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1252,6 +1589,10 @@ dissect_netlib_buffer(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|||
|
||||
/*
|
||||
* Deal with fragmentation.
|
||||
*
|
||||
* TODO: handle case where netlib headers 'packet-number'.is always 0
|
||||
* use fragment_add_seq_next in this case ?
|
||||
*
|
||||
*/
|
||||
save_fragmented = pinfo->fragmented;
|
||||
if (tds_defragment &&
|
||||
|
@ -1290,7 +1631,9 @@ dissect_netlib_buffer(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|||
next_tvb = tvb_new_subset(tvb, offset, -1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
if (next_tvb != NULL) {
|
||||
|
||||
switch (type) {
|
||||
|
||||
case TDS_RPC_PKT:
|
||||
|
@ -1307,6 +1650,9 @@ dissect_netlib_buffer(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|||
case TDS_QUERY_PKT:
|
||||
dissect_tds_query_packet(next_tvb, pinfo, tds_tree);
|
||||
break;
|
||||
case TDS_QUERY5_PKT:
|
||||
dissect_tds_query5_packet(next_tvb, pinfo, tds_tree);
|
||||
break;
|
||||
case TDS_NTLMAUTH_PKT:
|
||||
dissect_tds_ntlmssp(next_tvb, pinfo, tds_tree, offset - 8, -1);
|
||||
break;
|
||||
|
@ -1735,6 +2081,7 @@ proto_register_netlib(void)
|
|||
"", HFILL }
|
||||
},
|
||||
};
|
||||
|
||||
static gint *ett[] = {
|
||||
&ett_tds,
|
||||
&ett_tds_fragments,
|
||||
|
@ -1753,8 +2100,6 @@ proto_register_netlib(void)
|
|||
proto_register_field_array(proto_tds, hf, array_length(hf));
|
||||
proto_register_subtree_array(ett, array_length(ett));
|
||||
|
||||
tds_tcp_handle = create_dissector_handle(dissect_tds_tcp, proto_tds);
|
||||
|
||||
tds_module = prefs_register_protocol(proto_tds, NULL);
|
||||
prefs_register_bool_preference(tds_module, "desegment_buffers",
|
||||
"Reassemble TDS buffers spanning multiple TCP segments",
|
||||
|
@ -1765,6 +2110,18 @@ proto_register_netlib(void)
|
|||
"Reassemble fragmented TDS messages with multiple buffers",
|
||||
"Whether the TDS dissector should defragment messages spanning multiple Netlib buffers",
|
||||
&tds_defragment);
|
||||
prefs_register_enum_preference(tds_module, "protocol_type",
|
||||
"TDS Protocol Type",
|
||||
"Hint as to version of TDS protocol being decoded",
|
||||
&tds_protocol_type, tds_protocol_type_options, FALSE);
|
||||
prefs_register_enum_preference(tds_module, "endian_type",
|
||||
"TDS decode as",
|
||||
"Hint as to whether to decode TDS protocol as little-endian or big-endian. (TDS7/8 always decoded as little-endian)",
|
||||
&tds_little_endian, tds_endian_type_options, FALSE);
|
||||
prefs_register_range_preference(tds_module, "tcp_ports",
|
||||
"TDS TCP ports",
|
||||
"Additional TCP ports to decode as TDS",
|
||||
&tds_tcp_ports, 0xFFFF);
|
||||
|
||||
register_init_routine(tds_init);
|
||||
}
|
||||
|
@ -1776,8 +2133,12 @@ proto_register_netlib(void)
|
|||
void
|
||||
proto_reg_handoff_tds(void)
|
||||
{
|
||||
/* dissector_add("tcp.port", 1433, dissect_tds,
|
||||
proto_tds); */
|
||||
tds_tcp_handle = create_dissector_handle(dissect_tds_tcp, proto_tds);
|
||||
|
||||
/* Initial TDS ports: MS SQL default ports */
|
||||
dissector_add("tcp.port", 1433, tds_tcp_handle);
|
||||
dissector_add("tcp.port", 2433, tds_tcp_handle);
|
||||
|
||||
heur_dissector_add("tcp", dissect_tds_tcp_heur, proto_tds);
|
||||
|
||||
ntlmssp_handle = find_dissector("ntlmssp");
|
||||
|
|
Loading…
Reference in New Issue