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