/*      $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;
}