aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors
diff options
context:
space:
mode:
authorKovarththanan Rajaratnam <kovarththanan.rajaratnam@gmail.com>2011-01-02 08:13:40 +0000
committerKovarththanan Rajaratnam <kovarththanan.rajaratnam@gmail.com>2011-01-02 08:13:40 +0000
commit46dd093b945d4bc088741e22b9c0a87024fc6fe4 (patch)
tree5752f0f5c73ffd81be6abe6c2331f8e2ce71dbf5 /epan/dissectors
parent388453b79dcc220ced63cd854924cd0ad1411077 (diff)
From Allan M. Madsen via. bug 5445:
Bluetooth profiles and protocols above RFCOMM and L2CAP can not be dissected correctly because the required information (server channel and dynamic PSM value mappings to services/profiles) about the type of data carried in the payload is not available. RFCOMM is currently hardcoded to handoff all payload data to the obex dissector though it may carry e.g. handsfree, dial-up networking or serial port profile related data. The patch consists of modifcations to the following dissectors: btsdp: Extraction of RFCOMM server channel and L2CAP dynamic PSM with service mapping is provided to RFCOMM and L2CAP through a tap interface. In addition, the packet list info is beautyfied and extended with more details for better overview. btl2cap: Adds a new dissector table with services and dynamic PSM mapping which is filled by a tap listner catching the info from btsdp. More info added to packet list. btrfcomm: Adds a new dissector table with services and server channel mapping which is filled by a tap listner catching the info from btsdp. Dissectors for handsfree, dial-up netorking and serial port profiles (all based on RFCOMM) are also added. btobex: Registers several obex based profiles (e.g. obex push, file transfer, basic printing etc.) in both RFCOMM and L2CAP. Some cleanup. svn path=/trunk/; revision=35323
Diffstat (limited to 'epan/dissectors')
-rw-r--r--epan/dissectors/Makefile.common1
-rw-r--r--epan/dissectors/packet-btl2cap.c320
-rw-r--r--epan/dissectors/packet-btl2cap.h13
-rw-r--r--epan/dissectors/packet-btobex.c985
-rw-r--r--epan/dissectors/packet-btrfcomm.c324
-rw-r--r--epan/dissectors/packet-btsdp.c530
6 files changed, 1346 insertions, 827 deletions
diff --git a/epan/dissectors/Makefile.common b/epan/dissectors/Makefile.common
index 8b893c77bd..3dc8037524 100644
--- a/epan/dissectors/Makefile.common
+++ b/epan/dissectors/Makefile.common
@@ -1030,6 +1030,7 @@ DISSECTOR_INCLUDES = \
packet-bthci_acl.h \
packet-btl2cap.h \
packet-btrfcomm.h \
+ packet-btsdp.h \
packet-camel.h \
packet-chdlc.h \
packet-cdt.h \
diff --git a/epan/dissectors/packet-btl2cap.c b/epan/dissectors/packet-btl2cap.c
index ce725997ff..c4a4bf9fed 100644
--- a/epan/dissectors/packet-btl2cap.c
+++ b/epan/dissectors/packet-btl2cap.c
@@ -37,6 +37,8 @@
#include <etypes.h>
#include <epan/emem.h>
#include <epan/expert.h>
+#include <epan/tap.h>
+#include "packet-btsdp.h"
#include "packet-bthci_acl.h"
#include "packet-btl2cap.h"
@@ -51,6 +53,7 @@ static int hf_btl2cap_cmd_ident = -1;
static int hf_btl2cap_cmd_length = -1;
static int hf_btl2cap_cmd_data = -1;
static int hf_btl2cap_psm = -1;
+static int hf_btl2cap_psm_dynamic = -1;
static int hf_btl2cap_scid = -1;
static int hf_btl2cap_dcid = -1;
static int hf_btl2cap_icid = -1;
@@ -133,12 +136,15 @@ static gint ett_btl2cap_control = -1;
/* Initialize dissector table */
static dissector_table_t l2cap_psm_dissector_table;
static dissector_table_t l2cap_cid_dissector_table;
+static dissector_table_t l2cap_service_dissector_table;
/* This table maps cid values to psm values.
* The same table is used both for SCID and DCID.
* For received CIDs we mask the cid with 0x8000 in this table
*/
static emem_tree_t *cid_to_psm_table = NULL;
+static emem_tree_t *psm_to_service_table = NULL;
+
typedef struct _config_data_t {
guint8 mode;
guint8 txwindow;
@@ -146,6 +152,7 @@ typedef struct _config_data_t {
} config_data_t;
typedef struct _psm_data_t {
guint16 psm;
+ gboolean local_service;
config_data_t in;
config_data_t out;
} psm_data_t;
@@ -178,40 +185,41 @@ static const value_string psm_vals[] = {
{ 0x0005, "TCS-BIN" },
{ 0x0007, "TCS-BIN-CORDLESS" },
{ 0x000F, "BNEP" },
- { 0x0011, "HID_CONTROL" },
- { 0x0013, "HID_INTERRUPT" },
+ { 0x0011, "HID-Control" },
+ { 0x0013, "HID-Interrupt" },
{ 0x0015, "UPnP" },
- { 0x0017, "AVCTP" },
+ { 0x0017, "AVCTP-Control" },
{ 0x0019, "AVDTP" },
+ { 0x001B, "AVCTP-Browsing" },
{ 0x001D, "UDI_C-Plane" },
{ 0, NULL }
};
static const value_string result_vals[] = {
- { 0x0000, "Connection successful" },
- { 0x0001, "Connection pending" },
- { 0x0002, "Connection refused - PSM not supported" },
- { 0x0003, "Connection refused - security block" },
- { 0x0004, "Connection refused - no resources available" },
- { 0x0005, "Connection refused - Controller ID not supported" },
+ { 0x0000, "Successful" },
+ { 0x0001, "Pending" },
+ { 0x0002, "Refused - PSM not supported" },
+ { 0x0003, "Refused - security block" },
+ { 0x0004, "Refused - no resources available" },
+ { 0x0005, "Refused - Controller ID not supported" },
{ 0, NULL }
};
static const value_string move_result_vals[] = {
- { 0x0000, "Move success" },
- { 0x0001, "Move pending" },
- { 0x0002, "Move refused - Controller ID not supported" },
- { 0x0003, "Move refused - New Controller ID is same as old" },
- { 0x0004, "Move refused - Configuration not supported" },
- { 0x0005, "Move refused - Move Channel collision" },
- { 0x0006, "Move refused - Channel not allowed to be moved" },
+ { 0x0000, "Success" },
+ { 0x0001, "Pending" },
+ { 0x0002, "Refused - Controller ID not supported" },
+ { 0x0003, "Refused - New Controller ID is same as old" },
+ { 0x0004, "Refused - Configuration not supported" },
+ { 0x0005, "Refused - Move Channel collision" },
+ { 0x0006, "Refused - Channel not allowed to be moved" },
{ 0, NULL }
};
static const value_string move_result_confirmation_vals[] = {
- { 0x0000, "Move success - both sides succeed" },
- { 0x0001, "Move failure - one or both sides refuse" },
+ { 0x0000, "Success - both sides succeed" },
+ { 0x0001, "Failure - one or both sides refuse" },
{ 0, NULL }
};
@@ -307,6 +315,12 @@ static const value_string option_fcs_vals[] = {
{ 0, NULL }
};
+static const value_string ctrl_id_code_vals[] = {
+ { 0x00, "Bluetooth BR/EDR" },
+ { 0x01, "Wifi 802.11" },
+ { 0, NULL }
+};
+
static int
dissect_comrej(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
{
@@ -342,55 +356,50 @@ dissect_comrej(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tr
}
static int
-dissect_connrequest(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
+dissect_connrequest(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, gboolean is_ch_request)
{
guint16 scid, psm;
psm_data_t *psm_data;
+ const gchar *psm_str = "<NONE>";
psm=tvb_get_letohs(tvb, offset);
- proto_tree_add_item(tree, hf_btl2cap_psm, tvb, offset, 2, TRUE);
- offset+=2;
-
- scid=tvb_get_letohs(tvb, offset);
- proto_tree_add_item(tree, hf_btl2cap_scid, tvb, offset, 2, TRUE);
- offset+=2;
-
- if (pinfo->fd->flags.visited == 0) {
- psm_data=se_alloc(sizeof(psm_data_t));
- psm_data->psm=psm;
- psm_data->in.mode=0;
- psm_data->in.txwindow=0;
- psm_data->in.start_fragments=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "bthci_l2cap fragment starts");
- psm_data->out.mode=0;
- psm_data->out.txwindow=0;
- psm_data->out.start_fragments=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "bthci_l2cap fragment starts");
- se_tree_insert32(cid_to_psm_table, scid|((pinfo->p2p_dir == P2P_DIR_RECV)?0x8000:0x0000), psm_data);
-
+ if( psm < BTL2CAP_DYNAMIC_PSM_START ) {
+ proto_tree_add_item(tree, hf_btl2cap_psm, tvb, offset, 2, TRUE);
+ psm_str = val_to_str(psm, psm_vals, "Unknown PSM");
}
- return offset;
-}
-
+ else {
+ guint32 *service, token;
+ proto_item *item;
-static int
-dissect_chanrequest(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
-{
- guint16 scid, psm;
- psm_data_t *psm_data;
+ item = proto_tree_add_item(tree, hf_btl2cap_psm_dynamic, tvb, offset, 2, TRUE);
+ token = psm | ((pinfo->p2p_dir == P2P_DIR_RECV)?0x80000000:0x00000000);
+ service = se_tree_lookup32(psm_to_service_table, token);
- psm=tvb_get_letohs(tvb, offset);
- proto_tree_add_item(tree, hf_btl2cap_psm, tvb, offset, 2, TRUE);
+ if( service ) {
+ psm_str = val_to_str(*service, vs_service_classes, "Unknown PSM");
+ proto_item_append_text(item," (%s)", psm_str);
+ }
+ }
offset+=2;
scid=tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_scid, tvb, offset, 2, TRUE);
offset+=2;
- proto_tree_add_item(tree, hf_btl2cap_controller, tvb, offset, 1, TRUE);
- offset+=1;
+ if( psm_str )
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (%s, SCID: 0x%04x)", psm_str, scid);
+ else
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (SCID: 0x%04x)", scid);
+
+ if( is_ch_request ) {
+ proto_tree_add_item(tree, hf_btl2cap_controller, tvb, offset, 1, TRUE);
+ offset+=1;
+ }
if (pinfo->fd->flags.visited == 0) {
psm_data=se_alloc(sizeof(psm_data_t));
psm_data->psm=psm;
+ psm_data->local_service = (pinfo->p2p_dir == P2P_DIR_RECV) ? TRUE : FALSE;
psm_data->in.mode=0;
psm_data->in.txwindow=0;
psm_data->in.start_fragments=se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "bthci_l2cap fragment starts");
@@ -406,12 +415,19 @@ dissect_chanrequest(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *t
static int
dissect_movechanrequest(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
{
+ guint16 icid;
+ guint8 ctrl_id;
+
+ icid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_icid, tvb, offset, 2, TRUE);
offset+=2;
+ ctrl_id = tvb_get_guint8(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_dcontroller, tvb, offset, 1, TRUE);
offset+=1;
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (ICID: 0x%04x, move to %s)", icid, val_to_str(ctrl_id, ctrl_id_code_vals, "Unknown controller"));
+
return offset;
}
@@ -564,6 +580,8 @@ dissect_configrequest(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_t
proto_tree_add_item(tree, hf_btl2cap_dcid, tvb, offset, 2, TRUE);
offset+=2;
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (DCID: 0x%04x)", dcid);
+
proto_tree_add_item(tree, hf_btl2cap_continuation_flag, tvb, offset, 2, TRUE);
offset+=2;
@@ -585,16 +603,20 @@ dissect_configrequest(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_t
static int
dissect_inforequest(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
{
+ guint16 info_type;
+
+ info_type=tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_info_type, tvb, offset, 2, TRUE);
offset+=2;
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)", val_to_str(info_type, info_type_vals, "Unknown type"));
return offset;
}
static int
dissect_inforesponse(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
{
- guint16 info_type;
+ guint16 info_type, result;
proto_item *ti_features=NULL;
proto_tree *ti_features_subtree=NULL;
guint32 features;
@@ -603,9 +625,14 @@ dissect_inforesponse(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tr
proto_tree_add_item(tree, hf_btl2cap_info_type, tvb, offset, 2, TRUE);
offset+=2;
+ result = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_info_result, tvb, offset, 2, TRUE);
offset+=2;
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (%s, %s)",
+ val_to_str(info_type, info_type_vals, "Unknown type"),
+ val_to_str(result, info_result_vals, "Unknown result"));
+
if(tvb_length_remaining(tvb, offset)) {
switch(info_type){
case 0x0001: /* Connectionless MTU */
@@ -686,7 +713,7 @@ dissect_configresponse(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_
{
psm_data_t *psm_data;
config_data_t *config_data;
- guint16 scid;
+ guint16 scid, result;
scid = tvb_get_letohs(tvb, offset);
psm_data=se_tree_lookup32(cid_to_psm_table, scid|((pinfo->p2p_dir==P2P_DIR_RECV)?0x0000:0x8000));
@@ -696,9 +723,12 @@ dissect_configresponse(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_
proto_tree_add_item(tree, hf_btl2cap_continuation_flag, tvb, offset, 2, TRUE);
offset+=2;
+ result = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_configuration_result, tvb, offset, 2, TRUE);
offset+=2;
+ col_append_fstr(pinfo->cinfo, COL_INFO, " - %s (SCID: 0x%04x)", val_to_str(result, configuration_result_vals, "Unknown"), scid);
+
if(tvb_length_remaining(tvb, offset)){
if (psm_data)
if(pinfo->p2p_dir==P2P_DIR_RECV)
@@ -716,7 +746,7 @@ dissect_configresponse(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_
static int
dissect_connresponse(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
- guint16 scid, dcid;
+ guint16 scid, dcid, result;
psm_data_t *psm_data;
dcid = tvb_get_letohs(tvb, offset);
@@ -727,12 +757,20 @@ dissect_connresponse(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *
proto_tree_add_item(tree, hf_btl2cap_scid, tvb, offset, 2, TRUE);
offset+=2;
+ result = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_result, tvb, offset, 2, TRUE);
offset+=2;
proto_tree_add_item(tree, hf_btl2cap_status, tvb, offset, 2, TRUE);
offset+=2;
+ if(!result) {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " - Success (SCID: 0x%04x, DCID: 0x%04x)", scid, dcid);
+ }
+ else {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " - %s (SCID: 0x%04x)", val_to_str(result, result_vals, "Unknown"), scid);
+ }
+
if (pinfo->fd->flags.visited == 0) {
if((psm_data=se_tree_lookup32(cid_to_psm_table, scid|((pinfo->p2p_dir==P2P_DIR_RECV)?0x0000:0x8000)))){
se_tree_insert32(cid_to_psm_table, dcid|((pinfo->p2p_dir == P2P_DIR_RECV)?0x8000:0x0000), psm_data);
@@ -751,50 +789,72 @@ dissect_chanresponse(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *
static int
dissect_movechanresponse(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
{
+ guint16 icid, result;
+
+ icid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_icid, tvb, offset, 2, TRUE);
offset+=2;
+ result = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_move_result, tvb, offset, 2, TRUE);
offset+=2;
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (ICID: 0x%04x, %s)", icid, val_to_str(result, move_result_vals, "Unknown result"));
+
return offset;
}
static int
dissect_movechanconfirmation(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
{
+ guint16 icid, result;
+
+ icid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_icid, tvb, offset, 2, TRUE);
offset+=2;
+ result = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_move_confirmation_result, tvb, offset, 2, TRUE);
offset+=2;
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (ICID: 0x%04x, %s)", icid, val_to_str(result, move_result_confirmation_vals, "Unknown result"));
+
return offset;
}
static int
dissect_movechanconfirmationresponse(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
{
+ guint16 icid;
+
+ icid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_icid, tvb, offset, 2, TRUE);
offset+=2;
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (ICID: 0x%04x)", icid);
return offset;
}
static int
dissect_disconnrequestresponse(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
{
+ guint16 scid, dcid;
+
+ dcid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_dcid, tvb, offset, 2, TRUE);
offset+=2;
+ scid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_scid, tvb, offset, 2, TRUE);
offset+=2;
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (SCID: 0x%04x, DCID: 0x%04x)", scid, dcid);
+
return offset;
}
static void
-dissect_b_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree *btl2cap_tree, guint16 psm, guint16 length, int offset)
+dissect_b_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree *btl2cap_tree, guint16 psm, gboolean local_service, guint16 length, int offset)
{
tvbuff_t *next_tvb;
next_tvb = tvb_new_subset(tvb, offset, tvb_length_remaining(tvb, offset), length);
@@ -803,18 +863,29 @@ dissect_b_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree
if(psm){
proto_item *psm_item;
+ guint32 *service =se_tree_lookup32(psm_to_service_table, (local_service<<31) | psm);
+ if( psm < BTL2CAP_DYNAMIC_PSM_START ) {
psm_item=proto_tree_add_uint(btl2cap_tree, hf_btl2cap_psm, tvb, offset, 0, psm);
+ }
+ else {
+ psm_item=proto_tree_add_uint(btl2cap_tree, hf_btl2cap_psm_dynamic, tvb, offset, 0, psm);
+ if( service )
+ proto_item_append_text(psm_item,": %s", val_to_str(*service, vs_service_classes, "Unknown service"));
+ }
PROTO_ITEM_SET_GENERATED(psm_item);
/* call next dissector */
- if (!dissector_try_uint(l2cap_psm_dissector_table, (guint32) psm,
- next_tvb, pinfo, tree)) {
- /* unknown protocol. declare as data */
- proto_tree_add_item(btl2cap_tree, hf_btl2cap_payload, tvb, offset, length, TRUE);
+ if (!dissector_try_uint(l2cap_psm_dissector_table, (guint32) psm, next_tvb, pinfo, tree)) {
+ /* not a known fixed PSM, try to find a registered service to a dynamic PSM */
+ if(service && !dissector_try_uint(l2cap_service_dissector_table, *service, next_tvb, pinfo, tree)) {
+ /* unknown protocol. declare as data */
+ proto_tree_add_item(btl2cap_tree, hf_btl2cap_payload, tvb, offset, length, TRUE);
+ }
}
offset+=tvb_length_remaining(tvb, offset);
- } else {
+ }
+ else {
proto_tree_add_item(btl2cap_tree, hf_btl2cap_payload, tvb, offset, length, TRUE);
offset+=tvb_length_remaining(tvb, offset);
}
@@ -936,18 +1007,29 @@ dissect_i_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree
}
if(next_tvb) {
if(psm){
+ guint32 *service =se_tree_lookup32(psm_to_service_table, ((psm_data?psm_data->local_service:0)<<31) | psm);
proto_item *psm_item;
+ if( psm < BTL2CAP_DYNAMIC_PSM_START ) {
psm_item=proto_tree_add_uint(btl2cap_tree, hf_btl2cap_psm, tvb, offset, 0, psm);
+ }
+ else {
+ psm_item=proto_tree_add_uint(btl2cap_tree, hf_btl2cap_psm_dynamic, tvb, offset, 0, psm);
+ if(service)
+ proto_item_append_text(psm_item," (%s)", val_to_str(*service, vs_service_classes, "Unknown service"));
+ }
PROTO_ITEM_SET_GENERATED(psm_item);
/* call next dissector */
- if (!dissector_try_uint(l2cap_psm_dissector_table, (guint32) psm,
- next_tvb, pinfo, tree)) {
+ if (!dissector_try_uint(l2cap_psm_dissector_table, (guint32) psm, next_tvb, pinfo, tree)) {
+ /* not a known fixed PSM, try to find a registered service to a dynamic PSM */
+ if(service && !dissector_try_uint(l2cap_service_dissector_table, *service, next_tvb, pinfo, tree)) {
/* unknown protocol. declare as data */
proto_tree_add_item(btl2cap_tree, hf_btl2cap_payload, next_tvb, 0, tvb_length(next_tvb), TRUE);
}
- } else {
+ }
+ }
+ else {
proto_tree_add_item(btl2cap_tree, hf_btl2cap_payload, next_tvb, 0, tvb_length(next_tvb), TRUE);
}
}
@@ -1041,6 +1123,7 @@ dissect_btl2cap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
proto_item *ti_command=NULL;
guint8 cmd_code;
guint16 cmd_length;
+ const gchar *cmd_str;
ti_command=proto_tree_add_none_format(btl2cap_tree,
hf_btl2cap_command, tvb,
@@ -1060,131 +1143,105 @@ dissect_btl2cap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
proto_item_set_len(ti_command, cmd_length+4);
offset+=2;
+ cmd_str = val_to_str(cmd_code, command_code_vals, "Unknown cmd");
+ proto_item_append_text(ti_command,"%s", cmd_str);
+ col_append_fstr(pinfo->cinfo, COL_INFO, "%s", cmd_str);
+
switch(cmd_code) {
case 0x01: /* Command Reject */
offset=dissect_comrej(tvb, offset, pinfo, btl2cap_cmd_tree);
- proto_item_append_text(ti_command, "Command Reject");
- col_append_str(pinfo->cinfo, COL_INFO, "Command Reject");
break;
case 0x02: /* Connection Request */
- offset=dissect_connrequest(tvb, offset, pinfo, btl2cap_cmd_tree);
- proto_item_append_text(ti_command, "Connection Request");
- col_append_str(pinfo->cinfo, COL_INFO, "Connection Request");
+ offset=dissect_connrequest(tvb, offset, pinfo, btl2cap_cmd_tree, FALSE);
break;
case 0x03: /* Connection Response */
offset=dissect_connresponse(tvb, offset, pinfo, btl2cap_cmd_tree);
- proto_item_append_text(ti_command, "Connection Response");
- col_append_str(pinfo->cinfo, COL_INFO, "Connection Response");
break;
case 0x04: /* Configure Request */
offset=dissect_configrequest(tvb, offset, pinfo, btl2cap_cmd_tree, cmd_length);
- proto_item_append_text(ti_command, "Configure Request");
- col_append_str(pinfo->cinfo, COL_INFO, "Configure Request");
break;
case 0x05: /* Configure Response */
offset=dissect_configresponse(tvb, offset, pinfo, btl2cap_cmd_tree, cmd_length);
- proto_item_append_text(ti_command, "Configure Response");
- col_append_str(pinfo->cinfo, COL_INFO, "Configure Response");
break;
case 0x06: /* Disconnect Request */
offset=dissect_disconnrequestresponse(tvb, offset, pinfo, btl2cap_cmd_tree);
- proto_item_append_text(ti_command, "Disconnect Request");
- col_append_str(pinfo->cinfo, COL_INFO, "Disconnect Request");
break;
case 0x07: /* Disconnect Response */
offset=dissect_disconnrequestresponse(tvb, offset, pinfo, btl2cap_cmd_tree);
- proto_item_append_text(ti_command, "Disconnect Response");
- col_append_str(pinfo->cinfo, COL_INFO, "Disconnect Response");
break;
case 0x08: /* Echo Request */
- proto_item_append_text(ti_command, "Echo Request");
offset+=tvb_length_remaining(tvb, offset);
- col_append_str(pinfo->cinfo, COL_INFO, "Echo Request");
break;
case 0x09: /* Echo Response */
- proto_item_append_text(ti_command, "Echo Response");
offset+=tvb_length_remaining(tvb, offset);
- col_append_str(pinfo->cinfo, COL_INFO, "Echo Response");
break;
case 0x0a: /* Information Request */
offset=dissect_inforequest(tvb, offset, pinfo, btl2cap_cmd_tree);
-
- proto_item_append_text(ti_command, "Information Request");
- col_append_str(pinfo->cinfo, COL_INFO, "Information Request");
break;
case 0x0b: /* Information Response */
offset=dissect_inforesponse(tvb, offset, pinfo, btl2cap_cmd_tree);
- proto_item_append_text(ti_command, "Information Response");
- col_append_str(pinfo->cinfo, COL_INFO, "Information Response");
break;
case 0x0c: /* Create Channel Request */
- offset=dissect_chanrequest(tvb, offset, pinfo, btl2cap_cmd_tree);
- proto_item_append_text(ti_command, "Create Channel Request");
- col_append_str(pinfo->cinfo, COL_INFO, "Create Channel Request");
+ offset=dissect_connrequest(tvb, offset, pinfo, btl2cap_cmd_tree, TRUE);
break;
case 0x0d: /* Create Channel Response */
offset=dissect_chanresponse(tvb, offset, pinfo, btl2cap_cmd_tree);
- proto_item_append_text(ti_command, "Create Channel Response");
- col_append_str(pinfo->cinfo, COL_INFO, "Create Channel Response");
break;
case 0x0e: /* Move Channel Request */
offset=dissect_movechanrequest(tvb, offset, pinfo, btl2cap_cmd_tree);
- proto_item_append_text(ti_command, "Move Channel Request");
- col_append_str(pinfo->cinfo, COL_INFO, "Move Channel Request");
break;
case 0x0f: /* Move Channel Response */
offset=dissect_movechanresponse(tvb, offset, pinfo, btl2cap_cmd_tree);
- proto_item_append_text(ti_command, "Move Channel Response");
- col_append_str(pinfo->cinfo, COL_INFO, "Move Channel Response");
break;
case 0x10: /* Move Channel Confirmation */
offset=dissect_movechanconfirmation(tvb, offset, pinfo, btl2cap_cmd_tree);
- proto_item_append_text(ti_command, "Move Channel Confirmation");
- col_append_str(pinfo->cinfo, COL_INFO, "Move Channel Confirmation");
break;
case 0x11: /* Move Channel Confirmation Response */
offset=dissect_movechanconfirmationresponse(tvb, offset, pinfo, btl2cap_cmd_tree);
- proto_item_append_text(ti_command, "Move Channel Confirmation Response");
- col_append_str(pinfo->cinfo, COL_INFO, "Move Channel Confirmation Response");
break;
- default:
- proto_tree_add_item(btl2cap_cmd_tree, hf_btl2cap_cmd_data, tvb, offset, -1, TRUE);
- offset+=tvb_length_remaining(tvb, offset);
- break;
+ default:
+ proto_tree_add_item(btl2cap_cmd_tree, hf_btl2cap_cmd_data, tvb, offset, -1, TRUE);
+ offset+=tvb_length_remaining(tvb, offset);
+ break;
}
}
- } else if (cid == BTL2CAP_FIXED_CID_CONNLESS) { /* Connectionless reception channel */
+ }
+ else if (cid == BTL2CAP_FIXED_CID_CONNLESS) { /* Connectionless reception channel */
col_append_str(pinfo->cinfo, COL_INFO, "Connectionless reception channel");
psm = tvb_get_letohs(tvb, offset);
proto_tree_add_item(btl2cap_tree, hf_btl2cap_psm, tvb, offset, 2, TRUE);
offset+=2;
-
next_tvb = tvb_new_subset(tvb, offset, tvb_length_remaining(tvb, offset), length);
/* call next dissector */
- if(!dissector_try_uint(l2cap_psm_dissector_table, (guint32) psm,
- next_tvb, pinfo, tree)){
- /* unknown protocol. declare as data */
- proto_tree_add_item(btl2cap_tree, hf_btl2cap_payload, tvb, offset, length, TRUE);
+ if(!dissector_try_uint(l2cap_psm_dissector_table, (guint32) psm, next_tvb, pinfo, tree)) {
+ /* not a known fixed PSM, try to find a registered service to a dynamic PSM */
+ guint32 *service;
+ service=se_tree_lookup32(psm_to_service_table, ((pinfo->p2p_dir==P2P_DIR_RECV)?0x80000000:0) | psm);
+
+ if(!service || !dissector_try_uint(l2cap_service_dissector_table, *service, next_tvb, pinfo, tree)) {
+ /* unknown protocol. declare as data */
+ proto_tree_add_item(btl2cap_tree, hf_btl2cap_payload, tvb, offset, length, TRUE);
+ }
}
offset+=tvb_length_remaining(tvb, offset);
}
@@ -1233,7 +1290,7 @@ dissect_btl2cap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
else
config_data = &(psm_data->out);
if(config_data->mode == 0) {
- dissect_b_frame(tvb, pinfo, tree, btl2cap_tree, psm, length, offset);
+ dissect_b_frame(tvb, pinfo, tree, btl2cap_tree, psm, psm_data->local_service, length, offset);
} else {
control = tvb_get_letohs(tvb, offset);
if(control & 0x1) {
@@ -1244,13 +1301,34 @@ dissect_btl2cap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
}
} else {
psm=0;
- dissect_b_frame(tvb, pinfo, tree, btl2cap_tree, psm, length, offset);
+ dissect_b_frame(tvb, pinfo, tree, btl2cap_tree, psm, FALSE, length, offset);
}
}
pinfo->private_data = pd_save;
}
+static int
+btl2cap_sdp_tap_packet(void *arg _U_, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *arg2)
+{
+ btsdp_data_t *sdp_data = (btsdp_data_t *) arg2;
+
+ if( sdp_data->protocol == BTSDP_L2CAP_PROTOCOL_UUID ) {
+ guint32 token, *psm_service;
+
+ token = sdp_data->channel | ((sdp_data->flags & BTSDP_LOCAL_SERVICE_FLAG_MASK)<<31);
+
+ psm_service=se_tree_lookup32(psm_to_service_table, token);
+ if(!psm_service){
+ psm_service=se_alloc0(sizeof(guint32));
+ se_tree_insert32(psm_to_service_table, token, psm_service);
+ }
+ *psm_service = sdp_data->service;
+ }
+ return 0;
+}
+
+
/* Register the protocol with Wireshark */
void
proto_register_btl2cap(void)
@@ -1303,6 +1381,11 @@ proto_register_btl2cap(void)
FT_UINT16, BASE_HEX, VALS(psm_vals), 0x0,
"Protocol/Service Multiplexer", HFILL }
},
+ { &hf_btl2cap_psm_dynamic,
+ { "Dynamic PSM", "btl2cap.psm",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ "Dynamic Protocol/Service Multiplexer", HFILL }
+ },
{ &hf_btl2cap_scid,
{ "Source CID", "btl2cap.scid",
FT_UINT16, BASE_HEX, NULL, 0x0,
@@ -1320,12 +1403,12 @@ proto_register_btl2cap(void)
},
{ &hf_btl2cap_controller,
{ "Controller ID", "btl2cap.ctrl_id",
- FT_UINT8, BASE_DEC, NULL, 0x0,
+ FT_UINT8, BASE_DEC, VALS(ctrl_id_code_vals), 0x0,
NULL, HFILL }
},
{ &hf_btl2cap_dcontroller,
{ "Controller ID", "btl2cap.dctrl_id",
- FT_UINT8, BASE_DEC, NULL, 0x0,
+ FT_UINT8, BASE_DEC, VALS(ctrl_id_code_vals), 0x0,
"Destination Controller ID", HFILL }
},
{ &hf_btl2cap_result,
@@ -1661,12 +1744,13 @@ proto_register_btl2cap(void)
};
/* Register the protocol name and description */
- proto_btl2cap = proto_register_protocol("Bluetooth L2CAP Packet", "L2CAP", "btl2cap");
+ proto_btl2cap = proto_register_protocol("Bluetooth L2CAP Protocol", "L2CAP", "btl2cap");
register_dissector("btl2cap", dissect_btl2cap, proto_btl2cap);
/* subdissector code */
l2cap_psm_dissector_table = register_dissector_table("btl2cap.psm", "L2CAP PSM", FT_UINT16, BASE_HEX);
+ l2cap_service_dissector_table = register_dissector_table("btl2cap.service", "L2CAP Service", FT_UINT16, BASE_HEX);
l2cap_cid_dissector_table = register_dissector_table("btl2cap.cid", "L2CAP CID", FT_UINT16, BASE_HEX);
/* Required function calls to register the header fields and subtrees used */
@@ -1674,7 +1758,17 @@ proto_register_btl2cap(void)
proto_register_subtree_array(ett, array_length(ett));
cid_to_psm_table=se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "btl2cap scid to psm");
+ psm_to_service_table=se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "btl2cap psm to service uuid");
+}
+
+void
+proto_reg_handoff_btl2cap(void)
+{
+ /* tap into the btsdp dissector to look for l2cap PSM infomation that
+ helps us determine the type of l2cap payload, i.e. which service is
+ using the PSM channel so we know which sub-dissector to call */
+ register_tap_listener("btsdp", NULL, NULL, 0, NULL, btl2cap_sdp_tap_packet, NULL);
}
diff --git a/epan/dissectors/packet-btl2cap.h b/epan/dissectors/packet-btl2cap.h
index 42bef4fe02..53c0ac38a2 100644
--- a/epan/dissectors/packet-btl2cap.h
+++ b/epan/dissectors/packet-btl2cap.h
@@ -24,9 +24,16 @@
#ifndef __PACKET_BTL2CAP_H__
#define __PACKET_BTL2CAP_H__
-#define BTL2CAP_PSM_SDP 0x0001
-#define BTL2CAP_PSM_RFCOMM 0x0003
-#define BTL2CAP_PSM_BNEP 0x000f
+#define BTL2CAP_PSM_SDP 0x0001
+#define BTL2CAP_PSM_RFCOMM 0x0003
+#define BTL2CAP_PSM_BNEP 0x000f
+#define BTL2CAP_PSM_HID_CTRL 0x0011
+#define BTL2CAP_PSM_HID_INTR 0x0013
+#define BTL2CAP_PSM_AVCTP_CTRL 0x0017
+#define BTL2CAP_PSM_AVDTP 0x0019
+#define BTL2CAP_PSM_AVCTP_BRWS 0x001b
+
+#define BTL2CAP_DYNAMIC_PSM_START 0x1000
#define BTL2CAP_FIXED_CID_NULL 0x0000
#define BTL2CAP_FIXED_CID_SIGNAL 0x0001
diff --git a/epan/dissectors/packet-btobex.c b/epan/dissectors/packet-btobex.c
index c5890439c9..2c807916ac 100644
--- a/epan/dissectors/packet-btobex.c
+++ b/epan/dissectors/packet-btobex.c
@@ -1,7 +1,7 @@
/* packet-btobex.c
- * Routines for the Bluetooth OBEX dissection
+ * Routines for Bluetooth OBEX dissection
*
- * Allan M. Madsen 2010
+ * Copyright 2010, Allan M. Madsen
*
* $Id$
*
@@ -36,6 +36,7 @@
#include <epan/emem.h>
#include <epan/reassemble.h>
#include "packet-btl2cap.h"
+#include "packet-btsdp.h"
/* Initialize the protocol and registered fields */
static int proto_btobex = -1;
@@ -55,7 +56,6 @@ static int hf_hdr_val_unicode = -1;
static int hf_hdr_val_byte_seq = -1;
static int hf_hdr_val_byte = -1;
static int hf_hdr_val_long = -1;
-static int hf_data = -1;
/* ************************************************************************* */
/* Header values for reassembly */
@@ -209,7 +209,7 @@ static const value_string header_id_vals[] = {
{ 0x4f, "Object Class" },
{ 0xc0, "Count" },
{ 0xc3, "Length" },
- { 0xC4, "Time" },
+ { 0xc4, "Time" },
{ 0xcb, "Connection Id" },
{ 0x30, "User Defined" },
{ 0x31, "User Defined" },
@@ -230,521 +230,532 @@ static const value_string header_id_vals[] = {
{ 0, NULL }
};
-static void defragment_init(void)
+static void
+defragment_init(void)
{
- fragment_table_init(&fragment_table);
- reassembled_table_init(&reassembled_table);
+ fragment_table_init(&fragment_table);
+ reassembled_table_init(&reassembled_table);
}
-static int is_ascii_str(const guint8 *str, int length)
+static int
+is_ascii_str(const guint8 *str, int length)
{
- int i;
+ int i;
- if( (length < 1) || (str[length-1] != '\0') )
- return 0;
+ if( (length < 1) || (str[length-1] != '\0') )
+ return 0;
- for(i=0; i<length-1; i++)
- {
- if( (str[i] < 0x20) && (str[i] != 0x0a) ) /* not strict ascii */
- break;
- }
+ for(i=0; i<length-1; i++) {
+ if( (str[i] < 0x20) && (str[i] != 0x0a) ) /* not strict ascii */
+ break;
+ }
- if(i<(length-1))
- return 0;
+ if(i<(length-1))
+ return 0;
- return 1;
+ return 1;
}
-static int display_unicode_string(tvbuff_t *tvb, proto_tree *tree, int offset, char **data)
+static int
+display_unicode_string(tvbuff_t *tvb, proto_tree *tree, int offset, char **data)
{
- char *str, *p;
- int len;
- int charoffset;
- guint16 character;
-
- /* display a unicode string from the tree and return new offset */
- /*
- * Get the length of the string.
- */
- len = 0;
- while ((character = tvb_get_ntohs(tvb, offset + len)) != '\0')
- len += 2;
- len += 2; /* count the '\0' too */
-
- /*
- * Allocate a buffer for the string; "len" is the length in
- * bytes, not the length in characters.
- */
- str = ep_alloc(len/2);
-
- /* - this assumes the string is just ISO 8859-1 */
- charoffset = offset;
- p = str;
- while ((character = tvb_get_ntohs(tvb, charoffset)) != '\0') {
- *p++ = (char) character;
- charoffset += 2;
- }
- *p = '\0';
-
- if(!is_ascii_str(str, len/2))
- {
- *str = '\0';
- }
-
- proto_tree_add_string(tree, hf_hdr_val_unicode, tvb, offset, len, str);
-
- if (data)
- *data = str;
-
- return offset+len;
+ char *str, *p;
+ int len;
+ int charoffset;
+ guint16 character;
+
+ /* display a unicode string from the tree and return new offset */
+ /*
+ * Get the length of the string.
+ */
+ len = 0;
+ while ((character = tvb_get_ntohs(tvb, offset + len)) != '\0')
+ len += 2;
+
+ len += 2; /* count the '\0' too */
+
+ /*
+ * Allocate a buffer for the string; "len" is the length in
+ * bytes, not the length in characters.
+ */
+ str = ep_alloc(len/2);
+
+ /* - this assumes the string is just ISO 8859-1 */
+ charoffset = offset;
+ p = str;
+ while ((character = tvb_get_ntohs(tvb, charoffset)) != '\0') {
+ *p++ = (char) character;
+ charoffset += 2;
+ }
+ *p = '\0';
+
+ if(!is_ascii_str(str, len/2)) {
+ *str = '\0';
+ }
+
+ proto_tree_add_string(tree, hf_hdr_val_unicode, tvb, offset, len, str);
+
+ if (data)
+ *data = str;
+
+ return offset+len;
}
static int
dissect_headers(proto_tree *tree, tvbuff_t *tvb, int offset, packet_info *pinfo)
{
- proto_tree *hdrs_tree=NULL;
- proto_item *hdrs=NULL;
- proto_tree *hdr_tree=NULL;
- proto_item *hdr=NULL;
- proto_item *handle_item;
- gint item_length = -1;
- guint8 hdr_id, i;
- const guint8 *ptr;
-
- if(tvb_length_remaining(tvb, offset)>0)
- {
- hdrs = proto_tree_add_text(tree, tvb, offset, item_length, "Headers");
- hdrs_tree=proto_item_add_subtree(hdrs, ett_btobex_hdrs);
- }
- else
- {
- return offset;
- }
-
- while(tvb_length_remaining(tvb, offset)>0)
- {
- hdr_id = tvb_get_guint8(tvb, offset);
-
- switch(0xC0 & hdr_id)
- {
- case 0x00:
- item_length = tvb_get_ntohs(tvb, offset+1);
- break;
- case 0x40: /* byte sequence */
- item_length = tvb_get_ntohs(tvb, offset+1);
- break;
- case 0x80: /* 1 byte */
- item_length = 2;
- break;
- case 0xc0: /* 4 bytes */
- item_length = 5;
- break;
- }
-
- hdr = proto_tree_add_text(hdrs_tree, tvb, offset, item_length, "%s", val_to_str(hdr_id, header_id_vals, "Unknown"));
- hdr_tree=proto_item_add_subtree(hdr, ett_btobex_hdr);
-
- proto_tree_add_item(hdr_tree, hf_hdr_id, tvb, offset, 1, FALSE);
-
- offset++;
-
- switch(0xC0 & hdr_id)
- {
- case 0x00: /* null terminated unicode */
- {
- proto_tree_add_item(hdr_tree, hf_hdr_length, tvb, offset, 2, FALSE);
- offset += 2;
-
- if( (item_length - 3) > 0 )
- {
- char *str;
- display_unicode_string(tvb, hdr_tree, offset, &str);
-
- proto_item_append_text(hdr_tree, " (\"%s\")", str);
-
- col_append_fstr(pinfo->cinfo, COL_INFO, " \"%s\"", str);
- }
- else
- {
- col_append_str(pinfo->cinfo, COL_INFO, " \"\"");
- }
-
- offset += item_length - 3;
- }
- break;
- case 0x40: /* byte sequence */
- proto_tree_add_item(hdr_tree, hf_hdr_length, tvb, offset, 2, FALSE);
- offset += 2;
-
- handle_item = proto_tree_add_item(hdr_tree, hf_hdr_val_byte_seq, tvb, offset, item_length - 3, FALSE);
-
- if( ((hdr_id == 0x46) || (hdr_id == 0x4a)) && (item_length == 19) ) /* target or who */
- {
- ptr = tvb_get_ptr(tvb, offset, item_length - 3);
- for( i=0; target_vals[i].strptr != NULL; i++)
- {
- if( memcmp(ptr, target_vals[i].value, 16) == 0 )
- {
- proto_item_append_text(handle_item, ": %s", target_vals[i].strptr);
-
- proto_item_append_text(hdr_tree, " (%s)", target_vals[i].strptr);
-
- col_append_fstr(pinfo->cinfo, COL_INFO, " - %s", target_vals[i].strptr);
- }
- }
- }
-
- if( !tvb_strneql(tvb, offset, "<?xml", 5) )
- {
- tvbuff_t* next_tvb = tvb_new_subset(tvb, offset, -1, -1);
-
- call_dissector(xml_handle, next_tvb, pinfo, tree);
- }
- else if(is_ascii_str(tvb_get_ptr(tvb, offset,item_length - 3), item_length - 3))
- {
- proto_item_append_text(hdr_tree, " (\"%s\")", tvb_get_ptr(tvb, offset,item_length - 3));
-
- col_append_fstr(pinfo->cinfo, COL_INFO, " \"%s\"", tvb_get_ptr(tvb, offset,item_length - 3));
- }
-
- offset += item_length - 3;
- break;
- case 0x80: /* 1 byte */
- proto_item_append_text(hdr_tree, " (%i)", tvb_get_ntohl(tvb, offset));
- proto_tree_add_item(hdr_tree, hf_hdr_val_byte, tvb, offset, 1, FALSE);
- offset++;
- break;
- case 0xc0: /* 4 bytes */
- proto_item_append_text(hdr_tree, " (%i)", tvb_get_ntohl(tvb, offset));
- proto_tree_add_item(hdr_tree, hf_hdr_val_long, tvb, offset, 4, FALSE);
- offset += 4;
- break;
- default:
- break;
- }
-
- }
-
- return offset;
+ proto_tree *hdrs_tree=NULL;
+ proto_item *hdrs=NULL;
+ proto_tree *hdr_tree=NULL;
+ proto_item *hdr=NULL;
+ proto_item *handle_item;
+ gint item_length = -1;
+ guint8 hdr_id, i;
+ const guint8 *ptr;
+
+ if(tvb_length_remaining(tvb, offset)>0) {
+ hdrs = proto_tree_add_text(tree, tvb, offset, item_length, "Headers");
+ hdrs_tree=proto_item_add_subtree(hdrs, ett_btobex_hdrs);
+ }
+ else {
+ return offset;
+ }
+
+ while(tvb_length_remaining(tvb, offset)>0) {
+ hdr_id = tvb_get_guint8(tvb, offset);
+
+ switch(0xC0 & hdr_id)
+ {
+ case 0x00: /* null terminated unicode */
+ item_length = tvb_get_ntohs(tvb, offset+1);
+ break;
+ case 0x40: /* byte sequence */
+ item_length = tvb_get_ntohs(tvb, offset+1);
+ break;
+ case 0x80: /* 1 byte */
+ item_length = 2;
+ break;
+ case 0xc0: /* 4 bytes */
+ item_length = 5;
+ break;
+ }
+
+ hdr = proto_tree_add_text(hdrs_tree, tvb, offset, item_length, "%s", val_to_str(hdr_id, header_id_vals, "Unknown"));
+ hdr_tree=proto_item_add_subtree(hdr, ett_btobex_hdr);
+
+ proto_tree_add_item(hdr_tree, hf_hdr_id, tvb, offset, 1, FALSE);
+
+ offset++;
+
+ switch(0xC0 & hdr_id)
+ {
+ case 0x00: /* null terminated unicode */
+ {
+ proto_tree_add_item(hdr_tree, hf_hdr_length, tvb, offset, 2, FALSE);
+ offset += 2;
+
+ if( (item_length - 3) > 0 ) {
+ char *str;
+
+ display_unicode_string(tvb, hdr_tree, offset, &str);
+ proto_item_append_text(hdr_tree, " (\"%s\")", str);
+ col_append_fstr(pinfo->cinfo, COL_INFO, " \"%s\"", str);
+ }
+ else {
+ col_append_str(pinfo->cinfo, COL_INFO, " \"\"");
+ }
+
+ offset += item_length - 3;
+ }
+ break;
+ case 0x40: /* byte sequence */
+ proto_tree_add_item(hdr_tree, hf_hdr_length, tvb, offset, 2, FALSE);
+ offset += 2;
+
+ handle_item = proto_tree_add_item(hdr_tree, hf_hdr_val_byte_seq, tvb, offset, item_length - 3, FALSE);
+
+ if( ((hdr_id == 0x46) || (hdr_id == 0x4a)) && (item_length == 19) ) { /* target or who */
+ ptr = tvb_get_ptr(tvb, offset, item_length - 3);
+ for( i=0; target_vals[i].strptr != NULL; i++) {
+ if( memcmp(ptr, target_vals[i].value, 16) == 0 ) {
+ proto_item_append_text(handle_item, ": %s", target_vals[i].strptr);
+ proto_item_append_text(hdr_tree, " (%s)", target_vals[i].strptr);
+ col_append_fstr(pinfo->cinfo, COL_INFO, " - %s", target_vals[i].strptr);
+ }
+ }
+ }
+
+ if( !tvb_strneql(tvb, offset, "<?xml", 5) )
+ {
+ tvbuff_t* next_tvb = tvb_new_subset(tvb, offset, -1, -1);
+
+ call_dissector(xml_handle, next_tvb, pinfo, tree);
+ }
+ else if(is_ascii_str(tvb_get_ptr(tvb, offset,item_length - 3), item_length - 3))
+ {
+ proto_item_append_text(hdr_tree, " (\"%s\")", tvb_get_ptr(tvb, offset,item_length - 3));
+ col_append_fstr(pinfo->cinfo, COL_INFO, " \"%s\"", tvb_get_ptr(tvb, offset,item_length - 3));
+ }
+
+ offset += item_length - 3;
+ break;
+ case 0x80: /* 1 byte */
+ proto_item_append_text(hdr_tree, " (%i)", tvb_get_ntohl(tvb, offset));
+ proto_tree_add_item(hdr_tree, hf_hdr_val_byte, tvb, offset, 1, FALSE);
+ offset++;
+ break;
+ case 0xc0: /* 4 bytes */
+ proto_item_append_text(hdr_tree, " (%i)", tvb_get_ntohl(tvb, offset));
+ proto_tree_add_item(hdr_tree, hf_hdr_val_long, tvb, offset, 4, FALSE);
+ offset += 4;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return offset;
}
static void
dissect_btobex(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
- proto_item *ti;
- proto_tree *st;
- fragment_data *frag_msg = NULL;
- gboolean save_fragmented, complete;
- tvbuff_t* new_tvb = NULL;
- tvbuff_t* next_tvb = NULL;
- guint32 no_of_segments = 0;
- int offset=0;
-
- save_fragmented = pinfo->fragmented;
-
- frag_msg = NULL;
- complete = FALSE;
-
- if( fragment_get(pinfo, pinfo->p2p_dir, fragment_table) )
- {
- /* not the first fragment */
- frag_msg = fragment_add_seq_next(tvb, 0, pinfo, pinfo->p2p_dir,
- fragment_table, reassembled_table, tvb_length(tvb), TRUE);
-
- new_tvb = process_reassembled_data(tvb, 0, pinfo,
- "Reassembled Obex packet", frag_msg, &btobex_frag_items, NULL, tree);
-
- if(frag_msg != NULL) /* last fragment */
- complete = TRUE;
-
- }
- else
- {
- if(tvb_length(tvb) < tvb_get_ntohs(tvb, offset+1))
- {
- /* first fragment in a sequence */
- no_of_segments = tvb_get_ntohs(tvb, offset+1)/tvb_length(tvb);
- if ( tvb_get_ntohs(tvb, offset+1) > (no_of_segments * tvb_length(tvb)))
- no_of_segments++;
-
- frag_msg = fragment_add_seq_next(tvb, 0, pinfo, pinfo->p2p_dir,
- fragment_table, reassembled_table, tvb_length(tvb), TRUE);
-
- fragment_set_tot_len(pinfo, pinfo->p2p_dir, fragment_table, no_of_segments-1);
-
- new_tvb = process_reassembled_data(tvb, 0, pinfo,
- "Reassembled Obex packet", frag_msg, &btobex_frag_items, NULL, tree);
-
- pinfo->fragmented = TRUE;
- }
- else if( tvb_length(tvb) == tvb_get_ntohs(tvb, offset+1) )
- {
- /* non-fragmented */
- complete = TRUE;
- pinfo->fragmented = FALSE;
- }
- }
-
- if (new_tvb) { /* take it all */
- next_tvb = new_tvb;
- complete = TRUE;
- } else { /* make a new subset */
- next_tvb = tvb_new_subset(tvb, offset, -1, -1);
- }
-
- if( complete )
- {
- guint8 code;
-
- /* fully dissectable packet ready */
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "OBEX");
-
- ti = proto_tree_add_item(tree, proto_btobex, next_tvb, 0, -1, FALSE);
- st = proto_item_add_subtree(ti, ett_btobex);
-
- /* op/response code */
- code = tvb_get_guint8(next_tvb, offset) & BTOBEX_CODE_VALS_MASK;
-
- col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s",
- pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd",
- val_to_str(code, code_vals, "Unknown"));
-
- if( (code < BTOBEX_CODE_VALS_CONTINUE) || (code == BTOBEX_CODE_VALS_ABORT))
- {
- proto_tree_add_item(st, hf_opcode, next_tvb, offset, 1, FALSE);
- last_opcode[pinfo->p2p_dir] = code;
- }
- else
- {
- proto_tree_add_item(st, hf_response_code, next_tvb, offset, 1, FALSE);
- }
- proto_tree_add_item(st, hf_final_flag, next_tvb, offset, 1, FALSE);
- offset++;
-
- /* length */
- proto_tree_add_item(st, hf_length, next_tvb, offset, 2, FALSE);
- offset += 2;
-
- switch(code)
- {
- case BTOBEX_CODE_VALS_CONNECT:
- proto_tree_add_item(st, hf_version, next_tvb, offset, 1, FALSE);
- offset++;
-
- proto_tree_add_item(st, hf_flags, next_tvb, offset, 1, FALSE);
- offset++;
-
- proto_tree_add_item(st, hf_max_pkt_len, next_tvb, offset, 2, FALSE);
- offset += 2;
- break;
-
- case BTOBEX_CODE_VALS_PUT:
- case BTOBEX_CODE_VALS_GET:
- col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
- (tvb_get_guint8(next_tvb, offset) & 0x80)==0x80?"final":"continue");
- break;
-
- case BTOBEX_CODE_VALS_SET_PATH:
- proto_tree_add_item(st, hf_flags, next_tvb, offset, 1, FALSE);
- proto_tree_add_item(st, hf_set_path_flags_0, next_tvb, offset, 1, FALSE);
- proto_tree_add_item(st, hf_set_path_flags_1, next_tvb, offset, 1, FALSE);
- offset++;
-
- proto_tree_add_item(st, hf_constants, next_tvb, offset, 1, FALSE);
- offset++;
- break;
-
- case BTOBEX_CODE_VALS_DISCONNECT:
- case BTOBEX_CODE_VALS_ABORT:
- break;
-
- default:
- {
- guint8 response_opcode = last_opcode[(pinfo->p2p_dir + 1) & 0x01];
-
- if(response_opcode == BTOBEX_CODE_VALS_CONNECT)
- {
- proto_tree_add_item(st, hf_version, next_tvb, offset, 1, FALSE);
- offset++;
-
- proto_tree_add_item(st, hf_flags, next_tvb, offset, 1, FALSE);
- offset++;
-
- proto_tree_add_item(st, hf_max_pkt_len, next_tvb, offset, 2, FALSE);
- offset += 2;
- }
- }
- break;
- }
-
- offset = dissect_headers(st, next_tvb, offset, pinfo);
- }
- else
- {
- /* packet fragment */
- col_add_fstr(pinfo->cinfo, COL_INFO, "%s Obex fragment",
- pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd");
-
- call_dissector(data_handle, next_tvb, pinfo, tree);
- }
-
- pinfo->fragmented = save_fragmented;
+ proto_item *ti;
+ proto_tree *st;
+ fragment_data *frag_msg = NULL;
+ gboolean save_fragmented, complete;
+ tvbuff_t* new_tvb = NULL;
+ tvbuff_t* next_tvb = NULL;
+ guint32 no_of_segments = 0;
+ int offset=0;
+
+ save_fragmented = pinfo->fragmented;
+
+ frag_msg = NULL;
+ complete = FALSE;
+
+ if( fragment_get(pinfo, pinfo->p2p_dir, fragment_table) ) {
+ /* not the first fragment */
+ frag_msg = fragment_add_seq_next(tvb, 0, pinfo, pinfo->p2p_dir,
+ fragment_table, reassembled_table, tvb_length(tvb), TRUE);
+
+ new_tvb = process_reassembled_data(tvb, 0, pinfo,
+ "Reassembled Obex packet", frag_msg, &btobex_frag_items, NULL, tree);
+
+ pinfo->fragmented = TRUE;
+ }
+ else {
+ if(tvb_length(tvb) < tvb_get_ntohs(tvb, offset+1)) {
+ /* first fragment in a sequence */
+ no_of_segments = tvb_get_ntohs(tvb, offset+1)/tvb_length(tvb);
+ if ( tvb_get_ntohs(tvb, offset+1) > (no_of_segments * tvb_length(tvb)))
+ no_of_segments++;
+
+ frag_msg = fragment_add_seq_next(tvb, 0, pinfo, pinfo->p2p_dir,
+ fragment_table, reassembled_table, tvb_length(tvb), TRUE);
+
+ fragment_set_tot_len(pinfo, pinfo->p2p_dir, fragment_table, no_of_segments-1);
+
+ new_tvb = process_reassembled_data(tvb, 0, pinfo,
+ "Reassembled Obex packet", frag_msg, &btobex_frag_items, NULL, tree);
+
+ pinfo->fragmented = TRUE;
+ }
+ else if( tvb_length(tvb) == tvb_get_ntohs(tvb, offset+1) ) {
+ /* non-fragmented */
+ complete = TRUE;
+ pinfo->fragmented = FALSE;
+ }
+ }
+
+ if (new_tvb) { /* take it all */
+ next_tvb = new_tvb;
+ complete = TRUE;
+ }
+ else { /* make a new subset */
+ next_tvb = tvb_new_subset(tvb, offset, -1, -1);
+ }
+
+ if( complete ) {
+ guint8 code, final_flag;
+
+ /* fully dissectable packet ready */
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "OBEX");
+
+ ti = proto_tree_add_item(tree, proto_btobex, next_tvb, 0, -1, FALSE);
+ st = proto_item_add_subtree(ti, ett_btobex);
+
+ /* op/response code */
+ code = tvb_get_guint8(next_tvb, offset) & BTOBEX_CODE_VALS_MASK;
+ final_flag = tvb_get_guint8(next_tvb, offset) & 0x80;
+
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s",
+ pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd",
+ val_to_str(code, code_vals, "Unknown"));
+
+ if( (code < BTOBEX_CODE_VALS_CONTINUE) || (code == BTOBEX_CODE_VALS_ABORT)) {
+ proto_tree_add_item(st, hf_opcode, next_tvb, offset, 1, FALSE);
+ last_opcode[pinfo->p2p_dir] = code;
+ }
+ else {
+ proto_tree_add_item(st, hf_response_code, next_tvb, offset, 1, FALSE);
+ }
+ proto_tree_add_item(st, hf_final_flag, next_tvb, offset, 1, FALSE);
+ offset++;
+
+ /* length */
+ proto_tree_add_item(st, hf_length, next_tvb, offset, 2, FALSE);
+ offset += 2;
+
+ switch(code)
+ {
+ case BTOBEX_CODE_VALS_CONNECT:
+ proto_tree_add_item(st, hf_version, next_tvb, offset, 1, FALSE);
+ offset++;
+
+ proto_tree_add_item(st, hf_flags, next_tvb, offset, 1, FALSE);
+ offset++;
+
+ proto_tree_add_item(st, hf_max_pkt_len, next_tvb, offset, 2, FALSE);
+ offset += 2;
+ break;
+
+ case BTOBEX_CODE_VALS_PUT:
+ case BTOBEX_CODE_VALS_GET:
+ col_append_fstr(pinfo->cinfo, COL_INFO, " %s", final_flag==0x80?"final":"continue");
+ break;
+
+ case BTOBEX_CODE_VALS_SET_PATH:
+ proto_tree_add_item(st, hf_flags, next_tvb, offset, 1, FALSE);
+ proto_tree_add_item(st, hf_set_path_flags_0, next_tvb, offset, 1, FALSE);
+ proto_tree_add_item(st, hf_set_path_flags_1, next_tvb, offset, 1, FALSE);
+ offset++;
+
+ proto_tree_add_item(st, hf_constants, next_tvb, offset, 1, FALSE);
+ offset++;
+ break;
+
+ case BTOBEX_CODE_VALS_DISCONNECT:
+ case BTOBEX_CODE_VALS_ABORT:
+ break;
+
+ default:
+ {
+ guint8 response_opcode = last_opcode[(pinfo->p2p_dir + 1) & 0x01];
+
+ if(response_opcode == BTOBEX_CODE_VALS_CONNECT) {
+ proto_tree_add_item(st, hf_version, next_tvb, offset, 1, FALSE);
+ offset++;
+
+ proto_tree_add_item(st, hf_flags, next_tvb, offset, 1, FALSE);
+ offset++;
+
+ proto_tree_add_item(st, hf_max_pkt_len, next_tvb, offset, 2, FALSE);
+ offset += 2;
+ }
+ }
+ break;
+ }
+
+ offset = dissect_headers(st, next_tvb, offset, pinfo);
+ }
+ else
+ {
+ /* packet fragment */
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s Obex fragment",
+ pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd");
+
+ call_dissector(data_handle, next_tvb, pinfo, tree);
+ }
+
+ pinfo->fragmented = save_fragmented;
}
void
proto_register_btobex(void)
{
- static hf_register_info hf[] = {
- {&hf_opcode,
- {"Opcode", "btobex.opcode",
- FT_UINT8, BASE_HEX, VALS(code_vals), BTOBEX_CODE_VALS_MASK,
- "Request Opcode", HFILL}
- },
- {&hf_response_code,
- {"Response Code", "btobex.resp_code",
- FT_UINT8, BASE_HEX, VALS(code_vals), BTOBEX_CODE_VALS_MASK,
- NULL, HFILL}
- },
- {&hf_final_flag,
- {"Final Flag", "btobex.final_flag",
- FT_BOOLEAN, BASE_HEX, TFS(&true_false), 0x80,
- NULL, HFILL}
- },
- {&hf_length,
- {"Packet Length", "btobex.pkt_len",
- FT_UINT16, BASE_DEC, NULL, 0,
- NULL, HFILL}
- },
- {&hf_version,
- {"Version", "btobex.version",
- FT_UINT8, BASE_HEX, VALS(version_vals), 0x00,
- "Obex Version", HFILL}
- },
- {&hf_flags,
- {"Flags", "btobex.flags",
- FT_UINT8, BASE_HEX, NULL, 0x00,
- NULL, HFILL}
- },
- {&hf_constants,
- {"Constants", "btobex.constants",
- FT_UINT8, BASE_HEX, NULL, 0x00,
- NULL, HFILL}
- },
- {&hf_max_pkt_len,
- {"Max. Packet Length", "btobex.max_pkt_len",
- FT_UINT16, BASE_DEC, NULL, 0,
- "Maximum Packet Length", HFILL}
- },
- {&hf_set_path_flags_0,
- {"Go back one folder (../) first", "btobex.set_path_flags_0",
- FT_BOOLEAN, 8, TFS(&true_false), 0x01,
- NULL, HFILL}
- },
- {&hf_set_path_flags_1,
- {"Do not create folder, if not existing", "btobex.set_path_flags_1",
- FT_BOOLEAN, 8, TFS(&true_false), 0x02,
- NULL, HFILL}
- },
- {&hf_hdr_id,
- {"Header Id", "btobex.hdr_id",
- FT_UINT8, BASE_HEX, VALS(header_id_vals), 0x00,
- NULL, HFILL}
- },
- {&hf_hdr_length,
- {"Length", "btobex.pkt_hdr_len",
- FT_UINT16, BASE_DEC, NULL, 0,
- "Header Length", HFILL}
- },
- {&hf_hdr_val_unicode,
- { "Value", "btobex.pkt_hdr_val_uc",
- FT_STRING, BASE_NONE, NULL, 0,
- "Unicode Value", HFILL }
- },
- {&hf_hdr_val_byte_seq,
- {"Value", "btobex.hdr_val_byte_seq",
- FT_BYTES, BASE_NONE, NULL, 0,
- "Byte Value", HFILL}
- },
- {&hf_hdr_val_byte,
- {"Value", "btobex.hdr_val_byte",
- FT_UINT8, BASE_HEX, NULL, 0,
- "Byte Sequence Value", HFILL}
- },
- {&hf_hdr_val_long,
- {"Value", "btobex.hdr_val_long",
- FT_UINT32, BASE_DEC, NULL, 0,
- "4-byte Value", HFILL}
- },
- {&hf_data,
- {"Obex Data", "btobex.data",
- FT_NONE, BASE_NONE, NULL, 0,
- NULL, HFILL}
- },
-
- /* for fragmentation */
- { &hf_btobex_fragment_overlap,
- { "Fragment overlap", "btobex.fragment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
- "Fragment overlaps with other fragments", HFILL }},
-
- { &hf_btobex_fragment_overlap_conflict,
- { "Conflicting data in fragment overlap", "btobex.fragment.overlap.conflict", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
- "Overlapping fragments contained conflicting data", HFILL }},
-
- { &hf_btobex_fragment_multiple_tails,
- { "Multiple tail fragments found", "btobex.fragment.multipletails", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
- "Several tails were found when defragmenting the packet", HFILL }},
-
- { &hf_btobex_fragment_too_long_fragment,
- { "Fragment too long", "btobex.fragment.toolongfragment", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
- "Fragment contained data past end of packet", HFILL }},
-
- { &hf_btobex_fragment_error,
- { "Defragmentation error", "btobex.fragment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
- "Defragmentation error due to illegal fragments", HFILL }},
-
- { &hf_btobex_fragment,
- { "OBEX Fragment", "btobex.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
- "btobex Fragment", HFILL }},
-
- { &hf_btobex_fragments,
- { "OBEX Fragments", "btobex.fragments", FT_NONE, BASE_NONE, NULL, 0x0,
- "btobex Fragments", HFILL }},
-
- { &hf_btobex_reassembled_in,
- { "Reassembled OBEX in frame", "btobex.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
- "This OBEX frame is reassembled in this frame", HFILL }},
-
- { &hf_btobex_reassembled_length,
- { "Reassembled OBEX length", "btobex.reassembled.length", FT_UINT32, BASE_DEC, NULL, 0x0,
- "The total length of the reassembled payload", HFILL }}
- };
-
- /* Setup protocol subtree array */
- static gint *ett[] = {
- &ett_btobex,
- &ett_btobex_hdrs,
- &ett_btobex_hdr,
- &ett_btobex_fragment,
- &ett_btobex_fragments
- };
-
- proto_btobex = proto_register_protocol("Bluetooth OBEX", "OBEX", "btobex");
-
- register_dissector("btobex", dissect_btobex, proto_btobex);
-
- /* Required function calls to register the header fields and subtrees used */
- proto_register_field_array(proto_btobex, hf, array_length(hf));
- proto_register_subtree_array(ett, array_length(ett));
-
- register_init_routine(&defragment_init);
+ static hf_register_info hf[] = {
+ {&hf_opcode,
+ {"Opcode", "btobex.opcode",
+ FT_UINT8, BASE_HEX, VALS(code_vals), BTOBEX_CODE_VALS_MASK,
+ "Request Opcode", HFILL}
+ },
+ {&hf_response_code,
+ {"Response Code", "btobex.resp_code",
+ FT_UINT8, BASE_HEX, VALS(code_vals), BTOBEX_CODE_VALS_MASK,
+ NULL, HFILL}
+ },
+ {&hf_final_flag,
+ {"Final Flag", "btobex.final_flag",
+ FT_BOOLEAN, BASE_HEX, TFS(&true_false), 0x80,
+ NULL, HFILL}
+ },
+ {&hf_length,
+ {"Packet Length", "btobex.pkt_len",
+ FT_UINT16, BASE_DEC, NULL, 0,
+ NULL, HFILL}
+ },
+ {&hf_version,
+ {"Version", "btobex.version",
+ FT_UINT8, BASE_HEX, VALS(version_vals), 0x00,
+ "Obex Protocol Version", HFILL}
+ },
+ {&hf_flags,
+ {"Flags", "btobex.flags",
+ FT_UINT8, BASE_HEX, NULL, 0x00,
+ NULL, HFILL}
+ },
+ {&hf_constants,
+ {"Constants", "btobex.constants",
+ FT_UINT8, BASE_HEX, NULL, 0x00,
+ NULL, HFILL}
+ },
+ {&hf_max_pkt_len,
+ {"Max. Packet Length", "btobex.max_pkt_len",
+ FT_UINT16, BASE_DEC, NULL, 0,
+ NULL, HFILL}
+ },
+ {&hf_set_path_flags_0,
+ {"Go back one folder (../) first", "btobex.set_path_flags_0",
+ FT_BOOLEAN, 8, TFS(&true_false), 0x01,
+ NULL, HFILL}
+ },
+ {&hf_set_path_flags_1,
+ {"Do not create folder, if not existing", "btobex.set_path_flags_1",
+ FT_BOOLEAN, 8, TFS(&true_false), 0x02,
+ NULL, HFILL}
+ },
+ {&hf_hdr_id,
+ {"Header Id", "btobex.hdr_id",
+ FT_UINT8, BASE_HEX, VALS(header_id_vals), 0x00,
+ NULL, HFILL}
+ },
+ {&hf_hdr_length,
+ {"Length", "btobex.pkt_hdr_len",
+ FT_UINT16, BASE_DEC, NULL, 0,
+ "Header Length", HFILL}
+ },
+ {&hf_hdr_val_unicode,
+ { "Value", "btobex.pkt_hdr_val_uc",
+ FT_STRING, BASE_NONE, NULL, 0,
+ "Unicode Value", HFILL }
+ },
+ {&hf_hdr_val_byte_seq,
+ {"Value", "btobex.hdr_val_byte_seq",
+ FT_BYTES, BASE_NONE, NULL, 0,
+ "Byte Value", HFILL}
+ },
+ {&hf_hdr_val_byte,
+ {"Value", "btobex.hdr_val_byte",
+ FT_UINT8, BASE_HEX, NULL, 0,
+ "Byte Sequence Value", HFILL}
+ },
+ {&hf_hdr_val_long,
+ {"Value", "btobex.hdr_val_long",
+ FT_UINT32, BASE_DEC, NULL, 0,
+ "4-byte Value", HFILL}
+ },
+
+ /* for fragmentation */
+ { &hf_btobex_fragment_overlap,
+ { "Fragment overlap", "btobex.fragment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Fragment overlaps with other fragments", HFILL }
+ },
+ { &hf_btobex_fragment_overlap_conflict,
+ { "Conflicting data in fragment overlap", "btobex.fragment.overlap.conflict", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Overlapping fragments contained conflicting data", HFILL }
+ },
+ { &hf_btobex_fragment_multiple_tails,
+ { "Multiple tail fragments found", "btobex.fragment.multipletails", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Several tails were found when defragmenting the packet", HFILL }
+ },
+ { &hf_btobex_fragment_too_long_fragment,
+ { "Fragment too long", "btobex.fragment.toolongfragment", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Fragment contained data past end of packet", HFILL }
+ },
+ { &hf_btobex_fragment_error,
+ { "Defragmentation error", "btobex.fragment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "Defragmentation error due to illegal fragments", HFILL }
+ },
+ { &hf_btobex_fragment,
+ { "OBEX Fragment", "btobex.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "btobex Fragment", HFILL }
+ },
+ { &hf_btobex_fragments,
+ { "OBEX Fragments", "btobex.fragments", FT_NONE, BASE_NONE, NULL, 0x0,
+ "btobex Fragments", HFILL }
+ },
+ { &hf_btobex_reassembled_in,
+ { "Reassembled OBEX in frame", "btobex.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "This OBEX frame is reassembled in this frame", HFILL }
+ },
+ { &hf_btobex_reassembled_length,
+ { "Reassembled OBEX length", "btobex.reassembled.length", FT_UINT32, BASE_DEC, NULL, 0x0,
+ "The total length of the reassembled payload", HFILL }
+ }
+ };
+
+ /* Setup protocol subtree array */
+ static gint *ett[] = {
+ &ett_btobex,
+ &ett_btobex_hdrs,
+ &ett_btobex_hdr,
+ &ett_btobex_fragment,
+ &ett_btobex_fragments
+ };
+
+ proto_btobex = proto_register_protocol("Bluetooth OBEX Protocol", "OBEX", "btobex");
+
+ register_dissector("btobex", dissect_btobex, proto_btobex);
+
+ /* Required function calls to register the header fields and subtrees used */
+ proto_register_field_array(proto_btobex, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ register_init_routine(&defragment_init);
}
void
proto_reg_handoff_btobex(void)
{
- xml_handle = find_dissector("xml");
- data_handle = find_dissector("data");
+ dissector_handle_t btobex_handle;
+
+ btobex_handle = find_dissector("btobex");
+
+ /* register in rfcomm and l2cap the profiles/services this dissector should handle */
+ dissector_add_uint("btrfcomm.service", BTSDP_OPP_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btrfcomm.service", BTSDP_FTP_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btrfcomm.service", BTSDP_BPP_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btrfcomm.service", BTSDP_BPP_STATUS_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btrfcomm.service", BTSDP_BIP_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btrfcomm.service", BTSDP_BIP_RESPONDER_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btrfcomm.service", BTSDP_BIP_AUTO_ARCH_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btrfcomm.service", BTSDP_BIP_REF_OBJ_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btrfcomm.service", BTSDP_PBAP_PCE_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btrfcomm.service", BTSDP_PBAP_PSE_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btrfcomm.service", BTSDP_PBAP_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btrfcomm.service", BTSDP_MAP_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btrfcomm.service", BTSDP_MAP_ACCESS_SRV_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btrfcomm.service", BTSDP_MAP_NOIYFY_SRV_SERVICE_UUID, btobex_handle);
+
+ dissector_add_uint("btl2cap.service", BTSDP_OPP_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btl2cap.service", BTSDP_FTP_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btl2cap.service", BTSDP_BPP_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btl2cap.service", BTSDP_BPP_STATUS_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btl2cap.service", BTSDP_BIP_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btl2cap.service", BTSDP_BIP_RESPONDER_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btl2cap.service", BTSDP_BIP_AUTO_ARCH_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btl2cap.service", BTSDP_BIP_REF_OBJ_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btl2cap.service", BTSDP_PBAP_PCE_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btl2cap.service", BTSDP_PBAP_PSE_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btl2cap.service", BTSDP_PBAP_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btl2cap.service", BTSDP_MAP_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btl2cap.service", BTSDP_MAP_ACCESS_SRV_SERVICE_UUID, btobex_handle);
+ dissector_add_uint("btl2cap.service", BTSDP_MAP_NOIYFY_SRV_SERVICE_UUID, btobex_handle);
+
+ xml_handle = find_dissector("xml");
+ data_handle = find_dissector("data");
}
diff --git a/epan/dissectors/packet-btrfcomm.c b/epan/dissectors/packet-btrfcomm.c
index 0134abb144..1c03cd3251 100644
--- a/epan/dissectors/packet-btrfcomm.c
+++ b/epan/dissectors/packet-btrfcomm.c
@@ -1,5 +1,10 @@
/* packet-btrfcomm.c
* Routines for Bluetooth RFCOMM protocol dissection
+ * and RFCOMM based profile dissection:
+ * - Handsfree Profile (HFP)
+ * - Dial-Up Networking (DUN) Profile
+ * - Serial Port Profile (SPP)
+ *
* Copyright 2002, Wolfgang Hansmann <hansmann@cs.uni-bonn.de>
*
* Refactored for wireshark checkin
@@ -38,6 +43,8 @@
#include <etypes.h>
#include <epan/emem.h>
#include <epan/expert.h>
+#include <epan/tap.h>
+#include "packet-btsdp.h"
#include "packet-btl2cap.h"
#include "packet-btrfcomm.h"
@@ -70,9 +77,15 @@ static int hf_msc_l = -1;
static int hf_fcs = -1;
+static int hf_at_cmd = -1;
+static int hf_dun_at_cmd = -1;
+static int hf_data = -1;
+
/* Initialize the protocol and registered fields */
static int proto_btrfcomm = -1;
-
+static int proto_bthf = -1;
+static int proto_btdun = -1;
+static int proto_btspp = -1;
/* Initialize the subtree pointers */
static gint ett_btrfcomm = -1;
@@ -83,8 +96,15 @@ static gint ett_mcc = -1;
static gint ett_ctrl_pn_ci = -1;
static gint ett_ctrl_pn_v24 = -1;
+static gint ett_bthf = -1;
+static gint ett_btdun = -1;
+static gint ett_btspp = -1;
+
static emem_tree_t *dlci_table;
+/* Initialize dissector table */
+dissector_table_t rfcomm_service_dissector_table;
+
typedef struct _dlci_stream_t {
int len;
int current;
@@ -94,11 +114,12 @@ typedef struct _dlci_stream_t {
} dlci_stream_t;
typedef struct _dlci_state_t {
+ guint32 service;
char do_credit_fc;
- dlci_stream_t direction[2];
} dlci_state_t;
-static dissector_handle_t btobex_handle;
+static dissector_handle_t data_handle;
+static dissector_handle_t ppp_handle;
static const value_string vs_ctl_pn_i[] = {
{0x0, "use UIH Frames"},
@@ -266,12 +287,17 @@ dissect_ctrl_pn(packet_info *pinfo, proto_tree *t, tvbuff_t *tvb, int offset, in
offset++;
if(!pinfo->fd->flags.visited){
- dlci_state=se_tree_lookup32(dlci_table, dlci);
+ guint32 token;
+
+ if( pinfo->p2p_dir == cr_flag )
+ token = dlci | 0x01; /* local service */
+ else
+ token = dlci;
+
+ dlci_state=se_tree_lookup32(dlci_table, token);
if(!dlci_state){
dlci_state=se_alloc0(sizeof(dlci_state_t));
- dlci_state->direction[0].current=-1;
- dlci_state->direction[1].current=-1;
- se_tree_insert32(dlci_table, dlci, dlci_state);
+ se_tree_insert32(dlci_table, token, dlci_state);
}
if(!cl){
@@ -480,7 +506,7 @@ dissect_btrfcomm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
guint8 dlci, cr_flag, ea_flag;
guint8 frame_type, pf_flag;
guint16 frame_len;
- dlci_state_t *dlci_state;
+ dlci_state_t *dlci_state = NULL;
ti = proto_tree_add_item(tree, proto_btrfcomm, tvb, offset, -1, TRUE);
rfcomm_tree = proto_item_add_subtree(ti, ett_btrfcomm);
@@ -493,29 +519,32 @@ dissect_btrfcomm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
/* flags and dlci */
offset=dissect_btrfcomm_Address(tvb, offset, rfcomm_tree, &ea_flag, &cr_flag, &dlci);
-
-
- dlci_state=se_tree_lookup32(dlci_table, dlci);
- if(!dlci_state){
- dlci_state=se_alloc0(sizeof(dlci_state_t));
- dlci_state->direction[0].current=-1;
- dlci_state->direction[1].current=-1;
- se_tree_insert32(dlci_table, dlci, dlci_state);
- }
-
/* pf and frame type */
offset=dissect_btrfcomm_Control(tvb, offset, rfcomm_tree, &pf_flag, &frame_type);
-
-
+ /* payload length */
+ offset=dissect_btrfcomm_PayloadLen(tvb, offset, rfcomm_tree, &frame_len);
+
+ if (dlci && (frame_len || (frame_type == 0xef) || (frame_type == 0x2f) )) {
+ guint32 token;
+
+ if( pinfo->p2p_dir == cr_flag )
+ token = dlci | 0x01; /* local service */
+ else
+ token = dlci;
+
+ dlci_state=se_tree_lookup32(dlci_table, token);
+ if(!dlci_state){
+ dlci_state=se_alloc0(sizeof(dlci_state_t));
+ se_tree_insert32(dlci_table, token, dlci_state);
+ }
+ }
+
if ((check_col(pinfo->cinfo, COL_INFO))){
col_append_fstr(pinfo->cinfo, COL_INFO, "%s DLCI=%d ", val_to_str(frame_type, vs_frame_type_short, "Unknown"), dlci);
+ if(dlci && (frame_type == 0x2f))
+ col_append_fstr(pinfo->cinfo, COL_INFO, "(%s) ", val_to_str(dlci_state->service, vs_service_classes, "Unknown"));
}
-
- /* payload length */
- offset=dissect_btrfcomm_PayloadLen(tvb, offset, rfcomm_tree, &frame_len);
-
-
/* UID frame */
if(frame_type==0xef && dlci && pf_flag) {
col_append_str(pinfo->cinfo, COL_INFO, "UID ");
@@ -570,8 +599,11 @@ dissect_btrfcomm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
proto_item_set_len(mcc_ti, offset-start_offset);
}
- /* dissect everything as OBEX for now */
- if(dlci && frame_len && btobex_handle){
+
+ /* try to find a higher layer dissector that has registered to handle data
+ * for this kind of service, if none is found dissect it as raw "data"
+ */
+ if(dlci&&frame_len){
tvbuff_t *next_tvb;
btl2cap_data_t *l2cap_data;
btrfcomm_data_t rfcomm_data;
@@ -583,7 +615,12 @@ dissect_btrfcomm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
rfcomm_data.chandle = l2cap_data->chandle;
rfcomm_data.cid = l2cap_data->cid;
rfcomm_data.dlci = dlci;
- call_dissector(btobex_handle, next_tvb, pinfo, tree);
+
+ if(!dissector_try_uint(rfcomm_service_dissector_table, dlci_state->service,
+ next_tvb, pinfo, tree)){
+ /* unknown service, let the data dissector handle it */
+ call_dissector(data_handle, next_tvb, pinfo, tree);
+ }
}
proto_tree_add_item(rfcomm_tree, hf_fcs, tvb, fcs_offset, 1, TRUE);
@@ -730,7 +767,7 @@ proto_register_btrfcomm(void)
};
/* Register the protocol name and description */
- proto_btrfcomm = proto_register_protocol("Bluetooth RFCOMM Packet", "RFCOMM", "btrfcomm");
+ proto_btrfcomm = proto_register_protocol("Bluetooth RFCOMM Protocol", "RFCOMM", "btrfcomm");
register_dissector("btrfcomm", dissect_btrfcomm, proto_btrfcomm);
@@ -738,18 +775,241 @@ proto_register_btrfcomm(void)
proto_register_field_array(proto_btrfcomm, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
+ rfcomm_service_dissector_table = register_dissector_table("btrfcomm.service", "RFCOMM SERVICE", FT_UINT16, BASE_HEX);
+
dlci_table=se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "RFCOMM dlci table");
}
+static int
+btrfcomm_sdp_tap_packet(void *arg _U_, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *arg2)
+{
+ btsdp_data_t *sdp_data = (btsdp_data_t *) arg2;
+
+ if( sdp_data->protocol == BTSDP_RFCOMM_PROTOCOL_UUID ) {
+ guint32 token;
+ dlci_state_t *dlci_state;
+
+ /* rfcomm channel * 2 = dlci */
+ token = (sdp_data->channel<<1) | (sdp_data->flags & BTSDP_LOCAL_SERVICE_FLAG_MASK);
+
+ dlci_state=se_tree_lookup32(dlci_table, token);
+ if(!dlci_state){
+ dlci_state=se_alloc0(sizeof(dlci_state_t));
+ se_tree_insert32(dlci_table, token, dlci_state);
+ }
+ dlci_state->service = sdp_data->service;
+ }
+ return 0;
+}
void
proto_reg_handoff_btrfcomm(void)
{
dissector_handle_t btrfcomm_handle;
- btobex_handle = find_dissector("btobex");
-
- btrfcomm_handle = find_dissector("btrfcomm");
+ btrfcomm_handle = find_dissector("btrfcomm");
dissector_add_uint("btl2cap.psm", BTL2CAP_PSM_RFCOMM, btrfcomm_handle);
+
+ data_handle = find_dissector("data");
+
+ /* tap into the btsdp dissector to look for rfcomm channel infomation that
+ helps us determine the type of rfcomm payload, i.e. which service is
+ using the channels so we know which sub-dissector to call */
+ register_tap_listener("btsdp", NULL, NULL, 0, NULL, btrfcomm_sdp_tap_packet, NULL);
}
+/* Bluetooth Handsfree (HF) profile dissection */
+static void
+dissect_bthf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_item *ti;
+ proto_tree *st;
+
+ guint length = tvb_length(tvb);
+
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "HANDSFREE");
+
+ ti = proto_tree_add_item(tree, proto_bthf, tvb, 0, -1, FALSE);
+ st = proto_item_add_subtree(ti, ett_bthf);
+
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s \"%s\"",
+ pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd", tvb_format_text(tvb, 0, length));
+
+ proto_tree_add_item(st, hf_at_cmd, tvb, 0, -1, TRUE);
+}
+
+void
+proto_register_bthf(void)
+{
+ static hf_register_info hf[] = {
+ {&hf_at_cmd,
+ {"AT Cmd", "bthf.atcmd",
+ FT_STRING, BASE_NONE, NULL, 0,
+ "AT Command", HFILL}
+ },
+ };
+
+ /* Setup protocol subtree array */
+ static gint *ett[] = {
+ &ett_bthf,
+ };
+
+ proto_bthf = proto_register_protocol("Bluetooth Handsfree Packet", "BTHF", "bthf");
+
+ /* Required function calls to register the header fields and subtrees used */
+ proto_register_field_array(proto_bthf, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_bthf(void)
+{
+ dissector_handle_t bthf_handle;
+
+ bthf_handle = create_dissector_handle(dissect_bthf, proto_bthf);
+
+ dissector_add_uint("btrfcomm.service", BTSDP_HFP_SERVICE_UUID, bthf_handle);
+ dissector_add_uint("btrfcomm.service", BTSDP_HFP_GW_SERVICE_UUID, bthf_handle);
+}
+
+/* Bluetooth Dial-Up Networking (DUN) profile dissection */
+static void
+dissect_btdun(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_item *ti;
+ proto_tree *st;
+ gboolean is_at_cmd;
+ guint i, length;
+
+ length = tvb_length(tvb);
+
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "DUN");
+
+ ti = proto_tree_add_item(tree, proto_btdun, tvb, 0, -1, FALSE);
+ st = proto_item_add_subtree(ti, ett_btdun);
+
+ is_at_cmd = TRUE;
+ for(i=0;i<length && is_at_cmd;i++) {
+ is_at_cmd = tvb_get_guint8(tvb, i) < 0x7d;
+ }
+
+ if( is_at_cmd) {
+ /* presumably an AT command */
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s \"%s\"",
+ pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd", tvb_format_text(tvb, 0, length));
+
+ proto_tree_add_item(st, hf_dun_at_cmd, tvb, 0, -1, TRUE);
+ }
+ else {
+ /* ... or raw PPP */
+ if( ppp_handle )
+ call_dissector(ppp_handle, tvb, pinfo, tree);
+ else {
+ /* TODO: remove the above 'if' and this 'else-body' when "ppp_raw_hdlc" is available, requires that it is
+ made non-anonymous in ppp dissector to use */
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "PPP");
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s <PPP frame>", pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd");
+
+ call_dissector(data_handle, tvb, pinfo, tree);
+ }
+ }
+}
+
+void
+proto_register_btdun(void)
+{
+ static hf_register_info hf[] = {
+ {&hf_dun_at_cmd,
+ {"AT Cmd", "btdun.atcmd",
+ FT_STRING, BASE_NONE, NULL, 0,
+ "AT Command", HFILL}
+ },
+ };
+
+ /* Setup protocol subtree array */
+ static gint *ett[] = {
+ &ett_btdun,
+ };
+
+ proto_btdun = proto_register_protocol("Bluetooth DUN Packet", "BTDUN", "btdun");
+
+ /* Required function calls to register the header fields and subtrees used */
+ proto_register_field_array(proto_bthf, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_btdun(void)
+{
+ dissector_handle_t btdun_handle;
+
+ btdun_handle = create_dissector_handle(dissect_btdun, proto_btdun);
+
+ dissector_add_uint("btrfcomm.service", BTSDP_DUN_SERVICE_UUID, btdun_handle);
+
+ ppp_handle = find_dissector("ppp_raw_hdlc");
+}
+
+/* Bluetooth Serial Port profile (SPP) dissection */
+static void
+dissect_btspp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_item *ti;
+ proto_tree *st;
+ gboolean ascii_only;
+ guint i, length = tvb_length(tvb);
+
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "SPP");
+
+ ti = proto_tree_add_item(tree, proto_btspp, tvb, 0, -1, FALSE);
+ st = proto_item_add_subtree(ti, ett_btspp);
+
+ length = MIN(length,60);
+ ascii_only = TRUE;
+ for(i=0;i<length && ascii_only;i++) {
+ ascii_only = tvb_get_guint8(tvb, i) < 0x80;
+ }
+
+ if(ascii_only) {
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s \"%s%s\"",
+ pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd",
+ tvb_format_text(tvb, 0, length),
+ tvb_length(tvb) > length ? "...":"");
+ }
+
+ proto_tree_add_item(st, hf_data, tvb, 0, -1, TRUE);
+}
+
+void
+proto_register_btspp(void)
+{
+ static hf_register_info hf[] = {
+ {&hf_data,
+ {"Data", "btspp.data",
+ FT_BYTES, BASE_NONE, NULL, 0,
+ NULL, HFILL}},
+ };
+
+ /* Setup protocol subtree array */
+ static gint *ett[] = {
+ &ett_btspp,
+ };
+
+ proto_btspp = proto_register_protocol("Bluetooth SPP Packet", "BTSPP", "btspp");
+
+ /* Required function calls to register the header fields and subtrees used */
+ proto_register_field_array(proto_bthf, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_btspp(void)
+{
+ dissector_handle_t btspp_handle;
+
+ btspp_handle = create_dissector_handle(dissect_btspp, proto_btspp);
+
+ dissector_add_uint("btrfcomm.service", BTSDP_SPP_SERVICE_UUID, btspp_handle);
+}
+
+
diff --git a/epan/dissectors/packet-btsdp.c b/epan/dissectors/packet-btsdp.c
index 59454a548b..2168dd67bc 100644
--- a/epan/dissectors/packet-btsdp.c
+++ b/epan/dissectors/packet-btsdp.c
@@ -37,7 +37,9 @@
#include <epan/value_string.h>
#include <epan/emem.h>
#include <etypes.h>
-#include "packet-btl2cap.h"
+#include <epan/tap.h>
+#include <epan/dissectors/packet-btsdp.h>
+#include <epan/dissectors/packet-btl2cap.h>
/* Initialize the protocol and registered fields */
static int proto_btsdp = -1;
@@ -58,135 +60,71 @@ static gint ett_btsdp_attribute = -1;
static gint ett_btsdp_service_search_pattern = -1;
static gint ett_btsdp_attribute_idlist = -1;
+static int btsdp_tap = -1;
+
+/* This table maps rfcomm service channels and dynamic l2cap
+ * PSM's to profile service uuids.
+ * The same table is used both for local and remote servers.
+ * For received CIDs we mask the cid with 0x8000 in this table
+ */
+static emem_tree_t *service_table = NULL;
+
static const value_string vs_pduid[] = {
- {0x1, "SDP_ErrorResponse"},
- {0x2, "SDP_ServiceSearchRequest"},
- {0x3, "SDP_ServiceSearchResponse"},
- {0x4, "SDP_ServiceAttributeRequest"},
- {0x5, "SDP_ServiceAttributeResponse"},
- {0x6, "SDP_ServiceSearchAttributeRequest"},
- {0x7, "SDP_ServiceSearchAttributeResponse"},
+ {0x1, "Error Response"},
+ {0x2, "Service Search Request"},
+ {0x3, "Service Search Response"},
+ {0x4, "Service Attribute Request"},
+ {0x5, "Service Attribute Response"},
+ {0x6, "Service Search Attribute Request"},
+ {0x7, "Service Search Attribute Response"},
{0, NULL}
};
static const value_string vs_general_attribute_id[] = {
- {0x0000, "ServiceRecordHandle"},
- {0x0001, "ServiceClassIDList"},
- {0x0002, "ServiceRecordState"},
- {0x0003, "ServiceID"},
- {0x0004, "ProtocolDescriptorList"},
- {0x0005, "BrowseGroupList"},
- {0x0006, "LanguageBaseAttributeIDList"},
- {0x0007, "ServiceinfoTimeToLive"},
- {0x0008, "ServiceAvailability"},
- {0x0009, "BluetoothProfileDescriptorList"},
- {0x000a, "DocumentationURL"},
- {0x000b, "ClientExecutableURL"},
- {0x000c, "IconURL"},
+ {0x0000, "Service Record Handle"},
+ {0x0001, "Service Class ID List"},
+ {0x0002, "Service Record State"},
+ {0x0003, "Service ID"},
+ {0x0004, "Protocol Descriptor List"},
+ {0x0005, "Browse Group List"},
+ {0x0006, "Language Base Attribute ID List"},
+ {0x0007, "Serviceinfo Time To Live"},
+ {0x0008, "Service Availability"},
+ {0x0009, "Bluetooth Profile Descriptor List"},
+ {0x000a, "Documentation URL"},
+ {0x000b, "Client Executable URL"},
+ {0x000c, "Icon URL"},
+ {0x000d, "Additional Protocol Descriptor Lists"},
{0x0100, "Service Name"},
{0x0101, "Service Description"},
- {0x0102, "Service Provider"},
+ {0x0102, "Provider Name"},
+ {0x0200, "GOEP L2CAP PSM/Group Id/IP Subnet"},
+ {0x0201, "Service Database State"},
+ {0x0300, "Service Version"},
+ {0x0301, "Data Exchange Spec/Network/Supported Data Stores List"},
+ {0x0302, "Remote Audio Volume Control/MCAP Supported Features"},
+ {0x0303, "Supported Formats"},
+ {0x0304, "Fax Class 2 Support"},
+ {0x0305, "Audio Feedback Support"},
+ {0x0306, "Network Address"},
+ {0x0307, "WAP Gateway"},
+ {0x0308, "Home Page URL"},
+ {0x0309, "WAP Stack Type"},
+ {0x030a, "Security Description"},
+ {0x030b, "Net Access Type"},
+ {0x030c, "Max Net Accessrate"},
+ {0x030d, "IPv4 Subnet"},
+ {0x030e, "IPv6 Subnet"},
+ {0x0310, "Supported Capabilities"},
+ {0x0311, "Supported Features"},
+ {0x0312, "Supported Functions"},
+ {0x0313, "Total Imaging Data Capacity"},
+ {0x0314, "Supported Repositories"},
+ {0x0315, "MAS Instance ID"},
+ {0x0316, "Supported Message Types"},
{0, NULL}
};
-static const value_string vs_service_classes[] = {
-
- {0x0001, "SDP"},
- {0x0002, "UDP"},
- {0x0003, "RFCOMM"},
- {0x0004, "TCP"},
- {0x0005, "TCS-BIN"},
- {0x0006, "TCS-AT"},
- {0x0008, "OBEX"},
- {0x0009, "IP"},
- {0x000A, "FTP"},
- {0x000C, "HTTP"},
- {0x000E, "WSP"},
- {0x000F, "BNEP"},
- {0x0010, "UPNP"},
- {0x0011, "HIDP"},
- {0x0012, "HardcopyControlChannel"},
- {0x0014, "HardcopyDataChannel"},
- {0x0016, "HardcopyNotification"},
- {0x0017, "AVCTP"},
- {0x0019, "AVDTP"},
- {0x001B, "CMPT"},
- {0x001D, "UDI_C-Plane"},
- {0x001E, "MCAPControlChannel"},
- {0x001F, "MCAPDataChannel"},
- {0x0100, "L2CAP"},
- {0x1000, "ServiceDiscoveryServerServiceClassID"},
- {0x1001, "BrowseGroupDescriptorServiceClassID"},
- {0x1002, "PublicBrowseGroup"},
- {0x1101, "SerialPort"},
- {0x1102, "LANAccessUsingPPP"},
- {0x1103, "DialupNetworking"},
- {0x1104, "IrMCSync"},
- {0x1105, "OBEXObjectPush"},
- {0x1106, "OBEXFileTransfer"},
- {0x1107, "IrMCSyncCommand"},
- {0x1108, "Headset"},
- {0x1109, "CordlessTelephony"},
- {0x110A, "AudioSource"},
- {0x110B, "AudioSink"},
- {0x110C, "A/V_RemoteControlTarget"},
- {0x110D, "AdvancedAudioDistribution"},
- {0x110E, "A/V_RemoteControl"},
- {0x110F, "VideoConferencing"},
- {0x1110, "Intercom"},
- {0x1111, "Fax"},
- {0x1112, "HeadsetAudioGateway"},
- {0x1113, "WAP"},
- {0x1114, "WAP_CLIENT"},
- {0x1115, "PANU"},
- {0x1116, "NAP"},
- {0x1117, "GN"},
- {0x1118, "DirectPrinting"},
- {0x1119, "ReferencePrinting"},
- {0x111A, "Imaging"},
- {0x111B, "ImagingResponder"},
- {0x111C, "ImagingAutomaticArchive"},
- {0x111D, "ImagingReferencedObjects"},
- {0x111E, "Handsfree"},
- {0x111F, "HandsfreeAudioGateway"},
- {0x1120, "DirectPrintingReferenceObjectsService"},
- {0x1121, "ReflectedUI"},
- {0x1122, "BasicPrinting"},
- {0x1123, "PrintingStatus"},
- {0x1124, "HumanInterfaceDeviceService"},
- {0x1125, "HardcopyCableReplacement"},
- {0x1126, "HCR_Print"},
- {0x1127, "HCR_Scan"},
- {0x1128, "Common_ISDN_Access"},
- {0x1129, "VideoConferencingGW"},
- {0x112A, "UDI_MT"},
- {0x112B, "UDI_TA"},
- {0x112C, "Audio/Video"},
- {0x112D, "SIM_Access"},
- {0x112E, "PBAP client"},
- {0x112F, "PBAP server"},
- {0x1130, "PBAP"},
- {0x1200, "PnPInformation"},
- {0x1201, "GenericNetworking"},
- {0x1202, "GenericFileTransfer"},
- {0x1203, "GenericAudio"},
- {0x1204, "GenericTelephony"},
- {0x1205, "UPNP_Service"},
- {0x1206, "UPNP_IP_Service"},
- {0x1300, "ESDP_UPNP_IP_PAN"},
- {0x1301, "ESDP_UPNP_IP_LAP"},
- {0x1302, "ESDP_UPNP_L2CAP"},
- {0x1303, "VideoSource"},
- {0x1304, "VideoSink"},
- {0x1305, "VideoDistribution"},
- {0x1400, "Medical Device Profile"},
- {0x1401, "MDP Source"},
- {0x1402, "MDP Sink"},
- {0, NULL}
-};
-
-
-
static int
get_type_length(tvbuff_t *tvb, int offset, int *length)
{
@@ -262,7 +200,7 @@ get_int_by_size(tvbuff_t *tvb, int off, int size)
static int
-dissect_attribute_id_list(proto_tree *t, tvbuff_t *tvb, int offset)
+dissect_attribute_id_list(proto_tree *t, tvbuff_t *tvb, int offset, packet_info *pinfo)
{
proto_item *ti;
proto_tree *st;
@@ -271,7 +209,7 @@ dissect_attribute_id_list(proto_tree *t, tvbuff_t *tvb, int offset)
const char *att_name;
start_offset=offset;
- ti = proto_tree_add_text(t, tvb, offset, 2, "AttributeIDList");
+ ti = proto_tree_add_text(t, tvb, offset, 2, "Attribute ID List");
st = proto_item_add_subtree(ti, ett_btsdp_attribute_idlist);
offset = get_type_length(tvb, offset, &bytes_to_go);
@@ -287,8 +225,13 @@ dissect_attribute_id_list(proto_tree *t, tvbuff_t *tvb, int offset)
proto_tree_add_text(st, tvb, offset, 3, "%s (0x%04x)", att_name, id);
offset+=3;
bytes_to_go-=3;
- } else if (byte0 == 0x0a) { /* 32 bit attribute range */
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (%s) ", att_name);
+
+ } else if (byte0 == 0x0a) { /* 32 bit attribute range */
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (0x%04x - 0x%04x) ",
+ tvb_get_ntohs(tvb, offset + 1), tvb_get_ntohs(tvb, offset + 3));
+
proto_tree_add_text(st, tvb, offset, 5, "0x%04x - 0x%04x",
tvb_get_ntohs(tvb, offset + 1),
tvb_get_ntohs(tvb, offset + 3));
@@ -311,6 +254,121 @@ dissect_sdp_error_response(proto_tree *t, tvbuff_t *tvb, int offset) {
return offset;
}
+static int
+get_sdp_type(tvbuff_t *tvb, int offset, guint16 id, guint8 *type, guint8 **val, guint32 *service, guint32 *service_val)
+{
+ int size, start_offset, type_size;
+ guint8 byte0;
+ guint8 size_index;
+
+ byte0=tvb_get_guint8(tvb, offset);
+ *type = (byte0>>3) & 0x1f;
+ size_index = byte0 & 0x07;
+
+ start_offset=offset;
+ offset = get_type_length(tvb, offset, &size);
+ type_size = offset - start_offset + size;
+
+ switch (*type) {
+ case 0: { /* null */
+ *val = NULL;
+ break;
+ }
+ case 1: /* unsigned integer */
+ case 2: /* signed integer */
+ case 3: { /* uuid */
+ *val = ep_alloc(size);
+
+ if (size == 1) {
+ *((guint8 *) *val) = tvb_get_guint8(tvb, offset);
+ }
+ else if (size == 2) {
+ *((guint16 *) *val) = tvb_get_ntohs(tvb, offset);
+ }
+ else if (size == 4) {
+ *((guint32 *) *val) = tvb_get_ntohl(tvb, offset);
+ }
+ else if (size == 8) {
+ *((guint64 *) *val) = tvb_get_ntoh64(tvb, offset);
+ }
+ else if (size == 16) {
+ /* store the short UUID part of the 128-bit base UUID */
+ *((guint32 *) *val) = tvb_get_ntohl(tvb, offset);
+ }
+ break;
+ }
+ case 8: /* fall through */
+ case 4: {
+ *val = tvb_bytes_to_str(tvb, offset, size);
+ break;
+ }
+ case 5: {
+ *val = ep_alloc(sizeof(type_size));
+ *((guint8 *) *val) = tvb_get_guint8(tvb, offset);
+ break;
+ }
+ case 6: /* Data Element sequence */
+ case 7: /* Data Element alternative */ {
+ gboolean flag = FALSE;
+ int bytes_to_go = size;
+ int len;
+ guint32 value = 0;
+
+ while(bytes_to_go > 0) {
+ size = get_sdp_type(tvb, offset, id, type, val, service, service_val);
+ if (size < 1 || *val == NULL) {
+ break;
+ }
+
+ get_type_length(tvb, offset, &len);
+ offset += size;
+ bytes_to_go -= size;
+
+ if( len == 1 ) {
+ value = *((guint8 *) *val);
+ }
+ else if( len == 2 ) {
+ value = *((guint16 *) *val);
+ }
+ else if ( len == 4 ) {
+ value = *((guint32 *) *val);
+ }
+ else if ( len == 16 ) {
+ value = *((guint32 *) *val);
+ }
+
+ /* pick special values of interest */
+ /* protocol or additional protocol list with UUID values for L2CAP or RFCOMM */
+ if ( ((id == 4) || (id == 0xd)) && (*type == 3) && ((value == 0x100) || (value == 0x0003)) ) {
+ *service = value;
+ flag = TRUE;
+ }
+ /* profile descriptor list with UUID */
+ else if ( (id == 9) && (*type == 3) ) {
+ *service = value;
+ flag = TRUE;
+ }
+ /* service class id list with UUID */
+ else if ( (id == 1) && (*type == 3) ) {
+ *service = value;
+ flag = TRUE;
+ }
+ /* unsigned int found after value of interest */
+ else if ( (flag == TRUE) && *type == 1) {
+ *service_val = value;
+ flag = FALSE;
+ }
+ else {
+ flag = FALSE;
+ }
+ }
+ break;
+ }
+ }
+
+ return type_size;
+}
+
static int
dissect_sdp_type(proto_tree *t, tvbuff_t *tvb, int offset, char **attr_val)
@@ -361,25 +419,21 @@ dissect_sdp_type(proto_tree *t, tvbuff_t *tvb, int offset, char **attr_val)
break;
}
case 3: {
+ guint32 id;
+ const char *uuid_name;
char *ptr = tvb_bytes_to_str(tvb, offset, size);
if(size == 2){
-
- guint16 id = tvb_get_ntohs(tvb, offset);
- const char *uuid_name = val_to_str(id, vs_service_classes, "Unknown");
-
- proto_tree_add_text(t, tvb, start_offset, type_size,
- "%s(0x%s) ", uuid_name, ptr);
- if(strpos<MAX_SDP_LEN){
- strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "UUID:%s (0x%s) ", uuid_name, ptr);
- }
+ id = tvb_get_ntohs(tvb, offset);
} else {
+ id = tvb_get_ntohl(tvb, offset);
+ }
+ uuid_name = val_to_str(id, vs_service_classes, "Unknown service");
- proto_tree_add_text(t, tvb, start_offset, type_size,
- "UUID 0x%s ", ptr);
- if(strpos<MAX_SDP_LEN){
- strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "0x%s ", ptr);
- }
+ proto_tree_add_text(t, tvb, start_offset, type_size, "%s (0x%s) ", uuid_name, ptr);
+
+ if(strpos<MAX_SDP_LEN){
+ strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, ": %s", uuid_name);
}
break;
}
@@ -458,9 +512,8 @@ dissect_sdp_type(proto_tree *t, tvbuff_t *tvb, int offset, char **attr_val)
static int
-dissect_sdp_service_attribute(proto_tree *tree, tvbuff_t *tvb, int offset)
+dissect_sdp_service_attribute(proto_tree *tree, tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 token)
{
-
proto_tree *st, *ti_sa, *ti_av;
int size;
const char *att_name;
@@ -474,15 +527,60 @@ dissect_sdp_service_attribute(proto_tree *tree, tvbuff_t *tvb, int offset)
"Service Attribute: id = %s (0x%x)", att_name, id);
st = proto_item_add_subtree(ti_sa, ett_btsdp_attribute);
-
proto_tree_add_text(st, tvb, offset, 3, "Attribute ID: %s (0x%x)", att_name, id);
ti_av = proto_tree_add_text(st, tvb, offset + 3, -1, "Attribute Value");
st = proto_item_add_subtree(ti_av, ett_btsdp_attribute);
-
size = dissect_sdp_type(st, tvb, offset + 3, &attr_val);
proto_item_append_text(ti_sa, ", value = %s", attr_val);
+ if( pinfo->fd->flags.visited ==0) {
+ guint8 *val, type;
+ guint32 service, service_val;
+ btsdp_data_t *service_item;
+
+ service_item=se_tree_lookup32(service_table, token);
+
+ if(service_item != NULL) {
+ if(id == 4 || (id == 9) || (id == 0xd)) { /* profile/protocol discriptor list/additional protocol list */
+ get_sdp_type(tvb, offset+ 3, id, &type, &val, &service, &service_val);
+
+ if( (service == BTSDP_L2CAP_PROTOCOL_UUID)
+ || (service == BTSDP_RFCOMM_PROTOCOL_UUID) ) {
+ service_item->channel = service_val;
+ service_item->protocol = (guint16) service;
+ }
+ else {
+ service_item->service = service;
+ }
+
+ service_item->flags = 0;
+ if(id == 0xd) {
+ service_item->flags = BTSDP_SECONDARY_CHANNEL_FLAG_MASK;
+ }
+ }
+ else if( id == 1) { /* service class id list */
+ get_sdp_type(tvb, offset+ 3, id, &type, &val, &service, &service_val);
+ service_item->service = service;
+ }
+ else if(id == 0x200) { /* GOEP L2CAP PSM? */
+ guint16 *psm;
+
+ get_sdp_type(tvb, offset+ 3, id, &type, (guint8 **) &psm, &service, &service_val);
+
+ if( (type == 1) && (*psm > 0x1000) && (*psm & 0x1) ) {
+ service_item->channel = *psm;
+ service_item->protocol = BTSDP_L2CAP_PROTOCOL_UUID;
+ service_item->flags = 0;
+ }
+ }
+
+ if( service_item->service != 0 && service_item->channel != 0 ) {
+ service_item->flags |= token >>15; /* set flag when local service */
+ tap_queue_packet(btsdp_tap, NULL, (void *) service_item);
+ }
+ }
+ }
proto_item_set_len(ti_sa, size + 3);
proto_item_set_len(ti_av, size);
@@ -492,7 +590,7 @@ dissect_sdp_service_attribute(proto_tree *tree, tvbuff_t *tvb, int offset)
static int
-dissect_sdp_service_attribute_list(proto_tree *tree, tvbuff_t *tvb, int offset)
+dissect_sdp_service_attribute_list(proto_tree *tree, tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 token)
{
proto_item *ti;
proto_tree *st;
@@ -500,7 +598,7 @@ dissect_sdp_service_attribute_list(proto_tree *tree, tvbuff_t *tvb, int offset)
offset = get_type_length(tvb, offset, &len);
- ti = proto_tree_add_text(tree, tvb, start_offset, -1, "AttributeList");
+ ti = proto_tree_add_text(tree, tvb, start_offset, -1, "Attribute List");
st = proto_item_add_subtree(ti, ett_btsdp_attribute);
if(!len){
@@ -508,7 +606,7 @@ dissect_sdp_service_attribute_list(proto_tree *tree, tvbuff_t *tvb, int offset)
}
while (offset - start_offset < len) {
- offset = dissect_sdp_service_attribute(st, tvb, offset);
+ offset = dissect_sdp_service_attribute(st, tvb, offset, pinfo, token);
}
proto_item_set_len(ti, offset - start_offset);
@@ -518,7 +616,7 @@ dissect_sdp_service_attribute_list(proto_tree *tree, tvbuff_t *tvb, int offset)
static int
-dissect_sdp_service_attribute_list_array(proto_tree *tree, tvbuff_t *tvb, int offset)
+dissect_sdp_service_attribute_list_array(proto_tree *tree, tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 token)
{
proto_item *ti;
proto_tree *st;
@@ -526,12 +624,12 @@ dissect_sdp_service_attribute_list_array(proto_tree *tree, tvbuff_t *tvb, int of
start_offset=offset;
offset = get_type_length(tvb, offset, &len);
- ti = proto_tree_add_text(tree, tvb, start_offset, offset-start_offset+len, "AttributeLists");
+ ti = proto_tree_add_text(tree, tvb, start_offset, offset-start_offset+len, "Attribute Lists");
st = proto_item_add_subtree(ti, ett_btsdp_attribute);
start_offset=offset;
while(offset-start_offset < len) {
- offset = dissect_sdp_service_attribute_list(st, tvb, offset);
+ offset = dissect_sdp_service_attribute_list(st, tvb, offset, pinfo, token);
}
return offset;
@@ -540,38 +638,50 @@ dissect_sdp_service_attribute_list_array(proto_tree *tree, tvbuff_t *tvb, int of
static int
-dissect_sdp_service_search_attribute_response(proto_tree *tree, tvbuff_t *tvb, int offset)
+dissect_sdp_service_search_attribute_response(proto_tree *tree, tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 token)
{
-
proto_tree_add_item(tree, hf_ssares_al_bytecount, tvb, offset, 2, FALSE);
offset += 2;
- offset += dissect_sdp_service_attribute_list_array(tree, tvb, offset);
+ offset += dissect_sdp_service_attribute_list_array(tree, tvb, offset, pinfo, token);
return offset;
}
static int
-dissect_sdp_service_search_attribute_request(proto_tree *t, tvbuff_t *tvb, int offset)
+dissect_sdp_service_search_attribute_request(proto_tree *t, tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 token)
{
proto_tree *st;
proto_item *ti;
int start_offset;
int size, bytes_to_go;
char *str;
+ btsdp_data_t *service_item = NULL;
start_offset = offset;
- ti = proto_tree_add_text(t, tvb, offset, 2, "ServiceSearchPattern");
+ ti = proto_tree_add_text(t, tvb, offset, 2, "Service Search Pattern");
st = proto_item_add_subtree(ti, ett_btsdp_attribute);
offset = get_type_length(tvb, offset, &bytes_to_go);
proto_item_set_len(ti, offset - start_offset + bytes_to_go);
-
while(bytes_to_go>0) {
+ if (pinfo->fd->flags.visited == 0) {
+ service_item=se_tree_lookup32(service_table, token);
+
+ if(service_item == NULL) {
+ service_item=se_alloc(sizeof(btsdp_data_t));
+ se_tree_insert32(service_table, token, service_item);
+ }
+ service_item->channel = 0;
+ service_item->service = 0;
+ }
+
size = dissect_sdp_type(st, tvb, offset, &str);
proto_item_append_text(st, " %s", str);
+ col_append_fstr(pinfo->cinfo, COL_INFO, "%s", str);
+
if (size < 1) {
break;
}
@@ -580,27 +690,26 @@ dissect_sdp_service_search_attribute_request(proto_tree *t, tvbuff_t *tvb, int o
}
/* dissect maximum attribute byte count */
- proto_tree_add_text(t, tvb, offset, 2, "MaximumAttributeByteCount: %d", tvb_get_ntohs(tvb, offset));
+ proto_tree_add_text(t, tvb, offset, 2, "Maximum Attribute Byte Count: %d", tvb_get_ntohs(tvb, offset));
offset+=2;
+ offset += dissect_attribute_id_list(t, tvb, offset, pinfo);
- offset += dissect_attribute_id_list(t, tvb, offset);
-
- proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
+ proto_tree_add_text(t, tvb, offset, -1, "Continuation State");
return offset;
}
static int
-dissect_sdp_service_attribute_response(proto_tree *t, tvbuff_t *tvb, int offset)
+dissect_sdp_service_attribute_response(proto_tree *t, tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 token)
{
- proto_tree_add_text(t, tvb, offset, 2, "AttributeListByteCount: %d",
+ proto_tree_add_text(t, tvb, offset, 2, "Attribute List Byte Count: %d",
tvb_get_ntohs(tvb, offset));
offset+=2;
- offset = dissect_sdp_service_attribute_list(t, tvb, offset);
+ offset = dissect_sdp_service_attribute_list(t, tvb, offset, pinfo, token);
- proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
+ proto_tree_add_text(t, tvb, offset, -1, "Continuation State");
offset+=tvb_length_remaining(tvb, offset);
return offset;
@@ -608,33 +717,34 @@ dissect_sdp_service_attribute_response(proto_tree *t, tvbuff_t *tvb, int offset)
static int
-dissect_sdp_service_attribute_request(proto_tree *t, tvbuff_t *tvb, int offset)
+dissect_sdp_service_attribute_request(proto_tree *t, tvbuff_t *tvb, int offset, packet_info *pinfo)
{
- proto_tree_add_text(t, tvb, offset, 4, "ServiceRecordHandle: 0x%x",
+ proto_tree_add_text(t, tvb, offset, 4, "Service Record Handle: 0x%x",
tvb_get_ntohl(tvb, offset));
offset+=4;
- proto_tree_add_text(t, tvb, offset, 2, "MaximumAttributeByteCount: %d",
+ proto_tree_add_text(t, tvb, offset, 2, "Maximum Attribute Byte Count: %d",
tvb_get_ntohs(tvb, offset));
offset+=2;
- offset += dissect_attribute_id_list(t, tvb, offset);
+ offset += dissect_attribute_id_list(t, tvb, offset, pinfo);
- proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
+ proto_tree_add_text(t, tvb, offset, -1, "Continuation State");
offset+=tvb_length_remaining(tvb, offset);
return offset;
}
static int
-dissect_sdp_service_search_request(proto_tree *t, tvbuff_t *tvb, int offset)
+dissect_sdp_service_search_request(proto_tree *t, tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 token)
{
- int start_offset, bytes_to_go, size;
+ int start_offset, bytes_to_go, size;
proto_item *ti;
proto_tree *st;
start_offset=offset;
- ti = proto_tree_add_text(t, tvb, offset, 2, "ServiceSearchPattern");
+
+ ti = proto_tree_add_text(t, tvb, offset, 2, "Service Search Pattern");
st = proto_item_add_subtree(ti, ett_btsdp_service_search_pattern);
offset = get_type_length(tvb, offset, &bytes_to_go);
@@ -642,8 +752,33 @@ dissect_sdp_service_search_request(proto_tree *t, tvbuff_t *tvb, int offset)
while(bytes_to_go>0){
char *str;
+ btsdp_data_t *service_item = NULL;
+
+ if (pinfo->fd->flags.visited == 0) {
+ guint32 service, service_val;
+ guint8 type, *val = NULL;
+
+ service_item=se_tree_lookup32(service_table, token);
+
+ if(service_item == NULL) {
+ service_item=se_alloc(sizeof(btsdp_data_t));
+ se_tree_insert32(service_table, token, service_item);
+ }
+ service_item->channel = 0;
+ service_item->service = 0;
+
+ get_sdp_type(tvb, offset, 4, &type, &val, &service, &service_val);
+
+ if( type==3 && val != NULL)
+ service_item->service = *((guint32 *) val);
+ }
+
size = dissect_sdp_type(st, tvb, offset, &str);
+
proto_item_append_text(st, " %s", str);
+
+ col_append_fstr(pinfo->cinfo, COL_INFO, "%s", str);
+
if (size < 1) {
break;
}
@@ -652,12 +787,11 @@ dissect_sdp_service_search_request(proto_tree *t, tvbuff_t *tvb, int offset)
}
/* dissect maximum service record count */
-
- proto_tree_add_text(t, tvb, offset, 2, "MaximumServiceRecordCount: %d",
+ proto_tree_add_text(t, tvb, offset, 2, "Maximum Service Record Count: %d",
tvb_get_ntohs(tvb, offset));
offset+=2;
- proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
+ proto_tree_add_text(t, tvb, offset, -1, "Continuation State");
offset+=tvb_length_remaining(tvb, offset);
return offset;
}
@@ -678,7 +812,7 @@ dissect_sdp_service_search_response(proto_tree *t, tvbuff_t *tvb, int offset)
offset+=2;
ti = proto_tree_add_text(t, tvb, offset,
- curr_count * 4, "ServiceRecordHandleList");
+ curr_count * 4, "Service Record Handle List");
st = proto_item_add_subtree(ti, ett_btsdp_ssr);
while(curr_count>0){
@@ -687,7 +821,7 @@ dissect_sdp_service_search_response(proto_tree *t, tvbuff_t *tvb, int offset)
curr_count--;
}
- proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
+ proto_tree_add_text(t, tvb, offset, -1, "Continuation State");
offset+=tvb_length_remaining(tvb, offset);
return offset;
@@ -700,7 +834,8 @@ dissect_btsdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
proto_item *ti;
proto_tree *st;
guint8 pdu;
- guint16 plen;
+ guint16 tid, plen, acl_handle;
+ guint32 token;
const char *pdu_name;
int offset=0;
@@ -720,6 +855,7 @@ dissect_btsdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
offset++;
/* tid */
+ tid = tvb_get_ntohs(tvb, offset);
proto_tree_add_item(st, hf_tid, tvb, offset, 2, FALSE);
offset+=2;
@@ -728,32 +864,37 @@ dissect_btsdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
proto_tree_add_item(st, hf_plen, tvb, offset, 2, FALSE);
offset+=2;
+ acl_handle = ((btl2cap_data_t *) pinfo->private_data)->chandle;
+ if( pdu & 0x01)
+ token = acl_handle | ((pinfo->p2p_dir != P2P_DIR_RECV)?0x8000:0x0000);
+ else
+ token = acl_handle | ((pinfo->p2p_dir == P2P_DIR_RECV)?0x8000:0x0000);
+
switch(pdu) {
case 0x1:
offset=dissect_sdp_error_response(st, tvb, offset);
break;
case 0x2:
- offset=dissect_sdp_service_search_request(st, tvb, offset);
+ offset=dissect_sdp_service_search_request(st, tvb, offset, pinfo, token);
break;
case 0x3:
offset=dissect_sdp_service_search_response(st, tvb, offset);
break;
case 0x4:
- offset=dissect_sdp_service_attribute_request(st, tvb, offset);
+ offset=dissect_sdp_service_attribute_request(st, tvb, offset, pinfo);
break;
case 0x5:
- offset=dissect_sdp_service_attribute_response(st, tvb, offset);
+ offset=dissect_sdp_service_attribute_response(st, tvb, offset, pinfo, token);
break;
case 0x6:
- offset=dissect_sdp_service_search_attribute_request(st, tvb, offset);
+ offset=dissect_sdp_service_search_attribute_request(st, tvb, offset, pinfo, token);
break;
case 07:
- offset=dissect_sdp_service_search_attribute_response(st, tvb, offset);
+ offset=dissect_sdp_service_search_attribute_response(st, tvb, offset, pinfo, token);
break;
}
}
-
void
proto_register_btsdp(void)
{
@@ -764,34 +905,34 @@ proto_register_btsdp(void)
"PDU type", HFILL}
},
{&hf_tid,
- {"TransactionID", "btsdp.tid",
+ {"Transaction Id", "btsdp.tid",
FT_UINT16, BASE_HEX, NULL, 0,
- "Transaction ID", HFILL}
+ NULL, HFILL}
},
{&hf_plen,
- {"ParameterLength", "btsdp.len",
+ {"Parameter Length", "btsdp.len",
FT_UINT16, BASE_DEC, NULL, 0,
NULL, HFILL}
},
{&hf_error_code,
- {"ErrorCode", "btsdp.error_code",
+ {"Error Code", "btsdp.error_code",
FT_UINT16, BASE_HEX, NULL, 0,
- "Error Code", HFILL}
+ NULL, HFILL}
},
{&hf_ssr_total_count,
- {"TotalServiceRecordCount", "btsdp.ssr.total_count",
+ {"Total Service Record Count", "btsdp.ssr.total_count",
FT_UINT16, BASE_DEC, NULL, 0,
"Total count of service records", HFILL}
},
{&hf_ssr_current_count,
- {"CurrentServiceRecordCount", "btsdp.ssr.current_count",
+ {"Current Service Record Count", "btsdp.ssr.current_count",
FT_UINT16, BASE_DEC, NULL, 0,
- "count of service records in this message", HFILL}
+ "Count of service records in this message", HFILL}
},
{&hf_ssares_al_bytecount,
- {"AttributeListsByteCount", "btsdp.ssares.byte_count",
+ {"Attribute Lists Byte Count", "btsdp.ssares.byte_count",
FT_UINT16, BASE_DEC, NULL, 0,
- "count of bytes in attribute list response", HFILL}
+ "Count of bytes in attribute list response", HFILL}
}
};
@@ -806,13 +947,17 @@ proto_register_btsdp(void)
&ett_btsdp_attribute_idlist
};
- proto_btsdp = proto_register_protocol("Bluetooth SDP", "BTSDP", "btsdp");
+ proto_btsdp = proto_register_protocol("Bluetooth SDP Protocol", "BTSDP", "btsdp");
register_dissector("btsdp", dissect_btsdp, proto_btsdp);
+ btsdp_tap = register_tap("btsdp");
+
/* Required function calls to register the header fields and subtrees used */
proto_register_field_array(proto_btsdp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
+
+ service_table=se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "mapping of rfcomm channel/l2cap PSM to service uuid");
}
@@ -824,3 +969,4 @@ proto_reg_handoff_btsdp(void)
btsdp_handle = find_dissector("btsdp");
dissector_add_uint("btl2cap.psm", BTL2CAP_PSM_SDP, btsdp_handle);
}
+