Add escaping of an apostrophe in XML output.
[obnox/wireshark/wip.git] / packet-wtp.c
index 69f136430c7eae55beda769887f31f830c75bbf2..5b59266606fbc1fceffb5a78a2ae3bdf29a15a37 100644 (file)
@@ -2,7 +2,7 @@
  *
  * Routines to dissect WTP component of WAP traffic.
  *
- * $Id: packet-wtp.c,v 1.58 2003/12/23 12:07:14 obiot Exp $
+ * $Id: packet-wtp.c,v 1.63 2004/02/28 22:56:36 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -332,9 +332,10 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     int                        numMissing = 0;         /* Number of missing packets in a negative ack */
     int                        i;
     tvbuff_t           *wsp_tvb = NULL;
-    fragment_data      *fd_head = NULL;
     guint8             psn = 0;                /* Packet sequence number*/
     guint16            TID = 0;                /* Transaction-Id       */
+    int                        dataOffset;
+    gint               dataLen;
 
     if (szInfo == NULL)
        szInfo = g_string_sized_new(32);
@@ -349,10 +350,18 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
            ti = proto_tree_add_item(tree, proto_wtp,
                                    tvb, offCur, 1, bo_little_endian);
            wtp_tree = proto_item_add_subtree(ti, ett_wtp_sub_pdu_tree);
+               proto_item_append_text(ti, ", PDU concatenation");
        }
        offCur = 1;
        i = 1;
        while (offCur < (int) tvb_reported_length(tvb)) {
+           tvbuff_t *wtp_tvb;
+           /* The length of an embedded WTP PDU is coded as either:
+            *  - a 7-bit value contained in one octet with highest bit == 0.
+            *  - a 15-bit value contained in two octets (little endian)
+            *    if the 1st octet has its highest bit == 1.
+            * This means that this is NOT encoded as an uintvar-integer!!!
+            */
            b0 = tvb_get_guint8(tvb, offCur + 0);
            if (b0 & 0x80) {
                c_fieldlen = 2;
@@ -368,17 +377,28 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
            if (i > 1 && check_col(pinfo->cinfo, COL_INFO)) {
                col_append_str(pinfo->cinfo, COL_INFO, ", ");
            }
-           wsp_tvb = tvb_new_subset(tvb, offCur + c_fieldlen, c_pdulen, c_pdulen);
-           dissect_wtp_common(wsp_tvb, pinfo, wtp_tree);
+           /* Skip the length field for the WTP sub-tvb */
+           wtp_tvb = tvb_new_subset(tvb, offCur + c_fieldlen, c_pdulen, c_pdulen);
+           dissect_wtp_common(wtp_tvb, pinfo, wtp_tree);
            offCur += c_fieldlen + c_pdulen;
            i++;
        }
+       if (tree) {
+               proto_item_append_text(ti, ", PDU count: %u", i);
+       }
        return;
     }
+    /* No concatenation */
     fCon = b0 & 0x80;
     fRID = retransmission_indicator(b0);
     pdut = pdu_type(b0);
 
+#ifdef DEBUG
+       printf("WTP packet %u: tree = %p, pdu = %s (%u) length: %u\n",
+                       pinfo->fd->num, tree,
+                       match_strval(pdut, vals_wtp_pdu_type), pdut, tvb_length(tvb));
+#endif
+
     /* Develop the string to put in the Info column */
     g_string_sprintf(szInfo, "WTP %s",
                    val_to_str(pdut, vals_wtp_pdu_type, "Unknown PDU type 0x%x"));
@@ -398,6 +418,8 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
            fTTR = transmission_trailer(b0);
            TID = tvb_get_ntohs(tvb, offCur + 1);
            psn = tvb_get_guint8(tvb, offCur + 3);
+           if (psn != 0)
+               g_string_sprintfa(szInfo, " (%u)", psn);
            cbHeader = 4;
            break;
 
