diff options
Diffstat (limited to 'tools/sdptool.c')
| -rw-r--r-- | tools/sdptool.c | 4134 | 
1 files changed, 4134 insertions, 0 deletions
diff --git a/tools/sdptool.c b/tools/sdptool.c new file mode 100644 index 00000000..003d8875 --- /dev/null +++ b/tools/sdptool.c @@ -0,0 +1,4134 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2001-2002  Nokia Corporation + *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com> + *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org> + *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com> + *  Copyright (C) 2002-2003  Jean Tourrilhes <jt@hpl.hp.com> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> + +#include <netinet/in.h> + +#include "sdp-xml.h" + +#ifndef APPLE_AGENT_SVCLASS_ID +#define APPLE_AGENT_SVCLASS_ID 0x2112 +#endif + +#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, 0)) != -1) + +/* + * Convert a string to a BDADDR, with a few "enhancements" - Jean II + */ +static int estr2ba(char *str, bdaddr_t *ba) +{ +	/* Only trap "local", "any" is already dealt with */ +	if(!strcmp(str, "local")) { +		bacpy(ba, BDADDR_LOCAL); +		return 0; +	} +	return str2ba(str, ba); +} + +#define DEFAULT_VIEW	0	/* Display only known attribute */ +#define TREE_VIEW	1	/* Display full attribute tree */ +#define RAW_VIEW	2	/* Display raw tree */ +#define XML_VIEW	3	/* Display xml tree */ + +/* Pass args to the inquiry/search handler */ +struct search_context { +	char		*svc;		/* Service */ +	uuid_t		group;		/* Browse group */ +	int		view;		/* View mode */ +	uint32_t	handle;		/* Service record handle */ +}; + +typedef int (*handler_t)(bdaddr_t *bdaddr, struct search_context *arg); + +static char UUID_str[MAX_LEN_UUID_STR]; +static bdaddr_t interface; + +/* Definition of attribute members */ +struct member_def { +	char *name; +}; + +/* Definition of an attribute */ +struct attrib_def { +	int			num;		/* Numeric ID - 16 bits */ +	char			*name;		/* User readable name */ +	struct member_def	*members;	/* Definition of attribute args */ +	int			member_max;	/* Max of attribute arg definitions */ +}; + +/* Definition of a service or protocol */ +struct uuid_def { +	int			num;		/* Numeric ID - 16 bits */ +	char			*name;		/* User readable name */ +	struct attrib_def	*attribs;	/* Specific attribute definitions */ +	int			attrib_max;	/* Max of attribute definitions */ +}; + +/* Context information about current attribute */ +struct attrib_context { +	struct uuid_def		*service;	/* Service UUID, if known */ +	struct attrib_def	*attrib;	/* Description of the attribute */ +	int			member_index;	/* Index of current attribute member */ +}; + +/* Context information about the whole service */ +struct service_context { +	struct uuid_def		*service;	/* Service UUID, if known */ +}; + +/* Allow us to do nice formatting of the lists */ +static char *indent_spaces = "                                         "; + +/* ID of the service attribute. + * Most attributes after 0x200 are defined based on the service, so + * we need to find what is the service (which is messy) - Jean II */ +#define SERVICE_ATTR	0x1 + +/* Definition of the optional arguments in protocol list */ +static struct member_def protocol_members[] = { +	{ "Protocol"		}, +	{ "Channel/Port"	}, +	{ "Version"		}, +}; + +/* Definition of the optional arguments in profile list */ +static struct member_def profile_members[] = { +	{ "Profile"	}, +	{ "Version"	}, +}; + +/* Definition of the optional arguments in Language list */ +static struct member_def language_members[] = { +	{ "Code ISO639"		}, +	{ "Encoding"		}, +	{ "Base Offset"		}, +}; + +/* Name of the various common attributes. See BT assigned numbers */ +static struct attrib_def attrib_names[] = { +	{ 0x0, "ServiceRecordHandle", NULL, 0 }, +	{ 0x1, "ServiceClassIDList", NULL, 0 }, +	{ 0x2, "ServiceRecordState", NULL, 0 }, +	{ 0x3, "ServiceID", NULL, 0 }, +	{ 0x4, "ProtocolDescriptorList", +		protocol_members, sizeof(protocol_members)/sizeof(struct member_def) }, +	{ 0x5, "BrowseGroupList", NULL, 0 }, +	{ 0x6, "LanguageBaseAttributeIDList", +		language_members, sizeof(language_members)/sizeof(struct member_def) }, +	{ 0x7, "ServiceInfoTimeToLive", NULL, 0 }, +	{ 0x8, "ServiceAvailability", NULL, 0 }, +	{ 0x9, "BluetoothProfileDescriptorList", +		profile_members, sizeof(profile_members)/sizeof(struct member_def) }, +	{ 0xA, "DocumentationURL", NULL, 0 }, +	{ 0xB, "ClientExecutableURL", NULL, 0 }, +	{ 0xC, "IconURL", NULL, 0 }, +	{ 0xD, "AdditionalProtocolDescriptorLists", NULL, 0 }, +	/* Definitions after that are tricky (per profile or offset) */ +}; + +const int attrib_max = sizeof(attrib_names)/sizeof(struct attrib_def); + +/* Name of the various SPD attributes. See BT assigned numbers */ +static struct attrib_def sdp_attrib_names[] = { +	{ 0x200, "VersionNumberList", NULL, 0 }, +	{ 0x201, "ServiceDatabaseState", NULL, 0 }, +}; + +/* Name of the various SPD attributes. See BT assigned numbers */ +static struct attrib_def browse_attrib_names[] = { +	{ 0x200, "GroupID", NULL, 0 }, +}; + +/* Name of the various Device ID attributes. See Device Id spec. */ +static struct attrib_def did_attrib_names[] = { +	{ 0x200, "SpecificationID", NULL, 0 }, +	{ 0x201, "VendorID", NULL, 0 }, +	{ 0x202, "ProductID", NULL, 0 }, +	{ 0x203, "Version", NULL, 0 }, +	{ 0x204, "PrimaryRecord", NULL, 0 }, +	{ 0x205, "VendorIDSource", NULL, 0 }, +}; + +/* Name of the various HID attributes. See HID spec. */ +static struct attrib_def hid_attrib_names[] = { +	{ 0x200, "DeviceReleaseNum", NULL, 0 }, +	{ 0x201, "ParserVersion", NULL, 0 }, +	{ 0x202, "DeviceSubclass", NULL, 0 }, +	{ 0x203, "CountryCode", NULL, 0 }, +	{ 0x204, "VirtualCable", NULL, 0 }, +	{ 0x205, "ReconnectInitiate", NULL, 0 }, +	{ 0x206, "DescriptorList", NULL, 0 }, +	{ 0x207, "LangIDBaseList", NULL, 0 }, +	{ 0x208, "SDPDisable", NULL, 0 }, +	{ 0x209, "BatteryPower", NULL, 0 }, +	{ 0x20a, "RemoteWakeup", NULL, 0 }, +	{ 0x20b, "ProfileVersion", NULL, 0 }, +	{ 0x20c, "SupervisionTimeout", NULL, 0 }, +	{ 0x20d, "NormallyConnectable", NULL, 0 }, +	{ 0x20e, "BootDevice", NULL, 0 }, +}; + +/* Name of the various PAN attributes. See BT assigned numbers */ +/* Note : those need to be double checked - Jean II */ +static struct attrib_def pan_attrib_names[] = { +	{ 0x200, "IpSubnet", NULL, 0 },		/* Obsolete ??? */ +	{ 0x30A, "SecurityDescription", NULL, 0 }, +	{ 0x30B, "NetAccessType", NULL, 0 }, +	{ 0x30C, "MaxNetAccessrate", NULL, 0 }, +	{ 0x30D, "IPv4Subnet", NULL, 0 }, +	{ 0x30E, "IPv6Subnet", NULL, 0 }, +}; + +/* Name of the various Generic-Audio attributes. See BT assigned numbers */ +/* Note : totally untested - Jean II */ +static struct attrib_def audio_attrib_names[] = { +	{ 0x302, "Remote audio volume control", NULL, 0 }, +}; + +/* Same for the UUIDs. See BT assigned numbers */ +static struct uuid_def uuid16_names[] = { +	/* -- Protocols -- */ +	{ 0x0001, "SDP", NULL, 0 }, +	{ 0x0002, "UDP", NULL, 0 }, +	{ 0x0003, "RFCOMM", NULL, 0 }, +	{ 0x0004, "TCP", NULL, 0 }, +	{ 0x0005, "TCS-BIN", NULL, 0 }, +	{ 0x0006, "TCS-AT", NULL, 0 }, +	{ 0x0008, "OBEX", NULL, 0 }, +	{ 0x0009, "IP", NULL, 0 }, +	{ 0x000a, "FTP", NULL, 0 }, +	{ 0x000c, "HTTP", NULL, 0 }, +	{ 0x000e, "WSP", NULL, 0 }, +	{ 0x000f, "BNEP", NULL, 0 }, +	{ 0x0010, "UPnP/ESDP", NULL, 0 }, +	{ 0x0011, "HIDP", NULL, 0 }, +	{ 0x0012, "HardcopyControlChannel", NULL, 0 }, +	{ 0x0014, "HardcopyDataChannel", NULL, 0 }, +	{ 0x0016, "HardcopyNotification", NULL, 0 }, +	{ 0x0017, "AVCTP", NULL, 0 }, +	{ 0x0019, "AVDTP", NULL, 0 }, +	{ 0x001b, "CMTP", NULL, 0 }, +	{ 0x001d, "UDI_C-Plane", NULL, 0 }, +	{ 0x0100, "L2CAP", NULL, 0 }, +	/* -- Services -- */ +	{ 0x1000, "ServiceDiscoveryServerServiceClassID", +		sdp_attrib_names, sizeof(sdp_attrib_names)/sizeof(struct attrib_def) }, +	{ 0x1001, "BrowseGroupDescriptorServiceClassID", +		browse_attrib_names, sizeof(browse_attrib_names)/sizeof(struct attrib_def) }, +	{ 0x1002, "PublicBrowseGroup", NULL, 0 }, +	{ 0x1101, "SerialPort", NULL, 0 }, +	{ 0x1102, "LANAccessUsingPPP", NULL, 0 }, +	{ 0x1103, "DialupNetworking (DUN)", NULL, 0 }, +	{ 0x1104, "IrMCSync", NULL, 0 }, +	{ 0x1105, "OBEXObjectPush", NULL, 0 }, +	{ 0x1106, "OBEXFileTransfer", NULL, 0 }, +	{ 0x1107, "IrMCSyncCommand", NULL, 0 }, +	{ 0x1108, "Headset", +		audio_attrib_names, sizeof(audio_attrib_names)/sizeof(struct attrib_def) }, +	{ 0x1109, "CordlessTelephony", NULL, 0 }, +	{ 0x110a, "AudioSource", NULL, 0 }, +	{ 0x110b, "AudioSink", NULL, 0 }, +	{ 0x110c, "RemoteControlTarget", NULL, 0 }, +	{ 0x110d, "AdvancedAudio", NULL, 0 }, +	{ 0x110e, "RemoteControl", NULL, 0 }, +	{ 0x110f, "VideoConferencing", NULL, 0 }, +	{ 0x1110, "Intercom", NULL, 0 }, +	{ 0x1111, "Fax", NULL, 0 }, +	{ 0x1112, "HeadsetAudioGateway", NULL, 0 }, +	{ 0x1113, "WAP", NULL, 0 }, +	{ 0x1114, "WAP Client", NULL, 0 }, +	{ 0x1115, "PANU (PAN/BNEP)", +		pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) }, +	{ 0x1116, "NAP (PAN/BNEP)", +		pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) }, +	{ 0x1117, "GN (PAN/BNEP)", +		pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) }, +	{ 0x1118, "DirectPrinting (BPP)", NULL, 0 }, +	{ 0x1119, "ReferencePrinting (BPP)", NULL, 0 }, +	{ 0x111a, "Imaging (BIP)", NULL, 0 }, +	{ 0x111b, "ImagingResponder (BIP)", NULL, 0 }, +	{ 0x111c, "ImagingAutomaticArchive (BIP)", NULL, 0 }, +	{ 0x111d, "ImagingReferencedObjects (BIP)", NULL, 0 }, +	{ 0x111e, "Handsfree", NULL, 0 }, +	{ 0x111f, "HandsfreeAudioGateway", NULL, 0 }, +	{ 0x1120, "DirectPrintingReferenceObjectsService (BPP)", NULL, 0 }, +	{ 0x1121, "ReflectedUI (BPP)", NULL, 0 }, +	{ 0x1122, "BasicPrinting (BPP)", NULL, 0 }, +	{ 0x1123, "PrintingStatus (BPP)", NULL, 0 }, +	{ 0x1124, "HumanInterfaceDeviceService (HID)", +		hid_attrib_names, sizeof(hid_attrib_names)/sizeof(struct attrib_def) }, +	{ 0x1125, "HardcopyCableReplacement (HCR)", NULL, 0 }, +	{ 0x1126, "HCR_Print (HCR)", NULL, 0 }, +	{ 0x1127, "HCR_Scan (HCR)", NULL, 0 }, +	{ 0x1128, "Common ISDN Access (CIP)", NULL, 0 }, +	{ 0x1129, "VideoConferencingGW (VCP)", NULL, 0 }, +	{ 0x112a, "UDI-MT", NULL, 0 }, +	{ 0x112b, "UDI-TA", NULL, 0 }, +	{ 0x112c, "Audio/Video", NULL, 0 }, +	{ 0x112d, "SIM Access (SAP)", NULL, 0 }, +	{ 0x112e, "Phonebook Access (PBAP) - PCE", NULL, 0 }, +	{ 0x112f, "Phonebook Access (PBAP) - PSE", NULL, 0 }, +	{ 0x1130, "Phonebook Access (PBAP)", NULL, 0 }, +	/* ... */ +	{ 0x1200, "PnPInformation", +		did_attrib_names, sizeof(did_attrib_names)/sizeof(struct attrib_def) }, +	{ 0x1201, "GenericNetworking", NULL, 0 }, +	{ 0x1202, "GenericFileTransfer", NULL, 0 }, +	{ 0x1203, "GenericAudio", +		audio_attrib_names, sizeof(audio_attrib_names)/sizeof(struct attrib_def) }, +	{ 0x1204, "GenericTelephony", NULL, 0 }, +	/* ... */ +	{ 0x1303, "VideoSource", NULL, 0 }, +	{ 0x1304, "VideoSink", NULL, 0 }, +	{ 0x1305, "VideoDistribution", NULL, 0 }, +	{ 0x1400, "MDP", NULL, 0 }, +	{ 0x1401, "MDPSource", NULL, 0 }, +	{ 0x1402, "MDPSink", NULL, 0 }, +	{ 0x2112, "AppleAgent", NULL, 0 }, +}; + +static const int uuid16_max = sizeof(uuid16_names)/sizeof(struct uuid_def); + +static void sdp_data_printf(sdp_data_t *, struct attrib_context *, int); + +/* + * Parse a UUID. + * The BT assigned numbers only list UUID16, so I'm not sure the + * other types will ever get used... + */ +static void sdp_uuid_printf(uuid_t *uuid, struct attrib_context *context, int indent) +{ +	if (uuid) { +		if (uuid->type == SDP_UUID16) { +			uint16_t uuidNum = uuid->value.uuid16; +			struct uuid_def *uuidDef = NULL; +			int i; + +			for (i = 0; i < uuid16_max; i++) +				if (uuid16_names[i].num == uuidNum) { +					uuidDef = &uuid16_names[i]; +					break; +				} + +			/* Check if it's the service attribute */ +			if (context->attrib && context->attrib->num == SERVICE_ATTR) { +				/* We got the service ID !!! */ +				context->service = uuidDef; +			} + +			if (uuidDef) +				printf("%.*sUUID16 : 0x%.4x - %s\n", +					indent, indent_spaces, uuidNum, uuidDef->name); +			else +				printf("%.*sUUID16 : 0x%.4x\n", +					indent, indent_spaces, uuidNum); +		} else if (uuid->type == SDP_UUID32) { +			struct uuid_def *uuidDef = NULL; +			int i; + +			if (!(uuid->value.uuid32 & 0xffff0000)) { +				uint16_t uuidNum = uuid->value.uuid32; +				for (i = 0; i < uuid16_max; i++) +					if (uuid16_names[i].num == uuidNum) { +						uuidDef = &uuid16_names[i]; +						break; +					} +			} + +			if (uuidDef) +				printf("%.*sUUID32 : 0x%.8x - %s\n", +					indent, indent_spaces, uuid->value.uuid32, uuidDef->name); +			else +				printf("%.*sUUID32 : 0x%.8x\n", +					indent, indent_spaces, uuid->value.uuid32); +		} else if (uuid->type == SDP_UUID128) { +			unsigned int data0; +			unsigned short data1; +			unsigned short data2; +			unsigned short data3; +			unsigned int data4; +			unsigned short data5; + +			memcpy(&data0, &uuid->value.uuid128.data[0], 4); +			memcpy(&data1, &uuid->value.uuid128.data[4], 2); +			memcpy(&data2, &uuid->value.uuid128.data[6], 2); +			memcpy(&data3, &uuid->value.uuid128.data[8], 2); +			memcpy(&data4, &uuid->value.uuid128.data[10], 4); +			memcpy(&data5, &uuid->value.uuid128.data[14], 2); + +			printf("%.*sUUID128 : 0x%.8x-%.4x-%.4x-%.4x-%.8x-%.4x\n", +				indent, indent_spaces, +				ntohl(data0), ntohs(data1), ntohs(data2), +				ntohs(data3), ntohl(data4), ntohs(data5)); +		} else +			printf("%.*sEnum type of UUID not set\n", +				indent, indent_spaces); +	} else +		printf("%.*sNull passed to print UUID\n", +				indent, indent_spaces); +} + +/* + * Parse a sequence of data elements (i.e. a list) + */ +static void printf_dataseq(sdp_data_t * pData, struct attrib_context *context, int indent) +{ +	sdp_data_t *sdpdata = NULL; + +	sdpdata = pData; +	if (sdpdata) { +		context->member_index = 0; +		do { +			sdp_data_printf(sdpdata, context, indent + 2); +			sdpdata = sdpdata->next; +			context->member_index++; +		} while (sdpdata); +	} else { +		printf("%.*sBroken dataseq link\n", indent, indent_spaces); +	} +} + +/* + * Parse a single data element (either in the attribute or in a data + * sequence). + */ +static void sdp_data_printf(sdp_data_t *sdpdata, struct attrib_context *context, int indent) +{ +	char *member_name = NULL; + +	/* Find member name. Almost black magic ;-) */ +	if (context && context->attrib && context->attrib->members && +			context->member_index < context->attrib->member_max) { +		member_name = context->attrib->members[context->member_index].name; +	} + +	switch (sdpdata->dtd) { +	case SDP_DATA_NIL: +		printf("%.*sNil\n", indent, indent_spaces); +		break; +	case SDP_BOOL: +	case SDP_UINT8: +	case SDP_UINT16: +	case SDP_UINT32: +	case SDP_UINT64: +	case SDP_UINT128: +	case SDP_INT8: +	case SDP_INT16: +	case SDP_INT32: +	case SDP_INT64: +	case SDP_INT128: +		if (member_name) { +			printf("%.*s%s (Integer) : 0x%x\n", +				indent, indent_spaces, member_name, sdpdata->val.uint32); +		} else { +			printf("%.*sInteger : 0x%x\n", indent, indent_spaces, +				sdpdata->val.uint32); +		} +		break; + +	case SDP_UUID16: +	case SDP_UUID32: +	case SDP_UUID128: +		//printf("%.*sUUID\n", indent, indent_spaces); +		sdp_uuid_printf(&sdpdata->val.uuid, context, indent); +		break; + +	case SDP_TEXT_STR8: +	case SDP_TEXT_STR16: +	case SDP_TEXT_STR32: +		if (sdpdata->unitSize > strlen(sdpdata->val.str)) { +			int i; +			printf("%.*sData :", indent, indent_spaces); +			for (i = 0; i < sdpdata->unitSize; i++) +				printf(" %02x", (unsigned char) sdpdata->val.str[i]); +			printf("\n"); +		} else +			printf("%.*sText : \"%s\"\n", indent, indent_spaces, sdpdata->val.str); +		break; +	case SDP_URL_STR8: +	case SDP_URL_STR16: +	case SDP_URL_STR32: +		printf("%.*sURL : %s\n", indent, indent_spaces, sdpdata->val.str); +		break; + +	case SDP_SEQ8: +	case SDP_SEQ16: +	case SDP_SEQ32: +		printf("%.*sData Sequence\n", indent, indent_spaces); +		printf_dataseq(sdpdata->val.dataseq, context, indent); +		break; + +	case SDP_ALT8: +	case SDP_ALT16: +	case SDP_ALT32: +		printf("%.*sData Sequence Alternates\n", indent, indent_spaces); +		printf_dataseq(sdpdata->val.dataseq, context, indent); +		break; +	} +} + +/* + * Parse a single attribute. + */ +static void print_tree_attr_func(void *value, void *userData) +{ +	sdp_data_t *sdpdata = NULL; +	uint16_t attrId; +	struct service_context *service = (struct service_context *) userData; +	struct attrib_context context; +	struct attrib_def *attrDef = NULL; +	int i; + +	sdpdata = (sdp_data_t *)value; +	attrId = sdpdata->attrId; +	/* Search amongst the generic attributes */ +	for (i = 0; i < attrib_max; i++) +		if (attrib_names[i].num == attrId) { +			attrDef = &attrib_names[i]; +			break; +		} +	/* Search amongst the specific attributes of this service */ +	if ((attrDef == NULL) && (service->service != NULL) && +				(service->service->attribs != NULL)) { +		struct attrib_def *svc_attribs = service->service->attribs; +		int		svc_attrib_max = service->service->attrib_max; +		for (i = 0; i < svc_attrib_max; i++) +			if (svc_attribs[i].num == attrId) { +				attrDef = &svc_attribs[i]; +				break; +			} +	} + +	if (attrDef) +		printf("Attribute Identifier : 0x%x - %s\n", attrId, attrDef->name); +	else +		printf("Attribute Identifier : 0x%x\n", attrId); +	/* Build context */ +	context.service = service->service; +	context.attrib = attrDef; +	context.member_index = 0; +	/* Parse attribute members */ +	if (sdpdata) +		sdp_data_printf(sdpdata, &context, 2); +	else +		printf("  NULL value\n"); +	/* Update service */ +	service->service = context.service; +} + +/* + * Main entry point of this library. Parse a SDP record. + * We assume the record has already been read, parsed and cached + * locally. Jean II + */ +static void print_tree_attr(sdp_record_t *rec) +{ +	if (rec && rec->attrlist) { +		struct service_context service = { NULL }; +		sdp_list_foreach(rec->attrlist, print_tree_attr_func, &service); +	} +} + +static void print_raw_data(sdp_data_t *data, int indent) +{ +	struct uuid_def *def; +	int i, hex; + +	if (!data) +		return; + +	for (i = 0; i < indent; i++) +		printf("\t"); + +	switch (data->dtd) { +	case SDP_DATA_NIL: +		printf("NIL\n"); +		break; +	case SDP_BOOL: +		printf("Bool %s\n", data->val.uint8 ? "True" : "False"); +		break; +	case SDP_UINT8: +		printf("UINT8 0x%02x\n", data->val.uint8); +		break; +	case SDP_UINT16: +		printf("UINT16 0x%04x\n", data->val.uint16); +		break; +	case SDP_UINT32: +		printf("UINT32 0x%08x\n", data->val.uint32); +		break; +	case SDP_UINT64: +		printf("UINT64 0x%016jx\n", data->val.uint64); +		break; +	case SDP_UINT128: +		printf("UINT128 ...\n"); +		break; +	case SDP_INT8: +		printf("INT8 %d\n", data->val.int8); +		break; +	case SDP_INT16: +		printf("INT16 %d\n", data->val.int16); +		break; +	case SDP_INT32: +		printf("INT32 %d\n", data->val.int32); +		break; +	case SDP_INT64: +		printf("INT64 %jd\n", data->val.int64); +		break; +	case SDP_INT128: +		printf("INT128 ...\n"); +		break; +	case SDP_UUID16: +	case SDP_UUID32: +	case SDP_UUID128: +		switch (data->val.uuid.type) { +		case SDP_UUID16: +			def = NULL; +			for (i = 0; i < uuid16_max; i++) +				if (uuid16_names[i].num == data->val.uuid.value.uuid16) { +					def = &uuid16_names[i]; +					break; +				} +			if (def) +				printf("UUID16 0x%04x - %s\n", data->val.uuid.value.uuid16, def->name); +			else +				printf("UUID16 0x%04x\n", data->val.uuid.value.uuid16); +			break; +		case SDP_UUID32: +			def = NULL; +			if (!(data->val.uuid.value.uuid32 & 0xffff0000)) { +				uint16_t value = data->val.uuid.value.uuid32; +				for (i = 0; i < uuid16_max; i++) +					if (uuid16_names[i].num == value) { +						def = &uuid16_names[i]; +						break; +					} +			} +			if (def) +				printf("UUID32 0x%08x - %s\n", data->val.uuid.value.uuid32, def->name); +			else +				printf("UUID32 0x%08x\n", data->val.uuid.value.uuid32); +			break; +		case SDP_UUID128: +			printf("UUID128 "); +			for (i = 0; i < 16; i++) { +				switch (i) { +				case 4: +				case 6: +				case 8: +				case 10: +					printf("-"); +					break; +				} +				printf("%02x", (unsigned char ) data->val.uuid.value.uuid128.data[i]); +			} +			printf("\n"); +			break; +		default: +			printf("UUID type 0x%02x\n", data->val.uuid.type); +			break; +		} +		break; +	case SDP_TEXT_STR8: +	case SDP_TEXT_STR16: +	case SDP_TEXT_STR32: +		hex = 0; +		for (i = 0; i < data->unitSize; i++) { +			if (i == (data->unitSize - 1) && data->val.str[i] == '\0') +				break; +			if (!isprint(data->val.str[i])) { +				hex = 1; +				break; +			} +		} +		if (hex) { +			printf("Data"); +			for (i = 0; i < data->unitSize; i++) +				printf(" %02x", (unsigned char) data->val.str[i]); +		} else { +			printf("String "); +			for (i = 0; i < data->unitSize; i++) +				printf("%c", data->val.str[i]); +		} +		printf("\n"); +		break; +	case SDP_URL_STR8: +	case SDP_URL_STR16: +	case SDP_URL_STR32: +		printf("URL %s\n", data->val.str); +		break; +	case SDP_SEQ8: +	case SDP_SEQ16: +	case SDP_SEQ32: +		printf("Sequence\n"); +		print_raw_data(data->val.dataseq, indent + 1); +		break; +	case SDP_ALT8: +	case SDP_ALT16: +	case SDP_ALT32: +		printf("Alternate\n"); +		print_raw_data(data->val.dataseq, indent + 1); +		break; +	default: +		printf("Unknown type 0x%02x\n", data->dtd); +		break; +	} + +	print_raw_data(data->next, indent); +} + +static void print_raw_attr_func(void *value, void *userData) +{ +	sdp_data_t *data = (sdp_data_t *) value; +	struct attrib_def *def = NULL; +	int i; + +	/* Search amongst the generic attributes */ +	for (i = 0; i < attrib_max; i++) +		if (attrib_names[i].num == data->attrId) { +			def = &attrib_names[i]; +			break; +		} + +	if (def) +		printf("\tAttribute 0x%04x - %s\n", data->attrId, def->name); +	else +		printf("\tAttribute 0x%04x\n", data->attrId); + +	if (data) +		print_raw_data(data, 2); +	else +		printf("  NULL value\n"); +} + +static void print_raw_attr(sdp_record_t *rec) +{ +	if (rec && rec->attrlist) { +		printf("Sequence\n"); +		sdp_list_foreach(rec->attrlist, print_raw_attr_func, 0); +	} +} + +/* + * Set attributes with single values in SDP record + * Jean II + */ +static int set_attrib(sdp_session_t *sess, uint32_t handle, uint16_t attrib, char *value)  +{ +	sdp_list_t *attrid_list; +	uint32_t range = 0x0000ffff; +	sdp_record_t *rec; +	int ret; + +	/* Get the old SDP record */ +	attrid_list = sdp_list_append(NULL, &range); +	rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attrid_list); +	sdp_list_free(attrid_list, NULL); + +	if (!rec) { +		printf("Service get request failed.\n"); +		return -1; +	} + +	/* Check the type of attribute */ +	if (!strncasecmp(value, "u0x", 3)) { +		/* UUID16 */ +		uint16_t value_int = 0; +		uuid_t value_uuid; +		value_int = strtoul(value + 3, NULL, 16); +		sdp_uuid16_create(&value_uuid, value_int); +		printf("Adding attrib 0x%X uuid16 0x%X to record 0x%X\n", +			attrib, value_int, handle); + +		sdp_attr_add_new(rec, attrib, SDP_UUID16, &value_uuid.value.uuid16); +	} else if (!strncasecmp(value, "0x", 2)) { +		/* Int */ +		uint32_t value_int;   +		value_int = strtoul(value + 2, NULL, 16); +		printf("Adding attrib 0x%X int 0x%X to record 0x%X\n", +			attrib, value_int, handle); + +		sdp_attr_add_new(rec, attrib, SDP_UINT32, &value_int); +	} else { +		/* String */ +		printf("Adding attrib 0x%X string \"%s\" to record 0x%X\n", +			attrib, value, handle); + +		/* Add/Update our attribute to the record */ +		sdp_attr_add_new(rec, attrib, SDP_TEXT_STR8, value); +	} + +	/* Update on the server */ +	ret = sdp_device_record_update(sess, &interface, rec); +	if (ret < 0) +		printf("Service Record update failed (%d).\n", errno); +	sdp_record_free(rec); +	return ret; +} + +static struct option set_options[] = { +	{ "help",	0, 0, 'h' }, +	{ 0, 0, 0, 0 } +}; + +static char *set_help =  +	"Usage:\n" +	"\tget record_handle attrib_id attrib_value\n"; + +/* + * Add an attribute to an existing SDP record on the local SDP server + */ +static int cmd_setattr(int argc, char **argv) +{ +	int opt, status; +	uint32_t handle; +	uint16_t attrib; +	sdp_session_t *sess; + +	for_each_opt(opt, set_options, NULL) { +		switch(opt) { +		default: +			printf(set_help); +			return -1; +		} +	} + +	argc -= optind; +	argv += optind; + +	if (argc < 3) { +		printf(set_help); +		return -1; +	} + +	/* Convert command line args */ +	handle = strtoul(argv[0], NULL, 16); +	attrib = strtoul(argv[1], NULL, 16); + +	/* Do it */ +	sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0); +	if (!sess) +		return -1; + +	status = set_attrib(sess, handle, attrib, argv[2]); +	sdp_close(sess); + +	return status; +} + +/* + * We do only simple data sequences. Sequence of sequences is a pain ;-) + * Jean II + */ +static int set_attribseq(sdp_session_t *session, uint32_t handle, uint16_t attrib, int argc, char **argv) +{ +	sdp_list_t *attrid_list; +	uint32_t range = 0x0000ffff; +	sdp_record_t *rec; +	sdp_data_t *pSequenceHolder = NULL; +	void **dtdArray; +	void **valueArray; +	void **allocArray; +	uint8_t uuid16 = SDP_UUID16; +	uint8_t uint32 = SDP_UINT32; +	uint8_t str8 = SDP_TEXT_STR8; +	int i, ret = 0; + +	/* Get the old SDP record */ +	attrid_list = sdp_list_append(NULL, &range); +	rec = sdp_service_attr_req(session, handle, SDP_ATTR_REQ_RANGE, attrid_list); +	sdp_list_free(attrid_list, NULL); + +	if (!rec) { +		printf("Service get request failed.\n"); +		return -1; +	} + +	/* Create arrays */ +	dtdArray = (void **)malloc(argc * sizeof(void *)); +	valueArray = (void **)malloc(argc * sizeof(void *)); +	allocArray = (void **)malloc(argc * sizeof(void *)); + +	/* Loop on all args, add them in arrays */ +	for (i = 0; i < argc; i++) { +		/* Check the type of attribute */ +		if (!strncasecmp(argv[i], "u0x", 3)) { +			/* UUID16 */ +			uint16_t value_int = strtoul((argv[i]) + 3, NULL, 16); +			uuid_t *value_uuid = (uuid_t *) malloc(sizeof(uuid_t)); +			allocArray[i] = value_uuid; +			sdp_uuid16_create(value_uuid, value_int); + +			printf("Adding uuid16 0x%X to record 0x%X\n", value_int, handle); +			dtdArray[i] = &uuid16; +			valueArray[i] = &value_uuid->value.uuid16; +		} else if (!strncasecmp(argv[i], "0x", 2)) { +			/* Int */ +			uint32_t *value_int = (uint32_t *) malloc(sizeof(int)); +			allocArray[i] = value_int; +			*value_int = strtoul((argv[i]) + 2, NULL, 16); + +			printf("Adding int 0x%X to record 0x%X\n", *value_int, handle); +			dtdArray[i] = &uint32; +			valueArray[i] = value_int; +		} else { +			/* String */ +			printf("Adding string \"%s\" to record 0x%X\n", argv[i], handle); +			dtdArray[i] = &str8; +			valueArray[i] = argv[i]; +		} +	} + +	/* Add this sequence to the attrib list */ +	pSequenceHolder = sdp_seq_alloc(dtdArray, valueArray, argc); +	if (pSequenceHolder) { +		sdp_attr_replace(rec, attrib, pSequenceHolder); + +		/* Update on the server */ +		ret = sdp_device_record_update(session, &interface, rec); +		if (ret < 0) +			printf("Service Record update failed (%d).\n", errno); +	} else +		printf("Failed to create pSequenceHolder\n"); + +	/* Cleanup */ +	for (i = 0; i < argc; i++) +		free(allocArray[i]); + +	free(dtdArray); +	free(valueArray); +	free(allocArray); + +	sdp_record_free(rec); + +	return ret; +} + +static struct option seq_options[] = { +	{ "help",	0, 0, 'h' }, +	{ 0, 0, 0, 0 } +}; + +static char *seq_help =  +	"Usage:\n" +	"\tget record_handle attrib_id attrib_values\n"; + +/* + * Add an attribute sequence to an existing SDP record + * on the local SDP server + */ +static int cmd_setseq(int argc, char **argv) +{ +	int opt, status; +	uint32_t handle; +	uint16_t attrib; +	sdp_session_t *sess; + +	for_each_opt(opt, seq_options, NULL) { +		switch(opt) { +		default: +			printf(seq_help); +			return -1; +		} +	} + +	argc -= optind; +	argv += optind; + +	if (argc < 3) { +		printf(seq_help); +		return -1; +	} + +	/* Convert command line args */ +	handle = strtoul(argv[0], NULL, 16); +	attrib = strtoul(argv[1], NULL, 16); + +	argc -= 2; +	argv += 2; + +	/* Do it */ +	sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0); +	if (!sess) +		return -1; + +	status = set_attribseq(sess, handle, attrib, argc, argv); +	sdp_close(sess); + +	return status; +} + +static void print_service_class(void *value, void *userData) +{ +	char ServiceClassUUID_str[MAX_LEN_SERVICECLASS_UUID_STR]; +	uuid_t *uuid = (uuid_t *)value; + +	sdp_uuid2strn(uuid, UUID_str, MAX_LEN_UUID_STR); +	sdp_svclass_uuid2strn(uuid, ServiceClassUUID_str, MAX_LEN_SERVICECLASS_UUID_STR); +	if (uuid->type != SDP_UUID128) +		printf("  \"%s\" (0x%s)\n", ServiceClassUUID_str, UUID_str); +	else +		printf("  UUID 128: %s\n", UUID_str); +} + +static void print_service_desc(void *value, void *user) +{ +	char str[MAX_LEN_PROTOCOL_UUID_STR]; +	sdp_data_t *p = (sdp_data_t *)value, *s; +	int i = 0, proto = 0; + +	for (; p; p = p->next, i++) { +		switch (p->dtd) { +		case SDP_UUID16: +		case SDP_UUID32: +		case SDP_UUID128: +			sdp_uuid2strn(&p->val.uuid, UUID_str, MAX_LEN_UUID_STR); +			sdp_proto_uuid2strn(&p->val.uuid, str, sizeof(str)); +			proto = sdp_uuid_to_proto(&p->val.uuid); +			printf("  \"%s\" (0x%s)\n", str, UUID_str); +			break; +		case SDP_UINT8: +			if (proto == RFCOMM_UUID) +				printf("    Channel: %d\n", p->val.uint8); +			else +				printf("    uint8: 0x%x\n", p->val.uint8); +			break; +		case SDP_UINT16: +			if (proto == L2CAP_UUID) { +				if (i == 1) +					printf("    PSM: %d\n", p->val.uint16); +				else +					printf("    Version: 0x%04x\n", p->val.uint16); +			} else if (proto == BNEP_UUID) +				if (i == 1) +					printf("    Version: 0x%04x\n", p->val.uint16); +				else +					printf("    uint16: 0x%x\n", p->val.uint16); +			else +				printf("    uint16: 0x%x\n", p->val.uint16); +			break; +		case SDP_SEQ16: +			printf("    SEQ16:"); +			for (s = p->val.dataseq; s; s = s->next) +				printf(" %x", s->val.uint16); +			printf("\n"); +			break; +		case SDP_SEQ8: +			printf("    SEQ8:"); +			for (s = p->val.dataseq; s; s = s->next) +				printf(" %x", s->val.uint8); +			printf("\n"); +			break; +		default: +			printf("    FIXME: dtd=0%x\n", p->dtd); +			break; +		} +	} +} + +static void print_lang_attr(void *value, void *user) +{ +	sdp_lang_attr_t *lang = (sdp_lang_attr_t *)value; +	printf("  code_ISO639: 0x%02x\n", lang->code_ISO639); +	printf("  encoding:    0x%02x\n", lang->encoding); +	printf("  base_offset: 0x%02x\n", lang->base_offset); +} + +static void print_access_protos(void *value, void *userData) +{ +	sdp_list_t *protDescSeq = (sdp_list_t *)value; +	sdp_list_foreach(protDescSeq, print_service_desc, 0); +} + +static void print_profile_desc(void *value, void *userData) +{ +	sdp_profile_desc_t *desc = (sdp_profile_desc_t *)value; +	char str[MAX_LEN_PROFILEDESCRIPTOR_UUID_STR]; + +	sdp_uuid2strn(&desc->uuid, UUID_str, MAX_LEN_UUID_STR); +	sdp_profile_uuid2strn(&desc->uuid, str, MAX_LEN_PROFILEDESCRIPTOR_UUID_STR); + +	printf("  \"%s\" (0x%s)\n", str, UUID_str); +	if (desc->version) +		printf("    Version: 0x%04x\n", desc->version); +} + +/* + * Parse a SDP record in user friendly form. + */ +static void print_service_attr(sdp_record_t *rec) +{ +	sdp_list_t *list = 0, *proto = 0; + +	sdp_record_print(rec); + +	printf("Service RecHandle: 0x%x\n", rec->handle); + +	if (sdp_get_service_classes(rec, &list) == 0) { +		printf("Service Class ID List:\n"); +		sdp_list_foreach(list, print_service_class, 0); +		sdp_list_free(list, free); +	} +	if (sdp_get_access_protos(rec, &proto) == 0) { +		printf("Protocol Descriptor List:\n"); +		sdp_list_foreach(proto, print_access_protos, 0); +		sdp_list_foreach(proto, (sdp_list_func_t)sdp_list_free, 0); +		sdp_list_free(proto, 0); +	} +	if (sdp_get_lang_attr(rec, &list) == 0) { +		printf("Language Base Attr List:\n"); +		sdp_list_foreach(list, print_lang_attr, 0); +		sdp_list_free(list, free); +	} +	if (sdp_get_profile_descs(rec, &list) == 0) { +		printf("Profile Descriptor List:\n"); +		sdp_list_foreach(list, print_profile_desc, 0); +		sdp_list_free(list, free); +	} +} + +/* + * Support for Service (de)registration + */ +typedef struct { +	uint32_t handle; +	char *name; +	char *provider; +	char *desc; +	unsigned int class; +	unsigned int profile; +	uint16_t psm; +	uint8_t channel; +	uint8_t network; +} svc_info_t; + +static void add_lang_attr(sdp_record_t *r) +{ +	sdp_lang_attr_t base_lang; +	sdp_list_t *langs = 0; + +	/* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */ +	base_lang.code_ISO639 = (0x65 << 8) | 0x6e; +	base_lang.encoding = 106; +	base_lang.base_offset = SDP_PRIMARY_LANG_BASE; +	langs = sdp_list_append(0, &base_lang); +	sdp_set_lang_attr(r, langs); +	sdp_list_free(langs, 0); +} + +static int add_sp(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *apseq, *proto[2], *profiles, *root, *aproto; +	uuid_t root_uuid, sp_uuid, l2cap, rfcomm; +	sdp_profile_desc_t profile; +	sdp_record_t record; +	uint8_t u8 = si->channel ? si->channel : 1; +	sdp_data_t *channel; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); +	sdp_list_free(root, 0); + +	sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &sp_uuid); +	sdp_set_service_classes(&record, svclass_id); +	sdp_list_free(svclass_id, 0); + +	sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID); +	profile.version = 0x0100; +	profiles = sdp_list_append(0, &profile); +	sdp_set_profile_descs(&record, profiles); +	sdp_list_free(profiles, 0); + +	sdp_uuid16_create(&l2cap, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&rfcomm, RFCOMM_UUID); +	proto[1] = sdp_list_append(0, &rfcomm); +	channel = sdp_data_alloc(SDP_UINT8, &u8); +	proto[1] = sdp_list_append(proto[1], channel); +	apseq = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	add_lang_attr(&record); + +	sdp_set_info_attr(&record, "Serial Port", "BlueZ", "COM Port"); + +	sdp_set_url_attr(&record, "http://www.bluez.org/", +			"http://www.bluez.org/", "http://www.bluez.org/"); + +	sdp_set_service_id(&record, sp_uuid); +	sdp_set_service_ttl(&record, 0xffff); +	sdp_set_service_avail(&record, 0xff); +	sdp_set_record_state(&record, 0x00001234); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("Serial Port service registered\n"); + +end: +	sdp_data_free(channel); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_dun(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root, *aproto; +	uuid_t rootu, dun, gn, l2cap, rfcomm; +	sdp_profile_desc_t profile; +	sdp_list_t *proto[2]; +	sdp_record_t record; +	uint8_t u8 = si->channel ? si->channel : 2; +	sdp_data_t *channel; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&rootu, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &rootu); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&dun, DIALUP_NET_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &dun); +	sdp_uuid16_create(&gn,  GENERIC_NETWORKING_SVCLASS_ID); +	svclass_id = sdp_list_append(svclass_id, &gn); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile.uuid, DIALUP_NET_PROFILE_ID); +	profile.version = 0x0100; +	pfseq = sdp_list_append(0, &profile); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&rfcomm, RFCOMM_UUID); +	proto[1] = sdp_list_append(0, &rfcomm); +	channel = sdp_data_alloc(SDP_UINT8, &u8); +	proto[1] = sdp_list_append(proto[1], channel); +	apseq = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "Dial-Up Networking", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("Dial-Up Networking service registered\n"); + +end: +	sdp_data_free(channel); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_fax(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, fax_uuid, tel_uuid, l2cap_uuid, rfcomm_uuid; +	sdp_profile_desc_t profile; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	uint8_t u8 = si->channel? si->channel : 3; +	sdp_data_t *channel; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&fax_uuid, FAX_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &fax_uuid); +	sdp_uuid16_create(&tel_uuid, GENERIC_TELEPHONY_SVCLASS_ID); +	svclass_id = sdp_list_append(svclass_id, &tel_uuid); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile.uuid, FAX_PROFILE_ID); +	profile.version = 0x0100; +	pfseq = sdp_list_append(0, &profile); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap_uuid); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto[1] = sdp_list_append(0, &rfcomm_uuid); +	channel = sdp_data_alloc(SDP_UINT8, &u8); +	proto[1] = sdp_list_append(proto[1], channel); +	apseq  = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "Fax", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} +	printf("Fax service registered\n"); +end: +	sdp_data_free(channel); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); +	return ret; +} + +static int add_lan(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid; +	sdp_profile_desc_t profile; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	uint8_t u8 = si->channel ? si->channel : 4; +	sdp_data_t *channel; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&svclass_uuid, LAN_ACCESS_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &svclass_uuid); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile.uuid, LAN_ACCESS_PROFILE_ID); +	profile.version = 0x0100; +	pfseq = sdp_list_append(0, &profile); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap_uuid); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto[1] = sdp_list_append(0, &rfcomm_uuid); +	channel = sdp_data_alloc(SDP_UINT8, &u8); +	proto[1] = sdp_list_append(proto[1], channel); +	apseq = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "LAN Access over PPP", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("LAN Access service registered\n"); + +end: +	sdp_data_free(channel); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_headset(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid; +	sdp_profile_desc_t profile; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	uint8_t u8 = si->channel ? si->channel : 5; +	sdp_data_t *channel; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&svclass_uuid, HEADSET_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &svclass_uuid); +	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); +	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); +	profile.version = 0x0100; +	pfseq = sdp_list_append(0, &profile); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap_uuid); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto[1] = sdp_list_append(0, &rfcomm_uuid); +	channel = sdp_data_alloc(SDP_UINT8, &u8); +	proto[1] = sdp_list_append(proto[1], channel); +	apseq = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "Headset", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("Headset service registered\n"); + +end: +	sdp_data_free(channel); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_headset_ag(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid; +	sdp_profile_desc_t profile; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	uint8_t u8 = si->channel ? si->channel : 7; +	uint16_t u16 = 0x17; +	sdp_data_t *channel, *features; +	uint8_t netid = si->network ? si->network : 0x01; // ???? profile document +	sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &svclass_uuid); +	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); +	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); +	profile.version = 0x0100; +	pfseq = sdp_list_append(0, &profile); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap_uuid); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto[1] = sdp_list_append(0, &rfcomm_uuid); +	channel = sdp_data_alloc(SDP_UINT8, &u8); +	proto[1] = sdp_list_append(proto[1], channel); +	apseq = sdp_list_append(apseq, proto[1]); + +	features = sdp_data_alloc(SDP_UINT16, &u16); +	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "Voice Gateway", 0, 0); + +	sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); + +	if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("Headset AG service registered\n"); + +end: +	sdp_data_free(channel); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_handsfree(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid; +	sdp_profile_desc_t profile; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	uint8_t u8 = si->channel ? si->channel : 6; +	uint16_t u16 = 0x31; +	sdp_data_t *channel, *features; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &svclass_uuid); +	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); +	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); +	profile.version = 0x0101; +	pfseq = sdp_list_append(0, &profile); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap_uuid); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto[1] = sdp_list_append(0, &rfcomm_uuid); +	channel = sdp_data_alloc(SDP_UINT8, &u8); +	proto[1] = sdp_list_append(proto[1], channel); +	apseq = sdp_list_append(apseq, proto[1]); + +	features = sdp_data_alloc(SDP_UINT16, &u16); +	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "Handsfree", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("Handsfree service registered\n"); + +end: +	sdp_data_free(channel); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_handsfree_ag(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid; +	sdp_profile_desc_t profile; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	uint8_t u8 = si->channel ? si->channel : 7; +	uint16_t u16 = 0x17; +	sdp_data_t *channel, *features; +	uint8_t netid = si->network ? si->network : 0x01; // ???? profile document +	sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &svclass_uuid); +	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); +	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); +	profile.version = 0x0105; +	pfseq = sdp_list_append(0, &profile); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap_uuid); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto[1] = sdp_list_append(0, &rfcomm_uuid); +	channel = sdp_data_alloc(SDP_UINT8, &u8); +	proto[1] = sdp_list_append(proto[1], channel); +	apseq = sdp_list_append(apseq, proto[1]); + +	features = sdp_data_alloc(SDP_UINT16, &u16); +	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "Voice Gateway", 0, 0); + +	sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); + +	if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("Handsfree AG service registered\n"); + +end: +	sdp_data_free(channel); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_simaccess(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid; +	sdp_profile_desc_t profile; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	uint8_t u8 = si->channel? si->channel : 8; +	uint16_t u16 = 0x31; +	sdp_data_t *channel, *features;	 +	int ret = 0; + +	memset((void *)&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&svclass_uuid, SAP_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &svclass_uuid); +	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_TELEPHONY_SVCLASS_ID); +	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile.uuid, SAP_PROFILE_ID); +	profile.version = 0x0101; +	pfseq = sdp_list_append(0, &profile); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap_uuid); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto[1] = sdp_list_append(0, &rfcomm_uuid); +	channel = sdp_data_alloc(SDP_UINT8, &u8); +	proto[1] = sdp_list_append(proto[1], channel); +	apseq = sdp_list_append(apseq, proto[1]); + +	features = sdp_data_alloc(SDP_UINT16, &u16); +	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "SIM Access", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("SIM Access service registered\n"); + +end: +	sdp_data_free(channel); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_opush(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *aproto, *proto[3]; +	sdp_record_t record; +	uint8_t chan = si->channel ? si->channel : 9; +	sdp_data_t *channel; +	uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; +	//uint8_t formats[] = { 0xff }; +	void *dtds[sizeof(formats)], *values[sizeof(formats)]; +	int i; +	uint8_t dtd = SDP_UINT8; +	sdp_data_t *sflist; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &opush_uuid); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID); +	profile[0].version = 0x0100; +	pfseq = sdp_list_append(0, profile); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap_uuid); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto[1] = sdp_list_append(0, &rfcomm_uuid); +	channel = sdp_data_alloc(SDP_UINT8, &chan); +	proto[1] = sdp_list_append(proto[1], channel); +	apseq = sdp_list_append(apseq, proto[1]); + +	sdp_uuid16_create(&obex_uuid, OBEX_UUID); +	proto[2] = sdp_list_append(0, &obex_uuid); +	apseq = sdp_list_append(apseq, proto[2]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	for (i = 0; i < sizeof(formats); i++) { +		dtds[i] = &dtd; +		values[i] = &formats[i]; +	} +	sflist = sdp_seq_alloc(dtds, values, sizeof(formats)); +	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FORMATS_LIST, sflist); + +	sdp_set_info_attr(&record, "OBEX Object Push", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("OBEX Object Push service registered\n"); + +end: +	sdp_data_free(channel); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(proto[2], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_ftp(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, ftrn_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *aproto, *proto[3]; +	sdp_record_t record; +	uint8_t u8 = si->channel ? si->channel: 10; +	sdp_data_t *channel; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&ftrn_uuid, OBEX_FILETRANS_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &ftrn_uuid); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile[0].uuid, OBEX_FILETRANS_PROFILE_ID); +	profile[0].version = 0x0100; +	pfseq = sdp_list_append(0, &profile[0]); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap_uuid); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto[1] = sdp_list_append(0, &rfcomm_uuid); +	channel = sdp_data_alloc(SDP_UINT8, &u8); +	proto[1] = sdp_list_append(proto[1], channel); +	apseq = sdp_list_append(apseq, proto[1]); + +	sdp_uuid16_create(&obex_uuid, OBEX_UUID); +	proto[2] = sdp_list_append(0, &obex_uuid); +	apseq = sdp_list_append(apseq, proto[2]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "OBEX File Transfer", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("OBEX File Transfer service registered\n"); + +end: +	sdp_data_free(channel); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(proto[2], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_directprint(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *aproto, *proto[3]; +	sdp_record_t record; +	uint8_t chan = si->channel ? si->channel : 12; +	sdp_data_t *channel; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&opush_uuid, DIRECT_PRINTING_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &opush_uuid); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile[0].uuid, BASIC_PRINTING_PROFILE_ID); +	profile[0].version = 0x0100; +	pfseq = sdp_list_append(0, profile); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap_uuid); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto[1] = sdp_list_append(0, &rfcomm_uuid); +	channel = sdp_data_alloc(SDP_UINT8, &chan); +	proto[1] = sdp_list_append(proto[1], channel); +	apseq = sdp_list_append(apseq, proto[1]); + +	sdp_uuid16_create(&obex_uuid, OBEX_UUID); +	proto[2] = sdp_list_append(0, &obex_uuid); +	apseq = sdp_list_append(apseq, proto[2]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "Direct Printing", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("Direct Printing service registered\n"); + +end: +	sdp_data_free(channel); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(proto[2], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_nap(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	uint16_t lp = 0x000f, ver = 0x0100; +	sdp_data_t *psm, *version; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&ftrn_uuid, NAP_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &ftrn_uuid); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID); +	profile[0].version = 0x0100; +	pfseq = sdp_list_append(0, &profile[0]); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap_uuid); +	psm = sdp_data_alloc(SDP_UINT16, &lp); +	proto[0] = sdp_list_append(proto[0], psm); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&bnep_uuid, BNEP_UUID); +	proto[1] = sdp_list_append(0, &bnep_uuid); +	version  = sdp_data_alloc(SDP_UINT16, &ver); +	proto[1] = sdp_list_append(proto[1], version); + +	{ +		uint16_t ptype[4] = { 0x0010, 0x0020, 0x0030, 0x0040 }; +		sdp_data_t *head, *pseq; +		int p; + +		for (p = 0, head = NULL; p < 4; p++) { +			sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]); +			head = sdp_seq_append(head, data); +		} +		pseq = sdp_data_alloc(SDP_SEQ16, head); +		proto[1] = sdp_list_append(proto[1], pseq); +	} + +	apseq = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "Network Access Point Service", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("NAP service registered\n"); + +end: +	sdp_data_free(version); +	sdp_data_free(psm); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_gn(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	uint16_t lp = 0x000f, ver = 0x0100; +	sdp_data_t *psm, *version; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&ftrn_uuid, GN_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &ftrn_uuid); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID); +	profile[0].version = 0x0100; +	pfseq = sdp_list_append(0, &profile[0]); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap_uuid); +	psm = sdp_data_alloc(SDP_UINT16, &lp); +	proto[0] = sdp_list_append(proto[0], psm); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&bnep_uuid, BNEP_UUID); +	proto[1] = sdp_list_append(0, &bnep_uuid); +	version = sdp_data_alloc(SDP_UINT16, &ver); +	proto[1] = sdp_list_append(proto[1], version); +	apseq = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "Group Network Service", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("GN service registered\n"); + +end: +	sdp_data_free(version); +	sdp_data_free(psm); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_panu(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	uint16_t lp = 0x000f, ver = 0x0100; +	sdp_data_t *psm, *version; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(&record, root); +	sdp_list_free(root, NULL); + +	sdp_uuid16_create(&ftrn_uuid, PANU_SVCLASS_ID); +	svclass_id = sdp_list_append(NULL, &ftrn_uuid); +	sdp_set_service_classes(&record, svclass_id); +	sdp_list_free(svclass_id, NULL); + +	sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID); +	profile[0].version = 0x0100; +	pfseq = sdp_list_append(NULL, &profile[0]); +	sdp_set_profile_descs(&record, pfseq); +	sdp_list_free(pfseq, NULL); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[0] = sdp_list_append(NULL, &l2cap_uuid); +	psm = sdp_data_alloc(SDP_UINT16, &lp); +	proto[0] = sdp_list_append(proto[0], psm); +	apseq = sdp_list_append(NULL, proto[0]); + +	sdp_uuid16_create(&bnep_uuid, BNEP_UUID); +	proto[1] = sdp_list_append(NULL, &bnep_uuid); +	version = sdp_data_alloc(SDP_UINT16, &ver); +	proto[1] = sdp_list_append(proto[1], version); +	apseq = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(NULL, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "PAN User", NULL, NULL); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("PANU service registered\n"); + +end: +	sdp_data_free(version); +	sdp_data_free(psm); +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_hid_keyb(sdp_session_t *session, svc_info_t *si) +{ +	sdp_record_t record; +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, hidkb_uuid, l2cap_uuid, hidp_uuid; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *aproto, *proto[3]; +	sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2; +	int i; +	uint8_t dtd = SDP_UINT16; +	uint8_t dtd2 = SDP_UINT8; +	uint8_t dtd_data = SDP_TEXT_STR8; +	void *dtds[2]; +	void *values[2]; +	void *dtds2[2]; +	void *values2[2]; +	int leng[2]; +	uint8_t hid_spec_type = 0x22; +	uint16_t hid_attr_lang[] = { 0x409, 0x100 }; +	static const uint16_t ctrl = 0x11; +	static const uint16_t intr = 0x13; +	static const uint16_t hid_attr[] = { 0x100, 0x111, 0x40, 0x0d, 0x01, 0x01 }; +	static const uint16_t hid_attr2[] = { 0x0, 0x01, 0x100, 0x1f40, 0x01, 0x01 }; +	const uint8_t hid_spec[] = {  +		0x05, 0x01, // usage page +		0x09, 0x06, // keyboard +		0xa1, 0x01, // key codes +		0x85, 0x01, // minimum +		0x05, 0x07, // max +		0x19, 0xe0, // logical min +		0x29, 0xe7, // logical max +		0x15, 0x00, // report size +		0x25, 0x01, // report count +		0x75, 0x01, // input data variable absolute +		0x95, 0x08, // report count +		0x81, 0x02, // report size +		0x75, 0x08,  +		0x95, 0x01,  +		0x81, 0x01,  +		0x75, 0x01,  +		0x95, 0x05, +		0x05, 0x08, +		0x19, 0x01, +		0x29, 0x05,  +		0x91, 0x02, +		0x75, 0x03, +		0x95, 0x01, +		0x91, 0x01, +		0x75, 0x08, +		0x95, 0x06, +		0x15, 0x00, +		0x26, 0xff, +		0x00, 0x05, +		0x07, 0x19, +		0x00, 0x2a, +		0xff, 0x00, +		0x81, 0x00, +		0x75, 0x01, +		0x95, 0x01, +		0x15, 0x00, +		0x25, 0x01, +		0x05, 0x0c, +		0x09, 0xb8, +		0x81, 0x06, +		0x09, 0xe2, +		0x81, 0x06, +		0x09, 0xe9, +		0x81, 0x02, +		0x09, 0xea, +		0x81, 0x02, +		0x75, 0x01, +		0x95, 0x04, +		0x81, 0x01, +		0xc0         // end tag +	}; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	add_lang_attr(&record); + +	sdp_uuid16_create(&hidkb_uuid, HID_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &hidkb_uuid); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID); +	profile[0].version = 0x0100; +	pfseq = sdp_list_append(0, profile); +	sdp_set_profile_descs(&record, pfseq); + +	/* protocols */ +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[1] = sdp_list_append(0, &l2cap_uuid); +	psm = sdp_data_alloc(SDP_UINT16, &ctrl); +	proto[1] = sdp_list_append(proto[1], psm); +	apseq = sdp_list_append(0, proto[1]); + +	sdp_uuid16_create(&hidp_uuid, HIDP_UUID); +	proto[2] = sdp_list_append(0, &hidp_uuid); +	apseq = sdp_list_append(apseq, proto[2]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	/* additional protocols */ +	proto[1] = sdp_list_append(0, &l2cap_uuid); +	psm = sdp_data_alloc(SDP_UINT16, &intr); +	proto[1] = sdp_list_append(proto[1], psm); +	apseq = sdp_list_append(0, proto[1]); + +	sdp_uuid16_create(&hidp_uuid, HIDP_UUID); +	proto[2] = sdp_list_append(0, &hidp_uuid); +	apseq = sdp_list_append(apseq, proto[2]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_add_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "HID Keyboard", NULL, NULL); + +	for (i = 0; i < sizeof(hid_attr) / 2; i++) +		sdp_attr_add_new(&record, +					SDP_ATTR_HID_DEVICE_RELEASE_NUMBER + i, +					SDP_UINT16, &hid_attr[i]); + +	dtds[0] = &dtd2; +	values[0] = &hid_spec_type; +	dtds[1] = &dtd_data; +	values[1] = (uint8_t *) hid_spec; +	leng[0] = 0; +	leng[1] = sizeof(hid_spec); +	hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2); +	hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst); +	sdp_attr_add(&record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2); + +	for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) { +		dtds2[i] = &dtd; +		values2[i] = &hid_attr_lang[i]; +	} + +	lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2); +	lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst); +	sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2); + +	sdp_attr_add_new(&record, SDP_ATTR_HID_SDP_DISABLE, SDP_UINT16, &hid_attr2[0]); + +	for (i = 0; i < sizeof(hid_attr2) / 2 - 1; i++) +		sdp_attr_add_new(&record, SDP_ATTR_HID_REMOTE_WAKEUP + i, +						SDP_UINT16, &hid_attr2[i + 1]); + +	if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		return -1; +	} + +	printf("HID keyboard service registered\n"); + +	return 0; +} + +static int add_hid_wiimote(sdp_session_t *session, svc_info_t *si) +{ +	sdp_record_t record; +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, hid_uuid, l2cap_uuid, hidp_uuid; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *aproto, *proto[3]; +	sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2; +	int i; +	uint8_t dtd = SDP_UINT16; +	uint8_t dtd2 = SDP_UINT8; +	uint8_t dtd_data = SDP_TEXT_STR8; +	void *dtds[2]; +	void *values[2]; +	void *dtds2[2]; +	void *values2[2]; +	int leng[2]; +	uint8_t hid_spec_type = 0x22; +	uint16_t hid_attr_lang[] = { 0x409, 0x100 }; +	uint16_t ctrl = 0x11, intr = 0x13; +	uint16_t hid_release = 0x0100, parser_version = 0x0111; +	uint8_t subclass = 0x04, country = 0x33; +	uint8_t virtual_cable = 0, reconnect = 1, sdp_disable = 0; +	uint8_t battery = 1, remote_wakeup = 1; +	uint16_t profile_version = 0x0100, superv_timeout = 0x0c80; +	uint8_t norm_connect = 0, boot_device = 0; +	const uint8_t hid_spec[] = { +		0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x10, +		0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, +		0x01, 0x06, 0x00, 0xff, 0x09, 0x01, 0x91, 0x00, +		0x85, 0x11, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, +		0x85, 0x12, 0x95, 0x02, 0x09, 0x01, 0x91, 0x00, +		0x85, 0x13, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, +		0x85, 0x14, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, +		0x85, 0x15, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, +		0x85, 0x16, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00, +		0x85, 0x17, 0x95, 0x06, 0x09, 0x01, 0x91, 0x00, +		0x85, 0x18, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00, +		0x85, 0x19, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, +		0x85, 0x1a, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, +		0x85, 0x20, 0x95, 0x06, 0x09, 0x01, 0x81, 0x00, +		0x85, 0x21, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, +		0x85, 0x22, 0x95, 0x04, 0x09, 0x01, 0x81, 0x00, +		0x85, 0x30, 0x95, 0x02, 0x09, 0x01, 0x81, 0x00, +		0x85, 0x31, 0x95, 0x05, 0x09, 0x01, 0x81, 0x00, +		0x85, 0x32, 0x95, 0x0a, 0x09, 0x01, 0x81, 0x00, +		0x85, 0x33, 0x95, 0x11, 0x09, 0x01, 0x81, 0x00, +		0x85, 0x34, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, +		0x85, 0x35, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, +		0x85, 0x36, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, +		0x85, 0x37, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, +		0x85, 0x3d, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, +		0x85, 0x3e, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, +		0x85, 0x3f, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, +		0xc0, 0x00 +	}; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&hid_uuid, HID_SVCLASS_ID); +	svclass_id = sdp_list_append(NULL, &hid_uuid); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID); +	profile[0].version = 0x0100; +	pfseq = sdp_list_append(NULL, profile); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto[1] = sdp_list_append(0, &l2cap_uuid); +	psm = sdp_data_alloc(SDP_UINT16, &ctrl); +	proto[1] = sdp_list_append(proto[1], psm); +	apseq = sdp_list_append(0, proto[1]); + +	sdp_uuid16_create(&hidp_uuid, HIDP_UUID); +	proto[2] = sdp_list_append(0, &hidp_uuid); +	apseq = sdp_list_append(apseq, proto[2]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	proto[1] = sdp_list_append(0, &l2cap_uuid); +	psm = sdp_data_alloc(SDP_UINT16, &intr); +	proto[1] = sdp_list_append(proto[1], psm); +	apseq = sdp_list_append(0, proto[1]); + +	sdp_uuid16_create(&hidp_uuid, HIDP_UUID); +	proto[2] = sdp_list_append(0, &hidp_uuid); +	apseq = sdp_list_append(apseq, proto[2]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_add_access_protos(&record, aproto); + +	add_lang_attr(&record); + +	sdp_set_info_attr(&record, "Nintendo RVL-CNT-01", +					"Nintendo", "Nintendo RVL-CNT-01"); + +	sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_RELEASE_NUMBER, +						SDP_UINT16, &hid_release); + +	sdp_attr_add_new(&record, SDP_ATTR_HID_PARSER_VERSION, +						SDP_UINT16, &parser_version); + +	sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_SUBCLASS, +						SDP_UINT8, &subclass); + +	sdp_attr_add_new(&record, SDP_ATTR_HID_COUNTRY_CODE, +						SDP_UINT8, &country); + +	sdp_attr_add_new(&record, SDP_ATTR_HID_VIRTUAL_CABLE, +						SDP_BOOL, &virtual_cable); + +	sdp_attr_add_new(&record, SDP_ATTR_HID_RECONNECT_INITIATE, +						SDP_BOOL, &reconnect); + +	dtds[0] = &dtd2; +	values[0] = &hid_spec_type; +	dtds[1] = &dtd_data; +	values[1] = (uint8_t *) hid_spec; +	leng[0] = 0; +	leng[1] = sizeof(hid_spec); +	hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2); +	hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst); +	sdp_attr_add(&record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2); + +	for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) { +		dtds2[i] = &dtd; +		values2[i] = &hid_attr_lang[i]; +	} + +	lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2); +	lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst); +	sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2); + +	sdp_attr_add_new(&record, SDP_ATTR_HID_SDP_DISABLE, +						SDP_BOOL, &sdp_disable); + +	sdp_attr_add_new(&record, SDP_ATTR_HID_BATTERY_POWER, +						SDP_BOOL, &battery); + +	sdp_attr_add_new(&record, SDP_ATTR_HID_REMOTE_WAKEUP, +						SDP_BOOL, &remote_wakeup); + +	sdp_attr_add_new(&record, SDP_ATTR_HID_PROFILE_VERSION, +						SDP_UINT16, &profile_version); + +	sdp_attr_add_new(&record, SDP_ATTR_HID_SUPERVISION_TIMEOUT, +						SDP_UINT16, &superv_timeout); + +	sdp_attr_add_new(&record, SDP_ATTR_HID_NORMALLY_CONNECTABLE, +						SDP_BOOL, &norm_connect); + +	sdp_attr_add_new(&record, SDP_ATTR_HID_BOOT_DEVICE, +						SDP_BOOL, &boot_device); + +	if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		return -1; +	} + +	printf("Wii-Mote service registered\n"); + +	return 0; +} + +static int add_cip(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, l2cap, cmtp, cip; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	uint16_t psm = si->psm ? si->psm : 0x1001; +	uint8_t netid = si->network ? si->network : 0x02; // 0x02 = ISDN, 0x03 = GSM +	sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&cip, CIP_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &cip); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile[0].uuid, CIP_PROFILE_ID); +	profile[0].version = 0x0100; +	pfseq = sdp_list_append(0, &profile[0]); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap); +	apseq = sdp_list_append(0, proto[0]); +	proto[0] = sdp_list_append(proto[0], sdp_data_alloc(SDP_UINT16, &psm)); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&cmtp, CMTP_UUID); +	proto[1] = sdp_list_append(0, &cmtp); +	apseq = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); + +	sdp_set_info_attr(&record, "Common ISDN Access", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("CIP service registered\n"); + +end: +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); +	sdp_data_free(network); + +	return ret; +} + +static int add_ctp(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, l2cap, tcsbin, ctp; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	uint8_t netid = si->network ? si->network : 0x02; // 0x01-0x07 cf. p120 profile document +	sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&ctp, CORDLESS_TELEPHONY_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &ctp); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile[0].uuid, CORDLESS_TELEPHONY_PROFILE_ID); +	profile[0].version = 0x0100; +	pfseq = sdp_list_append(0, &profile[0]); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&tcsbin, TCS_BIN_UUID); +	proto[1] = sdp_list_append(0, &tcsbin); +	apseq = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); + +	sdp_set_info_attr(&record, "Cordless Telephony", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto end; +	} + +	printf("CTP service registered\n"); + +end: +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); +	sdp_data_free(network); + +	return ret; +} + +static int add_a2source(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, l2cap, avdtp, a2src; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	sdp_data_t *psm, *version; +	uint16_t lp = 0x0019, ver = 0x0100; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&a2src, AUDIO_SOURCE_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &a2src); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID); +	profile[0].version = 0x0100; +	pfseq = sdp_list_append(0, &profile[0]); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap); +	psm = sdp_data_alloc(SDP_UINT16, &lp); +	proto[0] = sdp_list_append(proto[0], psm); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&avdtp, AVDTP_UUID); +	proto[1] = sdp_list_append(0, &avdtp); +	version = sdp_data_alloc(SDP_UINT16, &ver); +	proto[1] = sdp_list_append(proto[1], version); +	apseq = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "Audio Source", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto done; +	} + +	printf("Audio source service registered\n"); + +done: +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_a2sink(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, l2cap, avdtp, a2snk; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	sdp_data_t *psm, *version; +	uint16_t lp = 0x0019, ver = 0x0100; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&a2snk, AUDIO_SINK_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &a2snk); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID); +	profile[0].version = 0x0100; +	pfseq = sdp_list_append(0, &profile[0]); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap); +	psm = sdp_data_alloc(SDP_UINT16, &lp); +	proto[0] = sdp_list_append(proto[0], psm); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&avdtp, AVDTP_UUID); +	proto[1] = sdp_list_append(0, &avdtp); +	version = sdp_data_alloc(SDP_UINT16, &ver); +	proto[1] = sdp_list_append(proto[1], version); +	apseq = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	sdp_set_info_attr(&record, "Audio Sink", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto done; +	} + +	printf("Audio sink service registered\n"); + +done: +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_avrct(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, l2cap, avctp, avrct; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	sdp_data_t *psm, *version, *features; +	uint16_t lp = 0x0017, ver = 0x0100, feat = 0x000f; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &avrct); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); +	profile[0].version = 0x0100; +	pfseq = sdp_list_append(0, &profile[0]); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap); +	psm = sdp_data_alloc(SDP_UINT16, &lp); +	proto[0] = sdp_list_append(proto[0], psm); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&avctp, AVCTP_UUID); +	proto[1] = sdp_list_append(0, &avctp); +	version = sdp_data_alloc(SDP_UINT16, &ver); +	proto[1] = sdp_list_append(proto[1], version); +	apseq = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	features = sdp_data_alloc(SDP_UINT16, &feat); +	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + +	sdp_set_info_attr(&record, "AVRCP CT", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto done; +	} + +	printf("Remote control service registered\n"); + +done: +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_avrtg(sdp_session_t *session, svc_info_t *si) +{ +	sdp_list_t *svclass_id, *pfseq, *apseq, *root; +	uuid_t root_uuid, l2cap, avctp, avrtg; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *aproto, *proto[2]; +	sdp_record_t record; +	sdp_data_t *psm, *version, *features; +	uint16_t lp = 0x0017, ver = 0x0100, feat = 0x000f; +	int ret = 0; + +	memset(&record, 0, sizeof(sdp_record_t)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(0, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID); +	svclass_id = sdp_list_append(0, &avrtg); +	sdp_set_service_classes(&record, svclass_id); + +	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); +	profile[0].version = 0x0100; +	pfseq = sdp_list_append(0, &profile[0]); +	sdp_set_profile_descs(&record, pfseq); + +	sdp_uuid16_create(&l2cap, L2CAP_UUID); +	proto[0] = sdp_list_append(0, &l2cap); +	psm = sdp_data_alloc(SDP_UINT16, &lp); +	proto[0] = sdp_list_append(proto[0], psm); +	apseq = sdp_list_append(0, proto[0]); + +	sdp_uuid16_create(&avctp, AVCTP_UUID); +	proto[1] = sdp_list_append(0, &avctp); +	version = sdp_data_alloc(SDP_UINT16, &ver); +	proto[1] = sdp_list_append(proto[1], version); +	apseq = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(0, apseq); +	sdp_set_access_protos(&record, aproto); + +	features = sdp_data_alloc(SDP_UINT16, &feat); +	sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + +	sdp_set_info_attr(&record, "AVRCP TG", 0, 0); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		ret = -1; +		goto done; +	} + +	printf("Remote target service registered\n"); + +done: +	sdp_list_free(proto[0], 0); +	sdp_list_free(proto[1], 0); +	sdp_list_free(apseq, 0); +	sdp_list_free(aproto, 0); + +	return ret; +} + +static int add_udi_ue(sdp_session_t *session, svc_info_t *si) +{ +	sdp_record_t record; +	sdp_list_t *root, *svclass, *proto; +	uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid; +	uint8_t channel = si->channel ? si->channel: 18; + +	memset(&record, 0, sizeof(record)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(&record, root); +	sdp_list_free(root, NULL); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid)); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto = sdp_list_append(proto, sdp_list_append( +		sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel))); + +	sdp_set_access_protos(&record, sdp_list_append(NULL, proto)); + +	sdp_uuid16_create(&svclass_uuid, UDI_MT_SVCLASS_ID); +	svclass = sdp_list_append(NULL, &svclass_uuid); +	sdp_set_service_classes(&record, svclass); +	sdp_list_free(svclass, NULL); + +	sdp_set_info_attr(&record, "UDI UE", NULL, NULL); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		return -1; +	} + +	printf("UDI UE service registered\n"); + +	return 0; +} + +static int add_udi_te(sdp_session_t *session, svc_info_t *si) +{ +	sdp_record_t record; +	sdp_list_t *root, *svclass, *proto; +	uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid; +	uint8_t channel = si->channel ? si->channel: 19; + +	memset(&record, 0, sizeof(record)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(&record, root); +	sdp_list_free(root, NULL); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid)); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto = sdp_list_append(proto, sdp_list_append( +		sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel))); + +	sdp_set_access_protos(&record, sdp_list_append(NULL, proto)); + +	sdp_uuid16_create(&svclass_uuid, UDI_TA_SVCLASS_ID); +	svclass = sdp_list_append(NULL, &svclass_uuid); +	sdp_set_service_classes(&record, svclass); +	sdp_list_free(svclass, NULL); + +	sdp_set_info_attr(&record, "UDI TE", NULL, NULL); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		return -1; +	} + +	printf("UDI TE service registered\n"); + +	return 0; +} + +static unsigned char sr1_uuid[] = {	0xbc, 0x19, 0x9c, 0x24, 0x95, 0x8b, 0x4c, 0xc0, +					0xa2, 0xcb, 0xfd, 0x8a, 0x30, 0xbf, 0x32, 0x06 }; + +static int add_sr1(sdp_session_t *session, svc_info_t *si) +{ +	sdp_record_t record; +	sdp_list_t *root, *svclass; +	uuid_t root_uuid, svclass_uuid; + +	memset(&record, 0, sizeof(record)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid128_create(&svclass_uuid, (void *) sr1_uuid); +	svclass = sdp_list_append(NULL, &svclass_uuid); +	sdp_set_service_classes(&record, svclass); + +	sdp_set_info_attr(&record, "TOSHIBA SR-1", NULL, NULL); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		return -1; +	} + +	printf("Toshiba Speech Recognition SR-1 service record registered\n"); + +	return 0; +} + +static unsigned char syncmls_uuid[] = {	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, +					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02 }; + +static unsigned char syncmlc_uuid[] = {	0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x00, +					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02 }; + +static int add_syncml(sdp_session_t *session, svc_info_t *si) +{ +	sdp_record_t record; +	sdp_list_t *root, *svclass, *proto; +	uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid; +	uint8_t channel = si->channel ? si->channel: 15; + +	memset(&record, 0, sizeof(record)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid128_create(&svclass_uuid, (void *) syncmlc_uuid); +	svclass = sdp_list_append(NULL, &svclass_uuid); +	sdp_set_service_classes(&record, svclass); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid)); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto = sdp_list_append(proto, sdp_list_append( +		sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel))); + +	sdp_uuid16_create(&obex_uuid, OBEX_UUID); +	proto = sdp_list_append(proto, sdp_list_append(NULL, &obex_uuid)); + +	sdp_set_access_protos(&record, sdp_list_append(NULL, proto)); + +	sdp_set_info_attr(&record, "SyncML Client", NULL, NULL); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		return -1; +	} + +	printf("SyncML Client service record registered\n"); + +	return 0; +} + +static unsigned char async_uuid[] = {	0x03, 0x50, 0x27, 0x8F, 0x3D, 0xCA, 0x4E, 0x62, +					0x83, 0x1D, 0xA4, 0x11, 0x65, 0xFF, 0x90, 0x6C }; + +static int add_activesync(sdp_session_t *session, svc_info_t *si) +{ +	sdp_record_t record; +	sdp_list_t *root, *svclass, *proto; +	uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid; +	uint8_t channel = si->channel ? si->channel: 21; + +	memset(&record, 0, sizeof(record)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid)); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto = sdp_list_append(proto, sdp_list_append( +	sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel))); + +	sdp_set_access_protos(&record, sdp_list_append(NULL, proto)); + +	sdp_uuid128_create(&svclass_uuid, (void *) async_uuid); +	svclass = sdp_list_append(NULL, &svclass_uuid); +	sdp_set_service_classes(&record, svclass); + +	sdp_set_info_attr(&record, "Microsoft ActiveSync", NULL, NULL); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		return -1; +	} + +	printf("ActiveSync service record registered\n"); + +	return 0; +} + +static unsigned char hotsync_uuid[] = {	0xD8, 0x0C, 0xF9, 0xEA, 0x13, 0x4C, 0x11, 0xD5, +					0x83, 0xCE, 0x00, 0x30, 0x65, 0x7C, 0x54, 0x3C }; + +static int add_hotsync(sdp_session_t *session, svc_info_t *si) +{ +	sdp_record_t record; +	sdp_list_t *root, *svclass, *proto; +	uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid; +	uint8_t channel = si->channel ? si->channel: 22; + +	memset(&record, 0, sizeof(record)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid)); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto = sdp_list_append(proto, sdp_list_append( +	sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel))); + +	sdp_set_access_protos(&record, sdp_list_append(NULL, proto)); + +	sdp_uuid128_create(&svclass_uuid, (void *) hotsync_uuid); +	svclass = sdp_list_append(NULL, &svclass_uuid); +	sdp_set_service_classes(&record, svclass); + +	sdp_set_info_attr(&record, "PalmOS HotSync", NULL, NULL); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		return -1; +	} + +	printf("HotSync service record registered\n"); + +	return 0; +} + +static unsigned char palmos_uuid[] = {	0xF5, 0xBE, 0xB6, 0x51, 0x41, 0x71, 0x40, 0x51, +					0xAC, 0xF5, 0x6C, 0xA7, 0x20, 0x22, 0x42, 0xF0 }; + +static int add_palmos(sdp_session_t *session, svc_info_t *si) +{ +	sdp_record_t record; +	sdp_list_t *root, *svclass; +	uuid_t root_uuid, svclass_uuid; + +	memset(&record, 0, sizeof(record)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid128_create(&svclass_uuid, (void *) palmos_uuid); +	svclass = sdp_list_append(NULL, &svclass_uuid); +	sdp_set_service_classes(&record, svclass); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		return -1; +	} + +	printf("PalmOS service record registered\n"); + +	return 0; +} + +static unsigned char nokid_uuid[] = {	0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x10, 0x00, +					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 }; + +static int add_nokiaid(sdp_session_t *session, svc_info_t *si) +{ +	sdp_record_t record; +	sdp_list_t *root, *svclass; +	uuid_t root_uuid, svclass_uuid; +	uint16_t verid = 0x005f; +	sdp_data_t *version = sdp_data_alloc(SDP_UINT16, &verid); + +	memset(&record, 0, sizeof(record)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid128_create(&svclass_uuid, (void *) nokid_uuid); +	svclass = sdp_list_append(NULL, &svclass_uuid); +	sdp_set_service_classes(&record, svclass); + +	sdp_attr_add(&record, SDP_ATTR_SERVICE_VERSION, version); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		sdp_data_free(version); +		return -1; +	} + +	printf("Nokia ID service record registered\n"); + +	return 0; +} + +static unsigned char pcsuite_uuid[] = {	0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x10, 0x00, +					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 }; + +static int add_pcsuite(sdp_session_t *session, svc_info_t *si) +{ +	sdp_record_t record; +	sdp_list_t *root, *svclass, *proto; +	uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid; +	uint8_t channel = si->channel ? si->channel: 14; + +	memset(&record, 0, sizeof(record)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid)); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto = sdp_list_append(proto, sdp_list_append( +		sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel))); + +	sdp_set_access_protos(&record, sdp_list_append(NULL, proto)); + +	sdp_uuid128_create(&svclass_uuid, (void *) pcsuite_uuid); +	svclass = sdp_list_append(NULL, &svclass_uuid); +	sdp_set_service_classes(&record, svclass); + +	sdp_set_info_attr(&record, "Nokia PC Suite", NULL, NULL); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		return -1; +	} + +	printf("Nokia PC Suite service registered\n"); + +	return 0; +} + +static unsigned char nftp_uuid[] = {	0x00, 0x00, 0x50, 0x05, 0x00, 0x00, 0x10, 0x00, +					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 }; + +static unsigned char nsyncml_uuid[] = {	0x00, 0x00, 0x56, 0x01, 0x00, 0x00, 0x10, 0x00, +					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 }; + +static unsigned char ngage_uuid[] = {	0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x10, 0x00, +					0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 }; + +static unsigned char apple_uuid[] = {	0xf0, 0x72, 0x2e, 0x20, 0x0f, 0x8b, 0x4e, 0x90, +					0x8c, 0xc2, 0x1b, 0x46, 0xf5, 0xf2, 0xef, 0xe2 }; + +static int add_apple(sdp_session_t *session, svc_info_t *si) +{ +	sdp_record_t record; +	sdp_list_t *root; +	uuid_t root_uuid; +	uint32_t attr783 = 0x00000000; +	uint32_t attr785 = 0x00000002; +	uint16_t attr786 = 0x1234; + +	memset(&record, 0, sizeof(record)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_attr_add_new(&record, 0x0780, SDP_UUID128, (void *) apple_uuid); +	sdp_attr_add_new(&record, 0x0781, SDP_TEXT_STR8, (void *) "Macmini"); +	sdp_attr_add_new(&record, 0x0782, SDP_TEXT_STR8, (void *) "PowerMac10,1"); +	sdp_attr_add_new(&record, 0x0783, SDP_UINT32, (void *) &attr783); +	sdp_attr_add_new(&record, 0x0784, SDP_TEXT_STR8, (void *) "1.6.6f22"); +	sdp_attr_add_new(&record, 0x0785, SDP_UINT32, (void *) &attr785); +	sdp_attr_add_new(&record, 0x0786, SDP_UUID16, (void *) &attr786); + +	sdp_set_info_attr(&record, "Apple Macintosh Attributes", NULL, NULL); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		return -1; +	} + +	printf("Apple attribute service registered\n"); + +	return 0; +} + +static int add_isync(sdp_session_t *session, svc_info_t *si) +{ +	sdp_record_t record; +	sdp_list_t *root, *svclass, *proto; +	uuid_t root_uuid, svclass_uuid, serial_uuid, l2cap_uuid, rfcomm_uuid; +	uint8_t channel = si->channel ? si->channel : 16; + +	memset(&record, 0, sizeof(record)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid)); + +	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); +	proto = sdp_list_append(proto, sdp_list_append( +		sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel))); + +	sdp_set_access_protos(&record, sdp_list_append(NULL, proto)); + +	sdp_uuid16_create(&serial_uuid, SERIAL_PORT_SVCLASS_ID); +	svclass = sdp_list_append(NULL, &serial_uuid); + +	sdp_uuid16_create(&svclass_uuid, APPLE_AGENT_SVCLASS_ID); +	svclass = sdp_list_append(svclass, &svclass_uuid); + +	sdp_set_service_classes(&record, svclass); + +	sdp_set_info_attr(&record, "AppleAgent", "Bluetooth acceptor", "Apple Computer Ltd."); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		return -1; +	} + +	printf("Apple iSync service registered\n"); + +	return 0; +} + +static int add_semchla(sdp_session_t *session, svc_info_t *si) +{ +	sdp_record_t record; +	sdp_profile_desc_t profile; +	sdp_list_t *root, *svclass, *proto, *profiles; +	uuid_t root_uuid, service_uuid, l2cap_uuid, semchla_uuid; +	uint16_t psm = 0xf0f9; + +	memset(&record, 0, sizeof(record)); +	record.handle = si->handle; + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(&record, root); + +	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); +	proto = sdp_list_append(NULL, sdp_list_append( +		sdp_list_append(NULL, &l2cap_uuid), sdp_data_alloc(SDP_UINT16, &psm))); + +	sdp_uuid32_create(&semchla_uuid, 0x8e770300); +	proto = sdp_list_append(proto, sdp_list_append(NULL, &semchla_uuid)); + +	sdp_set_access_protos(&record, sdp_list_append(NULL, proto)); + +	sdp_uuid32_create(&service_uuid, 0x8e771301); +	svclass = sdp_list_append(NULL, &service_uuid); + +	sdp_set_service_classes(&record, svclass); + +	sdp_uuid32_create(&profile.uuid, 0x8e771302);	// Headset +	//sdp_uuid32_create(&profile.uuid, 0x8e771303);	// Phone +	profile.version = 0x0100; +	profiles = sdp_list_append(NULL, &profile); +	sdp_set_profile_descs(&record, profiles); + +	sdp_set_info_attr(&record, "SEMC HLA", NULL, NULL); + +	if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) { +		printf("Service Record registration failed\n"); +		return -1; +	} + +	/* SEMC High Level Authentication */ +	printf("SEMC HLA service registered\n"); + +	return 0; +} + +struct { +	char		*name; +	uint32_t	class; +	int		(*add)(sdp_session_t *sess, svc_info_t *si); +	unsigned char *uuid; +} service[] = { +	{ "DID",	PNP_INFO_SVCLASS_ID,		NULL,		}, + +	{ "SP",		SERIAL_PORT_SVCLASS_ID,		add_sp		}, +	{ "DUN",	DIALUP_NET_SVCLASS_ID,		add_dun		}, +	{ "LAN",	LAN_ACCESS_SVCLASS_ID,		add_lan		}, +	{ "FAX",	FAX_SVCLASS_ID,			add_fax		}, +	{ "OPUSH",	OBEX_OBJPUSH_SVCLASS_ID,	add_opush	}, +	{ "FTP",	OBEX_FILETRANS_SVCLASS_ID,	add_ftp		}, +	{ "PRINT",	DIRECT_PRINTING_SVCLASS_ID,	add_directprint	}, + +	{ "HS",		HEADSET_SVCLASS_ID,		add_headset	}, +	{ "HSAG",	HEADSET_AGW_SVCLASS_ID,		add_headset_ag	}, +	{ "HF",		HANDSFREE_SVCLASS_ID,		add_handsfree	}, +	{ "HFAG",	HANDSFREE_AGW_SVCLASS_ID,	add_handsfree_ag}, +	{ "SAP",	SAP_SVCLASS_ID,			add_simaccess	}, + +	{ "NAP",	NAP_SVCLASS_ID,			add_nap		}, +	{ "GN",		GN_SVCLASS_ID,			add_gn		}, +	{ "PANU",	PANU_SVCLASS_ID,		add_panu	}, + +	{ "HCRP",	HCR_SVCLASS_ID,			NULL		}, +	{ "HID",	HID_SVCLASS_ID,			NULL		}, +	{ "KEYB",	HID_SVCLASS_ID,			add_hid_keyb	}, +	{ "WIIMOTE",	HID_SVCLASS_ID,			add_hid_wiimote	}, +	{ "CIP",	CIP_SVCLASS_ID,			add_cip		}, +	{ "CTP",	CORDLESS_TELEPHONY_SVCLASS_ID,	add_ctp		}, + +	{ "A2SRC",	AUDIO_SOURCE_SVCLASS_ID,	add_a2source	}, +	{ "A2SNK",	AUDIO_SINK_SVCLASS_ID,		add_a2sink	}, +	{ "AVRCT",	AV_REMOTE_SVCLASS_ID,		add_avrct	}, +	{ "AVRTG",	AV_REMOTE_TARGET_SVCLASS_ID,	add_avrtg	}, + +	{ "UDIUE",	UDI_MT_SVCLASS_ID,		add_udi_ue	}, +	{ "UDITE",	UDI_TA_SVCLASS_ID,		add_udi_te	}, + +	{ "SEMCHLA",	0x8e771301,			add_semchla	}, + +	{ "SR1",	0,				add_sr1,	sr1_uuid	}, +	{ "SYNCML",	0,				add_syncml,	syncmlc_uuid	}, +	{ "SYNCMLSERV",	0,				NULL,		syncmls_uuid	}, +	{ "ACTIVESYNC",	0,				add_activesync,	async_uuid	}, +	{ "HOTSYNC",	0,				add_hotsync,	hotsync_uuid	}, +	{ "PALMOS",	0,				add_palmos,	palmos_uuid	}, +	{ "NOKID",	0,				add_nokiaid,	nokid_uuid	}, +	{ "PCSUITE",	0,				add_pcsuite,	pcsuite_uuid	}, +	{ "NFTP",	0,				NULL,		nftp_uuid	}, +	{ "NSYNCML",	0,				NULL,		nsyncml_uuid	}, +	{ "NGAGE",	0,				NULL,		ngage_uuid	}, +	{ "APPLE",	0,				add_apple,	apple_uuid	}, + +	{ "ISYNC",	APPLE_AGENT_SVCLASS_ID,		add_isync,	}, + +	{ 0 } +}; + +/* Add local service */ +static int add_service(bdaddr_t *bdaddr, svc_info_t *si) +{ +	sdp_session_t *sess; +	int i, ret = -1; + +	if (!si->name) +		return -1; + +	sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY); +	if (!sess) +		return -1; + +	for (i = 0; service[i].name; i++) +		if (!strcasecmp(service[i].name, si->name)) { +			if (service[i].add) +				ret = service[i].add(sess, si); +			goto done; +		} + +	printf("Unknown service name: %s\n", si->name); + +done: +	free(si->name); +	sdp_close(sess); + +	return ret; +} + +static struct option add_options[] = { +	{ "help",	0, 0, 'h' }, +	{ "handle",	1, 0, 'r' }, +	{ "psm",	1, 0, 'p' }, +	{ "channel",	1, 0, 'c' }, +	{ "network",	1, 0, 'n' }, +	{ 0, 0, 0, 0 } +}; + +static char *add_help =  +	"Usage:\n" +	"\tadd [--handle=RECORD_HANDLE --channel=CHANNEL] service\n"; + +static int cmd_add(int argc, char **argv) +{ +	svc_info_t si; +	int opt; + +	memset(&si, 0, sizeof(si)); +	si.handle = 0xffffffff; + +	for_each_opt(opt, add_options, 0) { +		switch (opt) { +		case 'r': +			if (strncasecmp(optarg, "0x", 2)) +				si.handle = atoi(optarg); +			else +				si.handle = strtol(optarg + 2, NULL, 16); +			break; +		case 'p': +			if (strncasecmp(optarg, "0x", 2)) +				si.psm = atoi(optarg); +			else +				si.psm = strtol(optarg + 2, NULL, 16); +			break; +		case 'c': +			if (strncasecmp(optarg, "0x", 2)) +				si.channel = atoi(optarg); +			else +				si.channel = strtol(optarg + 2, NULL, 16); +			break; +		case 'n': +			if (strncasecmp(optarg, "0x", 2)) +				si.network = atoi(optarg); +			else +				si.network = strtol(optarg + 2, NULL, 16); +			break; +		default: +			printf(add_help); +			return -1; +		} +	} + +	argc -= optind; +	argv += optind; + +	if (argc < 1) { +		printf(add_help); +		return -1; +	} + +	si.name = strdup(argv[0]); + +	return add_service(0, &si); +} + +/* Delete local service */ +static int del_service(bdaddr_t *bdaddr, void *arg) +{ +	uint32_t handle, range = 0x0000ffff; +	sdp_list_t *attr; +	sdp_session_t *sess; +	sdp_record_t *rec; + +	if (!arg) {  +		printf("Record handle was not specified.\n"); +		return -1; +	} + +	sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY); +	if (!sess) { +		printf("No local SDP server!\n"); +		return -1; +	} + +	handle = strtoul((char *)arg, 0, 16); +	attr = sdp_list_append(0, &range); +	rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attr); +	sdp_list_free(attr, 0); + +	if (!rec) { +		printf("Service Record not found.\n"); +		sdp_close(sess); +		return -1; +	} + +	if (sdp_device_record_unregister(sess, &interface, rec)) { +		printf("Failed to unregister service record: %s\n", strerror(errno)); +		sdp_close(sess); +		return -1; +	} + +	printf("Service Record deleted.\n"); +	sdp_close(sess); + +	return 0; +} + +static struct option del_options[] = { +	{ "help",	0, 0, 'h' }, +	{ 0, 0, 0, 0 } +}; + +static char *del_help =  +	"Usage:\n" +	"\tdel record_handle\n"; + +static int cmd_del(int argc, char **argv) +{ +	int opt; + +	for_each_opt(opt, del_options, 0) { +		switch (opt) { +		default: +			printf(del_help); +			return -1; +		} +	} + +	argc -= optind; +	argv += optind; + +	if (argc < 1) { +		printf(del_help); +		return -1; +	} + +	return del_service(NULL, argv[0]); +} + +/* + * Perform an inquiry and search/browse all peer found. + */ +static void inquiry(handler_t handler, void *arg) +{ +	inquiry_info ii[20]; +	uint8_t count = 0; +	int i; + +	printf("Inquiring ...\n"); +	if (sdp_general_inquiry(ii, 20, 8, &count) < 0) { +		printf("Inquiry failed\n"); +		return; +	} + +	for (i = 0; i < count; i++) +		handler(&ii[i].bdaddr, arg); +} + +static void doprintf(void *data, const char *str) +{ +	printf(str); +} + +/* + * Search for a specific SDP service + */ +static int do_search(bdaddr_t *bdaddr, struct search_context *context) +{ +	sdp_list_t *attrid, *search, *seq, *next; +	uint32_t range = 0x0000ffff; +	char str[20]; +	sdp_session_t *sess; + +	if (!bdaddr) { +		inquiry(do_search, context); +		return 0; +	} + +	sess = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY); +	ba2str(bdaddr, str); +	if (!sess) { +		printf("Failed to connect to SDP server on %s: %s\n", str, strerror(errno)); +		return -1; +	} + +	if (context->view != RAW_VIEW) { +		if (context->svc) +			printf("Searching for %s on %s ...\n", context->svc, str); +		else +			printf("Browsing %s ...\n", str); +	} + +	attrid = sdp_list_append(0, &range); +	search = sdp_list_append(0, &context->group); +	if (sdp_service_search_attr_req(sess, search, SDP_ATTR_REQ_RANGE, attrid, &seq)) { +		printf("Service Search failed: %s\n", strerror(errno)); +		sdp_close(sess); +		return -1; +	} +	sdp_list_free(attrid, 0); +	sdp_list_free(search, 0); + +	for (; seq; seq = next) { +		sdp_record_t *rec = (sdp_record_t *) seq->data; +		struct search_context sub_context; + +		switch (context->view) { +		case DEFAULT_VIEW: +			/* Display user friendly form */ +			print_service_attr(rec); +			printf("\n"); +			break; +		case TREE_VIEW: +			/* Display full tree */ +			print_tree_attr(rec); +			printf("\n"); +			break; +		case XML_VIEW: +			/* Display raw XML tree */ +			convert_sdp_record_to_xml(rec, 0, doprintf); +			break; +		default: +			/* Display raw tree */ +			print_raw_attr(rec); +			break; +		} + +		if (sdp_get_group_id(rec, &sub_context.group) != -1) { +			/* Set the subcontext for browsing the sub tree */ +			memcpy(&sub_context, context, sizeof(struct search_context)); +			/* Browse the next level down if not done */ +			if (sub_context.group.value.uuid16 != context->group.value.uuid16) +				do_search(bdaddr, &sub_context); +		} +		next = seq->next; +		free(seq); +		sdp_record_free(rec); +	} + +	sdp_close(sess); +	return 0; +} + +static struct option browse_options[] = { +	{ "help",	0, 0, 'h' }, +	{ "tree",	0, 0, 't' }, +	{ "raw",	0, 0, 'r' }, +	{ "xml",	0, 0, 'x' }, +	{ "uuid",	1, 0, 'u' }, +	{ "l2cap",	0, 0, 'l' }, +	{ 0, 0, 0, 0 } +}; + +static char *browse_help =  +	"Usage:\n" +	"\tbrowse [--tree] [--raw] [--xml] [--uuid uuid] [--l2cap] [bdaddr]\n"; + +/* + * Browse the full SDP database (i.e. list all services starting from the + * root/top-level). + */ +static int cmd_browse(int argc, char **argv) +{ +	struct search_context context; +	int opt, num; + +	/* Initialise context */ +	memset(&context, '\0', sizeof(struct search_context)); +	/* We want to browse the top-level/root */ +	sdp_uuid16_create(&context.group, PUBLIC_BROWSE_GROUP); + +	for_each_opt(opt, browse_options, 0) { +		switch (opt) { +		case 't': +			context.view = TREE_VIEW; +			break; +		case 'r': +			context.view = RAW_VIEW; +			break; +		case 'x': +			context.view = XML_VIEW; +			break; +		case 'u': +			if (sscanf(optarg, "%i", &num) != 1 || num < 0 || num > 0xffff) { +				printf("Invalid uuid %s\n", optarg); +				return -1; +			} +			sdp_uuid16_create(&context.group, num); +			break; +		case 'l': +			sdp_uuid16_create(&context.group, L2CAP_UUID); +			break; +		default: +			printf(browse_help); +			return -1; +		} +	} + +	argc -= optind; +	argv += optind; + +	if (argc >= 1) { +		bdaddr_t bdaddr; +		estr2ba(argv[0], &bdaddr); +		return do_search(&bdaddr, &context); +	} + +	return do_search(NULL, &context); +} + +static struct option search_options[] = { +	{ "help",	0, 0, 'h' }, +	{ "bdaddr",	1, 0, 'b' }, +	{ "tree",	0, 0, 't' }, +	{ "raw",	0, 0, 'r' }, +	{ "xml",	0, 0, 'x' }, +	{ 0, 0, 0, 0} +}; + +static char *search_help =  +	"Usage:\n" +	"\tsearch [--bdaddr bdaddr] [--tree] [--raw] [--xml] SERVICE\n" +	"SERVICE is a name (string) or UUID (0x1002)\n"; + +/* + * Search for a specific SDP service + * + * Note : we should support multiple services on the command line : + *          sdptool search 0x0100 0x000f 0x1002 + * (this would search a service supporting both L2CAP and BNEP directly in + * the top level browse group) + */ +static int cmd_search(int argc, char **argv) +{ +	struct search_context context; +	unsigned char *uuid = NULL; +	uint32_t class = 0; +	bdaddr_t bdaddr; +	int has_addr = 0; +	int i; +	int opt; + +	/* Initialise context */ +	memset(&context, '\0', sizeof(struct search_context)); + +	for_each_opt(opt, search_options, 0) { +		switch (opt) { +		case 'b': +			estr2ba(optarg, &bdaddr); +			has_addr = 1; +			break; +		case 't': +			context.view = TREE_VIEW; +			break; +		case 'r': +			context.view = RAW_VIEW; +			break; +		case 'x': +			context.view = XML_VIEW; +			break; +		default: +			printf(search_help); +			return -1; +		} +	} + +	argc -= optind; +	argv += optind; + +	if (argc < 1) { +		printf(search_help); +		return -1; +	} + +	/* Note : we need to find a way to support search combining +	 * multiple services */ +	context.svc = strdup(argv[0]); +	if (!strncasecmp(context.svc, "0x", 2)) { +		int num; +		/* This is a UUID16, just convert to int */ +		sscanf(context.svc + 2, "%X", &num); +		class = num; +		printf("Class 0x%X\n", class); +	} else { +		/* Convert class name to an UUID */ + +		for (i = 0; service[i].name; i++) +			if (strcasecmp(context.svc, service[i].name) == 0) { +				class = service[i].class; +				uuid = service[i].uuid; +				break; +			} +		if (!class && !uuid) { +			printf("Unknown service %s\n", context.svc); +			return -1; +		} +	} + +	if (class) { +		if (class & 0xffff0000) +			sdp_uuid32_create(&context.group, class); +		else { +			uint16_t class16 = class & 0xffff; +			sdp_uuid16_create(&context.group, class16); +		} +	} else +		sdp_uuid128_create(&context.group, uuid); + +	if (has_addr) +		return do_search(&bdaddr, &context); + +	return do_search(NULL, &context); +} + +/* + * Show how to get a specific SDP record by its handle. + * Not really useful to the user, just show how it can be done... + */ +static int get_service(bdaddr_t *bdaddr, struct search_context *context, int quite) +{ +	sdp_list_t *attrid; +	uint32_t range = 0x0000ffff; +	sdp_record_t *rec; +	sdp_session_t *session = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY); + +	if (!session) { +		char str[20]; +		ba2str(bdaddr, str); +		printf("Failed to connect to SDP server on %s: %s\n", str, strerror(errno)); +		return -1; +	} + +	attrid = sdp_list_append(0, &range); +	rec = sdp_service_attr_req(session, context->handle, SDP_ATTR_REQ_RANGE, attrid); +	sdp_list_free(attrid, 0); +	sdp_close(session); + +	if (!rec) { +		if (!quite) { +			printf("Service get request failed.\n"); +			return -1; +		} else +			return 0; +	} + +	switch (context->view) { +	case DEFAULT_VIEW: +		/* Display user friendly form */ +		print_service_attr(rec); +		printf("\n"); +		break; +	case TREE_VIEW: +		/* Display full tree */ +		print_tree_attr(rec); +		printf("\n"); +		break; +	case XML_VIEW: +		/* Display raw XML tree */ +		convert_sdp_record_to_xml(rec, 0, doprintf); +		break; +	default: +		/* Display raw tree */ +		print_raw_attr(rec); +		break; +	} + +	sdp_record_free(rec); +	return 0; +} + +static struct option records_options[] = { +	{ "help",	0, 0, 'h' }, +	{ "tree",	0, 0, 't' }, +	{ "raw",	0, 0, 'r' }, +	{ "xml",	0, 0, 'x' }, +	{ 0, 0, 0, 0 } +}; + +static char *records_help =  +	"Usage:\n" +	"\trecords [--tree] [--raw] [--xml] bdaddr\n"; + +/* + * Request possible SDP service records + */ +static int cmd_records(int argc, char **argv) +{ +	struct search_context context; +	uint32_t base[] = { 0x10000, 0x10300, 0x10500, +			0x1002e, 0x110b, 0x90000, 0x2008000, +			0x4000000, 0x100000, 0x1000000, 0x4f491100 }; +	bdaddr_t bdaddr; +	int i, n, opt, err = 0, num = 32; + +	/* Initialise context */ +	memset(&context, '\0', sizeof(struct search_context)); + +	for_each_opt(opt, records_options, 0) { +		switch (opt) { +		case 't': +			context.view = TREE_VIEW; +			break; +		case 'r': +			context.view = RAW_VIEW; +			break; +		case 'x': +			context.view = XML_VIEW; +			break; +		default: +			printf(records_help); +			return -1; +		} +	} + +	argc -= optind; +	argv += optind; + +	if (argc < 1) { +		printf(records_help); +		return -1; +	} + +	/* Convert command line parameters */ +	estr2ba(argv[0], &bdaddr); + +	for (i = 0; i < sizeof(base) / sizeof(uint32_t); i++) +		for (n = 0; n < num; n++) { +			context.handle = base[i] + n; +			err = get_service(&bdaddr, &context, 1); +			if (err < 0) +				goto done; +		} + +done: +	return 0; +} + +static struct option get_options[] = { +	{ "help",	0, 0, 'h' }, +	{ "bdaddr",	1, 0, 'b' }, +	{ "tree",	0, 0, 't' }, +	{ "raw",	0, 0, 'r' }, +	{ "xml",	0, 0, 'x' }, +	{ 0, 0, 0, 0 } +}; + +static char *get_help =  +	"Usage:\n" +	"\tget [--tree] [--raw] [--xml] [--bdaddr bdaddr] record_handle\n"; + +/* + * Get a specific SDP record on the local SDP server + */ +static int cmd_get(int argc, char **argv) +{ +	struct search_context context; +	bdaddr_t bdaddr; +	int has_addr = 0; +	int opt; + +	/* Initialise context */ +	memset(&context, '\0', sizeof(struct search_context)); + +	for_each_opt(opt, get_options, 0) { +		switch (opt) { +		case 'b': +			estr2ba(optarg, &bdaddr); +			has_addr = 1; +			break; +		case 't': +			context.view = TREE_VIEW; +			break; +		case 'r': +			context.view = RAW_VIEW; +			break; +		case 'x': +			context.view = XML_VIEW; +			break; +		default: +			printf(get_help); +			return -1; +		} +	} + +	argc -= optind; +	argv += optind; + +	if (argc < 1) { +		printf(get_help); +		return -1; +	} + +	/* Convert command line parameters */ +	context.handle = strtoul(argv[0], 0, 16); + +	return get_service(has_addr ? &bdaddr : BDADDR_LOCAL, &context, 0); +} + +static struct { +	char *cmd; +	int (*func)(int argc, char **argv); +	char *doc; +} command[] = { +	{ "search",  cmd_search,      "Search for a service"          }, +	{ "browse",  cmd_browse,      "Browse all available services" }, +	{ "records", cmd_records,     "Request all records"           }, +	{ "add",     cmd_add,         "Add local service"             }, +	{ "del",     cmd_del,         "Delete local service"          }, +	{ "get",     cmd_get,         "Get local service"             }, +	{ "setattr", cmd_setattr,     "Set/Add attribute to a SDP record"          }, +	{ "setseq",  cmd_setseq,      "Set/Add attribute sequence to a SDP record" }, +	{ 0, 0, 0 } +}; + +static void usage(void) +{ +	int i, pos = 0; + +	printf("sdptool - SDP tool v%s\n", VERSION); +	printf("Usage:\n" +		"\tsdptool [options] <command> [command parameters]\n"); +	printf("Options:\n" +		"\t-h\t\tDisplay help\n" +		"\t-i\t\tSpecify source interface\n"); + +	printf("Commands:\n"); +	for (i = 0; command[i].cmd; i++) +		printf("\t%-4s\t\t%s\n", command[i].cmd, command[i].doc); + +	printf("\nServices:\n\t"); +	for (i = 0; service[i].name; i++) { +		printf("%s ", service[i].name); +		pos += strlen(service[i].name) + 1; +		if (pos > 60) { +			printf("\n\t"); +			pos = 0; +		} +	} +	printf("\n"); +} + +static struct option main_options[] = { +	{ "help",	0, 0, 'h' }, +	{ "device",	1, 0, 'i' }, +	{ 0, 0, 0, 0 } +}; + +int main(int argc, char *argv[]) +{ +	int i, opt; + +	bacpy(&interface, BDADDR_ANY); + +	while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) { +		switch(opt) { +		case 'i': +			if (!strncmp(optarg, "hci", 3)) +				hci_devba(atoi(optarg + 3), &interface); +			else +				str2ba(optarg, &interface); +			break; + +		case 'h': +			usage(); +			exit(0); + +		default: +			exit(1); +		} +	} + +	argc -= optind; +	argv += optind; +	optind = 0; + +	if (argc < 1) { +		usage(); +		exit(1); +	} + +	for (i = 0; command[i].cmd; i++) +		if (strncmp(command[i].cmd, argv[0], 4) == 0) +			return command[i].func(argc, argv); + +	return 1; +}  | 
