/*	$NetBSD: compat.c,v 1.2 2010/03/07 10:58:40 plunky Exp $	*/

/*-
 * Copyright (c) 2009 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Iain Hibbert.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__RCSID("$NetBSD: compat.c,v 1.2 2010/03/07 10:58:40 plunky Exp $");

#include <arpa/inet.h>

#include <netbt/rfcomm.h>

#include <bluetooth.h>
#include <errno.h>
#include <sdp.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "sdpd.h"

/*
 * This file provides for compatibility with the old ABI. Clients send
 * a data structure and we generate a record based from that using the
 * server output buffer as temporary storage. The sdp_put functions will
 * not write invalid data or overflow the buffer which is big enough to
 * contain all these records, no need to worry about that.
 */

static bool
dun_profile(sdp_data_t *buf, void *arg, ssize_t len)
{
	sdp_dun_profile_t	*data = arg;
	uint8_t			*first = buf->next;

	if (len != sizeof(*data)
	    || data->server_channel < RFCOMM_CHANNEL_MIN
	    || data->server_channel > RFCOMM_CHANNEL_MAX)
		return false;

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_RECORD_HANDLE);
	sdp_put_uint32(buf, 0x00000000);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_CLASS_ID_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_DIALUP_NETWORKING);

	sdp_put_uint16(buf, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 12);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_L2CAP);
	sdp_put_seq(buf, 5);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_RFCOMM);
	sdp_put_uint8(buf, data->server_channel);

	sdp_put_uint16(buf, SDP_ATTR_BROWSE_GROUP_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);

	sdp_put_uint16(buf, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
	sdp_put_seq(buf, 9);
	sdp_put_uint16(buf, 0x656e);	/* "en" */
	sdp_put_uint16(buf, 106);	/* UTF-8 */
	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);

	sdp_put_uint16(buf, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 8);
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_DIALUP_NETWORKING);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET);
	sdp_put_str(buf, "Dialup Networking", -1);

	sdp_put_uint16(buf, SDP_ATTR_AUDIO_FEEDBACK_SUPPORT);
	sdp_put_bool(buf, data->audio_feedback_support);

	buf->end = buf->next;
	buf->next = first;
	return true;
}

static bool
ftrn_profile(sdp_data_t *buf, void *arg, ssize_t len)
{
	sdp_ftrn_profile_t	*data = arg;
	uint8_t			*first = buf->next;

	if (len != sizeof(*data)
	    || data->server_channel < RFCOMM_CHANNEL_MIN
	    || data->server_channel > RFCOMM_CHANNEL_MAX)
		return false;

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_RECORD_HANDLE);
	sdp_put_uint32(buf, 0x00000000);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_CLASS_ID_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER);

	sdp_put_uint16(buf, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 17);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_L2CAP);
	sdp_put_seq(buf, 5);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_RFCOMM);
	sdp_put_uint8(buf, data->server_channel);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_OBEX);

	sdp_put_uint16(buf, SDP_ATTR_BROWSE_GROUP_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);

	sdp_put_uint16(buf, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
	sdp_put_seq(buf, 9);
	sdp_put_uint16(buf, 0x656e);	/* "en" */
	sdp_put_uint16(buf, 106);	/* UTF-8 */
	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);

	sdp_put_uint16(buf, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 8);
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET);
	sdp_put_str(buf, "OBEX File Transfer", -1);

	buf->end = buf->next;
	buf->next = first;
	return true;
}

static bool
hset_profile(sdp_data_t *buf, void *arg, ssize_t len)
{
	sdp_hset_profile_t	*data = arg;
	uint8_t			*first = buf->next;

	if (len != sizeof(*data)
	    || data->server_channel < RFCOMM_CHANNEL_MIN
	    || data->server_channel > RFCOMM_CHANNEL_MAX)
		return false;

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_RECORD_HANDLE);
	sdp_put_uint32(buf, 0x00000000);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_CLASS_ID_LIST);
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_HEADSET_AUDIO_GATEWAY);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_GENERIC_AUDIO);

	sdp_put_uint16(buf, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 12);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_L2CAP);
	sdp_put_seq(buf, 5);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_RFCOMM);
	sdp_put_uint8(buf, data->server_channel);

	sdp_put_uint16(buf, SDP_ATTR_BROWSE_GROUP_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);

	sdp_put_uint16(buf, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
	sdp_put_seq(buf, 9);
	sdp_put_uint16(buf, 0x656e);	/* "en" */
	sdp_put_uint16(buf, 106);	/* UTF-8 */
	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);

	sdp_put_uint16(buf, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 8);
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_HEADSET);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET);
	sdp_put_str(buf, "Voice Gateway", -1);

	buf->end = buf->next;
	buf->next = first;
	return true;
}

