Fixed bug in dissect_ndr_pointer(). Top level Unique and Full pointers are
[obnox/wireshark/wip.git] / packet-x25.c
index 0d8fe93df1b6d96039996edf4891988943c28969..3e0dfaee087246dfda5d4f7c21344c6f275dbdfa 100644 (file)
@@ -2,12 +2,11 @@
  * Routines for x25 packet disassembly
  * Olivier Abad <oabad@cybercable.fr>
  *
- * $Id: packet-x25.c,v 1.33 2000/07/01 08:55:28 guy Exp $
+ * $Id: packet-x25.c,v 1.64 2002/01/24 09:20:53 guy Exp $
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
+ * By Gerald Combs <gerald@ethereal.com>
  * Copyright 1998
- *
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 #include <glib.h>
 #include <stdlib.h>
 #include <string.h>
-#include "etypes.h"
-#include "packet.h"
-#include "packet-x25.h"
-#include "packet-ip.h"
-#include "packet-osi.h"
-#include "packet-clnp.h"
+#include "llcsaps.h"
+#include <epan/packet.h>
+#include "prefs.h"
 #include "nlpid.h"
+#include "x264_prt_id.h"
 
 #define FROM_DCE                       0x80
 
 #define X25_FAC_PRIORITY               0xD2
 
 static int proto_x25 = -1;
+static int hf_x25_gfi = -1;
+static int hf_x25_abit = -1;
 static int hf_x25_qbit = -1;
 static int hf_x25_dbit = -1;
 static int hf_x25_mod = -1;
 static int hf_x25_lcn = -1;
 static int hf_x25_type = -1;
-static int hf_x25_p_r = -1;
-static int hf_x25_mbit = -1;
-static int hf_x25_p_s = -1;
-static int proto_ex25 = -1;
-static int hf_ex25_qbit = -1;
-static int hf_ex25_dbit = -1;
-static int hf_ex25_mod = -1;
-static int hf_ex25_lcn = -1;
-static int hf_ex25_type = -1;
-static int hf_ex25_p_r = -1;
-static int hf_ex25_mbit = -1;
-static int hf_ex25_p_s = -1;
+static int hf_x25_p_r_mod8 = -1;
+static int hf_x25_p_r_mod128 = -1;
+static int hf_x25_mbit_mod8 = -1;
+static int hf_x25_mbit_mod128 = -1;
+static int hf_x25_p_s_mod8 = -1;
+static int hf_x25_p_s_mod128 = -1;
 
 static gint ett_x25 = -1;
+static gint ett_x25_gfi = -1;
 static gint ett_x25_fac = -1;
 static gint ett_x25_fac_unknown = -1;
 static gint ett_x25_fac_mark = -1;
@@ -132,6 +126,7 @@ static gint ett_x25_fac_ete_transit_delay = -1;
 static gint ett_x25_fac_calling_addr_ext = -1;
 static gint ett_x25_fac_call_deflect = -1;
 static gint ett_x25_fac_priority = -1;
+static gint ett_x25_user_data = -1;
 
 static const value_string vals_modulo[] = {
        { 1, "8" },
@@ -160,11 +155,23 @@ static const value_string vals_x25_type[] = {
        { 0,   NULL}
 };
 
+static dissector_handle_t ip_handle;
+static dissector_handle_t ositp_handle;
+static dissector_handle_t sna_handle;
+static dissector_handle_t qllc_handle;
+static dissector_handle_t data_handle;
+
+/* Preferences */
+static gboolean non_q_bit_is_sna = FALSE;
+
+static dissector_table_t x25_subdissector_table;
+static heur_dissector_list_t x25_heur_subdissector_list;
+
 /*
  * each vc_info node contains :
  *   the time of the first frame using this dissector (secs and usecs)
  *   the time of the last frame using this dissector (0 if it is unknown)
- *   a pointer to the dissector
+ *   a handle for the dissector
  *
  * the "time of first frame" is initialized when a Call Req. is received
  * the "time of last frame" is initialized when a Clear, Reset, or Restart
@@ -173,7 +180,7 @@ static const value_string vals_x25_type[] = {
 typedef struct _vc_info {
        guint32 first_frame_secs, first_frame_usecs;
        guint32 last_frame_secs, last_frame_usecs;
-       void (*dissect)(const u_char *, int, frame_data *, proto_tree *);
+       dissector_handle_t dissect;
        struct _vc_info *next;
 } vc_info;
 
@@ -191,7 +198,7 @@ typedef struct _global_vc_info {
 
 static global_vc_info *hash_table[64];
 
-void
+static void
 free_vc_info(vc_info *pt)
 {
   vc_info *vci = pt;
@@ -225,10 +232,9 @@ reinit_x25_hashtable(void)
   }
 }
 
-void
+static void
 x25_hash_add_proto_start(guint16 vc, guint32 frame_secs, guint32 frame_usecs,
-                        void (*dissect)(const u_char *, int, frame_data *,
-                                      proto_tree *))
+                        dissector_handle_t dissect)
 {
   int idx = vc % 64;
   global_vc_info *hash_ent;
@@ -308,7 +314,7 @@ x25_hash_add_proto_start(guint16 vc, guint32 frame_secs, guint32 frame_usecs,
   }
 }
 
-void
+static void
 x25_hash_add_proto_end(guint16 vc, guint32 frame_secs, guint32 frame_usecs)
 {
   global_vc_info *hash_ent = hash_table[vc%64];
@@ -324,16 +330,20 @@ x25_hash_add_proto_end(guint16 vc, guint32 frame_secs, guint32 frame_usecs)
   vci->last_frame_usecs = frame_usecs;
 }
 
-void (*x25_hash_get_dissect(guint32 frame_secs, guint32 frame_usecs, guint16 vc))(const u_char *, int, frame_data *, proto_tree *)
+static dissector_handle_t
+x25_hash_get_dissect(guint32 frame_secs, guint32 frame_usecs, guint16 vc)
 {
   global_vc_info *hash_ent = hash_table[vc%64];
   vc_info *vci;
   vc_info *vci2;
 
-  if (!hash_ent) return 0;
+  if (!hash_ent)
+    return NULL;
 
-  while(hash_ent && hash_ent->vc_num != vc) hash_ent = hash_ent->next;
-  if (!hash_ent) return 0;
+  while (hash_ent && hash_ent->vc_num != vc)
+    hash_ent = hash_ent->next;
+  if (!hash_ent)
+    return NULL;
 
   /* a hash_ent was found for this VC number */
   vci2 = vci = hash_ent->info;
@@ -347,7 +357,8 @@ void (*x25_hash_get_dissect(guint32 frame_secs, guint32 frame_usecs, guint16 vc)
   }
   /* we reached last record, and previous record has a non zero
    * last frame time ==> no dissector */
-  if (!vci && (vci2->last_frame_secs || vci2->last_frame_usecs)) return 0;
+  if (!vci && (vci2->last_frame_secs || vci2->last_frame_usecs))
+    return NULL;
 
   /* we reached last record, and previous record has a zero last frame time
    * ==> dissector for previous frame has not been "stopped" by a Clear, etc */
@@ -357,16 +368,16 @@ void (*x25_hash_get_dissect(guint32 frame_secs, guint32 frame_usecs, guint16 vc)
     if (frame_secs < vci2->first_frame_secs ||
         (frame_secs == vci2->first_frame_secs &&
          frame_usecs < vci2->first_frame_usecs))
-      return 0;
+      return NULL;
     else
       return vci2->dissect;
   }
 
-  /* our frame time is before vci's end. Check if it is adter vci's start */
+  /* our frame time is before vci's end. Check if it is after vci's start */
   if (frame_secs < vci->first_frame_secs ||
       (frame_secs == vci->first_frame_secs &&
        frame_usecs < vci->first_frame_usecs))
-    return 0;
+    return NULL;
   else
     return vci->dissect;
 }
@@ -379,10 +390,22 @@ static char *clear_code(unsigned char code)
        return "DTE Originated";
     if (code == 0x01)
        return "Number Busy";
+    if (code == 0x03)
+       return "Invalid Facility Requested";
+    if (code == 0x05)
+       return "Network Congestion";
     if (code == 0x09)
        return "Out Of Order";
+    if (code == 0x0B)
+       return "Access Barred";
+    if (code == 0x0D)
+       return "Not Obtainable";
     if (code == 0x11)
        return "Remote Procedure Error";
