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 */
41static 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 */
57static uint8_t		buffer[NRECS * attrs_len][BSIZE];
58
59/* SDP attributes */
60static 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
73static void
74print_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
156static void
157print_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
312static void
313print_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
386static void
387print_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 */
492static int
493do_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	/* Initialize attribute values array */
590	for (n = 0; n < values_len; n ++) {
591		values[n].flags = SDP_ATTR_INVALID;
592		values[n].attr = 0;
593		values[n].vlen = BSIZE;
594		values[n].value = buffer[n];
595	}
596
597	/* Do SDP Service Search Attribute Request */
598	n = sdp_search(xs, 1, &service, attrs_len, attrs, values_len, values);
599	if (n != 0)
600		return (ERROR);
601
602	/* Print attributes values */
603	for (n = 0; n < values_len; n ++) {
604		if (values[n].flags != SDP_ATTR_OK)
605			break;
606
607		switch (values[n].attr) {
608		case SDP_ATTR_SERVICE_RECORD_HANDLE:
609			fprintf(stdout, "\n");
610			if (values[n].vlen == 5) {
611				SDP_GET8(type, values[n].value);
612				if (type == SDP_DATA_UINT32) {
613					SDP_GET32(value, values[n].value);
614					fprintf(stdout, "Record Handle: " \
615							"%#8.8x\n", value);
616				} else
617					fprintf(stderr, "Invalid type=%#x " \
618							"Record Handle " \
619							"attribute!\n", type);
620			} else
621				fprintf(stderr, "Invalid size=%d for Record " \
622						"Handle attribute\n",
623						values[n].vlen);
624			break;
625
626		case SDP_ATTR_SERVICE_CLASS_ID_LIST:
627			fprintf(stdout, "Service Class ID List:\n");
628			print_service_class_id_list(values[n].value,
629					values[n].value + values[n].vlen);
630			break;
631
632		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
633			fprintf(stdout, "Protocol Descriptor List:\n");
634			print_protocol_descriptor_list(values[n].value,
635					values[n].value + values[n].vlen);
636			break;
637
638		case SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST:
639			fprintf(stdout, "Bluetooth Profile Descriptor List:\n");
640			print_bluetooth_profile_descriptor_list(values[n].value,
641					values[n].value + values[n].vlen);
642			break;
643
644		default:
645			fprintf(stderr, "Unexpected attribute ID=%#4.4x\n",
646					values[n].attr);
647			break;
648		}
649	}
650
651	return (OK);
652} /* do_sdp_search */
653
654/* Perform SDP browse command */
655static int
656do_sdp_browse(void *xs, int argc, char **argv)
657{
658#undef	_STR
659#undef	STR
660#define	_STR(x)	#x
661#define	STR(x)	_STR(x)
662
663	static char const * const	av[] = {
664		STR(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP),
665		NULL
666	};
667
668	switch (argc) {
669	case 0:
670		argc = 1;
671		argv = (char **) av;
672		/* FALL THROUGH */
673	case 1:
674		return (do_sdp_search(xs, argc, argv));
675	}
676
677	return (USAGE);
678} /* do_sdp_browse */
679
680/* List of SDP commands */
681struct sdp_command	sdp_commands[] = {
682{
683"Browse [<Group>]",
684"Browse for services. The <Group> parameter is a 16-bit UUID of the group\n" \
685"to browse. If omitted <Group> is set to Public Browse Group.\n\n" \
686"\t<Group> - xxxx; 16-bit UUID of the group to browse\n",
687do_sdp_browse
688},
689{
690"Search <Service>",
691"Search for the <Service>. The <Service> parameter is a 16-bit UUID of the\n" \
692"service to search for. For some services it is possible to use service name\n"\
693"instead of service UUID\n\n" \
694"\t<Service> - xxxx; 16-bit UUID of the service to search for\n\n" \
695"\tKnown service names\n" \
696"\t===================\n" \
697"\tCIP   - Common ISDN Access\n" \
698"\tCTP   - Cordless Telephony\n" \
699"\tDUN   - DialUp Networking\n" \
700"\tFAX   - Fax\n" \
701"\tFTRN  - OBEX File Transfer\n" \
702"\tGN    - GN\n" \
703"\tHID   - Human Interface Device\n" \
704"\tHSET  - Headset\n" \
705"\tLAN   - LAN Access Using PPP\n" \
706"\tNAP   - Network Access Point\n" \
707"\tOPUSH - OBEX Object Push\n" \
708"\tSP    - Serial Port\n",
709do_sdp_search
710},
711{ NULL, NULL, NULL }
712};
713
714