static bool
hf_profile(sdp_data_t *buf, void *arg, ssize_t len)
{
	sdp_hf_profile_t	*data = arg;
	uint8_t			*first = buf->next;

	if (len != sizeof(*data)
	    || data->server_channel < RFCOMM_CHANNEL_MIN
	    || data->server_channel > RFCOMM_CHANNEL_MAX
	    || (data->supported_features & ~0x001f))
		return false;

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_RECORD_HANDLE);
	sdp_put_uint32(buf, 0x00000000);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_CLASS_ID_LIST);
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_HANDSFREE);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_GENERIC_AUDIO);

	sdp_put_uint16(buf, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 12);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_L2CAP);
	sdp_put_seq(buf, 5);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_RFCOMM);
	sdp_put_uint8(buf, data->server_channel);

	sdp_put_uint16(buf, SDP_ATTR_BROWSE_GROUP_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);

	sdp_put_uint16(buf, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
	sdp_put_seq(buf, 9);
	sdp_put_uint16(buf, 0x656e);	/* "en" */
	sdp_put_uint16(buf, 106);	/* UTF-8 */
	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);

	sdp_put_uint16(buf, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 8);
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_HANDSFREE);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET);
	sdp_put_str(buf, "Hands-Free unit", -1);

	sdp_put_uint16(buf, SDP_ATTR_SUPPORTED_FEATURES);
	sdp_put_uint16(buf, data->supported_features);

	buf->end = buf->next;
	buf->next = first;
	return true;
}

static bool
irmc_profile(sdp_data_t *buf, void *arg, ssize_t len)
{
	sdp_irmc_profile_t	*data = arg;
	uint8_t			*first = buf->next;
	int			i;

	if (len != sizeof(*data)
	    || data->server_channel < RFCOMM_CHANNEL_MIN
	    || data->server_channel > RFCOMM_CHANNEL_MAX
	    || data->supported_formats_size == 0
	    || data->supported_formats_size > sizeof(data->supported_formats))
		return false;

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_RECORD_HANDLE);
	sdp_put_uint32(buf, 0x00000000);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_CLASS_ID_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_IR_MC_SYNC);

	sdp_put_uint16(buf, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 17);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_L2CAP);
	sdp_put_seq(buf, 5);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_RFCOMM);
	sdp_put_uint8(buf, data->server_channel);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_OBEX);

	sdp_put_uint16(buf, SDP_ATTR_BROWSE_GROUP_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);

	sdp_put_uint16(buf, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
	sdp_put_seq(buf, 9);
	sdp_put_uint16(buf, 0x656e);	/* "en" */
	sdp_put_uint16(buf, 106);	/* UTF-8 */
	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);

	sdp_put_uint16(buf, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 8);
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_IR_MC_SYNC);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET);
	sdp_put_str(buf, "IrMC Syncrhonization", -1);

	sdp_put_uint16(buf, SDP_ATTR_SUPPORTED_DATA_STORES_LIST);
	sdp_put_seq(buf, data->supported_formats_size * 2);
	for (i = 0; i < data->supported_formats_size; i++)
		sdp_put_uint8(buf, data->supported_formats[i]);

	buf->end = buf->next;
	buf->next = first;
	return true;
}