+    if (code == 0x13)
+       return "Local Procedure Error";
+    if (code == 0x15)
+       return "RPOA Out Of Order";
     if (code == 0x19)
        return "Reverse Charging Acceptance Not Subscribed";
     if (code == 0x21)
@@ -391,18 +414,6 @@ static char *clear_code(unsigned char code)
        return "Fast Select Acceptance Not Subscribed";
     if (code == 0x39)
        return "Destination Absent";
-    if (code == 0x03)
-       return "Invalid Facility Requested";
-    if (code == 0x0B)
-       return "Access Barred";
-    if (code == 0x13)
-       return "Local Procedure Error";
-    if (code == 0x05)
-       return "Network Congestion";
-    if (code == 0x0D)
-       return "Not Obtainable";
-    if (code == 0x15)
-       return "RPOA Out Of Order";
 
     sprintf(buffer, "Unknown %02X", code);
 
@@ -549,6 +560,87 @@ static char *clear_diag(unsigned char code)
        return "Unknown called DNIC";
     if (code == 122)
        return "Maintenance action";
+    if (code == 144)
+       return "Timer expired or retransmission count surpassed";
+    if (code == 145)
+       return "Timer expired or retransmission count surpassed for INTERRUPT";
+    if (code == 146)
+       return "Timer expired or retransmission count surpassed for DATA "
+              "packet transmission";
+    if (code == 147)
+       return "Timer expired or retransmission count surpassed for REJECT";
+    if (code == 160)
+       return "DTE-specific signals";
+    if (code == 161)
+       return "DTE operational";
+    if (code == 162)
+       return "DTE not operational";
+    if (code == 163)
+       return "DTE resource constraint";
+    if (code == 164)
+       return "Fast select not subscribed";
+    if (code == 165)
+       return "Invalid partially full DATA packet";
+    if (code == 166)
+       return "D-bit procedure not supported";
+    if (code == 167)
+       return "Registration/Cancellation confirmed";
+    if (code == 224)
+       return "OSI network service problem";
+    if (code == 225)
+       return "Disconnection (transient condition)";
+    if (code == 226)
+       return "Disconnection (permanent condition)";
+    if (code == 227)
+       return "Connection rejection - reason unspecified (transient "
+              "condition)";
+    if (code == 228)
+       return "Connection rejection - reason unspecified (permanent "
+              "condition)";
+    if (code == 229)
+       return "Connection rejection - quality of service not available "
+               "transient condition)";
+    if (code == 230)
+       return "Connection rejection - quality of service not available "
+               "permanent condition)";
+    if (code == 231)
+       return "Connection rejection - NSAP unreachable (transient condition)";
+    if (code == 232)
+       return "Connection rejection - NSAP unreachable (permanent condition)";
+    if (code == 233)
+       return "reset - reason unspecified";
+    if (code == 234)
+       return "reset - congestion";
+    if (code == 235)
+       return "Connection rejection - NSAP address unknown (permanent "
+               "condition)";
+    if (code == 240)
+       return "Higher layer initiated";
+    if (code == 241)
+       return "Disconnection - normal";
+    if (code == 242)
+       return "Disconnection - abnormal";
+    if (code == 243)
+       return "Disconnection - incompatible information in user data";
+    if (code == 244)
+       return "Connection rejection - reason unspecified (transient "
+               "condition)";
+    if (code == 245)
+       return "Connection rejection - reason unspecified (permanent "
+               "condition)";
+    if (code == 246)
+       return "Connection rejection - quality of service not available "
+               "(transient condition)";
+    if (code == 247)
+       return "Connection rejection - quality of service not available "
+               "(permanent condition)";
+    if (code == 248)
+       return "Connection rejection - incompatible information in user data";
+    if (code == 249)
+       return "Connection rejection - unrecognizable protocol indentifier "
+               "in user data";
+    if (code == 250)
+       return "Reset - user resynchronization";
 
     sprintf(buffer, "Unknown %d", code);
 
@@ -621,7 +713,7 @@ static char *registration_code(unsigned char code)
     return buffer;
 }
 
-void
+static void
 dump_facilities(proto_tree *tree, int *offset, tvbuff_t *tvb)
 {
     guint8 fac, byte1, byte2, byte3;
@@ -1200,9 +1292,9 @@ dump_facilities(proto_tree *tree, int *offset, tvbuff_t *tvb)
     }
 }
 
