2 * Routines for Bluetooth SDP dissection
3 * Copyright 2002, Wolfgang Hansmann <hansmann@cs.uni-bonn.de>
5 * Refactored for wireshark checkin
10 * Wireshark - Network traffic analyzer
11 * By Gerald Combs <gerald@wireshark.org>
12 * Copyright 1998 Gerald Combs
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36 #include <epan/packet.h>
37 #include <epan/value_string.h>
38 #include <epan/emem.h>
40 #include "packet-btl2cap.h"
42 /* Initialize the protocol and registered fields */
43 static int proto_btsdp = -1;
44 static int hf_pduid = -1;
45 static int hf_tid = -1;
46 static int hf_plen = -1;
47 static int hf_ssr_total_count = -1;
48 static int hf_ssr_current_count = -1;
49 static int hf_error_code = -1;
50 static int hf_ssares_al_bytecount = -1;
53 /* Initialize the subtree pointers */
54 static gint ett_btsdp = -1;
55 static gint ett_btsdp_ssr = -1;
56 static gint ett_btsdp_des = -1;
57 static gint ett_btsdp_attribute = -1;
58 static gint ett_btsdp_service_search_pattern = -1;
59 static gint ett_btsdp_attribute_idlist = -1;
61 static const value_string vs_pduid[] = {
62 {0x1, "SDP_ErrorResponse"},
63 {0x2, "SDP_ServiceSearchRequest"},
64 {0x3, "SDP_ServiceSearchResponse"},
65 {0x4, "SDP_ServiceAttributeRequest"},
66 {0x5, "SDP_ServiceAttributeResponse"},
67 {0x6, "SDP_ServiceSearchAttributeRequest"},
68 {0x7, "SDP_ServiceSearchAttributeResponse"},
72 static const value_string vs_general_attribute_id[] = {
73 {0x0000, "ServiceRecordHandle"},
74 {0x0001, "ServiceClassIDList"},
75 {0x0002, "ServiceRecordState"},
76 {0x0003, "ServiceID"},
77 {0x0004, "ProtocolDescriptorList"},
78 {0x0005, "BrowseGroupList"},
79 {0x0006, "LanguageBaseAttributeIDList"},
80 {0x0007, "ServiceinfoTimeToLive"},
81 {0x0008, "ServiceAvailability"},
82 {0x0009, "BluetoothProfileDescriptorList"},
83 {0x000a, "DocumentationURL"},
84 {0x000b, "ClientExecutableURL"},
86 {0x0100, "Service Name"},
87 {0x0101, "Service Description"},
88 {0x0102, "Service Provider"},
92 static const value_string vs_service_classes[] = {
108 {0x0012, "HardcopyControlChannel"},
109 {0x0014, "HardcopyDataChannel"},
110 {0x0016, "HardcopyNotification"},
114 {0x001D, "UDI_C-Plane"},
115 {0x001E, "MCAPControlChannel"},
116 {0x001F, "MCAPDataChannel"},
118 {0x1000, "ServiceDiscoveryServerServiceClassID"},
119 {0x1001, "BrowseGroupDescriptorServiceClassID"},
120 {0x1002, "PublicBrowseGroup"},
121 {0x1101, "SerialPort"},
122 {0x1102, "LANAccessUsingPPP"},
123 {0x1103, "DialupNetworking"},
124 {0x1104, "IrMCSync"},
125 {0x1105, "OBEXObjectPush"},
126 {0x1106, "OBEXFileTransfer"},
127 {0x1107, "IrMCSyncCommand"},
129 {0x1109, "CordlessTelephony"},
130 {0x110A, "AudioSource"},
131 {0x110B, "AudioSink"},
132 {0x110C, "A/V_RemoteControlTarget"},
133 {0x110D, "AdvancedAudioDistribution"},
134 {0x110E, "A/V_RemoteControl"},
135 {0x110F, "VideoConferencing"},
136 {0x1110, "Intercom"},
138 {0x1112, "HeadsetAudioGateway"},
140 {0x1114, "WAP_CLIENT"},
144 {0x1118, "DirectPrinting"},
145 {0x1119, "ReferencePrinting"},
147 {0x111B, "ImagingResponder"},
148 {0x111C, "ImagingAutomaticArchive"},
149 {0x111D, "ImagingReferencedObjects"},
150 {0x111E, "Handsfree"},
151 {0x111F, "HandsfreeAudioGateway"},
152 {0x1120, "DirectPrintingReferenceObjectsService"},
153 {0x1121, "ReflectedUI"},
154 {0x1122, "BasicPrinting"},
155 {0x1123, "PrintingStatus"},
156 {0x1124, "HumanInterfaceDeviceService"},
157 {0x1125, "HardcopyCableReplacement"},
158 {0x1126, "HCR_Print"},
159 {0x1127, "HCR_Scan"},
160 {0x1128, "Common_ISDN_Access"},
161 {0x1129, "VideoConferencingGW"},
164 {0x112C, "Audio/Video"},
165 {0x112D, "SIM_Access"},
166 {0x112E, "PBAP client"},
167 {0x112F, "PBAP server"},
169 {0x1200, "PnPInformation"},
170 {0x1201, "GenericNetworking"},
171 {0x1202, "GenericFileTransfer"},
172 {0x1203, "GenericAudio"},
173 {0x1204, "GenericTelephony"},
174 {0x1205, "UPNP_Service"},
175 {0x1206, "UPNP_IP_Service"},
176 {0x1300, "ESDP_UPNP_IP_PAN"},
177 {0x1301, "ESDP_UPNP_IP_LAP"},
178 {0x1302, "ESDP_UPNP_L2CAP"},
179 {0x1303, "VideoSource"},
180 {0x1304, "VideoSink"},
181 {0x1305, "VideoDistribution"},
182 {0x1400, "Medical Device Profile"},
183 {0x1401, "MDP Source"},
184 {0x1402, "MDP Sink"},
191 get_type_length(tvbuff_t *tvb, int offset, int *length)
194 guint8 byte0 = tvb_get_guint8(tvb, offset);
197 switch (byte0 & 0x07) {
199 size = (byte0 >> 3) == 0 ? 0 : 1;
214 size = tvb_get_guint8(tvb, offset);
218 size = tvb_get_ntohs(tvb, offset);
222 size = tvb_get_ntohl(tvb, offset);
233 get_uint_by_size(tvbuff_t *tvb, int off, int size)
237 return tvb_get_guint8(tvb, off);
239 return tvb_get_ntohs(tvb, off);
241 return tvb_get_ntohl(tvb, off);
249 get_int_by_size(tvbuff_t *tvb, int off, int size)
253 return tvb_get_guint8(tvb, off);
255 return tvb_get_ntohs(tvb, off);
257 return tvb_get_ntohl(tvb, off);
265 dissect_attribute_id_list(proto_tree *t, tvbuff_t *tvb, int offset)
269 int start_offset, bytes_to_go;
271 const char *att_name;
274 ti = proto_tree_add_text(t, tvb, offset, 2, "AttributeIDList");
275 st = proto_item_add_subtree(ti, ett_btsdp_attribute_idlist);
277 offset = get_type_length(tvb, offset, &bytes_to_go);
278 proto_item_set_len(ti, offset - start_offset + bytes_to_go);
280 while(bytes_to_go>0){
281 guint8 byte0 = tvb_get_guint8(tvb, offset);
283 if (byte0 == 0x09) { /* 16 bit attribute id */
285 id = tvb_get_ntohs(tvb, offset+1);
286 att_name = val_to_str(id, vs_general_attribute_id, "Unknown");
287 proto_tree_add_text(st, tvb, offset, 3, "%s (0x%04x)", att_name, id);
290 } else if (byte0 == 0x0a) { /* 32 bit attribute range */
292 proto_tree_add_text(st, tvb, offset, 5, "0x%04x - 0x%04x",
293 tvb_get_ntohs(tvb, offset + 1),
294 tvb_get_ntohs(tvb, offset + 3));
301 return offset - start_offset;
306 dissect_sdp_error_response(proto_tree *t, tvbuff_t *tvb, int offset) {
308 proto_tree_add_item(t, hf_error_code, tvb, offset, 2, FALSE);
316 dissect_sdp_type(proto_tree *t, tvbuff_t *tvb, int offset, char **attr_val)
318 #define MAX_SDP_LEN 1024
319 int strpos=0, size, start_offset, type_size;
326 str=ep_alloc(MAX_SDP_LEN+1);
330 byte0=tvb_get_guint8(tvb, offset);
331 type=(byte0>>3)&0x1f;
332 size_index=byte0&0x07;
335 offset = get_type_length(tvb, offset, &size);
336 type_size = offset - start_offset + size;
341 proto_tree_add_text(t, tvb, start_offset, type_size, "Nil ");
342 if(strpos<MAX_SDP_LEN){
343 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "Nil ");
347 guint32 val = get_uint_by_size(tvb, offset, size_index);
348 proto_tree_add_text(t, tvb, start_offset, type_size,
349 "unsigned int %d ", val);
350 if(strpos<MAX_SDP_LEN){
351 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "%u ", val);
356 guint32 val = get_int_by_size(tvb, offset, size_index);
357 proto_tree_add_text(t, tvb, start_offset, type_size,
358 "signed int %d ", val);
359 if(strpos<MAX_SDP_LEN){
360 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "%d ", val);
365 char *ptr = tvb_bytes_to_str(tvb, offset, size);
369 guint16 id = tvb_get_ntohs(tvb, offset);
370 const char *uuid_name = val_to_str(id, vs_service_classes, "Unknown");
372 proto_tree_add_text(t, tvb, start_offset, type_size,
373 "%s(0x%s) ", uuid_name, ptr);
374 if(strpos<MAX_SDP_LEN){
375 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "UUID:%s (0x%s) ", uuid_name, ptr);
379 proto_tree_add_text(t, tvb, start_offset, type_size,
381 if(strpos<MAX_SDP_LEN){
382 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "0x%s ", ptr);
387 case 8: /* fall through */
389 ptr = (gchar*)tvb_get_ephemeral_string(tvb, offset, size);
391 proto_tree_add_text(t, tvb, start_offset, type_size, "%s \"%s\"",
392 type == 8 ? "URL" : "String", ptr);
393 if(strpos<MAX_SDP_LEN){
394 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "%s ", ptr);
399 guint8 var = tvb_get_guint8(tvb, offset);
401 proto_tree_add_text(t, tvb, start_offset, type_size, "%s",
402 var ? "true" : "false");
403 if(strpos<MAX_SDP_LEN){
404 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "%s ", var?"true":"false");
408 case 6: /* Data Element sequence */
409 case 7: /* Data Element alternative */ {
412 int bytes_to_go = size;
416 ti = proto_tree_add_text(t, tvb, start_offset, type_size, "%s",
417 type == 6 ? "Data Element sequence" :
418 "Data Element alternative");
419 st = proto_item_add_subtree(ti, ett_btsdp_des);
421 if(strpos<MAX_SDP_LEN){
422 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "{ ");
425 while(bytes_to_go > 0){
427 if(strpos<MAX_SDP_LEN){
428 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, ", ");
434 size = dissect_sdp_type(st, tvb, offset, &substr);
438 if(strpos<MAX_SDP_LEN){
439 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "%s ", substr);
445 if(strpos<MAX_SDP_LEN){
446 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "} ");
452 /* make sure the string is 0 terminated */
462 dissect_sdp_service_attribute(proto_tree *tree, tvbuff_t *tvb, int offset)
465 proto_tree *st, *ti_sa, *ti_av;
467 const char *att_name;
471 id = tvb_get_ntohs(tvb, offset+1);
472 att_name = val_to_str(id, vs_general_attribute_id, "Unknown");
474 ti_sa = proto_tree_add_text(tree, tvb, offset, -1,
475 "Service Attribute: id = %s (0x%x)", att_name, id);
476 st = proto_item_add_subtree(ti_sa, ett_btsdp_attribute);
479 proto_tree_add_text(st, tvb, offset, 3, "Attribute ID: %s (0x%x)", att_name, id);
480 ti_av = proto_tree_add_text(st, tvb, offset + 3, -1, "Attribute Value");
481 st = proto_item_add_subtree(ti_av, ett_btsdp_attribute);
484 size = dissect_sdp_type(st, tvb, offset + 3, &attr_val);
485 proto_item_append_text(ti_sa, ", value = %s", attr_val);
488 proto_item_set_len(ti_sa, size + 3);
489 proto_item_set_len(ti_av, size);
491 return offset+size+3;
496 dissect_sdp_service_attribute_list(proto_tree *tree, tvbuff_t *tvb, int offset)
500 int start_offset = offset, len;
502 offset = get_type_length(tvb, offset, &len);
504 ti = proto_tree_add_text(tree, tvb, start_offset, -1, "AttributeList");
505 st = proto_item_add_subtree(ti, ett_btsdp_attribute);
511 while (offset - start_offset < len) {
512 offset = dissect_sdp_service_attribute(st, tvb, offset);
515 proto_item_set_len(ti, offset - start_offset);
522 dissect_sdp_service_attribute_list_array(proto_tree *tree, tvbuff_t *tvb, int offset)
526 int start_offset, len;
529 offset = get_type_length(tvb, offset, &len);
530 ti = proto_tree_add_text(tree, tvb, start_offset, offset-start_offset+len, "AttributeLists");
531 st = proto_item_add_subtree(ti, ett_btsdp_attribute);
534 while(offset-start_offset < len) {
535 offset = dissect_sdp_service_attribute_list(st, tvb, offset);
544 dissect_sdp_service_search_attribute_response(proto_tree *tree, tvbuff_t *tvb, int offset)
547 proto_tree_add_item(tree, hf_ssares_al_bytecount, tvb, offset, 2, FALSE);
550 offset += dissect_sdp_service_attribute_list_array(tree, tvb, offset);
557 dissect_sdp_service_search_attribute_request(proto_tree *t, tvbuff_t *tvb, int offset)
562 int size, bytes_to_go;
565 start_offset = offset;
566 ti = proto_tree_add_text(t, tvb, offset, 2, "ServiceSearchPattern");
567 st = proto_item_add_subtree(ti, ett_btsdp_attribute);
569 offset = get_type_length(tvb, offset, &bytes_to_go);
570 proto_item_set_len(ti, offset - start_offset + bytes_to_go);
573 while(bytes_to_go>0) {
574 size = dissect_sdp_type(st, tvb, offset, &str);
575 proto_item_append_text(st, " %s", str);
583 /* dissect maximum attribute byte count */
584 proto_tree_add_text(t, tvb, offset, 2, "MaximumAttributeByteCount: %d", tvb_get_ntohs(tvb, offset));
588 offset += dissect_attribute_id_list(t, tvb, offset);
590 proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
596 dissect_sdp_service_attribute_response(proto_tree *t, tvbuff_t *tvb, int offset)
598 proto_tree_add_text(t, tvb, offset, 2, "AttributeListByteCount: %d",
599 tvb_get_ntohs(tvb, offset));
602 offset = dissect_sdp_service_attribute_list(t, tvb, offset);
604 proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
605 offset+=tvb_length_remaining(tvb, offset);
612 dissect_sdp_service_attribute_request(proto_tree *t, tvbuff_t *tvb, int offset)
614 proto_tree_add_text(t, tvb, offset, 4, "ServiceRecordHandle: 0x%x",
615 tvb_get_ntohl(tvb, offset));
618 proto_tree_add_text(t, tvb, offset, 2, "MaximumAttributeByteCount: %d",
619 tvb_get_ntohs(tvb, offset));
622 offset += dissect_attribute_id_list(t, tvb, offset);
624 proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
625 offset+=tvb_length_remaining(tvb, offset);
631 dissect_sdp_service_search_request(proto_tree *t, tvbuff_t *tvb, int offset)
633 int start_offset, bytes_to_go, size;
638 ti = proto_tree_add_text(t, tvb, offset, 2, "ServiceSearchPattern");
639 st = proto_item_add_subtree(ti, ett_btsdp_service_search_pattern);
641 offset = get_type_length(tvb, offset, &bytes_to_go);
642 proto_item_set_len(ti, offset - start_offset + bytes_to_go);
644 while(bytes_to_go>0){
646 size = dissect_sdp_type(st, tvb, offset, &str);
647 proto_item_append_text(st, " %s", str);
655 /* dissect maximum service record count */
657 proto_tree_add_text(t, tvb, offset, 2, "MaximumServiceRecordCount: %d",
658 tvb_get_ntohs(tvb, offset));
661 proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
662 offset+=tvb_length_remaining(tvb, offset);
668 dissect_sdp_service_search_response(proto_tree *t, tvbuff_t *tvb, int offset)
674 proto_tree_add_item(t, hf_ssr_total_count, tvb, offset, 2, FALSE);
677 curr_count = tvb_get_ntohs(tvb, offset);
678 proto_tree_add_item(t, hf_ssr_current_count, tvb, offset, 2, FALSE);
681 ti = proto_tree_add_text(t, tvb, offset,
682 curr_count * 4, "ServiceRecordHandleList");
683 st = proto_item_add_subtree(ti, ett_btsdp_ssr);
686 proto_tree_add_text(st, tvb, offset, 4, "0x%x", tvb_get_ntohl(tvb, offset));
691 proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
692 offset+=tvb_length_remaining(tvb, offset);
699 dissect_btsdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
705 const char *pdu_name;
708 if (check_col(pinfo->cinfo, COL_PROTOCOL))
709 col_set_str(pinfo->cinfo, COL_PROTOCOL, "SDP");
711 ti = proto_tree_add_item(tree, proto_btsdp, tvb, 0, -1, FALSE);
712 st = proto_item_add_subtree(ti, ett_btsdp);
715 pdu = tvb_get_guint8(tvb, offset);
716 proto_tree_add_item(st, hf_pduid, tvb, offset, 1, FALSE);
717 pdu_name = val_to_str(pdu, vs_pduid, "Unknown");
718 if (check_col(pinfo->cinfo, COL_INFO)) {
719 col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s ",pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd", pdu_name);
721 proto_item_append_text(ti, ": %s (0x%x)", pdu_name, pdu);
725 proto_tree_add_item(st, hf_tid, tvb, offset, 2, FALSE);
729 plen = tvb_get_ntohs(tvb, offset);
730 proto_tree_add_item(st, hf_plen, tvb, offset, 2, FALSE);
735 offset=dissect_sdp_error_response(st, tvb, offset);
738 offset=dissect_sdp_service_search_request(st, tvb, offset);
741 offset=dissect_sdp_service_search_response(st, tvb, offset);
744 offset=dissect_sdp_service_attribute_request(st, tvb, offset);
747 offset=dissect_sdp_service_attribute_response(st, tvb, offset);
750 offset=dissect_sdp_service_search_attribute_request(st, tvb, offset);
753 offset=dissect_sdp_service_search_attribute_response(st, tvb, offset);
760 proto_register_btsdp(void)
762 static hf_register_info hf[] = {
765 FT_UINT8, BASE_HEX, VALS(vs_pduid), 0,
769 {"TransactionID", "btsdp.tid",
770 FT_UINT16, BASE_HEX, NULL, 0,
771 "Transaction ID", HFILL}
774 {"ParameterLength", "btsdp.len",
775 FT_UINT16, BASE_DEC, NULL, 0,
776 "ParameterLength", HFILL}
779 {"ErrorCode", "btsdp.error_code",
780 FT_UINT16, BASE_HEX, NULL, 0,
783 {&hf_ssr_total_count,
784 {"TotalServiceRecordCount", "btsdp.ssr.total_count",
785 FT_UINT16, BASE_DEC, NULL, 0,
786 "Total count of service records", HFILL}
788 {&hf_ssr_current_count,
789 {"CurrentServiceRecordCount", "btsdp.ssr.current_count",
790 FT_UINT16, BASE_DEC, NULL, 0,
791 "count of service records in this message", HFILL}
793 {&hf_ssares_al_bytecount,
794 {"AttributeListsByteCount", "btsdp.ssares.byte_count",
795 FT_UINT16, BASE_DEC, NULL, 0,
796 "count of bytes in attribute list response", HFILL}
800 /* Setup protocol subtree array */
802 static gint *ett[] = {
806 &ett_btsdp_attribute,
807 &ett_btsdp_service_search_pattern,
808 &ett_btsdp_attribute_idlist
811 proto_btsdp = proto_register_protocol("Bluetooth SDP", "BTSDP", "btsdp");
813 register_dissector("btsdp", dissect_btsdp, proto_btsdp);
815 /* Required function calls to register the header fields and subtrees used */
816 proto_register_field_array(proto_btsdp, hf, array_length(hf));
817 proto_register_subtree_array(ett, array_length(ett));
822 proto_reg_handoff_btsdp(void)
824 dissector_handle_t btsdp_handle;
826 btsdp_handle = find_dissector("btsdp");
827 dissector_add("btl2cap.psm", BTL2CAP_PSM_SDP, btsdp_handle);