static bool
irmc_cmd_profile(sdp_data_t *buf, void *arg, ssize_t len)
{
	sdp_irmc_command_profile_t	*data = arg;
	uint8_t				*first = buf->next;

	if (len != sizeof(*data)
	    || data->server_channel < RFCOMM_CHANNEL_MIN
	    || data->server_channel > RFCOMM_CHANNEL_MAX)
		return false;

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_RECORD_HANDLE);
	sdp_put_uint32(buf, 0x00000000);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_CLASS_ID_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_IR_MC_SYNC_COMMAND);

	sdp_put_uint16(buf, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 17);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_L2CAP);
	sdp_put_seq(buf, 5);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_RFCOMM);
	sdp_put_uint8(buf, data->server_channel);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_OBEX);

	sdp_put_uint16(buf, SDP_ATTR_BROWSE_GROUP_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);

	sdp_put_uint16(buf, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
	sdp_put_seq(buf, 9);
	sdp_put_uint16(buf, 0x656e);	/* "en" */
	sdp_put_uint16(buf, 106);	/* UTF-8 */
	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);

	sdp_put_uint16(buf, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 8);
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_IR_MC_SYNC_COMMAND);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET);
	sdp_put_str(buf, "Sync Command Service", -1);

	buf->end = buf->next;
	buf->next = first;
	return true;
}

static bool
lan_profile(sdp_data_t *buf, void *arg, ssize_t len)
{
	sdp_lan_profile_t	*data = arg;
	uint8_t			*first = buf->next;
	struct in_addr		in;
	char			str[32];

	if (len != sizeof(*data)
	    || data->server_channel < RFCOMM_CHANNEL_MIN
	    || data->server_channel > RFCOMM_CHANNEL_MAX
	    || data->ip_subnet_radius > 32)
		return false;

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_RECORD_HANDLE);
	sdp_put_uint32(buf, 0x00000000);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_CLASS_ID_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP);

	sdp_put_uint16(buf, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 12);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_L2CAP);
	sdp_put_seq(buf, 5);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_RFCOMM);
	sdp_put_uint8(buf, data->server_channel);

	sdp_put_uint16(buf, SDP_ATTR_BROWSE_GROUP_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);

	sdp_put_uint16(buf, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
	sdp_put_seq(buf, 9);
	sdp_put_uint16(buf, 0x656e);	/* "en" */
	sdp_put_uint16(buf, 106);	/* UTF-8 */
	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_AVAILABILITY);
	sdp_put_uint8(buf, data->load_factor);

	sdp_put_uint16(buf, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 8);
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET);
	sdp_put_str(buf, "LAN Access using PPP", -1);

	in.s_addr= data->ip_subnet;
	sprintf(str, "%s/%d", inet_ntoa(in), data->ip_subnet_radius);
	sdp_put_uint16(buf, SDP_ATTR_IP_SUBNET);
	sdp_put_str(buf, str, -1);

	buf->end = buf->next;
	buf->next = first;
	return true;
}

static bool
opush_profile(sdp_data_t *buf, void *arg, ssize_t len)
{
	sdp_opush_profile_t	*data = arg;
	uint8_t			*first = buf->next;
	int			i;

	if (len != sizeof(*data)
	    || data->server_channel < RFCOMM_CHANNEL_MIN
	    || data->server_channel > RFCOMM_CHANNEL_MAX
	    || data->supported_formats_size == 0
	    || data->supported_formats_size > sizeof(data->supported_formats))
		return false;

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_RECORD_HANDLE);
	sdp_put_uint32(buf, 0x00000000);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_CLASS_ID_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH);

	sdp_put_uint16(buf, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 17);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_L2CAP);
	sdp_put_seq(buf, 5);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_RFCOMM);
	sdp_put_uint8(buf, data->server_channel);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_OBEX);

	sdp_put_uint16(buf, SDP_ATTR_BROWSE_GROUP_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);

	sdp_put_uint16(buf, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
	sdp_put_seq(buf, 9);
	sdp_put_uint16(buf, 0x656e);	/* "en" */
	sdp_put_uint16(buf, 106);	/* UTF-8 */
	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);

	sdp_put_uint16(buf, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 8);
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET);
	sdp_put_str(buf, "OBEX Object Push", -1);

	sdp_put_uint16(buf, SDP_ATTR_SUPPORTED_FORMATS_LIST);
	sdp_put_seq(buf, data->supported_formats_size * 2);
	for (i = 0; i < data->supported_formats_size; i++)
		sdp_put_uint8(buf, data->supported_formats[i]);

	buf->end = buf->next;
	buf->next = first;
	return true;
}

