From ad1b785fe80df6ecffee396a617960e1af390274 Mon Sep 17 00:00:00 2001 From: Michael Mann Date: Tue, 18 Aug 2015 23:14:09 -0400 Subject: [PATCH] udp_dissect_pdus follow-up Add heuristic support Better documentation Change-Id: I236c1f4d3613aa58d608aee0e5edc40c3b158d25 Reviewed-on: https://code.wireshark.org/review/10120 Petri-Dish: Michael Mann Tested-by: Petri Dish Buildbot Reviewed-by: Michael Mann --- doc/README.dissector | 93 ++++++++++++++++++++++++++++++++++-- doc/README.heuristic | 35 ++++++-------- epan/dissectors/packet-dnp.c | 29 +++++------ epan/dissectors/packet-udp.c | 25 ++++++++-- epan/dissectors/packet-udp.h | 22 +++++---- 5 files changed, 153 insertions(+), 51 deletions(-) diff --git a/doc/README.dissector b/doc/README.dissector index 97db00b14f..0a9d7ae043 100644 --- a/doc/README.dissector +++ b/doc/README.dissector @@ -3147,7 +3147,92 @@ will then be added to the protocol tree. Note that there may be more than one complete C string in the tvbuff, so the dissection is done in a loop. -2.8 ptvcursors. +2.8 Using udp_dissect_pdus(). + +As noted in section 2.7.1, TCP has an API to dissect its PDU that can handle +a PDU spread across multiple packets or multiple PDUs spread across a single +packet. This section describes a similar mechanism for UDP, but is only +applicable for one or more PDUs in a single packet. If a protocol runs on top +of TCP as well as UDP, a common PDU dissection function can be created for both. + +To register the distinct dissector functions, consider the following +example using UDP and TCP dissection, stolen from packet-dnp.c: + + #include "packet-tcp.h" + #include "packet-udp.h" + + dissector_handle_t dnp3_tcp_handle; + dissector_handle_t dnp3_udp_handle; + + dnp3_tcp_handle = new_create_dissector_handle(dissect_dnp3_tcp, proto_dnp3); + dnp3_udp_handle = new_create_dissector_handle(dissect_dnp3_udp, proto_dnp3); + + dissector_add_uint("tcp.port", TCP_PORT_DNP, dnp3_tcp_handle); + dissector_add_uint("udp.port", UDP_PORT_DNP, dnp3_udp_handle); + +Both dissect_dnp3_tcp and dissect_dnp3_udp call tcp_dissect_pdus and +udp_dissect_pdus respectively, with a reference to the same callbacks which +are called to handle PDU data. + + static int + dissect_dnp3_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) + { + return udp_dissect_pdus(tvb, pinfo, tree, DNP_HDR_LEN, dnp3_udp_check_header, + get_dnp3_message_len, dissect_dnp3_message, data); + } + + static int + dissect_dnp3_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) + { + if (!check_dnp3_header(tvb, FALSE)) { + return 0; + } + + tcp_dissect_pdus(tvb, pinfo, tree, TRUE, DNP_HDR_LEN, + get_dnp3_message_len, dissect_dnp3_message, data); + + return tvb_captured_length(tvb); + } + +(udp_dissect_pdus has an option of a heuristic check function within it while +tcp_dissect_pdus does not, so it's done outside) + +The arguments to udp_dissect_pdus are: + + the tvbuff pointer, packet_info pointer, and proto_tree pointer + passed to the dissector; + + the number of bytes of PDU data required to determine the length + of the PDU; + + an optional routine (passing NULL is okay) that takes as arguments a + packet_info pointer, a tvbuff pointer and an offset value representing the + offset into the tvbuff at which a PDU begins, and a void pointer for user + data, and should return TRUE if the packet belongs to the dissector. + The routine must not throw exceptions (it is guaranteed that the + number of bytes specified by the previous argument to + udp_dissect_pdus is available, but more data might not be available, + so don't refer to any data past that); + + a routine that takes as arguments a packet_info pointer, a tvbuff + pointer and an offset value representing the offset into the tvbuff + at which a PDU begins, and a void pointer for user data, and should + return the total length of the PDU in bytes. If return value is 0, + it's treated the same as a failed heuristic. + The routine must not throw exceptions (it is guaranteed that the + number of bytes specified by the previous argument to + tcp_dissect_pdus is available, but more data might not be available, + so don't refer to any data past that); + + a new_dissector_t routine to dissect the pdu that's passed a tvbuff + pointer, packet_info pointer, proto_tree pointer and a void pointer for + user data, with the tvbuff containing a possibly-reassembled PDU. (The + "reported_length" of the tvbuff will be the length of the PDU); + + a void pointer to user data that is passed to the length-determining + routine, and the dissector routine referenced in the previous parameter. + +2.9 ptvcursors. The ptvcursor API allows a simpler approach to writing dissectors for simple protocols. The ptvcursor API works best for protocols whose fields @@ -3180,7 +3265,7 @@ To use the ptvcursor API, include the "ptvcursor.h" file. The PGM dissector is an example of how to use it. You don't need to look at it as a guide; instead, the API description here should be good enough. -2.8.1 ptvcursor API. +2.9.1 ptvcursor API. ptvcursor_t* ptvcursor_new(proto_tree* tree, tvbuff_t* tvb, gint offset) @@ -3236,7 +3321,7 @@ If the length is unknown, length may be defined as SUBTREE_UNDEFINED_LENGTH. In this case, at the next pop, the item length will be equal to the advancement of the cursor since the creation of the subtree. -2.8.2 Miscellaneous functions. +2.9.2 Miscellaneous functions. tvbuff_t* ptvcursor_tvbuff(ptvcursor_t* ptvc) @@ -3259,7 +3344,7 @@ ptvcursor_set_subtree(ptvcursor_t* ptvc, proto_item* it, gint ett_subtree); Creates a subtree and adds it to the cursor as the working tree but does not save the old working tree. -2.9 Optimizations +2.10 Optimizations A protocol dissector may be called in 2 different ways - with, or without a non-null "tree" argument. diff --git a/doc/README.heuristic b/doc/README.heuristic index 2c4c8db56d..bac71b5677 100644 --- a/doc/README.heuristic +++ b/doc/README.heuristic @@ -103,7 +103,7 @@ Heuristic Code Example ---------------------- You can find a lot of code examples in the Wireshark sources, e.g.: grep -l heur_dissector_add epan/dissectors/*.c -returns 163 files (November 2014). +returns 177 files (October 2015). For the above example criteria, the following code example might do the work (combine this with the dissector skeleton in README.developer): @@ -118,7 +118,7 @@ static dissector_handle_t PROTOABBREV_pdu_handle; /* Heuristics test */ static gboolean -test_PROTOABBREV(tvbuff_t *tvb) +test_PROTOABBREV(packet_info *pinfo _U_, tvbuff_t *tvb, int offset _U_, void *data _U_) { /* 0) Verify needed bytes available in tvb so tvb_get...() doesn't cause exception. if (tvb_captured_length(tvb) < 5) @@ -172,7 +172,7 @@ dissect_PROTOABBREV_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, voi static gboolean dissect_PROTOABBREV_heur_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { - if (!test_PROTOABBREV(tvb)) + if (!test_PROTOABBREV(pinfo, tvb, 0, data)) return FALSE; /* specify that dissect_PROTOABBREV is to be called directly from now on for @@ -190,26 +190,21 @@ dissect_PROTOABBREV_heur_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree return (TRUE); } +static int +dissect_PROTOABBREV_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + udp_dissect_pdus(tvb, pinfo, tree, TRUE, 5, NULL, + get_PROTOABBREV_len, dissect_PROTOABBREV_pdu, data); + return tvb_reported_length(tvb); +} + static gboolean dissect_PROTOABBREV_heur_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { ... - If (!test_PROTOABBREV(tvb)) - return FALSE; - - /* specify that dissect_PROTOABBREV is to be called directly from now on for - * packets for this "connection" ... but only do this if your heuristic sits directly - * on top of (was called by) a dissector which established a conversation for the - * protocol "port type". In other words: only directly over TCP, UDP, DCCP, ... - * otherwise you'll be overriding the dissector that called your heuristic dissector. - */ - conversation = find_or_create_conversation(pinfo); - conversation_set_dissector(conversation, PROTOABBREV_pdu_handle); - /* and do the dissection */ - dissect_PROTOABBREV_pdu(tvb, pinfo, tree, data); - - return (TRUE); + return (udp_dissect_pdus(tvb, pinfo, tree, TRUE, 5, test_PROTOABBREV, + get_PROTOABBREV_len, dissect_PROTOABBREV_pdu, data) != 0); } void @@ -221,9 +216,9 @@ proto_reg_handoff_PROTOABBREV(void) proto_PROTOABBREV); /* register as heuristic dissector for both TCP and UDP */ - heur_dissector_add("tcp", dissect_PROTOABBREV_tcp_heur, "PROTOABBREV over TCP", + heur_dissector_add("tcp", dissect_PROTOABBREV_heur_tcp, "PROTOABBREV over TCP", "PROTOABBREV_tcp", proto_PROTOABBREV, HEURISTIC_ENABLE); - heur_dissector_add("udp", dissect_PROTOABBREV_udp_heur, "PROTOABBREV over UDP", + heur_dissector_add("udp", dissect_PROTOABBREV_heur_udp, "PROTOABBREV over UDP", "PROTOABBREV_udp", proto_PROTOABBREV, HEURISTIC_ENABLE); #ifdef OPTIONAL diff --git a/epan/dissectors/packet-dnp.c b/epan/dissectors/packet-dnp.c index ce0f7221e7..7970b5144c 100644 --- a/epan/dissectors/packet-dnp.c +++ b/epan/dissectors/packet-dnp.c @@ -3521,30 +3521,31 @@ dissect_dnp3_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void return TRUE; } +static gboolean +dnp3_udp_check_header(packet_info *pinfo _U_, tvbuff_t *tvb, int offset _U_, void *data _U_) +{ + return check_dnp3_header(tvb, FALSE); +} + +static gboolean +dnp3_udp_check_header_heur(packet_info *pinfo _U_, tvbuff_t *tvb, int offset _U_, void *data _U_) +{ + return check_dnp3_header(tvb, TRUE); +} + static int dissect_dnp3_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { - if (!check_dnp3_header(tvb, FALSE)) { - return 0; - } - - udp_dissect_pdus(tvb, pinfo, tree, DNP_HDR_LEN, + return udp_dissect_pdus(tvb, pinfo, tree, DNP_HDR_LEN, dnp3_udp_check_header, get_dnp3_message_len, dissect_dnp3_message, data); - - return tvb_captured_length(tvb); } static gboolean dissect_dnp3_udp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { - if (!check_dnp3_header(tvb, FALSE)) { - return FALSE; - } + return (udp_dissect_pdus(tvb, pinfo, tree, DNP_HDR_LEN, dnp3_udp_check_header_heur, + get_dnp3_message_len, dissect_dnp3_message, data) != 0); - udp_dissect_pdus(tvb, pinfo, tree, DNP_HDR_LEN, - get_dnp3_message_len, dissect_dnp3_message, data); - - return TRUE; } static void diff --git a/epan/dissectors/packet-udp.c b/epan/dissectors/packet-udp.c index dd323118e1..197131c15c 100644 --- a/epan/dissectors/packet-udp.c +++ b/epan/dissectors/packet-udp.c @@ -549,9 +549,10 @@ decode_udp_ports(tvbuff_t *tvb, int offset, packet_info *pinfo, call_dissector(data_handle,next_tvb, pinfo, tree); } -void +int udp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, - guint fixed_len, guint (*get_pdu_len)(packet_info *, tvbuff_t *, int, void*), + guint fixed_len, gboolean (*heuristic_check)(packet_info *, tvbuff_t *, int, void*), + guint (*get_pdu_len)(packet_info *, tvbuff_t *, int, void*), new_dissector_t dissect_pdu, void* dissector_data) { volatile int offset = 0; @@ -579,10 +580,26 @@ udp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, */ captured_length_remaining = tvb_ensure_captured_length_remaining(tvb, offset); + /* + * If there is a heuristic function, check it + */ + if ((heuristic_check != NULL) && + ((*heuristic_check)(pinfo, tvb, offset, dissector_data) == FALSE)) { + return offset; + } + /* * Get the length of the PDU. */ plen = (*get_pdu_len)(pinfo, tvb, offset, dissector_data); + if (plen == 0) { + /* + * Either protocol has variable length (which isn't supposed by UDP) + * or packet doesn't belong to protocol + */ + return offset; + } + if (plen < fixed_len) { /* * Either: @@ -600,7 +617,7 @@ udp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, * Report this as a bounds error. */ show_reported_bounds_error(tvb, pinfo, tree); - return; + return offset; } curr_layer_num = pinfo->curr_layer_num-1; @@ -668,6 +685,8 @@ udp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, if (offset <= offset_before) break; } + + return offset; } static void diff --git a/epan/dissectors/packet-udp.h b/epan/dissectors/packet-udp.h index 79d1af492f..a3c9bc646d 100644 --- a/epan/dissectors/packet-udp.h +++ b/epan/dissectors/packet-udp.h @@ -115,20 +115,22 @@ WS_DLL_PUBLIC struct udp_analysis *get_udp_conversation_data(conversation_t *, * fixed-length chunk of data that contains enough information * to determine the length of the PDU, followed by rest of the PDU. * - * The first three arguments are the arguments passed to the dissector - * that calls this routine. - * - * "fixed_len" is the length of the fixed-length part of the PDU. - * - * "get_pdu_len()" is a routine called to get the length of the PDU from + * @param tvb the tvbuff with the (remaining) packet data passed to dissector + * @param pinfo the packet info of this packet (additional info) passed to dissector + * @param tree the protocol tree to be build or NULL passed to dissector + * @param fixed_len is the length of the fixed-length part of the PDU. + * @param heuristic_check is the optional routine called to see if dissection + * should be done; it's passed "pinfo", "tvb", "offset" and "dissector_data". + * @param get_pdu_len is a routine called to get the length of the PDU from * the fixed-length part of the PDU; it's passed "pinfo", "tvb", "offset" and * "dissector_data". - * - * "dissect_pdu()" is the routine to dissect a PDU. + * @param dissect_pdu the sub-dissector to be called + * @param dissector_data parameter to pass to subdissector */ -WS_DLL_PUBLIC void +WS_DLL_PUBLIC int udp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, - guint fixed_len, guint (*get_pdu_len)(packet_info *, tvbuff_t *, int, void*), + guint fixed_len, gboolean (*heuristic_check)(packet_info *, tvbuff_t *, int, void*), + guint (*get_pdu_len)(packet_info *, tvbuff_t *, int, void*), new_dissector_t dissect_pdu, void* dissector_data); #ifdef __cplusplus