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_error_code[] = {
73 {0x0001, "Invalid/unsupported SDP version"},
74 {0x0002, "Invalid Service Record Handle"},
75 {0x0003, "Invalid request syntax"},
76 {0x0004, "Invalid PDU size"},
77 {0x0005, "Invalid Continuation State"},
78 {0x0006, "Insufficient Resources to satisfy Request"},
82 static const value_string vs_general_attribute_id[] = {
83 {0x0000, "ServiceRecordHandle"},
84 {0x0001, "ServiceClassIDList"},
85 {0x0002, "ServiceRecordState"},
86 {0x0003, "ServiceID"},
87 {0x0004, "ProtocolDescriptorList"},
88 {0x0005, "BrowseGroupList"},
89 {0x0006, "LanguageBaseAttributeIDList"},
90 {0x0007, "ServiceinfoTimeToLive"},
91 {0x0008, "ServiceAvailability"},
92 {0x0009, "BluetoothProfileDescriptorList"},
93 {0x000a, "DocumentationURL"},
94 {0x000b, "ClientExecutableURL"},
96 {0x0100, "Service Name"},
97 {0x0101, "Service Description"},
98 {0x0102, "Service Provider"},
103 static const value_string vs_protocols[] = {
118 {0x0012, "HardcopyControlChannel"},
119 {0x0014, "HardcopyDataChannel"},
120 {0x0016, "HardcopyNotification"},
124 {0x001D, "UDI_C-Plane"},
129 static const value_string vs_service_classes[] = {
145 {0x0012, "HardcopyControlChannel"},
146 {0x0014, "HardcopyDataChannel"},
147 {0x0016, "HardcopyNotification"},
151 {0x001D, "UDI_C-Plane"},
153 {0x1000, "ServiceDiscoveryServerServiceClassID"},
154 {0x1001, "BrowseGroupDescriptorServiceClassID"},
155 {0x1002, "PublicBrowseGroup"},
156 {0x1101, "SerialPort"},
157 {0x1102, "LANAccessUsingPPP"},
158 {0x1103, "DialupNetworking"},
159 {0x1104, "IrMCSync"},
160 {0x1105, "OBEXObjectPush"},
161 {0x1106, "OBEXFileTransfer"},
162 {0x1107, "IrMCSyncCommand"},
164 {0x1109, "CordlessTelephony"},
165 {0x110A, "AudioSource"},
166 {0x110B, "AudioSink"},
167 {0x110C, "A/V_RemoteControlTarget"},
168 {0x110D, "AdvancedAudioDistribution"},
169 {0x110E, "A/V_RemoteControl"},
170 {0x110F, "VideoConferencing"},
171 {0x1110, "Intercom"},
173 {0x1112, "HeadsetAudioGateway"},
175 {0x1114, "WAP_CLIENT"},
179 {0x1118, "DirectPrinting"},
180 {0x1119, "ReferencePrinting"},
182 {0x111B, "ImagingResponder"},
183 {0x111C, "ImagingAutomaticArchive"},
184 {0x111D, "ImagingReferencedObjects"},
188 {0x1118, "DirectPrinting"},
189 {0x1119, "ReferencePrinting"},
191 {0x111B, "ImagingResponder"},
192 {0x111C, "ImagingAutomaticArchive"},
193 {0x111D, "ImagingReferencedObjects"},
194 {0x111E, "Handsfree"},
195 {0x111F, "HandsfreeAudioGateway"},
196 {0x1120, "DirectPrintingReferenceObjectsService"},
197 {0x1121, "ReflectedUI"},
198 {0x1122, "BasicPrinting"},
199 {0x1123, "PrintingStatus"},
200 {0x1124, "HumanInterfaceDeviceService"},
201 {0x1125, "HardcopyCableReplacement"},
202 {0x1126, "HCR_Print"},
203 {0x1127, "HCR_Scan"},
204 {0x1128, "Common_ISDN_Access"},
205 {0x1129, "VideoConferencingGW"},
208 {0x112C, "Audio/Video"},
209 {0x112D, "SIM_Access"},
210 {0x1200, "PnPInformation"},
211 {0x1201, "GenericNetworking"},
212 {0x1202, "GenericFileTransfer"},
213 {0x1203, "GenericAudio"},
214 {0x1204, "GenericTelephony"},
215 {0x1205, "UPNP_Service"},
216 {0x1206, "UPNP_IP_Service"},
217 {0x1300, "ESDP_UPNP_IP_PAN"},
218 {0x1301, "ESDP_UPNP_IP_LAP"},
219 {0x1302, "ESDP_UPNP_L2CAP"},
226 get_type_length(tvbuff_t *tvb, int offset, int *length)
229 guint8 byte0 = tvb_get_guint8(tvb, offset);
232 switch (byte0 & 0x07) {
234 size = (byte0 >> 3) == 0 ? 0 : 1;
249 size = tvb_get_guint8(tvb, offset);
253 size = tvb_get_ntohs(tvb, offset);
257 size = tvb_get_ntohl(tvb, offset);
268 get_uint_by_size(tvbuff_t *tvb, int off, int size)
272 return tvb_get_guint8(tvb, off);
274 return tvb_get_ntohs(tvb, off);
276 return tvb_get_ntohl(tvb, off);
284 get_int_by_size(tvbuff_t *tvb, int off, int size)
288 return tvb_get_guint8(tvb, off);
290 return tvb_get_ntohs(tvb, off);
292 return tvb_get_ntohl(tvb, off);
300 dissect_attribute_id_list(proto_tree *t, tvbuff_t *tvb, int offset)
304 int start_offset, bytes_to_go;
307 ti = proto_tree_add_text(t, tvb, offset, 2, "AttributeIDList");
308 st = proto_item_add_subtree(ti, ett_btsdp_attribute_idlist);
310 offset = get_type_length(tvb, offset, &bytes_to_go);
311 proto_item_set_len(ti, offset - start_offset + bytes_to_go);
313 while(bytes_to_go>0){
314 guint8 byte0 = tvb_get_guint8(tvb, offset);
316 if (byte0 == 0x09) { /* 16 bit attribute id */
318 proto_tree_add_text(st, tvb, offset, 3, "0x%04x",
319 tvb_get_ntohs(tvb, offset + 1));
322 } else if (byte0 == 0x0a) { /* 32 bit attribute range */
324 proto_tree_add_text(st, tvb, offset, 5, "0x%04x - 0x%04x",
325 tvb_get_ntohs(tvb, offset + 1),
326 tvb_get_ntohs(tvb, offset + 3));
331 return offset - start_offset;
336 dissect_sdp_error_response(proto_tree *t, tvbuff_t *tvb, int offset) {
338 proto_tree_add_item(t, hf_error_code, tvb, offset, 2, FALSE);
346 dissect_sdp_type(proto_tree *t, tvbuff_t *tvb, int offset, char **attr_val)
348 #define MAX_SDP_LEN 1024
349 int strpos=0, size, start_offset, type_size;
356 str=ep_alloc(MAX_SDP_LEN+1);
360 byte0=tvb_get_guint8(tvb, offset);
361 type=(byte0>>3)&0x1f;
362 size_index=byte0&0x07;
365 offset = get_type_length(tvb, offset, &size);
366 type_size = offset - start_offset + size;
371 proto_tree_add_text(t, tvb, start_offset, type_size, "Nil ");
372 if(strpos<MAX_SDP_LEN){
373 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "Nil ");
377 guint32 val = get_uint_by_size(tvb, offset, size_index);
378 proto_tree_add_text(t, tvb, start_offset, type_size,
379 "unsigned int %d ", val);
380 if(strpos<MAX_SDP_LEN){
381 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "%u ", val);
386 guint32 val = get_int_by_size(tvb, offset, size_index);
387 proto_tree_add_text(t, tvb, start_offset, type_size,
388 "signed int %d ", val);
389 if(strpos<MAX_SDP_LEN){
390 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "%d ", val);
395 char *ptr = tvb_bytes_to_str(tvb, offset, size);
399 guint16 id = tvb_get_ntohs(tvb, offset);
400 const char *uuid_name = val_to_str(id, vs_service_classes, "Unknown");
402 proto_tree_add_text(t, tvb, start_offset, type_size,
403 "%s(0x%s) ", uuid_name, ptr);
404 if(strpos<MAX_SDP_LEN){
405 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "UUID:%s (0x%s) ", uuid_name, ptr);
409 proto_tree_add_text(t, tvb, start_offset, type_size,
411 if(strpos<MAX_SDP_LEN){
412 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "0x%s ", ptr);
417 case 8: /* fall through */
419 char *ptr = tvb_get_ephemeral_string(tvb, offset, size);
421 proto_tree_add_text(t, tvb, start_offset, type_size, "%s \"%s\"",
422 type == 8 ? "URL" : "String", ptr);
423 if(strpos<MAX_SDP_LEN){
424 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "%s ", ptr);
429 guint8 var = tvb_get_guint8(tvb, offset);
431 proto_tree_add_text(t, tvb, start_offset, type_size, "%s",
432 var ? "true" : "false");
433 if(strpos<MAX_SDP_LEN){
434 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "%s ", var?"true":"false");
438 case 6: /* Data Element sequence */
439 case 7: /* Data Element alternative */ {
442 int bytes_to_go = size;
446 ti = proto_tree_add_text(t, tvb, start_offset, type_size, "%s",
447 type == 6 ? "Data Element sequence" :
448 "Data Element alternative");
449 st = proto_item_add_subtree(ti, ett_btsdp_des);
451 if(strpos<MAX_SDP_LEN){
452 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "{ ");
455 while(bytes_to_go > 0){
457 if(strpos<MAX_SDP_LEN){
458 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, ", ");
464 size = dissect_sdp_type(st, tvb, offset, &substr);
465 if(strpos<MAX_SDP_LEN){
466 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "%s ", substr);
472 if(strpos<MAX_SDP_LEN){
473 strpos+=g_snprintf(str+strpos, MAX_SDP_LEN-strpos, "} ");
479 /* make sure the string is 0 terminated */
489 dissect_sdp_service_attribute(proto_tree *tree, tvbuff_t *tvb, int offset)
492 proto_tree *st, *ti_sa, *ti_av;
494 const char *att_name;
498 id = tvb_get_ntohs(tvb, offset+1);
499 att_name = val_to_str(id, vs_general_attribute_id, "Unknown");
501 ti_sa = proto_tree_add_text(tree, tvb, offset, -1,
502 "Service Attribute: id = %s (0x%x)", att_name, id);
503 st = proto_item_add_subtree(ti_sa, ett_btsdp_attribute);
506 proto_tree_add_text(st, tvb, offset, 3, "Attribute ID: %s (0x%x)", att_name, id);
507 ti_av = proto_tree_add_text(st, tvb, offset + 3, -1, "Attribute Value");
508 st = proto_item_add_subtree(ti_av, ett_btsdp_attribute);
511 size = dissect_sdp_type(st, tvb, offset + 3, &attr_val);
512 proto_item_append_text(ti_sa, ", value = %s", attr_val);
515 proto_item_set_len(ti_sa, size + 3);
516 proto_item_set_len(ti_av, size);
518 return offset+size+3;
523 dissect_sdp_service_attribute_list(proto_tree *tree, tvbuff_t *tvb, int offset)
527 int start_offset = offset, len;
529 offset = get_type_length(tvb, offset, &len);
531 ti = proto_tree_add_text(tree, tvb, start_offset, -1, "AttributeList");
532 st = proto_item_add_subtree(ti, ett_btsdp_attribute);
538 while (offset - start_offset < len) {
539 offset = dissect_sdp_service_attribute(st, tvb, offset);
542 proto_item_set_len(ti, offset - start_offset);
549 dissect_sdp_service_attribute_list_array(proto_tree *tree, tvbuff_t *tvb, int offset)
553 int start_offset, len;
556 offset = get_type_length(tvb, offset, &len);
557 ti = proto_tree_add_text(tree, tvb, start_offset, offset-start_offset+len, "AttributeLists");
558 st = proto_item_add_subtree(ti, ett_btsdp_attribute);
561 while(offset-start_offset < len) {
562 offset = dissect_sdp_service_attribute_list(st, tvb, offset);
571 dissect_sdp_service_search_attribute_response(proto_tree *tree, tvbuff_t *tvb, int offset)
574 proto_tree_add_item(tree, hf_ssares_al_bytecount, tvb, offset, 2, FALSE);
577 offset += dissect_sdp_service_attribute_list_array(tree, tvb, offset);
584 dissect_sdp_service_search_attribute_request(proto_tree *t, tvbuff_t *tvb, int offset)
589 int size, bytes_to_go;
592 start_offset = offset;
593 ti = proto_tree_add_text(t, tvb, offset, 2, "ServiceSearchPattern");
594 st = proto_item_add_subtree(ti, ett_btsdp_attribute);
596 offset = get_type_length(tvb, offset, &bytes_to_go);
597 proto_item_set_len(ti, offset - start_offset + bytes_to_go);
600 while(bytes_to_go>0) {
601 size = dissect_sdp_type(st, tvb, offset, &str);
602 proto_item_append_text(st, " %s", str);
607 /* dissect maximum attribute byte count */
608 proto_tree_add_text(t, tvb, offset, 2, "MaximumAttributeByteCount: %d", tvb_get_ntohs(tvb, offset));
612 offset += dissect_attribute_id_list(t, tvb, offset);
614 proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
620 dissect_sdp_service_attribute_response(proto_tree *t, tvbuff_t *tvb, int offset)
622 proto_tree_add_text(t, tvb, offset, 2, "AttributeListByteCount: %d",
623 tvb_get_ntohs(tvb, offset));
626 offset = dissect_sdp_service_attribute_list(t, tvb, offset);
628 proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
629 offset+=tvb_length_remaining(tvb, offset);
636 dissect_sdp_service_attribute_request(proto_tree *t, tvbuff_t *tvb, int offset)
638 proto_tree_add_text(t, tvb, offset, 4, "ServiceRecordHandle: 0x%x",
639 tvb_get_ntohl(tvb, offset));
642 proto_tree_add_text(t, tvb, offset, 2, "MaximumAttributeByteCount: %d",
643 tvb_get_ntohs(tvb, offset));
646 offset += dissect_attribute_id_list(t, tvb, offset);
648 proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
649 offset+=tvb_length_remaining(tvb, offset);
655 dissect_sdp_service_search_request(proto_tree *t, tvbuff_t *tvb, int offset)
657 int start_offset, bytes_to_go, size;
662 ti = proto_tree_add_text(t, tvb, offset, 2, "ServiceSearchPattern");
663 st = proto_item_add_subtree(ti, ett_btsdp_service_search_pattern);
665 offset = get_type_length(tvb, offset, &bytes_to_go);
666 proto_item_set_len(ti, offset - start_offset + bytes_to_go);
668 while(bytes_to_go>0){
670 size = dissect_sdp_type(st, tvb, offset, &str);
671 proto_item_append_text(st, " %s", str);
676 /* dissect maximum service record count */
678 proto_tree_add_text(t, tvb, offset, 2, "MaximumServiceRecordCount: %d",
679 tvb_get_ntohs(tvb, offset));
682 proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
683 offset+=tvb_length_remaining(tvb, offset);
689 dissect_sdp_service_search_response(proto_tree *t, tvbuff_t *tvb, int offset)
695 proto_tree_add_item(t, hf_ssr_total_count, tvb, offset, 2, FALSE);
698 curr_count = tvb_get_ntohs(tvb, offset);
699 proto_tree_add_item(t, hf_ssr_current_count, tvb, offset, 2, FALSE);
702 ti = proto_tree_add_text(t, tvb, offset,
703 curr_count * 4, "ServiceRecordHandleList");
704 st = proto_item_add_subtree(ti, ett_btsdp_ssr);
708 proto_tree_add_text(st, tvb, offset, 4, "0x%x", tvb_get_ntohl(tvb, offset));
713 proto_tree_add_text(t, tvb, offset, -1, "ContinuationState");
714 offset+=tvb_length_remaining(tvb, offset);
721 dissect_btsdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
727 const char *pdu_name;
730 if (check_col(pinfo->cinfo, COL_PROTOCOL))
731 col_set_str(pinfo->cinfo, COL_PROTOCOL, "SDP");
733 ti = proto_tree_add_item(tree, proto_btsdp, tvb, 0, -1, FALSE);
734 st = proto_item_add_subtree(ti, ett_btsdp);
737 pdu = tvb_get_guint8(tvb, offset);
738 proto_tree_add_item(st, hf_pduid, tvb, offset, 1, FALSE);
739 pdu_name = val_to_str(pdu, vs_pduid, "Unknown");
740 if (check_col(pinfo->cinfo, COL_INFO)) {
741 col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s ",pinfo->p2p_dir==P2P_DIR_SENT?"Sent":"Rcvd", pdu_name);
743 proto_item_append_text(ti, ": %s (0x%x)", pdu_name, pdu);
747 proto_tree_add_item(st, hf_tid, tvb, offset, 2, FALSE);
751 plen = tvb_get_ntohs(tvb, offset);
752 proto_tree_add_item(st, hf_plen, tvb, offset, 2, FALSE);
757 offset=dissect_sdp_error_response(st, tvb, offset);
760 offset=dissect_sdp_service_search_request(st, tvb, offset);
763 offset=dissect_sdp_service_search_response(st, tvb, offset);
766 offset=dissect_sdp_service_attribute_request(st, tvb, offset);
769 offset=dissect_sdp_service_attribute_response(st, tvb, offset);
772 offset=dissect_sdp_service_search_attribute_request(st, tvb, offset);
775 offset=dissect_sdp_service_search_attribute_response(st, tvb, offset);
782 proto_register_btsdp(void)
784 static hf_register_info hf[] = {
787 FT_UINT8, BASE_HEX, VALS(vs_pduid), 0,
791 {"TransactionID", "btsdp.tid",
792 FT_UINT16, BASE_HEX, NULL, 0,
793 "Transaction ID", HFILL}
796 {"ParameterLength", "btsdp.len",
797 FT_UINT16, BASE_DEC, NULL, 0,
798 "ParameterLength", HFILL}
801 {"ErrorCode", "btsdp.error_code",
802 FT_UINT16, BASE_HEX, NULL, 0,
805 {&hf_ssr_total_count,
806 {"TotalServiceRecordCount", "btsdp.ssr.total_count",
807 FT_UINT16, BASE_DEC, NULL, 0,
808 "Total count of service records", HFILL}
810 {&hf_ssr_current_count,
811 {"CurrentServiceRecordCount", "btsdp.ssr.current_count",
812 FT_UINT16, BASE_DEC, NULL, 0,
813 "count of service records in this message", HFILL}
815 {&hf_ssares_al_bytecount,
816 {"AttributeListsByteCount", "btsdp.ssares.byte_count",
817 FT_UINT16, BASE_DEC, NULL, 0,
818 "count of bytes in attribute list response", HFILL}
822 /* Setup protocol subtree array */
824 static gint *ett[] = {
828 &ett_btsdp_attribute,
829 &ett_btsdp_service_search_pattern,
830 &ett_btsdp_attribute_idlist
833 proto_btsdp = proto_register_protocol("Bluetooth SDP", "BTSDP", "btsdp");
835 register_dissector("btsdp", dissect_btsdp, proto_btsdp);
837 /* Required function calls to register the header fields and subtrees used */
838 proto_register_field_array(proto_btsdp, hf, array_length(hf));
839 proto_register_subtree_array(ett, array_length(ett));
844 proto_reg_handoff_btsdp(void)
846 dissector_handle_t btsdp_handle;
848 btsdp_handle = find_dissector("btsdp");
849 dissector_add("btl2cap.psm", BTL2CAP_PSM_SDP, btsdp_handle);