static bool
sp_profile(sdp_data_t *buf, void *arg, ssize_t len)
{
	sdp_sp_profile_t	*data = arg;
	uint8_t			*first = buf->next;

	if (len != sizeof(*data)
	    || data->server_channel < RFCOMM_CHANNEL_MIN
	    || data->server_channel > RFCOMM_CHANNEL_MAX)
		return false;

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_RECORD_HANDLE);
	sdp_put_uint32(buf, 0x00000000);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_CLASS_ID_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_SERIAL_PORT);

	sdp_put_uint16(buf, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 12);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_L2CAP);
	sdp_put_seq(buf, 5);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_RFCOMM);
	sdp_put_uint8(buf, data->server_channel);

	sdp_put_uint16(buf, SDP_ATTR_BROWSE_GROUP_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);

	sdp_put_uint16(buf, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
	sdp_put_seq(buf, 9);
	sdp_put_uint16(buf, 0x656e);	/* "en" */
	sdp_put_uint16(buf, 106);	/* UTF-8 */
	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);

	sdp_put_uint16(buf, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 8);
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_SERIAL_PORT);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET);
	sdp_put_str(buf, "Serial Port", -1);

	buf->end = buf->next;
	buf->next = first;
	return true;
}

/* list of protocols used by PAN profiles.  */
static const uint16_t proto[] = {
	0x0800,	/* IPv4 */
	0x0806,	/* ARP  */
#ifdef INET6
	0x86dd,	/* IPv6 */
#endif
};

static bool
nap_profile(sdp_data_t *buf, void *arg, ssize_t len)
{
	sdp_nap_profile_t	*data = arg;
	uint8_t			*first = buf->next;
	size_t			i;

	if (len != sizeof(*data)
	    || L2CAP_PSM_INVALID(data->psm)
	    || data->security_description > 0x0002)
		return false;

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_RECORD_HANDLE);
	sdp_put_uint32(buf, 0x00000000);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_CLASS_ID_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_NAP);

	sdp_put_uint16(buf, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 18 + 3 * __arraycount(proto));
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_L2CAP);
	sdp_put_uint16(buf, data->psm);
	sdp_put_seq(buf, 8 + 3 * __arraycount(proto));
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_BNEP);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */
	sdp_put_seq(buf, 3 * __arraycount(proto));
	for (i = 0; i < __arraycount(proto); i++)
		sdp_put_uint16(buf, proto[i]);

	sdp_put_uint16(buf, SDP_ATTR_BROWSE_GROUP_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);

	sdp_put_uint16(buf, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
	sdp_put_seq(buf, 9);
	sdp_put_uint16(buf, 0x656e);	/* "en" */
	sdp_put_uint16(buf, 106);	/* UTF-8 */
	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_AVAILABILITY);
	sdp_put_uint8(buf, data->load_factor);

	sdp_put_uint16(buf, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 8);
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_NAP);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET);
	sdp_put_str(buf, "Network Access Point", -1);

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_DESCRIPTION_OFFSET);
	sdp_put_str(buf, "Personal Ad-hoc Network Service", -1);

	sdp_put_uint16(buf, SDP_ATTR_SECURITY_DESCRIPTION);
	sdp_put_uint16(buf, data->security_description);

	sdp_put_uint16(buf, SDP_ATTR_NET_ACCESS_TYPE);
	sdp_put_uint16(buf, data->net_access_type);

	sdp_put_uint16(buf, SDP_ATTR_MAX_NET_ACCESS_RATE);
	sdp_put_uint32(buf, data->max_net_access_rate);

	buf->end = buf->next;
	buf->next = first;
	return true;
}

