diff options
Diffstat (limited to 'epan/dissectors')
-rw-r--r-- | epan/dissectors/Makefile.common | 1 | ||||
-rw-r--r-- | epan/dissectors/packet-btl2cap.c | 320 | ||||
-rw-r--r-- | epan/dissectors/packet-btl2cap.h | 13 | ||||
-rw-r--r-- | epan/dissectors/packet-btobex.c | 985 | ||||
-rw-r--r-- | epan/dissectors/packet-btrfcomm.c | 324 | ||||
-rw-r--r-- | epan/dissectors/packet-btsdp.c | 530 |
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); } + |