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