static bool
gn_profile(sdp_data_t *buf, void *arg, ssize_t len)
{
	sdp_gn_profile_t	*data = arg;
	uint8_t			*first = buf->next;
	size_t			i;

	if (len != sizeof(*data)
	    || L2CAP_PSM_INVALID(data->psm)
	    || data->security_description > 0x0002)
		return false;

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_RECORD_HANDLE);
	sdp_put_uint32(buf, 0x00000000);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_CLASS_ID_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_GN);

	sdp_put_uint16(buf, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 18 + 3 * __arraycount(proto));
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_L2CAP);
	sdp_put_uint16(buf, data->psm);
	sdp_put_seq(buf, 8 + 3 * __arraycount(proto));
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_BNEP);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */
	sdp_put_seq(buf, 3 * __arraycount(proto));
	for (i = 0; i < __arraycount(proto); i++)
		sdp_put_uint16(buf, proto[i]);

	sdp_put_uint16(buf, SDP_ATTR_BROWSE_GROUP_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);

	sdp_put_uint16(buf, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
	sdp_put_seq(buf, 9);
	sdp_put_uint16(buf, 0x656e);	/* "en" */
	sdp_put_uint16(buf, 106);	/* UTF-8 */
	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_AVAILABILITY);
	sdp_put_uint8(buf, data->load_factor);

	sdp_put_uint16(buf, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 8);
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_GN);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET);
	sdp_put_str(buf, "Group Ad-hoc Network", -1);

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_DESCRIPTION_OFFSET);
	sdp_put_str(buf, "Personal Group Ad-hoc Network Service", -1);

	sdp_put_uint16(buf, SDP_ATTR_SECURITY_DESCRIPTION);
	sdp_put_uint16(buf, data->security_description);

	buf->end = buf->next;
	buf->next = first;
	return true;
}

static bool
panu_profile(sdp_data_t *buf, void *arg, ssize_t len)
{
	sdp_panu_profile_t	*data = arg;
	uint8_t			*first = buf->next;
	size_t			i;

	if (len != sizeof(*data)
	    || L2CAP_PSM_INVALID(data->psm)
	    || data->security_description > 0x0002)
		return false;

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_RECORD_HANDLE);
	sdp_put_uint32(buf, 0x00000000);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_CLASS_ID_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_PANU);

	sdp_put_uint16(buf, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 18 + 3 * __arraycount(proto));
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_L2CAP);
	sdp_put_uint16(buf, data->psm);
	sdp_put_seq(buf, 8 + 3 * __arraycount(proto));
	sdp_put_uuid16(buf, SDP_UUID_PROTOCOL_BNEP);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */
	sdp_put_seq(buf, 3 * __arraycount(proto));
	for (i = 0; i < __arraycount(proto); i++)
		sdp_put_uint16(buf, proto[i]);

	sdp_put_uint16(buf, SDP_ATTR_BROWSE_GROUP_LIST);
	sdp_put_seq(buf, 3);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);

	sdp_put_uint16(buf, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
	sdp_put_seq(buf, 9);
	sdp_put_uint16(buf, 0x656e);	/* "en" */
	sdp_put_uint16(buf, 106);	/* UTF-8 */
	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);

	sdp_put_uint16(buf, SDP_ATTR_SERVICE_AVAILABILITY);
	sdp_put_uint8(buf, data->load_factor);

	sdp_put_uint16(buf, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
	sdp_put_seq(buf, 8);
	sdp_put_seq(buf, 6);
	sdp_put_uuid16(buf, SDP_SERVICE_CLASS_PANU);
	sdp_put_uint16(buf, 0x0100);	/* v1.0 */

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET);
	sdp_put_str(buf, "Personal Ad-hoc User Service", -1);

	sdp_put_uint16(buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_DESCRIPTION_OFFSET);
	sdp_put_str(buf, "Personal Ad-hoc User Service", -1);

	sdp_put_uint16(buf, SDP_ATTR_SECURITY_DESCRIPTION);
	sdp_put_uint16(buf, data->security_description);

	buf->end = buf->next;
	buf->next = first;
	return true;
}

static const struct {
	uint16_t	class;
	bool		(*create)(sdp_data_t *, void *, ssize_t);
} known[] = {
	{ SDP_SERVICE_CLASS_DIALUP_NETWORKING,		dun_profile	    },
	{ SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER,		ftrn_profile	    },
	{ SDP_SERVICE_CLASS_HEADSET_AUDIO_GATEWAY,	hset_profile	    },
	{ SDP_SERVICE_CLASS_HANDSFREE,			hf_profile	    },
	{ SDP_SERVICE_CLASS_IR_MC_SYNC,			irmc_profile	    },
	{ SDP_SERVICE_CLASS_IR_MC_SYNC_COMMAND,		irmc_cmd_profile    },
	{ SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,	lan_profile	    },
	{ SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH,		opush_profile	    },
	{ SDP_SERVICE_CLASS_SERIAL_PORT,		sp_profile	    },
	{ SDP_SERVICE_CLASS_NAP,			nap_profile	    },
	{ SDP_SERVICE_CLASS_GN,				gn_profile	    },
	{ SDP_SERVICE_CLASS_PANU,			panu_profile	    },
};

