1 /* $NetBSD: search.c,v 1.6 2007/11/06 21:35:52 plunky Exp $ */ 2 /* $DragonFly: src/usr.bin/sdpquery/search.c,v 1.2 2008/02/11 20:09:42 swildner Exp $ */ 3 4 /*- 5 * Copyright (c) 2006 Itronix Inc. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of Itronix Inc. may not be used to endorse 17 * or promote products derived from this software without specific 18 * prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY 24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 * ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 /* 33 * search.c 34 * 35 * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com> 36 * All rights reserved. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 50 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 57 * SUCH DAMAGE. 58 * 59 * $Id: search.c,v 1.6 2007/11/06 21:35:52 plunky Exp $ 60 * $FreeBSD: src/usr.sbin/bluetooth/sdpcontrol/search.c,v 1.4 2005/05/27 19:11:33 emax Exp $ 61 */ 62 63 #include <netinet/in.h> 64 #include <bluetooth.h> 65 #include <err.h> 66 #include <errno.h> 67 #include <ctype.h> 68 #include <sdp.h> 69 #include <stdio.h> 70 #include <stdlib.h> 71 72 #include "sdpquery.h" 73 74 /* List of the attributes we are looking for */ 75 static uint32_t attrs[] = 76 { 77 SDP_ATTR_RANGE( SDP_ATTR_SERVICE_RECORD_HANDLE, 78 SDP_ATTR_SERVICE_RECORD_HANDLE), 79 SDP_ATTR_RANGE( SDP_ATTR_SERVICE_CLASS_ID_LIST, 80 SDP_ATTR_SERVICE_CLASS_ID_LIST), 81 SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, 82 SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST), 83 SDP_ATTR_RANGE( SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, 84 SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST) 85 }; 86 #define attrs_len (sizeof(attrs)/sizeof(attrs[0])) 87 88 /* Buffer for the attributes */ 89 #define NRECS 25 /* request this much records from the SDP server */ 90 #define BSIZE 256 /* one attribute buffer size */ 91 static uint8_t buffer[NRECS * attrs_len][BSIZE]; 92 93 /* SDP attributes */ 94 static sdp_attr_t values[NRECS * attrs_len]; 95 #define values_len (sizeof(values)/sizeof(values[0])) 96 97 /* 98 * Print Service Class ID List 99 * 100 * The ServiceClassIDList attribute consists of a data element sequence in 101 * which each data element is a UUID representing the service classes that 102 * a given service record conforms to. The UUIDs are listed in order from 103 * the most specific class to the most general class. The ServiceClassIDList 104 * must contain at least one service class UUID. 105 */ 106 107 static void 108 print_service_class_id_list(uint8_t const *start, uint8_t const *end) 109 { 110 uint32_t type, len, value; 111 112 if (end - start < 2) { 113 fprintf(stderr, "Invalid Service Class ID List. " \ 114 "Too short, len=%zd\n", end - start); 115 return; 116 } 117 118 SDP_GET8(type, start); 119 switch (type) { 120 case SDP_DATA_SEQ8: 121 SDP_GET8(len, start); 122 break; 123 124 case SDP_DATA_SEQ16: 125 SDP_GET16(len, start); 126 break; 127 128 case SDP_DATA_SEQ32: 129 SDP_GET32(len, start); 130 break; 131 132 default: 133 fprintf(stderr, "Invalid Service Class ID List. " \ 134 "Not a sequence, type=%#x\n", type); 135 return; 136 /* NOT REACHED */ 137 } 138 139 while (start < end) { 140 SDP_GET8(type, start); 141 switch (type) { 142 case SDP_DATA_UUID16: 143 SDP_GET16(value, start); 144 fprintf(stdout, "\t%s (%#4.4x)\n", 145 sdp_uuid2desc(value), value); 146 break; 147 148 case SDP_DATA_UUID32: 149 SDP_GET32(value, start); 150 fprintf(stdout, "\t%#8.8x\n", value); 151 break; 152 153 case SDP_DATA_UUID128: { 154 int128_t uuid; 155 156 SDP_GET_UUID128(&uuid, start); 157 fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n", 158 ntohl(*(uint32_t *)&uuid.b[0]), 159 ntohs(*(uint16_t *)&uuid.b[4]), 160 ntohs(*(uint16_t *)&uuid.b[6]), 161 ntohs(*(uint16_t *)&uuid.b[8]), 162 ntohs(*(uint16_t *)&uuid.b[10]), 163 ntohl(*(uint32_t *)&uuid.b[12])); 164 } break; 165 166 default: 167 fprintf(stderr, "Invalid Service Class ID List. " \ 168 "Not a UUID, type=%#x\n", type); 169 return; 170 /* NOT REACHED */ 171 } 172 } 173 } /* print_service_class_id_list */ 174 175 /* 176 * Print Protocol Descriptor List 177 * 178 * If the ProtocolDescriptorList describes a single stack, it takes the form 179 * of a data element sequence in which each element of the sequence is a 180 * protocol descriptor. Each protocol descriptor is, in turn, a data element 181 * sequence whose first element is a UUID identifying the protocol and whose 182 * successive elements are protocol-specific parameters. The protocol 183 * descriptors are listed in order from the lowest layer protocol to the 184 * highest layer protocol used to gain access to the service. If it is possible 185 * for more than one kind of protocol stack to be used to gain access to the 186 * service, the ProtocolDescriptorList takes the form of a data element 187 * alternative where each member is a data element sequence as described above. 188 */ 189 190 static void 191 print_protocol_descriptor(uint8_t const *start, uint8_t const *end) 192 { 193 union { 194 uint8_t uint8; 195 uint16_t uint16; 196 uint32_t uint32; 197 uint64_t uint64; 198 int128_t int128; 199 } value; 200 uint32_t type, len, param; 201 202 /* Get Protocol UUID */ 203 SDP_GET8(type, start); 204 switch (type) { 205 case SDP_DATA_UUID16: 206 SDP_GET16(value.uint16, start); 207 fprintf(stdout, "\t%s (%#4.4x)\n", sdp_uuid2desc(value.uint16), 208 value.uint16); 209 break; 210 211 case SDP_DATA_UUID32: 212 SDP_GET32(value.uint32, start); 213 fprintf(stdout, "\t%#8.8x\n", value.uint32); 214 break; 215 216 case SDP_DATA_UUID128: 217 SDP_GET_UUID128(&value.int128, start); 218 fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n", 219 ntohl(*(uint32_t *)&value.int128.b[0]), 220 ntohs(*(uint16_t *)&value.int128.b[4]), 221 ntohs(*(uint16_t *)&value.int128.b[6]), 222 ntohs(*(uint16_t *)&value.int128.b[8]), 223 ntohs(*(uint16_t *)&value.int128.b[10]), 224 ntohl(*(uint32_t *)&value.int128.b[12])); 225 break; 226 227 default: 228 fprintf(stderr, "Invalid Protocol Descriptor. " \ 229 "Not a UUID, type=%#x\n", type); 230 return; 231 /* NOT REACHED */ 232 } 233 234 /* Protocol specific parameters */ 235 for (param = 1; start < end; param ++) { 236 fprintf(stdout, "\t\tProtocol specific parameter #%d: ", param); 237 238 SDP_GET8(type, start); 239 switch (type) { 240 case SDP_DATA_NIL: 241 fprintf(stdout, "nil\n"); 242 break; 243 244 case SDP_DATA_UINT8: 245 case SDP_DATA_INT8: 246 case SDP_DATA_BOOL: 247 SDP_GET8(value.uint8, start); 248 fprintf(stdout, "u/int8/bool %u\n", value.uint8); 249 break; 250 251 case SDP_DATA_UINT16: 252 case SDP_DATA_INT16: 253 case SDP_DATA_UUID16: 254 SDP_GET16(value.uint16, start); 255 fprintf(stdout, "u/int/uuid16 %u\n", value.uint16); 256 break; 257 258 case SDP_DATA_UINT32: 259 case SDP_DATA_INT32: 260 case SDP_DATA_UUID32: 261 SDP_GET32(value.uint32, start); 262 fprintf(stdout, "u/int/uuid32 %u\n", value.uint32); 263 break; 264 265 case SDP_DATA_UINT64: 266 case SDP_DATA_INT64: 267 SDP_GET64(value.uint64, start); 268 fprintf(stdout, "u/int64 %ju\n", value.uint64); 269 break; 270 271 case SDP_DATA_UINT128: 272 case SDP_DATA_INT128: 273 SDP_GET128(&value.int128, start); 274 fprintf(stdout, "u/int128 %#8.8x%8.8x%8.8x%8.8x\n", 275 *(uint32_t *)&value.int128.b[0], 276 *(uint32_t *)&value.int128.b[4], 277 *(uint32_t *)&value.int128.b[8], 278 *(uint32_t *)&value.int128.b[12]); 279 break; 280 281 case SDP_DATA_UUID128: 282 SDP_GET_UUID128(&value.int128, start); 283 fprintf(stdout, "uuid128 %#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n", 284 ntohl(*(uint32_t *)&value.int128.b[0]), 285 ntohs(*(uint16_t *)&value.int128.b[4]), 286 ntohs(*(uint16_t *)&value.int128.b[6]), 287 ntohs(*(uint16_t *)&value.int128.b[8]), 288 ntohs(*(uint16_t *)&value.int128.b[10]), 289 ntohl(*(uint32_t *)&value.int128.b[12])); 290 break; 291 292 case SDP_DATA_STR8: 293 case SDP_DATA_URL8: 294 SDP_GET8(len, start); 295 fprintf(stdout, "%*.*s\n", len, len, start); 296 start += len; 297 break; 298 299 case SDP_DATA_STR16: 300 case SDP_DATA_URL16: 301 SDP_GET16(len, start); 302 fprintf(stdout, "%*.*s\n", len, len, start); 303 start += len; 304 break; 305 306 case SDP_DATA_STR32: 307 case SDP_DATA_URL32: 308 SDP_GET32(len, start); 309 fprintf(stdout, "%*.*s\n", len, len, start); 310 start += len; 311 break; 312 313 case SDP_DATA_SEQ8: 314 case SDP_DATA_ALT8: 315 SDP_GET8(len, start); 316 for (; len > 0; start ++, len --) 317 fprintf(stdout, "%#2.2x ", *start); 318 fprintf(stdout, "\n"); 319 break; 320 321 case SDP_DATA_SEQ16: 322 case SDP_DATA_ALT16: 323 SDP_GET16(len, start); 324 for (; len > 0; start ++, len --) 325 fprintf(stdout, "%#2.2x ", *start); 326 fprintf(stdout, "\n"); 327 break; 328 329 case SDP_DATA_SEQ32: 330 case SDP_DATA_ALT32: 331 SDP_GET32(len, start); 332 for (; len > 0; start ++, len --) 333 fprintf(stdout, "%#2.2x ", *start); 334 fprintf(stdout, "\n"); 335 break; 336 337 default: 338 fprintf(stderr, "Invalid Protocol Descriptor. " \ 339 "Unknown data type: %#02x\n", type); 340 return; 341 /* NOT REACHED */ 342 } 343 } 344 } /* print_protocol_descriptor */ 345 346 static void 347 print_protocol_descriptor_list(uint8_t const *start, uint8_t const *end) 348 { 349 uint32_t type, len; 350 351 if (end - start < 2) { 352 fprintf(stderr, "Invalid Protocol Descriptor List. " \ 353 "Too short, len=%zd\n", end - start); 354 return; 355 } 356 357 SDP_GET8(type, start); 358 switch (type) { 359 case SDP_DATA_SEQ8: 360 SDP_GET8(len, start); 361 break; 362 363 case SDP_DATA_SEQ16: 364 SDP_GET16(len, start); 365 break; 366 367 case SDP_DATA_SEQ32: 368 SDP_GET32(len, start); 369 break; 370 371 default: 372 fprintf(stderr, "Invalid Protocol Descriptor List. " \ 373 "Not a sequence, type=%#x\n", type); 374 return; 375 /* NOT REACHED */ 376 } 377 378 while (start < end) { 379 SDP_GET8(type, start); 380 switch (type) { 381 case SDP_DATA_SEQ8: 382 SDP_GET8(len, start); 383 break; 384 385 case SDP_DATA_SEQ16: 386 SDP_GET16(len, start); 387 break; 388 389 case SDP_DATA_SEQ32: 390 SDP_GET32(len, start); 391 break; 392 393 default: 394 fprintf(stderr, "Invalid Protocol Descriptor List. " \ 395 "Not a sequence, type=%#x\n", type); 396 return; 397 /* NOT REACHED */ 398 } 399 400 print_protocol_descriptor(start, start + len); 401 start += len; 402 } 403 } /* print_protocol_descriptor_list */ 404 405 /* 406 * Print Bluetooth Profile Descriptor List 407 * 408 * The BluetoothProfileDescriptorList attribute consists of a data element 409 * sequence in which each element is a profile descriptor that contains 410 * information about a Bluetooth profile to which the service represented by 411 * this service record conforms. Each profile descriptor is a data element 412 * sequence whose first element is the UUID assigned to the profile and whose 413 * second element is a 16-bit profile version number. Each version of a profile 414 * is assigned a 16-bit unsigned integer profile version number, which consists 415 * of two 8-bit fields. The higher-order 8 bits contain the major version 416 * number field and the lower-order 8 bits contain the minor version number 417 * field. 418 */ 419 420 static void 421 print_bluetooth_profile_descriptor_list(uint8_t const *start, uint8_t const *end) 422 { 423 uint32_t type, len, value; 424 425 if (end - start < 2) { 426 fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \ 427 "Too short, len=%zd\n", end - start); 428 return; 429 } 430 431 SDP_GET8(type, start); 432 switch (type) { 433 case SDP_DATA_SEQ8: 434 SDP_GET8(len, start); 435 break; 436 437 case SDP_DATA_SEQ16: 438 SDP_GET16(len, start); 439 break; 440 441 case SDP_DATA_SEQ32: 442 SDP_GET32(len, start); 443 break; 444 445 default: 446 fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \ 447 "Not a sequence, type=%#x\n", type); 448 return; 449 /* NOT REACHED */ 450 } 451 452 while (start < end) { 453 SDP_GET8(type, start); 454 switch (type) { 455 case SDP_DATA_SEQ8: 456 SDP_GET8(len, start); 457 break; 458 459 case SDP_DATA_SEQ16: 460 SDP_GET16(len, start); 461 break; 462 463 case SDP_DATA_SEQ32: 464 SDP_GET32(len, start); 465 break; 466 467 default: 468 fprintf(stderr, "Invalid Bluetooth Profile " \ 469 "Descriptor List. " \ 470 "Not a sequence, type=%#x\n", type); 471 return; 472 /* NOT REACHED */ 473 } 474 475 /* Get UUID */ 476 SDP_GET8(type, start); 477 switch (type) { 478 case SDP_DATA_UUID16: 479 SDP_GET16(value, start); 480 fprintf(stdout, "\t%s (%#4.4x) ", 481 sdp_uuid2desc(value), value); 482 break; 483 484 case SDP_DATA_UUID32: 485 SDP_GET32(value, start); 486 fprintf(stdout, "\t%#8.8x ", value); 487 break; 488 489 case SDP_DATA_UUID128: { 490 int128_t uuid; 491 492 SDP_GET_UUID128(&uuid, start); 493 fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x ", 494 ntohl(*(uint32_t *)&uuid.b[0]), 495 ntohs(*(uint16_t *)&uuid.b[4]), 496 ntohs(*(uint16_t *)&uuid.b[6]), 497 ntohs(*(uint16_t *)&uuid.b[8]), 498 ntohs(*(uint16_t *)&uuid.b[10]), 499 ntohl(*(uint32_t *)&uuid.b[12])); 500 } break; 501 502 default: 503 fprintf(stderr, "Invalid Bluetooth Profile " \ 504 "Descriptor List. " \ 505 "Not a UUID, type=%#x\n", type); 506 return; 507 /* NOT REACHED */ 508 } 509 510 /* Get version */ 511 SDP_GET8(type, start); 512 if (type != SDP_DATA_UINT16) { 513 fprintf(stderr, "Invalid Bluetooth Profile " \ 514 "Descriptor List. " \ 515 "Invalid version type=%#x\n", type); 516 return; 517 } 518 519 SDP_GET16(value, start); 520 fprintf(stdout, "ver. %d.%d\n", 521 (value >> 8) & 0xff, value & 0xff); 522 } 523 } /* print_bluetooth_profile_descriptor_list */ 524 525 struct service { 526 const char *name; 527 uint16_t class; 528 const char *description; 529 } services[] = { 530 { "CIP", SDP_SERVICE_CLASS_COMMON_ISDN_ACCESS, 531 "Common ISDN Access" }, 532 { "CTP", SDP_SERVICE_CLASS_CORDLESS_TELEPHONY, 533 "Cordless Telephony" }, 534 { "DUN", SDP_SERVICE_CLASS_DIALUP_NETWORKING, 535 "Dial Up Networking" }, 536 { "FAX", SDP_SERVICE_CLASS_FAX, 537 "Fax" }, 538 { "FTRN", SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER, 539 "Obex File Transfer" }, 540 { "GN", SDP_SERVICE_CLASS_GN, 541 "Group ad-hoc Network" }, 542 { "HID", SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE, 543 "Human Interface Device" }, 544 { "HF", SDP_SERVICE_CLASS_HANDSFREE, 545 "Handsfree" }, 546 { "HSET", SDP_SERVICE_CLASS_HEADSET, 547 "Headset" }, 548 { "LAN", SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP, 549 "Lan access using PPP" }, 550 { "NAP", SDP_SERVICE_CLASS_NAP, 551 "Network Access Point" }, 552 { "OPUSH", SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH, 553 "OBEX Object Push" }, 554 { "PANU", SDP_SERVICE_CLASS_PANU, 555 "Personal Area Networking User" }, 556 { "SP", SDP_SERVICE_CLASS_SERIAL_PORT, 557 "Serial Port" }, 558 { NULL, 0, 559 NULL } 560 }; 561 562 /* Perform SDP search command */ 563 int 564 do_sdp_search(bdaddr_t *laddr, bdaddr_t *raddr, int argc, char const **argv) 565 { 566 struct service *s; 567 void *xs; 568 char *ep; 569 uint32_t n; 570 int32_t type, value; 571 uint16_t service; 572 573 if (argc != 1) 574 goto usage; 575 576 service = strtoul(*argv, &ep, 16); 577 if (*ep != 0) { 578 for (s = services ; ; s++) { 579 if (s->name == NULL) 580 goto usage; 581 582 if (strcasecmp(s->name, *argv) == 0) 583 break; 584 } 585 service = s->class; 586 } 587 588 /* Initialize attribute values array */ 589 for (n = 0; n < values_len; n ++) { 590 values[n].flags = SDP_ATTR_INVALID; 591 values[n].attr = 0; 592 values[n].vlen = BSIZE; 593 values[n].value = buffer[n]; 594 } 595 596 if (bdaddr_any(raddr)) 597 xs = sdp_open_local(control_socket); 598 else 599 xs = sdp_open(laddr, raddr); 600 601 if (xs == NULL || (errno = sdp_error(xs)) != 0) 602 err(EXIT_FAILURE, "sdp_open"); 603 604 /* Do SDP Service Search Attribute Request */ 605 n = sdp_search(xs, 1, &service, attrs_len, attrs, values_len, values); 606 if (n != 0) 607 err(EXIT_FAILURE, "sdp_search"); 608 609 sdp_close(xs); 610 611 /* Print attributes values */ 612 for (n = 0; n < values_len; n ++) { 613 if (values[n].flags != SDP_ATTR_OK) 614 break; 615 616 switch (values[n].attr) { 617 case SDP_ATTR_SERVICE_RECORD_HANDLE: 618 fprintf(stdout, "\n"); 619 if (values[n].vlen == 5) { 620 SDP_GET8(type, values[n].value); 621 if (type == SDP_DATA_UINT32) { 622 SDP_GET32(value, values[n].value); 623 fprintf(stdout, "Record Handle: " \ 624 "%#8.8x\n", value); 625 } else 626 fprintf(stderr, "Invalid type=%#x " \ 627 "Record Handle " \ 628 "attribute!\n", type); 629 } else 630 fprintf(stderr, "Invalid size=%d for Record " \ 631 "Handle attribute\n", 632 values[n].vlen); 633 break; 634 635 case SDP_ATTR_SERVICE_CLASS_ID_LIST: 636 fprintf(stdout, "Service Class ID List:\n"); 637 print_service_class_id_list(values[n].value, 638 values[n].value + values[n].vlen); 639 break; 640 641 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: 642 fprintf(stdout, "Protocol Descriptor List:\n"); 643 print_protocol_descriptor_list(values[n].value, 644 values[n].value + values[n].vlen); 645 break; 646 647 case SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST: 648 fprintf(stdout, "Bluetooth Profile Descriptor List:\n"); 649 print_bluetooth_profile_descriptor_list(values[n].value, 650 values[n].value + values[n].vlen); 651 break; 652 653 default: 654 fprintf(stderr, "Unexpected attribute ID=%#4.4x\n", 655 values[n].attr); 656 break; 657 } 658 } 659 660 return EXIT_SUCCESS; 661 662 usage: 663 fprintf(stderr, "Known services:\n"); 664 for (s = services ; s->name != NULL ; s++) 665 fprintf(stderr, "\t%s\t%s\n", s->name, s->description); 666 667 return EXIT_FAILURE; 668 } /* do_sdp_search */ 669 670 /* Perform SDP browse command */ 671 int 672 do_sdp_browse(bdaddr_t *laddr, bdaddr_t *raddr, int argc, char const **argv) 673 { 674 #undef _STR 675 #undef STR 676 #define _STR(x) #x 677 #define STR(x) _STR(x) 678 679 static char const * av[] = { 680 STR(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP), 681 NULL 682 }; 683 684 switch (argc) { 685 case 0: 686 argc = 1; 687 argv = (char const **) av; 688 /* FALL THROUGH */ 689 case 1: 690 return (do_sdp_search(laddr, raddr, argc, argv)); 691 } 692 693 return EXIT_FAILURE; 694 } /* do_sdp_browse */ 695