@@ -469,6 +491,12 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                proto_tree_add_item(wtp_tree, hf_wtp_header_Inv_flag_UP, tvb, offCur + 3, 1, bo_little_endian);
                proto_tree_add_item(wtp_tree, hf_wtp_header_Inv_Reserved, tvb, offCur + 3, 1, bo_little_endian);
                proto_tree_add_item(wtp_tree, hf_wtp_header_Inv_TransactionClass, tvb, offCur + 3, 1, bo_little_endian);
+               proto_item_append_text(ti,
+                               ", PDU: Invoke (%u)"
+                               ", Transaction Class: %s (%u)",
+                               INVOKE,
+                               match_strval(clsTransaction, vals_transaction_classes),
+                               clsTransaction);
                break;
 
            case RESULT:
@@ -476,6 +504,7 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_RID, tvb, offCur, 1, bo_little_endian);
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID_response, tvb, offCur + 1, 2, bo_big_endian);
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, bo_big_endian);
+               proto_item_append_text(ti, ", PDU: Result (%u)", RESULT);
                break;
 
            case ACK:
@@ -484,6 +513,7 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_RID, tvb, offCur, 1, bo_little_endian);
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID_response, tvb, offCur + 1, 2, bo_big_endian);
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, bo_big_endian);
+               proto_item_append_text(ti, ", PDU: ACK (%u)", ACK);
                break;
 
            case ABORT:
@@ -494,11 +524,29 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
                if (abortType == PROVIDER)
                {
+                       guint8 reason = tvb_get_guint8(tvb, offCur + 3);
                    proto_tree_add_item( wtp_tree, hf_wtp_header_Abort_reason_provider , tvb, offCur + 3 , 1, bo_little_endian);
+                       proto_item_append_text(ti,
+                                       ", PDU: Abort (%u)"
+                                       ", Type: Provider (%u)"
+                                       ", Reason: %s (%u)",
+                                       ABORT,
+                                       PROVIDER,
+                                       match_strval(reason, vals_abort_reason_provider), 
+                                       reason);
                }
                else if (abortType == USER)
                {
+                       guint8 reason = tvb_get_guint8(tvb, offCur + 3);
                    proto_tree_add_item(wtp_tree, hf_wtp_header_Abort_reason_user , tvb, offCur + 3 , 1, bo_little_endian);
+                       proto_item_append_text(ti,
+                                       ", PDU: Abort (%u)"
+                                       ", Type: User (%u)"
+                                       ", Reason: %s (%u)",
+                                       ABORT,
+                                       PROVIDER,
+                                       match_strval(reason, vals_wsp_reason_codes), 
+                                       reason);
                }
                break;
 
@@ -509,6 +557,10 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, bo_big_endian);
 
                proto_tree_add_item(wtp_tree, hf_wtp_header_sequence_number , tvb, offCur + 3, 1, bo_little_endian);
+               proto_item_append_text(ti,
+                               ", PDU: Segmented Invoke (%u)"
+                               ", Packet Sequence Number: %u",
+                               SEGMENTED_INVOKE, psn);
                break;
 
            case SEGMENTED_RESULT:
@@ -518,6 +570,10 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                proto_tree_add_item(wtp_tree, hf_wtp_header_flag_TID, tvb, offCur + 1, 2, bo_big_endian);
 
                proto_tree_add_item(wtp_tree, hf_wtp_header_sequence_number , tvb, offCur + 3, 1, bo_little_endian);
+               proto_item_append_text(ti,
+                               ", PDU: Segmented Result (%u)"
+                               ", Packet Sequence Number: %u",
+                               SEGMENTED_RESULT, psn);
                break;
 
            case NEGATIVE_ACK:
@@ -531,17 +587,24 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                {
                    proto_tree_add_item(wtp_tree, hf_wtp_header_sequence_number, tvb, offCur + 4 + i, 1, bo_little_endian);
                }
+               proto_item_append_text(ti,
+                               ", PDU: Negative Ack (%u)"
+                               ", Missing Packets: %u",
+                               NEGATIVE_ACK, numMissing);
                break;
 
            default:
                break;
        };
-    } else { /* Tree = closed */
+       if (fRID) {
+               proto_item_append_text(ti, ", Retransmission");
+       }
+    } else { /* tree is NULL */
 #ifdef DEBUG
        fprintf(stderr, "dissect_wtp: (4) tree was %p\n", tree);
 #endif
     }
