udp_dissect_pdus follow-up
authorMichael Mann <mmann78@netscape.net>
Wed, 19 Aug 2015 03:14:09 +0000 (23:14 -0400)
committerMichael Mann <mmann78@netscape.net>
Sun, 1 Nov 2015 21:42:51 +0000 (21:42 +0000)
Add heuristic support
Better documentation

Change-Id: I236c1f4d3613aa58d608aee0e5edc40c3b158d25
Reviewed-on: https://code.wireshark.org/review/10120
Petri-Dish: Michael Mann <mmann78@netscape.net>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Michael Mann <mmann78@netscape.net>
doc/README.dissector
doc/README.heuristic
epan/dissectors/packet-dnp.c
epan/dissectors/packet-udp.c
epan/dissectors/packet-udp.h

index 97db00b14f36a4af9dc98ddf8f1bddca83b3344f..0a9d7ae043b4e8948269bf78ed983d70ffe4d850 100644 (file)
@@ -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.
index 2c4c8db56dfc0e74cece81e264a1612a5a16f92b..bac71b56775abe1ca558594fe2c703c3534beed8 100644 (file)
@@ -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
index ce0f7221e7ccbaa8c23a009c71db020c8426e187..7970b5144c577e619af111e9282d23e9cd92798f 100644 (file)
@@ -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
index dd323118e19524c798e86fe0e164a1c55525c3b1..197131c15c89d567e500de5e87f29ad4c3c292f7 100644 (file)
@@ -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
index 79d1af492f0400dac679f8a34d92202307ebc9f6..a3c9bc646d52d1c3972e313b5fa57677d102c94b 100644 (file)
@@ -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