/* packet-btsdp.c * Routines for Bluetooth SDP dissection * Copyright 2002, Wolfgang Hansmann * * Refactored for wireshark checkin * Ronnie Sahlberg 2006 * * $Id$ * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include "packet-btl2cap.h" /* Initialize the protocol and registered fields */ static int proto_btsdp = -1; static int hf_pduid = -1; static int hf_tid = -1; static int hf_plen = -1; static int hf_ssr_total_count = -1; static int hf_ssr_current_count = -1; static int hf_error_code = -1; static int hf_ssares_al_bytecount = -1; /* Initialize the subtree pointers */ static gint ett_btsdp = -1; static gint ett_btsdp_ssr = -1; static gint ett_btsdp_des = -1; static gint ett_btsdp_attribute = -1; static gint ett_btsdp_service_search_pattern = -1; static gint ett_btsdp_attribute_idlist = -1; 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"}, {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"}, {0x0100, "Service Name"}, {0x0101, "Service Description"}, {0x0102, "Service Provider"}, {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"}, {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"}, {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"}, {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"}, {0, NULL} }; static int get_type_length(tvbuff_t *tvb, int offset, int *length) { int size = 0; guint8 byte0 = tvb_get_guint8(tvb, offset); offset++; switch (byte0 & 0x07) { case 0: size = (byte0 >> 3) == 0 ? 0 : 1; break; case 1: size = 2; break; case 2: size = 4; break; case 3: size = 8; break; case 4: size = 16; break; case 5: size = tvb_get_guint8(tvb, offset); offset++; break; case 6: size = tvb_get_ntohs(tvb, offset); offset += 2; break; case 7: size = tvb_get_ntohl(tvb, offset); offset += 4; break; } *length = size; return offset; } static guint32 get_uint_by_size(tvbuff_t *tvb, int off, int size) { switch(size) { case 0: return tvb_get_guint8(tvb, off); case 1: return tvb_get_ntohs(tvb, off); case 2: return tvb_get_ntohl(tvb, off); default: return 0xffffffff; } } static gint32 get_int_by_size(tvbuff_t *tvb, int off, int size) { switch(size) { case 0: return tvb_get_guint8(tvb, off); case 1: return tvb_get_ntohs(tvb, off); case 2: return tvb_get_ntohl(tvb, off); default: return -1; } } static int dissect_attribute_id_list(proto_tree *t, tvbuff_t *tvb, int offset) { proto_item *ti; proto_tree *st; int start_offset, bytes_to_go; start_offset=offset; ti = proto_tree_add_text(t, tvb, offset, 2, "AttributeIDList"); st = proto_item_add_subtree(ti, ett_btsdp_attribute_idlist); 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){ guint8 byte0 = tvb_get_guint8(tvb, offset); if (byte0 == 0x09) { /* 16 bit attribute id */ proto_tree_add_text(st, tvb, offset, 3, "0x%04x", tvb_get_ntohs(tvb, offset + 1)); offset+=3; bytes_to_go-=3; } else if (byte0 == 0x0a) { /* 32 bit attribute range */ proto_tree_add_text(st, tvb, offset, 5, "0x%04x - 0x%04x", tvb_get_ntohs(tvb, offset + 1), tvb_get_ntohs(tvb, offset + 3)); offset+=5; bytes_to_go-=5; } } return offset - start_offset; } static int dissect_sdp_error_response(proto_tree *t, tvbuff_t *tvb, int offset) { proto_tree_add_item(t, hf_error_code, tvb, offset, 2, FALSE); offset+=2; return offset; } static int dissect_sdp_type(proto_tree *t, tvbuff_t *tvb, int offset, char **attr_val) { #define MAX_SDP_LEN 1024 int strpos=0, size, start_offset, type_size; char *str; guint8 byte0; guint8 type; guint8 size_index; char *ptr; str=ep_alloc(MAX_SDP_LEN+1); *attr_val=str; str[0]=0; 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: proto_tree_add_text(t, tvb, start_offset, type_size, "Nil "); if(strpos 0){ if(!first){ if(strpos0) { size = dissect_sdp_type(st, tvb, offset, &str); proto_item_append_text(st, " %s", str); offset+=size; bytes_to_go-=size; } /* dissect maximum attribute byte count */ proto_tree_add_text(t, tvb, offset, 2, "MaximumAttributeByteCount: %d", tvb_get_ntohs(tvb, offset)); offset+=2; offset += dissect_attribute_id_list(t, tvb, offset); proto_tree_add_text(t, tvb, offset, -1, "ContinuationState"); return offset; } static int dissect_sdp_service_attribute_response(proto_tree *t, tvbuff_t *tvb, int offset) { proto_tree_add_text(t, tvb, offset, 2, "AttributeListByteCount: %d", tvb_get_ntohs(tvb, offset)); offset+=2; offset = dissect_sdp_service_attribute_list(t, tvb, offset); proto_tree_add_text(t, tvb, offset, -1, "ContinuationState"); offset+=tvb_length_remaining(tvb, offset); return offset; } static int dissect_sdp_service_attribute_request(proto_tree *t, tvbuff_t *tvb, int offset) { proto_tree_add_text(t, tvb, offset, 4, "ServiceRecordHandle: 0x%x", tvb_get_ntohl(tvb, offset)); offset+=4; proto_tree_add_text(t, tvb, offset, 2, "MaximumAttributeByteCount: %d", tvb_get_ntohs(tvb, offset)); offset+=2; offset += dissect_attribute_id_list(t, tvb, offset); proto_tree_add_text(t, tvb, offset, -1, "ContinuationState"); offset+=tvb_length_remaining(tvb, offset); return offset; } static int dissect_sdp_service_search_request(proto_tree *t, tvbuff_t *tvb, int offset) { 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"); st = proto_item_add_subtree(ti, ett_btsdp_service_search_pattern); 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){ char *str; size = dissect_sdp_type(st, tvb, offset, &str); proto_item_append_text(st, " %s", str); offset+=size; bytes_to_go-=size; } /* dissect maximum service record count */ proto_tree_add_text(t, tvb, offset, 2, "MaximumServiceRecordCount: %d", tvb_get_ntohs(tvb, offset)); offset+=2; proto_tree_add_text(t, tvb, offset, -1, "ContinuationState"); offset+=tvb_length_remaining(tvb, offset); return offset; } static int dissect_sdp_service_search_response(proto_tree *t, tvbuff_t *tvb, int offset) { proto_tree *st; proto_item *ti; guint16 curr_count; proto_tree_add_item(t, hf_ssr_total_count, tvb, offset, 2, FALSE); offset+=2; curr_count = tvb_get_ntohs(tvb, offset); proto_tree_add_item(t, hf_ssr_current_count, tvb, offset, 2, FALSE); offset+=2; ti = proto_tree_add_text(t, tvb, offset, curr_count * 4, "ServiceRecordHandleList"); st = proto_item_add_subtree(ti, ett_btsdp_ssr); offset+=4; while(curr_count>0){ proto_tree_add_text(st, tvb, offset, 4, "0x%x", tvb_get_ntohl(tvb, offset)); offset+=4; curr_count--; } proto_tree_add_text(t, tvb, offset, -1, "ContinuationState"); offset+=tvb_length_remaining(tvb, offset); return offset; } static void dissect_btsdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_item *ti; proto_tree *st; guint8 pdu; guint16 plen; const char *pdu_name; int offset=0; if (check_col(pinfo->cinfo, COL_PROTOCOL)) col_set_str(pinfo->cinfo, COL_PROTOCOL, "SDP"); ti = proto_tree_add_item(tree, proto_btsdp, tvb, 0, -1, FALSE); st = proto_item_add_subtree(ti, ett_btsdp); /* pdu id */ pdu = tvb_get_guint8(tvb, offset); proto_tree_add_item(st, hf_pduid, tvb, offset, 1, FALSE); pdu_name = val_to_str(pdu, vs_pduid, "Unknown"); if (check_col(pinfo->cinfo, COL_INFO)) { col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s ",pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd", pdu_name); } proto_item_append_text(ti, ": %s (0x%x)", pdu_name, pdu); offset++; /* tid */ proto_tree_add_item(st, hf_tid, tvb, offset, 2, FALSE); offset+=2; /* plen */ plen = tvb_get_ntohs(tvb, offset); proto_tree_add_item(st, hf_plen, tvb, offset, 2, FALSE); offset+=2; switch(pdu) { case 0x1: offset=dissect_sdp_error_response(st, tvb, offset); break; case 0x2: offset=dissect_sdp_service_search_request(st, tvb, offset); break; case 0x3: offset=dissect_sdp_service_search_response(st, tvb, offset); break; case 0x4: offset=dissect_sdp_service_attribute_request(st, tvb, offset); break; case 0x5: offset=dissect_sdp_service_attribute_response(st, tvb, offset); break; case 0x6: offset=dissect_sdp_service_search_attribute_request(st, tvb, offset); break; case 07: offset=dissect_sdp_service_search_attribute_response(st, tvb, offset); break; } } void proto_register_btsdp(void) { static hf_register_info hf[] = { {&hf_pduid, {"PDU", "btsdp.pdu", FT_UINT8, BASE_HEX, VALS(vs_pduid), 0, "PDU type", HFILL} }, {&hf_tid, {"TransactionID", "btsdp.tid", FT_UINT16, BASE_HEX, NULL, 0, "Transaction ID", HFILL} }, {&hf_plen, {"ParameterLength", "btsdp.len", FT_UINT16, BASE_DEC, NULL, 0, "ParameterLength", HFILL} }, {&hf_error_code, {"ErrorCode", "btsdp.error_code", FT_UINT16, BASE_HEX, NULL, 0, "Error Code", HFILL} }, {&hf_ssr_total_count, {"TotalServiceRecordCount", "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", FT_UINT16, BASE_DEC, NULL, 0, "count of service records in this message", HFILL} }, {&hf_ssares_al_bytecount, {"AttributeListsByteCount", "btsdp.ssares.byte_count", FT_UINT16, BASE_DEC, NULL, 0, "count of bytes in attribute list response", HFILL} } }; /* Setup protocol subtree array */ static gint *ett[] = { &ett_btsdp, &ett_btsdp_ssr, &ett_btsdp_des, &ett_btsdp_attribute, &ett_btsdp_service_search_pattern, &ett_btsdp_attribute_idlist }; proto_btsdp = proto_register_protocol("Bluetooth SDP", "BTSDP", "btsdp"); register_dissector("btsdp", dissect_btsdp, proto_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)); } void proto_reg_handoff_btsdp(void) { dissector_handle_t btsdp_handle; btsdp_handle = find_dissector("btsdp"); dissector_add("btl2cap.psm", BTL2CAP_PSM_SDP, btsdp_handle); }