-void
+static void
 x25_ntoa(proto_tree *tree, int *offset, tvbuff_t *tvb,
-        frame_data *fd, gboolean toa)
+        packet_info *pinfo, gboolean toa)
 {
     int len1, len2;
     int i;
@@ -1256,12 +1348,12 @@ x25_ntoa(proto_tree *tree, int *offset, tvbuff_t *tvb,
 
     if (len1) {
        if (toa) {
-           if (check_col(fd, COL_RES_DL_DST))
-               col_add_str(fd, COL_RES_DL_DST, addr1);
+           if (check_col(pinfo->cinfo, COL_RES_DL_DST))
+               col_add_str(pinfo->cinfo, COL_RES_DL_DST, addr1);
        }
        else {
-           if(check_col(fd, COL_RES_DL_SRC))
-               col_add_str(fd, COL_RES_DL_SRC, addr1);
+           if(check_col(pinfo->cinfo, COL_RES_DL_SRC))
+               col_add_str(pinfo->cinfo, COL_RES_DL_SRC, addr1);
        }
        if (tree)
            proto_tree_add_text(tree, tvb, *offset,
@@ -1272,12 +1364,12 @@ x25_ntoa(proto_tree *tree, int *offset, tvbuff_t *tvb,
     }
     if (len2) {
        if (toa) {
-           if (check_col(fd, COL_RES_DL_SRC))
-               col_add_str(fd, COL_RES_DL_SRC, addr2);
+           if (check_col(pinfo->cinfo, COL_RES_DL_SRC))
+               col_add_str(pinfo->cinfo, COL_RES_DL_SRC, addr2);
        }
        else {
-           if(check_col(fd, COL_RES_DL_DST))
-               col_add_str(fd, COL_RES_DL_DST, addr2);
+           if(check_col(pinfo->cinfo, COL_RES_DL_DST))
+               col_add_str(pinfo->cinfo, COL_RES_DL_DST, addr2);
        }
        if (tree)
            proto_tree_add_text(tree, tvb, *offset + len1/2,
@@ -1289,10 +1381,10 @@ x25_ntoa(proto_tree *tree, int *offset, tvbuff_t *tvb,
     (*offset) += ((len1 + len2 + 1) / 2);
 }
 
-int
+static int
 get_x25_pkt_len(tvbuff_t *tvb)
 {
-    int length, called_len, calling_len, dte_len, dce_len;
+    guint length, called_len, calling_len, dte_len, dce_len;
     guint8 byte2, bytex;
 
     byte2 = tvb_get_guint8(tvb, 2);
@@ -1309,6 +1401,11 @@ get_x25_pkt_len(tvbuff_t *tvb)
        return MIN(tvb_reported_length(tvb),length);
 
     case X25_CALL_ACCEPTED:
+       /* The calling/called address length byte (following the packet type)
+        * is not mandatory, so we must check the packet length before trying
+        * to read it */
+       if (tvb_reported_length(tvb) == 3)
+           return(3);
        bytex = tvb_get_guint8(tvb, 3);
        called_len  = (bytex >> 0) & 0x0F;
        calling_len = (bytex >> 4) & 0x0F;
@@ -1371,14 +1468,12 @@ get_x25_pkt_len(tvbuff_t *tvb)
     return 0;
 }
 
-#define        PRT_ID_ISO_8073 0x01
-
 static const value_string prt_id_vals[] = {
-        {PRT_ID_ISO_8073, "ISO 8073 COTP"},
-        {0x02,            "ISO 8602"},
-        {0x03,            "ISO 10732 in conjunction with ISO 8073"},
-        {0x04,            "ISO 10736 in conjunction with ISO 8602"},
-        {0x00,            NULL}
+        {PRT_ID_ISO_8073,           "ISO 8073 COTP"},
+        {PRT_ID_ISO_8602,           "ISO 8602 CLTP"},
+        {PRT_ID_ISO_10736_ISO_8073, "ISO 10736 in conjunction with ISO 8073 COTP"},
+        {PRT_ID_ISO_10736_ISO_8602, "ISO 10736 in conjunction with ISO 8602 CLTP"},
+        {0x00,                      NULL}
 };
 
 static const value_string sharing_strategy_vals[] = {
@@ -1386,26 +1481,24 @@ static const value_string sharing_strategy_vals[] = {
         {0x00,            NULL}
 };
 
-void
+static void
 dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
-    proto_tree *x25_tree=0, *ti;
-    int localoffset=0;
-    int x25_pkt_len;
+    proto_tree *x25_tree=0, *gfi_tree=0, *userdata_tree=0;
+    proto_item *ti;
+    guint localoffset=0;
+    guint x25_pkt_len;
     int modulo;
     guint16 vc;
-    void (*dissect)(const u_char *, int, frame_data *, proto_tree *);
+    dissector_handle_t dissect;
     gboolean toa;         /* TOA/NPI address format */
     guint16 bytes0_1;
     guint8 pkt_type;
     tvbuff_t *next_tvb;
-    const guint8 *next_pd;
-    int next_offset;
+    gboolean q_bit_set = FALSE;
 
-    pinfo->current_proto = "X.25";
-
-    if (check_col(pinfo->fd, COL_PROTOCOL))
-       col_add_str(pinfo->fd, COL_PROTOCOL, "X.25");
+    if (check_col(pinfo->cinfo, COL_PROTOCOL))
+       col_set_str(pinfo->cinfo, COL_PROTOCOL, "X.25");
 
     bytes0_1 = tvb_get_ntohs(tvb, 0);
 
@@ -1418,52 +1511,63 @@ dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     x25_pkt_len = get_x25_pkt_len(tvb);
     if (x25_pkt_len < 3) /* packet too short */
     {
-       if (check_col(pinfo->fd, COL_INFO))
-           col_add_str(pinfo->fd, COL_INFO, "Invalid/short X.25 packet");
+       if (check_col(pinfo->cinfo, COL_INFO))
+           col_set_str(pinfo->cinfo, COL_INFO, "Invalid/short X.25 packet");
        if (tree)
-           proto_tree_add_protocol_format(tree,
-                   (modulo == 8 ? proto_x25 : proto_ex25), tvb, 0,
-                   tvb_length(tvb), "Invalid/short X.25 packet");
+           proto_tree_add_protocol_format(tree, proto_x25, tvb, 0, -1,
+                   "Invalid/short X.25 packet");
        return;
     }
+
+    pkt_type = tvb_get_guint8(tvb, 2);
+
     if (tree) {
-       ti = proto_tree_add_protocol_format(tree,
-               (modulo == 8) ? proto_x25 : proto_ex25, tvb, 0, x25_pkt_len,
-               "X.25");
-       x25_tree = proto_item_add_subtree(ti, ett_x25);
-       if (bytes0_1 & 0x8000)
-           proto_tree_add_boolean(x25_tree,
-                   (modulo == 8) ? hf_x25_qbit : hf_ex25_qbit, tvb, 0, 2,
-                   bytes0_1);
-       if (bytes0_1 & 0x4000)
-           proto_tree_add_boolean(x25_tree,
-                   (modulo == 8) ? hf_x25_dbit : hf_ex25_dbit, tvb, 0, 2,
-                   bytes0_1);
-       proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_mod : hf_ex25_mod,
-               tvb, 0, 2, bytes0_1);
+        ti = proto_tree_add_item(tree, proto_x25, tvb, 0, x25_pkt_len, FALSE);
+        x25_tree = proto_item_add_subtree(ti, ett_x25);
+        ti = proto_tree_add_item(x25_tree, hf_x25_gfi, tvb, 0, 2, FALSE);
+        gfi_tree = proto_item_add_subtree(ti, ett_x25_gfi);
+
+        if ((pkt_type & 0x01) == X25_DATA) {
+            proto_tree_add_boolean(gfi_tree, hf_x25_qbit, tvb, 0, 2,
+                bytes0_1);
+            if (bytes0_1 & 0x8000) {
+                q_bit_set = TRUE;
+            }
+        }
+        else if (pkt_type == X25_CALL_REQUEST ||
+            pkt_type == X25_CALL_ACCEPTED ||
+            pkt_type == X25_CLEAR_REQUEST ||
+            pkt_type == X25_CLEAR_CONFIRMATION) {
+            proto_tree_add_boolean(gfi_tree, hf_x25_abit, tvb, 0, 2,
+                bytes0_1);
+        }
+
+        if (pkt_type == X25_CALL_REQUEST || pkt_type == X25_CALL_ACCEPTED ||
+            (pkt_type & 0x01) == X25_DATA) {
+            proto_tree_add_boolean(gfi_tree, hf_x25_dbit, tvb, 0, 2,
+                bytes0_1);
+        }
+        proto_tree_add_uint(gfi_tree, hf_x25_mod, tvb, 0, 2, bytes0_1);
     }
 
-    pkt_type = tvb_get_guint8(tvb, 2);
     switch (pkt_type) {
     case X25_CALL_REQUEST:
-       if (check_col(pinfo->fd, COL_INFO))
-           col_add_fstr(pinfo->fd, COL_INFO, "%s VC:%d",
+       if (check_col(pinfo->cinfo, COL_INFO))
+           col_add_fstr(pinfo->cinfo, COL_INFO, "%s VC:%d",
                    (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Inc. call"
                                                                 : "Call req." ,
                     vc);
        if (x25_tree) {
-           proto_tree_add_uint(x25_tree,
-                   (modulo == 8) ? hf_x25_lcn : hf_ex25_lcn, tvb,
+           proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb,
                    0, 2, bytes0_1);
-           proto_tree_add_uint_format(x25_tree,
-                   (modulo == 8) ? hf_x25_type : hf_ex25_type, tvb, 2, 1,
+           proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb, 2, 1,
                    X25_CALL_REQUEST,
                    (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Incoming call"
                                                                 : "Call request");
        }
        localoffset = 3;
        if (localoffset < x25_pkt_len) /* calling/called addresses */
-           x25_ntoa(x25_tree, &localoffset, tvb, pinfo->fd, toa);
+           x25_ntoa(x25_tree, &localoffset, tvb, pinfo, toa);
 
        if (localoffset < x25_pkt_len) /* facilities */
            dump_facilities(x25_tree, &localoffset, tvb);
@@ -1471,112 +1575,235 @@ dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        if (localoffset < tvb_reported_length(tvb)) /* user data */
        {
            guint8 spi;
+           int is_x_264;
            guint8 prt_id;
 
-           /* Compare the first octet of the CALL REQUEST packet with
-              various ISO 9577 NLPIDs, as per Annex A of ISO 9577. */
-           spi = tvb_get_guint8(tvb, localoffset);
-           switch (spi) {
-
-           /* XXX - handle other NLPIDs, e.g. PPP? */
-
-           case NLPID_IP:
-               x25_hash_add_proto_start(vc, pinfo->fd->abs_secs,
-                                        pinfo->fd->abs_usecs, dissect_ip);
-               if (x25_tree)
-                   proto_tree_add_text(x25_tree, tvb, localoffset, 1,
-                                       "X.224 secondary protocol ID: IP");
-               localoffset++;
-               break;
+           if (x25_tree) {
+               ti = proto_tree_add_text(x25_tree, tvb, localoffset, -1,
+                       "User data");
+               userdata_tree = proto_item_add_subtree(ti, ett_x25_user_data);
+           }
 
-           default:
-               if ((spi >= 0x03 && spi <= 0x82)
-                   && tvb_get_guint8(tvb, localoffset+1) == 0x01) {
-                   /* ISO 9577 claims that a SPI in that range is a
-                      length field for X.224/ISO 8073 or X.264/ISO 11570;
-                      however, some of them collide with NLPIDs such
-                      as 0x81 for ISO 8473 CLNP or ISO 8542 ESIS, so
-                      I don't know how you run those over X.25, assuming
-                      you do.
-
-                      I'm also not sure what the "or" means there; it
-                      looks as if X.264 specifies the layout of a
-                      "UN TPDU" ("Use of network connection TPDU"),
-                      which specifies the transport protocol to use
-                      over this network connection, and 0x03 0x01 0x01
-                      0x00 is such a TPDU, with a length of 3, a UN
-                      field of 1 (as is required), a PRT-ID ("protocol
-                      identifier") field of 1 (X.224/ISO 8073, a/k/a
-                      COTP service), and a SHARE ("sharing strategy")
-                      field of 0 ("no sharing", which is the only one
-                      allowed).
-
-                      So we'll assume that's what it is, as the SPI
-                      is in the right range for a length, and the UN
-                      field is 0x01. */
-                   prt_id = tvb_get_guint8(tvb, localoffset+2);
-                   if (x25_tree) {
-                       proto_tree_add_text(x25_tree, tvb, localoffset, 1,
+           /* X.263/ISO 9577 says that:
+
+                   When CLNP or ESIS are run over X.25, the SPI
+                   is 0x81 or 0x82, respectively; those are the
+                   NLPIDs for those protocol.
+
+                   When X.224/ISO 8073 COTP is run over X.25, and
+                   when ISO 11570 explicit identification is being
+                   used, the first octet of the user data field is
+                   a TPDU length field, and the rest is "as defined
+                   in ITU-T Rec. X.225 | ISO/IEC 8073, Annex B,
+                   or ITU-T Rec. X.264 and ISO/IEC 11570".
+
+                   When X.264/ISO 11570 default identification is
+                   being used, there is no user data field in the
+                   CALL REQUEST packet.  This is for X.225/ISO 8073
+                   COTP.
+
+              It also says that SPI values from 0x03 through 0x3f are
+              reserved and are in use by X.224/ISO 8073 Annex B and
+              X.264/ISO 11570.  The note says that those values are
+              not NLPIDs, they're "used by the respective higher layer
+              protocol" and "not used for higher layer protocol
+              identification".  I infer from this and from what
+              X.264/ISO 11570 says that this means that values in those
+              range are valid values for the first octet of an
+              X.224/ISO 8073 packet or for X.264/ISO 11570.
+
+              Annex B of X.225/ISO 8073 mentions some additional TPDU
+              types that can be put in what I presume is the user
+              data of connect requests.  It says that:
+
+                   The sending transport entity shall:
+
+                       a) either not transmit any TPDU in the NS-user data
+                          parameter of the N-CONNECT request primitive; or
+
+                       b) transmit the UN-TPDU (see ITU-T Rec. X.264 and
+                          ISO/IEC 11570) followed by the NCM-TPDU in the
+                          NS-user data parameter of the N-CONNECT request
+                          primitive.
+
+              I don't know if this means that the user data field
+              will contain a UN TPDU followed by an NCM TPDU or not.
+
+              X.264/ISO 11570 says that:
+
+                   When default identification is being used,
+                   X.225/ISO 8073 COTP is identified.  No user data
+                   is sent in the network-layer connection request.
+
+                   When explicit identification is being used,
+                   the user data is a UN TPDU ("Use of network
+                   connection TPDU"), which specifies the transport
+                   protocol to use over this network connection.
+                   It also says that the length of a UN TPDU shall
+                   not exceed 32 octets, i.e. shall not exceed 0x20;
+                   it says this is "due to the desire not to conflict
+                   with the protocol identifier field carried by X.25
+                   CALL REQUEST/INCOMING CALL packets", and says that
+                   field has values specified in X.244.  X.244 has been
+                   superseded by X.263/ISO 9577, so that presumably
+                   means the goal is to allow a UN TPDU's length
+                   field to be distinguished from an NLPID, allowing
+                   you to tell whether X.264/ISO 11570 explicit
+                   identification is being used or an NLPID is
+                   being used as the SPI.
+
+              I read this as meaning that, if the ISO mechanisms are
+              used to identify the protocol being carried over X.25:
+
+                   if there's no user data in the CALL REQUEST/
+                   INCOMING CALL packet, it's COTP;
+
+                   if there is user data, then:
+
+                       if the first octet is less than or equal to
+                       32, it might be a UN TPDU, and that identifies
+                       the transport protocol being used, and
+                       it may be followed by more data, such
+                       as a COTP NCM TPDU if it's COTP;
+
+                       if the first octet is greater than 32, it's
+                       an NLPID, *not* a TPDU length, and the
+                       stuff following it is *not* a TPDU.
+
+              Figure A.2 of X.263/ISO 9577 seems to say that the
+              first octet of the user data is a TPDU length field,
+              in the range 0x03 through 0x82, and says they are
+              for X.225/ISO 8073 Annex B or X.264/ISO 11570.
+
+              However, X.264/ISO 11570 seems to imply that the length
+              field would be that of a UN TPDU, which must be less
+              than or equal to 0x20, and X.225/ISO 8073 Annex B seems
+              to indicate that the user data must begin with
+              an X.264/ISO 11570 UN TPDU, so I'd say that A.2 should
+              have said "in the range 0x03 through 0x20", instead
+              (the length value doesn't include the length field,
+              and the minimum UN TPDU has length, type, PRT-ID,
+              and SHARE, so that's 3 bytes without the length). */
+           spi = tvb_get_guint8(tvb, localoffset);
+           if (spi > 32 || spi < 3) {
+               /* First octet is > 32, or < 3, so the user data isn't an
+                  X.264/ISO 11570 UN TPDU */
+               is_x_264 = FALSE;
+           } else {
+               /* First octet is >= 3 and <= 32, so the user data *might*
+                  be an X.264/ISO 11570 UN TPDU.  Check whether we have
+                  enough data to see if it is. */
+               if (tvb_bytes_exist(tvb, localoffset+1, 1)) {
+                   /* We do; check whether the second octet is 1. */
+                   if (tvb_get_guint8(tvb, localoffset+1) == 0x01) {
+                       /* Yes, the second byte is 1, so it looks like
+                          a UN TPDU. */
+                       is_x_264 = TRUE;
+                   } else {
+                       /* No, the second byte is not 1, so it's not a
+                          UN TPDU. */
+                       is_x_264 = FALSE;
+                   }
+               } else {
+                   /* We can't see the second byte of the putative UN
+                      TPDU, so we don't know if that's what it is. */
+                   is_x_264 = -1;
+               }
+           }
+           if (is_x_264 == -1) {
+               /*
+                * We don't know what it is; just skip it.
+                */
+               localoffset = tvb_length(tvb);
+           } else if (is_x_264) {
+               /* It looks like an X.264 UN TPDU, so show it as such. */
+               if (userdata_tree) {
+                   proto_tree_add_text(userdata_tree, tvb, localoffset, 1,
                                        "X.264 length indicator: %u",
                                        spi);
-                       proto_tree_add_text(x25_tree, tvb, localoffset+1, 1,
-                                       "X.264 UN TPDU identifier: 0x%02X",
-                                       tvb_get_guint8(tvb, localoffset+1));
-                       proto_tree_add_text(x25_tree, tvb, localoffset+2, 1,
-                                       "X.264 protocol identifier: %s",
+                   proto_tree_add_text(userdata_tree, tvb, localoffset+1, 1,
+                                       "X.264 UN TPDU identifier: 0x%02X",
+                                       tvb_get_guint8(tvb, localoffset+1));
+               }
+               prt_id = tvb_get_guint8(tvb, localoffset+2);
+               if (userdata_tree) {
+                   proto_tree_add_text(x25_tree, tvb, localoffset+2, 1,
+                                       "X.264 protocol identifier: %s",
                                        val_to_str(prt_id, prt_id_vals,
-                                           "Unknown (0x%02X)"));
-                       proto_tree_add_text(x25_tree, tvb, localoffset+3, 1,
-                                       "X.264 sharing strategy: %s",
+                                              "Unknown (0x%02X)"));
+                   proto_tree_add_text(x25_tree, tvb, localoffset+3, 1,
+                                       "X.264 sharing strategy: %s",
                                        val_to_str(tvb_get_guint8(tvb, localoffset+3),
-                                           sharing_strategy_vals, "Unknown (0x%02X)"));
-                   }
+                                       sharing_strategy_vals, "Unknown (0x%02X)"));
+               }
 
-                   /* XXX - dissect the variable part? */
+               /* XXX - dissect the variable part? */
 
-                   /* The length doesn't include the length octet itself. */
-                   localoffset += spi + 1;
+               /* The length doesn't include the length octet itself. */
+               localoffset += spi + 1;
 
-                   switch (prt_id) {
+               switch (prt_id) {
 
-                   case PRT_ID_ISO_8073:
-                       /* ISO 8073 COTP */
-                       x25_hash_add_proto_start(vc, pinfo->fd->abs_secs,
-                                        pinfo->fd->abs_usecs, dissect_ositp);
-                       break;
+               case PRT_ID_ISO_8073:
+                   /* ISO 8073 COTP */
+                   x25_hash_add_proto_start(vc, pinfo->fd->abs_secs,
+                                            pinfo->fd->abs_usecs,
+                                            ositp_handle);
+                   /* XXX - disssect the rest of the user data as COTP?
+                      That needs support for NCM TPDUs, etc. */
+                   break;
 
-                   default:
-                       goto unknown;
-                   }
-               } else {
-               unknown:
-                   if (x25_tree) {
-                       proto_tree_add_text(x25_tree, tvb, localoffset,
-                               tvb_reported_length(tvb)-localoffset, "Data");
-                   }
-                   localoffset = tvb_reported_length(tvb);
+               case PRT_ID_ISO_8602:
+                   /* ISO 8602 CLTP */
+                   x25_hash_add_proto_start(vc, pinfo->fd->abs_secs,
+                                            pinfo->fd->abs_usecs,
+                                            ositp_handle);
+                   break;
+               }
+           } else if (is_x_264 == 0) {
+               /* It doesn't look like a UN TPDU, so compare the first
+                  octet of the CALL REQUEST packet with various X.263/
+                  ISO 9577 NLPIDs, as per Annex A of X.263/ISO 9577. */ 
+
+               if (userdata_tree) {
+                   proto_tree_add_text(userdata_tree, tvb, localoffset, 1,
+                                       "X.263 secondary protocol ID: %s",
+                                       val_to_str(spi, nlpid_vals, "Unknown (0x%02x)"));
+               }
+               localoffset++;
+               
+               /*
+                * What's the dissector handle for this SPI?
+                */
+               dissect = dissector_get_port_handle(x25_subdissector_table, spi);
+               x25_hash_add_proto_start(vc, pinfo->fd->abs_secs,
+                                        pinfo->fd->abs_usecs, dissect);
+           }
+           if (localoffset < tvb_length(tvb)) {
+               if (userdata_tree) {
+                   proto_tree_add_text(userdata_tree, tvb, localoffset, -1,
+                               "Data");
                }
+               localoffset = tvb_length(tvb);
            }
        }
        break;
     case X25_CALL_ACCEPTED:
-       if(check_col(pinfo->fd, COL_INFO))
-           col_add_fstr(pinfo->fd, COL_INFO, "%s VC:%d",
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_add_fstr(pinfo->cinfo, COL_INFO, "%s VC:%d",
                    (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Call conn."
                                                                 : "Call acc." ,
                    vc);
        if (x25_tree) {
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_lcn : hf_ex25_lcn,
-                   tvb, 0, 2, bytes0_1);
-           proto_tree_add_uint_format(x25_tree,
-                   (modulo == 8) ? hf_x25_type : hf_ex25_type,
-                   tvb, 2, 1, X25_CALL_ACCEPTED,
+           proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+           proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_CALL_ACCEPTED,
                    (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Call connected"
                                                                 : "Call accepted");
        }
        localoffset = 3;
         if (localoffset < x25_pkt_len) /* calling/called addresses */
-           x25_ntoa(x25_tree, &localoffset, tvb, pinfo->fd, toa);
+           x25_ntoa(x25_tree, &localoffset, tvb, pinfo, toa);
 
        if (localoffset < x25_pkt_len) /* facilities */
            dump_facilities(x25_tree, &localoffset, tvb);
@@ -1589,8 +1816,8 @@ dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        }
        break;
     case X25_CLEAR_REQUEST:
-       if(check_col(pinfo->fd, COL_INFO)) {
-           col_add_fstr(pinfo->fd, COL_INFO, "%s VC:%d %s - %s",
+       if(check_col(pinfo->cinfo, COL_INFO)) {
+           col_add_fstr(pinfo->cinfo, COL_INFO, "%s VC:%d %s - %s",
                    (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Clear ind."
                                                                 : "Clear req." ,
                    vc, clear_code(tvb_get_guint8(tvb, 3)),
@@ -1598,11 +1825,9 @@ dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        }
        x25_hash_add_proto_end(vc, pinfo->fd->abs_secs, pinfo->fd->abs_usecs);
        if (x25_tree) {
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_lcn : hf_ex25_lcn, tvb,
-                   0, 2, bytes0_1);
-           proto_tree_add_uint_format(x25_tree,
-                   (modulo == 8) ? hf_x25_type : hf_ex25_type,
-                   tvb, localoffset+2, 1, X25_CLEAR_REQUEST,
+           proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+           proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb,
+                   localoffset+2, 1, X25_CLEAR_REQUEST,
                    (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Clear indication"
                                                                 : "Clear request");
            proto_tree_add_text(x25_tree, tvb, 3, 1,
@@ -1613,60 +1838,57 @@ dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        localoffset = x25_pkt_len;
        break;
     case X25_CLEAR_CONFIRMATION:
-       if(check_col(pinfo->fd, COL_INFO))
-           col_add_fstr(pinfo->fd, COL_INFO, "Clear Conf. VC:%d", vc);
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_add_fstr(pinfo->cinfo, COL_INFO, "Clear Conf. VC:%d", vc);
        if (x25_tree) {
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_lcn : hf_ex25_lcn, tvb,
-                   0, 2, bytes0_1);
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_type : hf_ex25_type, tvb,
-                   2, 1, X25_CLEAR_CONFIRMATION);
+           proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_CLEAR_CONFIRMATION);
        }
        localoffset = x25_pkt_len;
 
        if (localoffset < tvb_reported_length(tvb)) /* extended clear conf format */
-           x25_ntoa(x25_tree, &localoffset, tvb, pinfo->fd, toa);
+           x25_ntoa(x25_tree, &localoffset, tvb, pinfo, toa);
 
        if (localoffset < tvb_reported_length(tvb)) /* facilities */
            dump_facilities(x25_tree, &localoffset, tvb);
        break;
     case X25_DIAGNOSTIC:
-       if(check_col(pinfo->fd, COL_INFO)) {
-           col_add_fstr(pinfo->fd, COL_INFO, "Diag. %d",
+       if(check_col(pinfo->cinfo, COL_INFO)) {
+           col_add_fstr(pinfo->cinfo, COL_INFO, "Diag. %d",
                    (int)tvb_get_guint8(tvb, 3));
        }
        if (x25_tree) {
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_type : hf_ex25_type, tvb,
-                   2, 1, X25_DIAGNOSTIC);
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_DIAGNOSTIC);
            proto_tree_add_text(x25_tree, tvb, 3, 1,
                    "Diagnostic : %d", (int)tvb_get_guint8(tvb, 3));
        }
        localoffset = x25_pkt_len;
        break;
     case X25_INTERRUPT:
-       if(check_col(pinfo->fd, COL_INFO))
-           col_add_fstr(pinfo->fd, COL_INFO, "Interrupt VC:%d", vc);
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_add_fstr(pinfo->cinfo, COL_INFO, "Interrupt VC:%d", vc);
        if (x25_tree) {
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_lcn : hf_ex25_lcn, tvb,
-                   0, 2, bytes0_1);
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_type : hf_ex25_type, tvb,
-                   2, 1, X25_INTERRUPT);
+           proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_INTERRUPT);
        }
        localoffset = x25_pkt_len;
        break;
     case X25_INTERRUPT_CONFIRMATION:
-       if(check_col(pinfo->fd, COL_INFO))
-           col_add_fstr(pinfo->fd, COL_INFO, "Interrupt Conf. VC:%d", vc);
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_add_fstr(pinfo->cinfo, COL_INFO, "Interrupt Conf. VC:%d", vc);
        if (x25_tree) {
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_lcn : hf_ex25_lcn, tvb,
-                   0, 2, bytes0_1);
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_type : hf_ex25_type, tvb,
-                   2, 1, X25_INTERRUPT_CONFIRMATION);
+           proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_INTERRUPT_CONFIRMATION);
        }
        localoffset = x25_pkt_len;
        break;
     case X25_RESET_REQUEST:
-       if(check_col(pinfo->fd, COL_INFO)) {
-           col_add_fstr(pinfo->fd, COL_INFO, "%s VC:%d %s - Diag.:%d",
+       if(check_col(pinfo->cinfo, COL_INFO)) {
+           col_add_fstr(pinfo->cinfo, COL_INFO, "%s VC:%d %s - Diag.:%d",
                    (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Reset ind."
                                                                 : "Reset req.",
                    vc, reset_code(tvb_get_guint8(tvb, 3)),
@@ -1674,10 +1896,8 @@ dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        }
        x25_hash_add_proto_end(vc, pinfo->fd->abs_secs, pinfo->fd->abs_usecs);
        if (x25_tree) {
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_lcn : hf_ex25_lcn, tvb,
-                   0, 2, bytes0_1);
-           proto_tree_add_uint_format(x25_tree,
-                   (modulo == 8) ? hf_x25_type : hf_ex25_type, tvb, 2, 1,
+           proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+           proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb, 2, 1,
                    X25_RESET_REQUEST,
                    (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Reset indication"
                                                                  : "Reset request");
@@ -1689,27 +1909,25 @@ dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        localoffset = x25_pkt_len;
        break;
     case X25_RESET_CONFIRMATION:
-       if(check_col(pinfo->fd, COL_INFO))
-           col_add_fstr(pinfo->fd, COL_INFO, "Reset conf. VC:%d", vc);
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_add_fstr(pinfo->cinfo, COL_INFO, "Reset conf. VC:%d", vc);
        if (x25_tree) {
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_lcn : hf_ex25_lcn, tvb,
-                   0, 2, bytes0_1);
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_type : hf_ex25_type, tvb,
-                   2, 1, X25_RESET_CONFIRMATION);
+           proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_RESET_CONFIRMATION);
        }
        localoffset = x25_pkt_len;
        break;
     case X25_RESTART_REQUEST:
-       if(check_col(pinfo->fd, COL_INFO)) {
-           col_add_fstr(pinfo->fd, COL_INFO, "%s %s - Diag.:%d",
+       if(check_col(pinfo->cinfo, COL_INFO)) {
+           col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s - Diag.:%d",
                    (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Restart ind."
                                                                 : "Restart req.",
                    restart_code(tvb_get_guint8(tvb, 3)),
                    (int)tvb_get_guint8(tvb, 3));
        }
        if (x25_tree) {
-           proto_tree_add_uint_format(x25_tree,
-                   (modulo == 8) ? hf_x25_type : hf_ex25_type, tvb, 2, 1,
+           proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb, 2, 1,
                    X25_RESTART_REQUEST,
                    (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Restart indication"
                                                                 : "Restart request");
@@ -1721,22 +1939,22 @@ dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        localoffset = x25_pkt_len;
        break;
     case X25_RESTART_CONFIRMATION:
-       if(check_col(pinfo->fd, COL_INFO))
-           col_add_str(pinfo->fd, COL_INFO, "Restart conf.");
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_set_str(pinfo->cinfo, COL_INFO, "Restart conf.");
        if (x25_tree)
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_type : hf_ex25_type, tvb,
-                   2, 1, X25_RESTART_CONFIRMATION);
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_RESTART_CONFIRMATION);
        localoffset = x25_pkt_len;
        break;
     case X25_REGISTRATION_REQUEST:
-       if(check_col(pinfo->fd, COL_INFO))
-           col_add_str(pinfo->fd, COL_INFO, "Registration req.");
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_set_str(pinfo->cinfo, COL_INFO, "Registration req.");
        if (x25_tree)
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_type : hf_ex25_type, tvb,
-                   2, 1, X25_REGISTRATION_REQUEST);
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_REGISTRATION_REQUEST);
        localoffset = 3;
        if (localoffset < x25_pkt_len)
-           x25_ntoa(x25_tree, &localoffset, tvb, pinfo->fd, FALSE);
+           x25_ntoa(x25_tree, &localoffset, tvb, pinfo, FALSE);
 
        if (x25_tree) {
            if (localoffset < x25_pkt_len)
@@ -1751,11 +1969,11 @@ dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        localoffset = tvb_reported_length(tvb);
        break;
     case X25_REGISTRATION_CONFIRMATION:
-       if(check_col(pinfo->fd, COL_INFO))
-           col_add_str(pinfo->fd, COL_INFO, "Registration conf.");
+       if(check_col(pinfo->cinfo, COL_INFO))
+           col_set_str(pinfo->cinfo, COL_INFO, "Registration conf.");
        if (x25_tree) {
-           proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_type : hf_ex25_type, tvb,
-                   2, 1, X25_REGISTRATION_CONFIRMATION);
+           proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+                   X25_REGISTRATION_CONFIRMATION);
            proto_tree_add_text(x25_tree, tvb, 3, 1,
                    "Cause: %s", registration_code(tvb_get_guint8(tvb, 3)));
            proto_tree_add_text(x25_tree, tvb, 4, 1,
@@ -1763,7 +1981,7 @@ dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        }
        localoffset = 5;
        if (localoffset < x25_pkt_len)
-           x25_ntoa(x25_tree, &localoffset, tvb, pinfo->fd, TRUE);
+           x25_ntoa(x25_tree, &localoffset, tvb, pinfo, TRUE);
 
        if (x25_tree) {
            if (localoffset < x25_pkt_len)
@@ -1781,47 +1999,47 @@ dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        localoffset = 2;
        if ((pkt_type & 0x01) == X25_DATA)
        {
-           if(check_col(pinfo->fd, COL_INFO)) {
+           if(check_col(pinfo->cinfo, COL_INFO)) {
                if (modulo == 8)
-                   col_add_fstr(pinfo->fd, COL_INFO,
+                   col_add_fstr(pinfo->cinfo, COL_INFO,
                            "Data VC:%d P(S):%d P(R):%d %s", vc,
                            (pkt_type >> 1) & 0x07,
                            (pkt_type >> 5) & 0x07,
                            ((pkt_type >> 4) & 0x01) ? " M" : "");
                else
-                   col_add_fstr(pinfo->fd, COL_INFO,
+                   col_add_fstr(pinfo->cinfo, COL_INFO,
                            "Data VC:%d P(S):%d P(R):%d %s", vc,
                            tvb_get_guint8(tvb, localoffset+1) >> 1,
                            pkt_type >> 1,
                            (tvb_get_guint8(tvb, localoffset+1) & 0x01) ? " M" : "");
            }
            if (x25_tree) {
-               proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_lcn : hf_ex25_lcn,
-                       tvb, localoffset-2, 2, bytes0_1);
+               proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2,
+                       2, bytes0_1);
+               proto_tree_add_uint_hidden(x25_tree, hf_x25_type, tvb,
+                       localoffset, 1, X25_DATA);
                if (modulo == 8) {
-                   proto_tree_add_uint_hidden(x25_tree, hf_x25_type, tvb,
-                           localoffset, 1, X25_DATA);
-                   proto_tree_add_uint(x25_tree, hf_x25_p_r, tvb,
+                   proto_tree_add_uint(x25_tree, hf_x25_p_r_mod8, tvb,
                            localoffset, 1, pkt_type);
                    if (pkt_type & 0x10)
-                       proto_tree_add_boolean(x25_tree, hf_x25_mbit, tvb, localoffset, 1,
-                           pkt_type);
-                   proto_tree_add_uint(x25_tree, hf_x25_p_s, tvb, localoffset, 1,
-                           pkt_type);
+                       proto_tree_add_boolean(x25_tree, hf_x25_mbit_mod8, tvb,
+                           localoffset, 1, pkt_type);
+                   proto_tree_add_uint(x25_tree, hf_x25_p_s_mod8, tvb,
+                           localoffset, 1, pkt_type);
                    proto_tree_add_text(x25_tree, tvb, localoffset, 1,
                            decode_boolean_bitfield(pkt_type, 0x01, 1*8,
                                NULL, "DATA"));
                }
                else {
-                   proto_tree_add_uint_hidden(x25_tree, hf_ex25_type, tvb,
-                           localoffset, 1, X25_DATA);
-                   proto_tree_add_uint(x25_tree, hf_x25_p_r, tvb,
+                   proto_tree_add_uint(x25_tree, hf_x25_p_r_mod128, tvb,
                            localoffset, 1, pkt_type);
-                   proto_tree_add_uint(x25_tree, hf_x25_p_s, tvb,
-                           localoffset+1, 1, tvb_get_guint8(tvb, localoffset+1));
+                   proto_tree_add_uint(x25_tree, hf_x25_p_s_mod128, tvb,
+                           localoffset+1, 1,
+                           tvb_get_guint8(tvb, localoffset+1));
                    if (tvb_get_guint8(tvb, localoffset+1) & 0x01)
-                       proto_tree_add_boolean(x25_tree, hf_ex25_mbit, tvb,
-                               localoffset+1, 1, tvb_get_guint8(tvb, localoffset+1));
+                       proto_tree_add_boolean(x25_tree, hf_x25_mbit_mod128, tvb,
+                               localoffset+1, 1,
+                               tvb_get_guint8(tvb, localoffset+1));
                }
            }
            localoffset += (modulo == 8) ? 1 : 2;
@@ -1830,81 +2048,81 @@ dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        switch (pkt_type & 0x1F)
        {
        case X25_RR:
-           if(check_col(pinfo->fd, COL_INFO)) {
+           if(check_col(pinfo->cinfo, COL_INFO)) {
                if (modulo == 8)
-                   col_add_fstr(pinfo->fd, COL_INFO, "RR VC:%d P(R):%d",
+                   col_add_fstr(pinfo->cinfo, COL_INFO, "RR VC:%d P(R):%d",
                            vc, (pkt_type >> 5) & 0x07);
                else
-                   col_add_fstr(pinfo->fd, COL_INFO, "RR VC:%d P(R):%d",
+                   col_add_fstr(pinfo->cinfo, COL_INFO, "RR VC:%d P(R):%d",
                            vc, tvb_get_guint8(tvb, localoffset+1) >> 1);
            }
            if (x25_tree) {
-               proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_lcn : hf_ex25_lcn,
-                       tvb, localoffset-2, 2, bytes0_1);
+               proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2,
+                       2, bytes0_1);
                if (modulo == 8) {
-                   proto_tree_add_uint(x25_tree, hf_x25_p_r, tvb,
+                   proto_tree_add_uint(x25_tree, hf_x25_p_r_mod8, tvb,
                            localoffset, 1, pkt_type);
                    proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
                            localoffset, 1, X25_RR);
                }
                else {
-                   proto_tree_add_uint(x25_tree, hf_ex25_type, tvb,
+                   proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
                            localoffset, 1, X25_RR);
-                   proto_tree_add_item(x25_tree, hf_ex25_p_r, tvb,
+                   proto_tree_add_item(x25_tree, hf_x25_p_r_mod128, tvb,
                            localoffset+1, 1, FALSE);
                }
            }
            break;
 
        case X25_RNR:
-           if(check_col(pinfo->fd, COL_INFO)) {
+           if(check_col(pinfo->cinfo, COL_INFO)) {
                if (modulo == 8)
-                   col_add_fstr(pinfo->fd, COL_INFO, "RNR VC:%d P(R):%d",
+                   col_add_fstr(pinfo->cinfo, COL_INFO, "RNR VC:%d P(R):%d",
                            vc, (pkt_type >> 5) & 0x07);
                else
-                   col_add_fstr(pinfo->fd, COL_INFO, "RNR VC:%d P(R):%d",
+                   col_add_fstr(pinfo->cinfo, COL_INFO, "RNR VC:%d P(R):%d",
                            vc, tvb_get_guint8(tvb, localoffset+1) >> 1);
            }
            if (x25_tree) {
-               proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_lcn : hf_ex25_lcn,
-                       tvb, localoffset-2, 2, bytes0_1);
+               proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2,
+                       2, bytes0_1);
                if (modulo == 8) {
-                   proto_tree_add_uint(x25_tree, hf_x25_p_r, tvb,
+                   proto_tree_add_uint(x25_tree, hf_x25_p_r_mod8, tvb,
                            localoffset, 1, pkt_type);
                    proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
                            localoffset, 1, X25_RNR);
                }
                else {
-                   proto_tree_add_uint(x25_tree, hf_ex25_type, tvb,
+                   proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
                            localoffset, 1, X25_RNR);
-                   proto_tree_add_item(x25_tree, hf_ex25_p_r, tvb,
+                   proto_tree_add_item(x25_tree, hf_x25_p_r_mod128, tvb,
                            localoffset+1, 1, FALSE);
                }
            }
            break;
 
        case X25_REJ:
-           if(check_col(pinfo->fd, COL_INFO)) {
+           if(check_col(pinfo->cinfo, COL_INFO)) {
                if (modulo == 8)
-                   col_add_fstr(pinfo->fd, COL_INFO, "REJ VC:%d P(R):%d",
+                   col_add_fstr(pinfo->cinfo, COL_INFO, "REJ VC:%d P(R):%d",
                            vc, (pkt_type >> 5) & 0x07);
                else
-                   col_add_fstr(pinfo->fd, COL_INFO, "REJ VC:%d P(R):%d",
+                   col_add_fstr(pinfo->cinfo, COL_INFO, "REJ VC:%d P(R):%d",
                            vc, tvb_get_guint8(tvb, localoffset+1) >> 1);
            }
            if (x25_tree) {
-               proto_tree_add_uint(x25_tree, (modulo == 8) ? hf_x25_lcn : hf_ex25_lcn,
-                       tvb, localoffset-2, 2, bytes0_1);
+               proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2,
+                       2, bytes0_1);
                if (modulo == 8) {
-                   proto_tree_add_uint(x25_tree, hf_x25_p_r, tvb,
+                   proto_tree_add_uint(x25_tree, hf_x25_p_r_mod8, tvb,
                            localoffset, 1, pkt_type);
                    proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
                            localoffset, 1, X25_REJ);
                }
                else {
-                   proto_tree_add_uint(x25_tree, hf_ex25_type, tvb,
+                   proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
                            localoffset, 1, X25_REJ);
-                   proto_tree_add_item(x25_tree, hf_ex25_p_r, tvb,
+                   proto_tree_add_item(x25_tree, hf_x25_p_r_mod128, tvb,
                            localoffset+1, 1, FALSE);
                }
            }
@@ -1915,81 +2133,95 @@ dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     if (localoffset >= tvb_reported_length(tvb)) return;
 
     next_tvb = tvb_new_subset(tvb, localoffset, -1, -1);
-    tvb_compat(next_tvb, &next_pd, &next_offset);
+
+    /* QLLC ? */
+    if (q_bit_set) {
+        call_dissector(qllc_handle, next_tvb, pinfo, tree);
+        return;
+    }
+
     /* search the dissector in the hash table */
-    if ((dissect = x25_hash_get_dissect(pinfo->fd->abs_secs, pinfo->fd->abs_usecs, vc)))
-       (*dissect)(next_pd, next_offset, pinfo->fd, tree);
-    else {
-       /* If the Call Req. has not been captured, assume these packets carry IP */
-       if (tvb_get_guint8(tvb, localoffset) == 0x45) {
-           x25_hash_add_proto_start(vc, pinfo->fd->abs_secs,
-                   pinfo->fd->abs_usecs, dissect_ip);
-           dissect_ip(next_pd, next_offset, pinfo->fd, tree);
-       }
-       else {
-           dissect_data(next_pd, next_offset, pinfo->fd, tree);
-       }
+    if ((dissect = x25_hash_get_dissect(pinfo->fd->abs_secs, pinfo->fd->abs_usecs, vc))) {
+        /* Found it in the hash table; use it. */
+        call_dissector(dissect, next_tvb, pinfo, tree);
+        return;
+    }
+
+    /* Did the user suggest SNA-over-X.25? */
+    if (non_q_bit_is_sna) {
+        /* Yes - dissect it as SNA. */
+       x25_hash_add_proto_start(vc, pinfo->fd->abs_secs,
+                                pinfo->fd->abs_usecs, sna_handle);
+       call_dissector(sna_handle, next_tvb, pinfo, tree);
+       return;
+    }
+
+    /* If the Call Req. has not been captured, and the payload begins
+       with what appears to be an IP header, assume these packets carry
+       IP */
+    if (tvb_get_guint8(tvb, localoffset) == 0x45) {
+       x25_hash_add_proto_start(vc, pinfo->fd->abs_secs,
+                                pinfo->fd->abs_usecs, ip_handle);
+       call_dissector(ip_handle, next_tvb, pinfo, tree);
+       return;
     }
+
+    /* Try the heuristic dissectors. */
+    if (dissector_try_heuristic(x25_heur_subdissector_list, next_tvb, pinfo,
+                               tree))
+       return;
+
+    /* All else failed; dissect it as raw data */
+    call_dissector(data_handle, next_tvb, pinfo, tree);
 }
 
 void
 proto_register_x25(void)
 {
-    static hf_register_info hf8[] = {
+    static hf_register_info hf[] = {
+       { &hf_x25_gfi,
+         { "GFI", "x.25.gfi", FT_UINT16, BASE_BIN, NULL, 0xF000,
+               "General format identifier", HFILL }},
+       { &hf_x25_abit,
+         { "A Bit", "x.25.a", FT_BOOLEAN, 16, NULL, 0x8000,
+               "Address Bit", HFILL }},
        { &hf_x25_qbit,
-         { "Q Bit", "x25.q", FT_BOOLEAN, 2, NULL, 0x8000,
-               "Qualifier Bit" } },
+         { "Q Bit", "x.25.q", FT_BOOLEAN, 16, NULL, 0x8000,
+               "Qualifier Bit", HFILL }},
        { &hf_x25_dbit,
-         { "D Bit", "x25.d", FT_BOOLEAN, 2, NULL, 0x4000,
-               "Delivery Confirmation Bit" } },
+         { "D Bit", "x.25.d", FT_BOOLEAN, 16, NULL, 0x4000,
+               "Delivery Confirmation Bit", HFILL }},
        { &hf_x25_mod,
-         { "Modulo", "x25.mod", FT_UINT16, BASE_DEC, VALS(vals_modulo), 0x3000,
-               "Specifies whether the frame is modulo 8 or 128" } },
+         { "Modulo", "x.25.mod", FT_UINT16, BASE_DEC, VALS(vals_modulo), 0x3000,
+               "Specifies whether the frame is modulo 8 or 128", HFILL }},
        { &hf_x25_lcn,
-         { "Logical Channel", "x25.lcn", FT_UINT16, BASE_DEC, NULL, 0x0FFF,
-               "Logical Channel Number" } },
+         { "Logical Channel", "x.25.lcn", FT_UINT16, BASE_DEC, NULL, 0x0FFF,
+               "Logical Channel Number", HFILL }},
        { &hf_x25_type,
-         { "Packet Type", "x25.type", FT_UINT8, BASE_HEX, VALS(vals_x25_type), 0x0,
-               "Packet Type" } },
-       { &hf_x25_p_r,
-         { "P(R)", "x25.p_r", FT_UINT8, BASE_HEX, NULL, 0xE0,
-               "Packet Receive Sequence Number" } },
-       { &hf_x25_mbit,
-         { "M Bit", "x25.m", FT_BOOLEAN, 1, NULL, 0x10,
-               "More Bit" } },
-       { &hf_x25_p_s,
-         { "P(S)", "x25.p_s", FT_UINT8, BASE_HEX, NULL, 0x0E,
-               "Packet Send Sequence Number" } },
-    };
-
-    static hf_register_info hf128[] = {
-       { &hf_ex25_qbit,
-         { "Q Bit", "ex25.q", FT_BOOLEAN, 2, NULL, 0x8000,
-               "Qualifier Bit" } },
-       { &hf_ex25_dbit,
-         { "D Bit", "ex25.d", FT_BOOLEAN, 2, NULL, 0x4000,
-               "Delivery Confirmation Bit" } },
-       { &hf_ex25_mod,
-         { "Modulo", "ex25.mod", FT_UINT16, BASE_DEC, VALS(vals_modulo), 0x3000,
-               "Specifies whether the frame is modulo 8 or 128" } },
-       { &hf_ex25_lcn,
-         { "Logical Channel", "ex25.lcn", FT_UINT16, BASE_HEX, NULL, 0x0FFF,
-               "Logical Channel Number" } },
-       { &hf_ex25_type,
-         { "Packet Type", "ex25.type", FT_UINT8, BASE_HEX, VALS(vals_x25_type), 0x0,
-               "Packet Type" } },
-       { &hf_ex25_p_r,
-         { "P(R)", "ex25.p_r", FT_UINT8, BASE_HEX, NULL, 0xFE,
-               "Packet Receive Sequence Number" } },
-       { &hf_ex25_mbit,
-         { "M Bit", "ex25.m", FT_BOOLEAN, 1, NULL, 0x01,
-               "More Bit" } },
-       { &hf_ex25_p_s,
-         { "P(S)", "ex25.p_s", FT_UINT8, BASE_HEX, NULL, 0xFE,
-               "Packet Send Sequence Number" } },
+         { "Packet Type", "x.25.type", FT_UINT8, BASE_HEX, VALS(vals_x25_type), 0x0,
+               "Packet Type", HFILL }},
+       { &hf_x25_p_r_mod8,
+         { "P(R)", "x.25.p_r", FT_UINT8, BASE_HEX, NULL, 0xE0,
+               "Packet Receive Sequence Number", HFILL }},
+       { &hf_x25_p_r_mod128,
+         { "P(R)", "x.25.p_r", FT_UINT8, BASE_HEX, NULL, 0xFE,
+               "Packet Receive Sequence Number", HFILL }},
+       { &hf_x25_mbit_mod8,
+         { "M Bit", "x.25.m", FT_BOOLEAN, 8, NULL, 0x10,
+               "More Bit", HFILL }},
+       { &hf_x25_mbit_mod128,
+         { "M Bit", "x.25.m", FT_BOOLEAN, 8, NULL, 0x01,
+               "More Bit", HFILL }},
+       { &hf_x25_p_s_mod8,
+         { "P(S)", "x.25.p_s", FT_UINT8, BASE_HEX, NULL, 0x0E,
+               "Packet Send Sequence Number", HFILL }},
+       { &hf_x25_p_s_mod128,
+         { "P(S)", "x.25.p_s", FT_UINT8, BASE_HEX, NULL, 0xFE,
+               "Packet Send Sequence Number", HFILL }},
     };
     static gint *ett[] = {
         &ett_x25,
+       &ett_x25_gfi,
        &ett_x25_fac,
        &ett_x25_fac_unknown,
        &ett_x25_fac_mark,
@@ -2010,13 +2242,43 @@ proto_register_x25(void)
        &ett_x25_fac_ete_transit_delay,
        &ett_x25_fac_calling_addr_ext,
        &ett_x25_fac_call_deflect,
-       &ett_x25_fac_priority
+       &ett_x25_fac_priority,
+       &ett_x25_user_data
     };
+    module_t *x25_module;
 
-    proto_x25 = proto_register_protocol ("X.25", "x25");
-    proto_ex25 = proto_register_protocol ("Extended X.25 (modulo 128)", "ex25");
-    proto_register_field_array (proto_x25, hf8, array_length(hf8));
-    proto_register_field_array (proto_ex25, hf128, array_length(hf128));
+    proto_x25 = proto_register_protocol ("X.25", "X.25", "x.25");
+    proto_register_field_array (proto_x25, hf, array_length(hf));
     proto_register_subtree_array(ett, array_length(ett));
     register_init_routine(&reinit_x25_hashtable);
+
+    x25_subdissector_table = register_dissector_table("x.25.spi",
+       "X.25 secondary protocol identifier", FT_UINT8, BASE_HEX);
+    register_heur_dissector_list("x.25", &x25_heur_subdissector_list);
+
+    register_dissector("x.25", dissect_x25, proto_x25);
+
+    /* Preferences */
+    x25_module = prefs_register_protocol(proto_x25, NULL);
+    prefs_register_bool_preference(x25_module, "non_q_bit_is_sna",
+            "When Q-bit is 0, payload is SNA", "When Q-bit is 0, payload is SNA",
+            &non_q_bit_is_sna);
+}
+
+void
+proto_reg_handoff_x25(void)
+{
+    dissector_handle_t x25_handle;
+
+    /*
+     * Get handles for various dissectors.
+     */
+    ip_handle = find_dissector("ip");
+    ositp_handle = find_dissector("ositp");
+    sna_handle = find_dissector("sna");
+    qllc_handle = find_dissector("qllc");
+    data_handle = find_dissector("data");
+
+    x25_handle = find_dissector("x.25");
+    dissector_add("llc.dsap", SAP_X25, x25_handle);
 }