uint16_t
compat_register_request(server_t *srv, int fd)
{
	sdp_data_t d, r;
	bdaddr_t bdaddr;
	uint16_t class;
	int i;

	log_debug("compat RegisterRequest by client on fd#%d", fd);

	if (!srv->fdidx[fd].control
	    || !srv->fdidx[fd].priv)
		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;

	srv->fdidx[fd].offset = 0;
	db_unselect(srv, fd);
	d.next = srv->ibuf;
	d.end = srv->ibuf + srv->pdu.len;

	if (d.next + sizeof(uint16_t) + sizeof(bdaddr_t) > d.end)
		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;

	class = be16dec(d.next);
	d.next += sizeof(uint16_t);

	memcpy(&bdaddr, d.next, sizeof(bdaddr_t));
	d.next += sizeof(bdaddr_t);

	for (i = 0;; i++) {
		if (i == __arraycount(known))
			return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;

		if (known[i].class == class)
			break;
	}

	r.next = srv->obuf;
	r.end = srv->obuf + srv->omtu;
	if (!(known[i].create(&r, d.next, d.end - d.next)))
		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;

	if (!db_create(srv, fd, &bdaddr, srv->handle, &r))
		return SDP_ERROR_CODE_INSUFFICIENT_RESOURCES;

	/* successful return */
	be16enc(srv->obuf, 0x0000);
	be32enc(srv->obuf + sizeof(uint16_t), srv->handle++);
	srv->pdu.pid = SDP_PDU_ERROR_RESPONSE;
	srv->pdu.len = sizeof(uint16_t) + sizeof(uint32_t);
	return 0;
}

uint16_t
compat_change_request(server_t *srv, int fd)
{
	record_t *rec;
	sdp_data_t d, r;
	int i;

	log_debug("compat ChangeRequest by client on fd#%d", fd);

	if (!srv->fdidx[fd].control
	    || !srv->fdidx[fd].priv)
		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;

	srv->fdidx[fd].offset = 0;
	db_unselect(srv, fd);
	d.next = srv->ibuf;
	d.end = srv->ibuf + srv->pdu.len;

	if (d.next + sizeof(uint32_t) > d.end)
		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;

	db_select_handle(srv, fd, be32dec(d.next));
	d.next += sizeof(uint32_t);

	rec = NULL;
	db_next(srv, fd, &rec);
	if (rec == NULL || rec->fd != fd)
		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;

	/*
	 * This is rather simplistic but it works. We don't keep a
	 * record of the ServiceClass but we do know the format of
	 * of the stored record and where it should be. If we dont
	 * find it there, just give up.
	 */
	r = rec->data;
	r.next += 3;		/* uint16 ServiceRecordHandle	*/
	r.next += 5;		/* uint32 %handle%		*/
	r.next += 3;		/* uint16 ServiceClassIDList	*/
	r.next += 2;		/* seq8				*/
	for (i = 0;; i++) {
		if (i == __arraycount(known))
			return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;

		if (sdp_match_uuid16(&r, known[i].class))
			break;
	}

	r.next = srv->obuf;
	r.end = srv->obuf + srv->omtu;
	if (!(known[i].create(&r, d.next, d.end - d.next)))
		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;

	if (!db_create(srv, fd, &rec->bdaddr, rec->handle, &r))
		return SDP_ERROR_CODE_INSUFFICIENT_RESOURCES;

	db_unselect(srv, fd);

	/* successful return */
	be16enc(srv->obuf, 0x0000);
	srv->pdu.pid = SDP_PDU_ERROR_RESPONSE;
	srv->pdu.len = sizeof(uint16_t);
	return 0;
}