-       /* Process the varialbe part */
+       /* Process the variable part */
        if (fCon) {                     /* Now, analyze variable part   */
            unsigned char        tCon;
            unsigned char        tByte;
@@ -579,109 +642,134 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     fprintf( stderr, "dissect_wtp: cbHeader = %d\n", cbHeader );
 #endif
 
-    /* Only update "Info" column when no data in this PDU will
-     * be handed off to a subsequent dissector.
-     */
-    if (check_col(pinfo->cinfo, COL_INFO) &&
-       ((tvb_length_remaining(tvb, offCur + cbHeader + vHeader) <= 0) ||
-        (pdut == ACK) || (pdut==NEGATIVE_ACK) || (pdut==ABORT)) ) {
-#ifdef DEBUG
-       fprintf(stderr, "dissect_wtp: (6) About to set info_col header to %s\n", szInfo->str);
-#endif
-       col_append_str(pinfo->cinfo, COL_INFO, szInfo->str);
-    };
-
-       /*
+    /*
      * Any remaining data ought to be WSP data (if not WTP ACK, NACK
-     * or ABORT pdu), so hand off (defragmented) to the WSP dissector.
+     * or ABORT pdu), so, if we have any remaining data, and it's
+     * not an ACK, NACK, or ABORT PDU, hand it off (defragmented) to the
+     * WSP dissector.
      * Note that the last packet of a fragmented WTP message needn't
      * contain any data, so we allow payloadless packets to be
      * reassembled.  (XXX - does the reassembly code handle this
      * for packets other than the last packet?)
-     */
-    if ((tvb_reported_length_remaining(tvb, offCur + cbHeader + vHeader) >= 0) &&
-       ! ((pdut==ACK) || (pdut==NEGATIVE_ACK) || (pdut==ABORT)))
+     *
+        * Try calling a subdissector only if:
+        *      - The WTP payload is ressembled in this very packet,
+        *      - The WTP payload is not fragmented across packets.
+        */
+    dataOffset = offCur + cbHeader + vHeader;
+    dataLen = tvb_reported_length_remaining(tvb, dataOffset);
+    if ((dataLen >= 0) &&
+                       ! ((pdut==ACK) || (pdut==NEGATIVE_ACK) || (pdut==ABORT)))
     {
-       int     dataOffset = offCur + cbHeader + vHeader;
-       gint    dataLen = tvb_reported_length_remaining(tvb, dataOffset);
-       gboolean save_fragmented;
-
-       if (((pdut == SEGMENTED_INVOKE) || (pdut == SEGMENTED_RESULT) ||
-           (((pdut == INVOKE) || (pdut == RESULT)) && (!fTTR))) &&
-           tvb_bytes_exist(tvb, dataOffset, dataLen))
-       {                                       /* 1st part of segment  */
-           save_fragmented = pinfo->fragmented;
-           pinfo->fragmented = TRUE;
-           fd_head = fragment_add_seq(tvb, dataOffset, pinfo, TID,
-                           wtp_fragment_table, psn, dataLen, !fTTR);
-           if (fd_head != NULL)                /* Reassembled  */
-           {
-               /* Reassembly is complete; show the reassembled PDU */
-               wsp_tvb = tvb_new_real_data(fd_head->data,
-                                           fd_head->len,
-                                           fd_head->len);
-               tvb_set_child_real_data_tvbuff(tvb, wsp_tvb);
-               add_new_data_source(pinfo, wsp_tvb,
-                                       "Reassembled WTP");
-               pinfo->fragmented = FALSE;
-
-               /* show all fragments */
-               show_fragment_seq_tree(fd_head, &wtp_frag_items,
-                                       wtp_tree, pinfo, wsp_tvb);
-
-               if (pinfo->fd->num == fd_head->reassembled_in) {
-                       /* Reassembled in this packet */
+               /* Try to reassemble if needed, and hand over to WSP
+                * A fragmented WTP packet is either:
+                *      - An INVOKE with fTTR (transmission trailer) not set,
+                *      - a SEGMENTED_INVOKE,
+                *      - A RESULT with fTTR (transmission trailer) not set,
+                *      - a SEGMENTED_RESULT.
+                */
+               if ( ( (pdut == SEGMENTED_INVOKE) || (pdut == SEGMENTED_RESULT)
+                               || ( ((pdut == INVOKE) || (pdut == RESULT)) && (!fTTR) )
+                       ) && tvb_bytes_exist(tvb, dataOffset, dataLen) )
+               {
+                       /* Try reassembling fragments */
+                       fragment_data *fd_wtp = NULL;
+                       guint32 reassembled_in = 0;
+                       gboolean save_fragmented = pinfo->fragmented;
+
+                       pinfo->fragmented = TRUE;
+                       fd_wtp = fragment_add_seq(tvb, dataOffset, pinfo, TID,
+                                       wtp_fragment_table, psn, dataLen, !fTTR);
+                       /* XXX - fragment_add_seq() yields NULL unless Ethereal knows
+                        * that the packet is part of a reassembled whole. This means
+                        * that fd_wtp will be NULL as long as Ethereal did not encounter
+                        * (and process) the packet containing the last fragment.
+                        * This implies that Ethereal needs two passes over the data for
+                        * correct reassembly. At the first pass, a capture containing
+                        * three fragments plus a retransmssion of the last fragment
+                        * will progressively show:
+                        *
+                        *              Packet 1: (Unreassembled fragment 1)
+                        *              Packet 2: (Unreassembled fragment 2)
+                        *              Packet 3: (Reassembled WTP)
+                        *              Packet 4: (WTP payload reassembled in packet 3)
+                        *
+                        * However at subsequent evaluation (e.g., by applying a display
+                        * filter) the packet summary will show:
+                        *
+                        *              Packet 1: (WTP payload reassembled in packet 3)
+                        *              Packet 2: (WTP payload reassembled in packet 3)
+                        *              Packet 3: (Reassembled WTP)
+                        *              Packet 4: (WTP payload reassembled in packet 3)
+                        *
+                        * This is important to know, and also affects read filters!
+                        */
+                       wsp_tvb = process_reassembled_data(tvb, dataOffset, pinfo,
+                                       "Reassembled WTP", fd_wtp, &wtp_frag_items,
+                                       NULL, wtp_tree);
+#ifdef DEBUG
+                       printf("WTP: Packet %u %s -> %d: wsp_tvb = %p, fd_wtp = %p, frame = %u\n",
+                                       pinfo->fd->num,
+                                       fd_wtp ? "Reassembled" : "Not reassembled",
+                                       fd_wtp ? fd_wtp->reassembled_in : -1,
+                                       wsp_tvb,
+                                       fd_wtp
+                                       );
+#endif
+                       if (fd_wtp) {
+                               /* Reassembled */
+                               reassembled_in = fd_wtp->reassembled_in;
+                               if (pinfo->fd->num == reassembled_in) {
+                                       /* Reassembled in this very packet:
+                                        * We can safely hand the tvb to the WSP dissector */
+                                       call_dissector(wsp_handle, wsp_tvb, pinfo, tree);
+                               } else {
+                                       /* Not reassembled in this packet */
+                                       if (check_col(pinfo->cinfo, COL_INFO)) {
+                                               col_append_fstr(pinfo->cinfo, COL_INFO,
+                                                               "%s (WTP payload reassembled in packet %u)",
+                                                               szInfo->str, fd_wtp->reassembled_in);
+                                       }
+                                       if (tree) {
+                                               proto_tree_add_text(wtp_tree, tvb, dataOffset, -1,
+                                                               "Payload");
+                                       }
+                               }
+                       } else {
+                               /* Not reassembled yet, or not reassembled at all */
+                               if (check_col(pinfo->cinfo, COL_INFO)) {
+                                       col_append_fstr(pinfo->cinfo, COL_INFO,
+                                                               "%s (Unreassembled fragment %u)",
+                                                               szInfo->str, psn);
+                               }
+                               if (tree) {
+                                       proto_tree_add_text(wtp_tree, tvb, dataOffset, -1,
+                                                       "Payload");
+                               }
+                       }
+                       /* Now reset fragmentation information in pinfo */
+                       pinfo->fragmented = save_fragmented;
+               }
+               else if ( ((pdut == INVOKE) || (pdut == RESULT)) && (fTTR) )
+               {
+                       /* Non-fragmented payload */
+                       wsp_tvb = tvb_new_subset(tvb, dataOffset, -1, -1);
+                       /* We can safely hand the tvb to the WSP dissector */
                        call_dissector(wsp_handle, wsp_tvb, pinfo, tree);
-               } else {
-                       /* Reassembled in another packet */
+               }
+               else
+               {
+                       /* Nothing to hand to subdissector */
                        if (check_col(pinfo->cinfo, COL_INFO))
-                               /* Won't call WSP so display */
-                       col_append_str(pinfo->cinfo, COL_INFO, szInfo->str);
-                       if (tree != NULL)
-                           proto_tree_add_text(wtp_tree, tvb, dataOffset, -1, "Payload");
-                       proto_tree_add_uint(wtp_tree, hf_wtp_reassembled_in,
-                                       tvb, 0, 0, fd_head->reassembled_in);
+                               col_append_str(pinfo->cinfo, COL_INFO, szInfo->str);
                }
-           }
-           else
-           {
-               /* Reassembly isn't complete; just show the fragment */
-               if (check_col(pinfo->cinfo, COL_INFO))
-                       /* Won't call WSP so display */
-                   col_append_str(pinfo->cinfo, COL_INFO, szInfo->str);
-               if (tree != NULL)
-                   proto_tree_add_text(wtp_tree, tvb, dataOffset, -1, "Payload");
-           }
-           pinfo->fragmented = save_fragmented;
        }
        else
        {
-           /*
-            * Normal packet, or not all the fragment data is available;
-            * call next dissector, unless this is a segment of a
-            * segmented invoke or result and isn't the first segment.
-            */
-           if ((pdut == SEGMENTED_INVOKE || pdut == SEGMENTED_RESULT) &&
-                       psn != 0) {
-               /*
-                * This is a segmented invoke or result, and not the first
-                * segment; just show it as a segmented invoke or result,
-                * don't try to dissect its payload.
-                */
+               /* Nothing to hand to subdissector */
                if (check_col(pinfo->cinfo, COL_INFO))
-                   col_add_fstr(pinfo->cinfo, COL_INFO,
-                                "WTP %s (%u)",
-                                (pdut == SEGMENTED_INVOKE ?
-                                 "Segmented Invoke" : "Segmented Result"),
-                                psn);
-               if (tree != NULL)
-                   proto_tree_add_text(wtp_tree, tvb, dataOffset, -1, "Payload");
-           } else {
-               wsp_tvb = tvb_new_subset(tvb, dataOffset, -1, -1);
-               call_dissector(wsp_handle, wsp_tvb, pinfo, tree);
-           }
+                       col_append_str(pinfo->cinfo, COL_INFO, szInfo->str);
        }
-    }
 }
 
 /*
@@ -977,7 +1065,7 @@ proto_register_wtp(void)
        "Wireless Transaction Protocol",   /* protocol name for use by ethereal */
        "WTP",                             /* short version of name */
        "wtp"                      /* Abbreviated protocol name, should Match IANA
-                                           < URL:http://www.isi.edu/in-notes/iana/assignments/port-numbers/ >
+                                           < URL:http://www.iana.org/assignments/port-numbers/ >
                                            */
     );
 
@@ -1003,5 +1091,5 @@ proto_reg_handoff_wtp(void)
 
     wtp_fromudp_handle = find_dissector("wtp-udp");
     dissector_add("udp.port", UDP_PORT_WTP_WSP, wtp_fromudp_handle);
-    dissector_add("smpp.gsm-sms.udh.port", UDP_PORT_WTP_WSP, wtp_fromudp_handle);
+    dissector_add("gsm-sms-ud.udh.port", UDP_PORT_WTP_WSP, wtp_fromudp_handle);
 }