update to use the new Service Discovery API

This commit is contained in:
plunky 2009-05-12 21:50:38 +00:00
parent d2379b2f18
commit ab1f45acf7
7 changed files with 211 additions and 334 deletions

View File

@ -1,13 +1,11 @@
# $NetBSD: Makefile,v 1.4 2009/05/12 13:11:18 plunky Exp $
# $NetBSD: Makefile,v 1.5 2009/05/12 21:50:38 plunky Exp $
#
PROG= btpand
MAN= btpand.8
SRCS= btpand.c bnep.c channel.c client.c packet.c server.c sdp.c tap.c
SRCS= btpand.c bnep.c channel.c client.c packet.c server.c tap.c
DPADD+= ${LIBBLUETOOTH} ${LIBEVENT} ${LIBUTIL}
LDADD+= -lbluetooth -levent -lutil
CPPFLAGS+= -DSDP_COMPAT
.include <bsd.prog.mk>

View File

@ -1,7 +1,7 @@
/* $NetBSD: btpand.c,v 1.3 2009/05/12 21:08:30 plunky Exp $ */
/* $NetBSD: btpand.c,v 1.4 2009/05/12 21:50:38 plunky Exp $ */
/*-
* Copyright (c) 2008 Iain Hibbert
* Copyright (c) 2008-2009 Iain Hibbert
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,8 +26,8 @@
*/
#include <sys/cdefs.h>
__COPYRIGHT("@(#) Copyright (c) 2008 Iain Hibbert. All rights reserved.");
__RCSID("$NetBSD: btpand.c,v 1.3 2009/05/12 21:08:30 plunky Exp $");
__COPYRIGHT("@(#) Copyright (c) 2008-2009 Iain Hibbert. All rights reserved.");
__RCSID("$NetBSD: btpand.c,v 1.4 2009/05/12 21:50:38 plunky Exp $");
#include <sys/wait.h>
@ -47,24 +47,35 @@ __RCSID("$NetBSD: btpand.c,v 1.3 2009/05/12 21:08:30 plunky Exp $");
/* global variables */
const char * control_path; /* -c <path> */
const char * interface_name; /* -i <ifname> */
const char * service_name; /* -s <service> */
const char * service_type; /* -s <service> */
uint16_t service_class;
const char * service_name;
const char * service_desc;
bdaddr_t local_bdaddr; /* -d <addr> */
bdaddr_t remote_bdaddr; /* -a <addr> */
uint16_t l2cap_psm; /* -p <psm> */
int l2cap_mode; /* -m <mode> */
int server_limit; /* -n <limit> */
static const struct {
const char * name;
const char * type;
uint16_t class;
const char * name;
const char * desc;
} services[] = {
{ "PANU", SDP_SERVICE_CLASS_PANU, "Personal Area Networking User" },
{ "NAP", SDP_SERVICE_CLASS_NAP, "Network Acess Point" },
{ "GN", SDP_SERVICE_CLASS_GN, "Group Network" },
{ "PANU", SDP_SERVICE_CLASS_PANU,
"Personal Ad-hoc User Service",
"Personal Ad-hoc User Service"
},
{ "NAP", SDP_SERVICE_CLASS_NAP,
"Network Acess Point",
"Personal Ad-hoc Network Service"
},
{ "GN", SDP_SERVICE_CLASS_GN,
"Group Ad-hoc Network",
"Personal Group Ad-hoc Network Service"
},
};
static void main_exit(int);
@ -139,18 +150,21 @@ main(int argc, char *argv[])
|| ul > 0xffff || L2CAP_PSM_INVALID(ul))
errx(EXIT_FAILURE, "%s: invalid PSM", optarg);
l2cap_psm = ul;
l2cap_psm = (uint16_t)ul;
break;
case 's': /* service */
case 'S': /* service (no SDP) */
for (ul = 0; strcasecmp(optarg, services[ul].name); ul++) {
for (ul = 0; strcasecmp(optarg, services[ul].type); ul++) {
if (ul == __arraycount(services))
errx(EXIT_FAILURE, "%s: unknown service", optarg);
}
if (ch == 's')
if (ch == 's') {
service_type = services[ul].type;
service_name = services[ul].name;
service_desc = services[ul].desc;
}
service_class = services[ul].class;
break;
@ -169,7 +183,7 @@ main(int argc, char *argv[])
usage();
if (!bdaddr_any(&remote_bdaddr) && (server_limit != 0 ||
control_path != 0 || (service_name != NULL && l2cap_psm != 0)))
control_path != 0 || (service_type != NULL && l2cap_psm != 0)))
usage();
/* default options */
@ -261,7 +275,7 @@ static void
usage(void)
{
const char *p = getprogname();
int n = strlen(p);
size_t n = strlen(p);
fprintf(stderr,
"usage: %s [-i ifname] [-m mode] -a address -d device\n"
@ -283,8 +297,8 @@ usage(void)
"Known services:\n"
"", p, n, "", p, n, "");
for (n = 0; n < (int)__arraycount(services); n++)
fprintf(stderr, "\t%s\t%s\n", services[n].name, services[n].desc);
for (n = 0; n < __arraycount(services); n++)
fprintf(stderr, "\t%s\t%s\n", services[n].type, services[n].name);
exit(EXIT_FAILURE);
}

View File

@ -1,7 +1,7 @@
/* $NetBSD: btpand.h,v 1.2 2009/05/12 21:08:30 plunky Exp $ */
/* $NetBSD: btpand.h,v 1.3 2009/05/12 21:50:38 plunky Exp $ */
/*-
* Copyright (c) 2008 Iain Hibbert
* Copyright (c) 2008-2009 Iain Hibbert
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -135,7 +135,9 @@ struct pkthdr {
/* global variables */
extern const char * control_path;
extern const char * service_type;
extern const char * service_name;
extern const char * service_desc;
extern const char * interface_name;
extern bdaddr_t local_bdaddr;
extern bdaddr_t remote_bdaddr;

View File

@ -1,7 +1,7 @@
/* $NetBSD: client.c,v 1.3 2009/05/12 21:08:30 plunky Exp $ */
/* $NetBSD: client.c,v 1.4 2009/05/12 21:50:38 plunky Exp $ */
/*-
* Copyright (c) 2008 Iain Hibbert
* Copyright (c) 2008-2009 Iain Hibbert
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,7 +26,7 @@
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: client.c,v 1.3 2009/05/12 21:08:30 plunky Exp $");
__RCSID("$NetBSD: client.c,v 1.4 2009/05/12 21:50:38 plunky Exp $");
#include <bluetooth.h>
#include <errno.h>
@ -35,7 +35,6 @@ __RCSID("$NetBSD: client.c,v 1.3 2009/05/12 21:08:30 plunky Exp $");
#include "btpand.h"
#include "bnep.h"
#include "sdp.h"
static void client_down(channel_t *);
static void client_query(void);
@ -52,7 +51,7 @@ client_init(void)
if (bdaddr_any(&remote_bdaddr))
return;
if (service_name)
if (service_type)
client_query();
fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
@ -144,64 +143,83 @@ client_down(channel_t *chan)
static void
client_query(void)
{
uint8_t buffer[512];
sdp_attr_t attr;
uint32_t range;
void *ss;
int rv;
uint8_t *seq0, *seq1;
attr.flags = SDP_ATTR_INVALID;
attr.attr = 0;
attr.vlen = sizeof(buffer);
attr.value = buffer;
range = SDP_ATTR_RANGE(SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
uint8_t buf[12]; /* enough for SSP and AIL both */
sdp_session_t ss;
sdp_data_t ssp, ail, rsp, rec, value, pdl, seq;
uintmax_t psm;
uint16_t attr;
bool rv;
ss = sdp_open(&local_bdaddr, &remote_bdaddr);
if (ss == NULL || (errno = sdp_error(ss)) != 0) {
log_err("%s: %m", service_name);
if (ss == NULL) {
log_err("%s: %m", service_type);
exit(EXIT_FAILURE);
}
log_info("Searching for %s service at %s",
service_name, bt_ntoa(&remote_bdaddr, NULL));
service_type, bt_ntoa(&remote_bdaddr, NULL));
rv = sdp_search(ss, 1, &service_class, 1, &range, 1, &attr);
if (rv != 0) {
log_err("%s: %s", service_name, strerror(sdp_error(ss)));
exit(EXIT_FAILURE);
}
seq.next = buf;
seq.end = buf + sizeof(buf);
sdp_close(ss);
/*
* build ServiceSearchPattern (9 bytes)
*
* uuid16 "service_class"
* uuid16 L2CAP
* uuid16 BNEP
*/
ssp.next = seq.next;
sdp_put_uuid16(&seq, service_class);
sdp_put_uuid16(&seq, SDP_UUID_PROTOCOL_L2CAP);
sdp_put_uuid16(&seq, SDP_UUID_PROTOCOL_BNEP);
ssp.end = seq.next;
if (attr.flags != SDP_ATTR_OK
|| attr.attr != SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST) {
log_err("%s service not found", service_name);
/*
* build AttributeIDList (3 bytes)
*
* uint16 ProtocolDescriptorList
*/
ail.next = seq.next;
sdp_put_uint16(&seq, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
ail.end = seq.next;
rv = sdp_service_search_attribute(ss, &ssp, &ail, &rsp);
if (!rv) {
log_err("%s: %m", service_type);
exit(EXIT_FAILURE);
}
/*
* we expect the following protocol descriptor list
*
* seq len
* seq len
* uuid value == L2CAP
* uint16 value16 => PSM
* seq len
* uuid value == BNEP
* we expect the response to contain a list of records
* containing a ProtocolDescriptorList. Find the first
* one containing L2CAP and BNEP protocols and extract
* the PSM.
*/
if (_sdp_get_seq(&attr.value, attr.value + attr.vlen, &seq0)
&& _sdp_get_seq(&seq0, attr.value, &seq1)
&& _sdp_match_uuid16(&seq1, seq0, SDP_UUID_PROTOCOL_L2CAP)
&& _sdp_get_uint16(&seq1, seq0, &l2cap_psm)
&& _sdp_get_seq(&seq0, attr.value, &seq1)
&& _sdp_match_uuid16(&seq1, seq0, SDP_UUID_PROTOCOL_BNEP)) {
log_info("Found PSM %d for service %s", l2cap_psm, service_name);
return;
rv = false;
while (!rv && sdp_get_seq(&rsp, &rec)) {
if (!sdp_get_attr(&rec, &attr, &value)
|| attr != SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST)
continue;
sdp_get_alt(&value, &value); /* drop any alt header */
while (!rv && sdp_get_seq(&value, &pdl)) {
if (sdp_get_seq(&pdl, &seq)
&& sdp_match_uuid16(&seq, SDP_UUID_PROTOCOL_L2CAP)
&& sdp_get_uint(&seq, &psm)
&& sdp_get_seq(&pdl, &seq)
&& sdp_match_uuid16(&seq, SDP_UUID_PROTOCOL_BNEP))
rv = true;
}
}
log_err("%s query failed", service_name);
exit(EXIT_FAILURE);
sdp_close(ss);
if (!rv) {
log_err("%s query failed", service_type);
exit(EXIT_FAILURE);
}
l2cap_psm = (uint16_t)psm;
log_info("Found PSM %u for service %s", l2cap_psm, service_type);
}

View File

@ -1,207 +0,0 @@
/* $NetBSD: sdp.c,v 1.2 2008/12/06 20:01:14 plunky Exp $ */
/*-
* Copyright (c) 2008 Iain Hibbert
* All rights reserved.
*
* 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 AUTHOR ``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 AUTHOR 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: sdp.c,v 1.2 2008/12/06 20:01:14 plunky Exp $");
#include <string.h>
#include "sdp.h"
/*
* SDP data stream manipulation routines
*/
/* Bluetooth Base UUID */
static const uuid_t BASE_UUID = {
0x00000000,
0x0000,
0x1000,
0x80,
0x00,
{ 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb }
};
/*
* _sdp_match_uuid16(ptr, limit, uuid)
*
* examine SDP data stream at ptr for a UUID, and return
* true if it matches the supplied short alias bluetooth UUID.
* limit is the first address past the end of valid data.
*/
bool
_sdp_match_uuid16(uint8_t **ptr, uint8_t *limit, uint16_t uuid)
{
uint8_t *p = *ptr;
uuid_t u1, u2;
memcpy(&u1, &BASE_UUID, sizeof(uuid_t));
u1.time_low = uuid;
if (!_sdp_get_uuid(&p, limit, &u2)
|| !uuid_equal(&u1, &u2, NULL))
return false;
*ptr = p;
return true;
}
/*
* _sdp_get_uuid(ptr, limit, uuid)
*
* examine SDP data stream at ptr for a UUID, and extract
* to given storage, advancing ptr.
* limit is the first address past the end of valid data.
*/
bool
_sdp_get_uuid(uint8_t **ptr, uint8_t *limit, uuid_t *uuid)
{
uint8_t *p = *ptr;
if (p + 1 > limit)
return false;
switch (*p++) {
case SDP_DATA_UUID16:
if (p + 2 > limit)
return false;
memcpy(uuid, &BASE_UUID, sizeof(uuid_t));
uuid->time_low = be16dec(p);
p += 2;
break;
case SDP_DATA_UUID32:
if (p + 4 > limit)
return false;
memcpy(uuid, &BASE_UUID, sizeof(uuid_t));
uuid->time_low = be32dec(p);
p += 4;
break;
case SDP_DATA_UUID128:
if (p + 16 > limit)
return false;
uuid_dec_be(p, uuid);
p += 16;
break;
default:
return false;
}
*ptr = p;
return true;
}
/*
* _sdp_get_seq(ptr, limit, seq)
*
* examine SDP data stream at ptr for a sequence. return
* seq pointer if found and advance ptr to next object.
* limit is the first address past the end of valid data.
*/
bool
_sdp_get_seq(uint8_t **ptr, uint8_t *limit, uint8_t **seq)
{
uint8_t *p = *ptr;
int32_t l;
if (p + 1 > limit)
return false;
switch (*p++) {
case SDP_DATA_SEQ8:
if (p + 1 > limit)
return false;
l = *p;
p += 1;
break;
case SDP_DATA_SEQ16:
if (p + 2 > limit)
return false;
l = be16dec(p);
p += 2;
break;
case SDP_DATA_SEQ32:
if (p + 4 > limit)
return false;
l = be32dec(p);
p += 4;
break;
default:
return false;
}
if (p + l > limit)
return false;
*seq = p;
*ptr = p + l;
return true;
}
/*
* _sdp_get_uint16(ptr, limit, value)
*
* examine SDP data stream at ptr for a uint16_t, and
* extract to given storage, advancing ptr.
* limit is the first address past the end of valid data.
*/
bool
_sdp_get_uint16(uint8_t **ptr, uint8_t *limit, uint16_t *value)
{
uint8_t *p = *ptr;
uint16_t v;
if (p + 1 > limit)
return false;
switch (*p++) {
case SDP_DATA_UINT16:
if (p + 2 > limit)
return false;
v = be16dec(p);
p += 2;
break;
default:
return false;
}
*value = v;
*ptr = p;
return true;
}

View File

@ -1,36 +0,0 @@
/* $NetBSD: sdp.h,v 1.2 2008/12/06 20:01:15 plunky Exp $ */
/*-
* Copyright (c) 2008 Iain Hibbert
* All rights reserved.
*
* 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 AUTHOR ``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 AUTHOR 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 <bluetooth.h>
#include <sdp.h>
#include <stdbool.h>
#include <uuid.h>
bool _sdp_match_uuid16(uint8_t **, uint8_t *, uint16_t);
bool _sdp_get_uuid(uint8_t **, uint8_t *, uuid_t *);
bool _sdp_get_seq(uint8_t **, uint8_t *, uint8_t **);
bool _sdp_get_uint16(uint8_t **, uint8_t *, uint16_t *);

View File

@ -1,4 +1,4 @@
/* $NetBSD: server.c,v 1.3 2009/05/12 21:08:30 plunky Exp $ */
/* $NetBSD: server.c,v 1.4 2009/05/12 21:50:38 plunky Exp $ */
/*-
* Copyright (c) 2008-2009 Iain Hibbert
@ -26,10 +26,12 @@
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: server.c,v 1.3 2009/05/12 21:08:30 plunky Exp $");
__RCSID("$NetBSD: server.c,v 1.4 2009/05/12 21:50:38 plunky Exp $");
#include <sys/ioctl.h>
#include <net/ethertypes.h>
#include <bluetooth.h>
#include <errno.h>
#include <sdp.h>
@ -41,13 +43,20 @@ __RCSID("$NetBSD: server.c,v 1.3 2009/05/12 21:08:30 plunky Exp $");
static struct event server_ev;
static int server_count;
static void * server_ss;
static sdp_session_t server_ss;
static uint32_t server_handle;
static sdp_data_t server_record;
static char * server_ipv4_subnet;
static char * server_ipv6_subnet;
static uint16_t server_proto[] = { ETHERTYPE_IP, ETHERTYPE_ARP, ETHERTYPE_IPV6 };
static size_t server_nproto = __arraycount(server_proto);
static void server_open(void);
static void server_read(int, short, void *);
static void server_down(channel_t *);
static void server_update(void);
static void server_mkrecord(void);
void
server_init(void)
@ -247,35 +256,114 @@ server_down(channel_t *chan)
static void
server_update(void)
{
sdp_nap_profile_t p;
int rv;
bool rv;
if (service_name == NULL)
if (service_type == NULL)
return;
if (server_ss == NULL) {
server_ss = sdp_open_local(control_path);
if (server_ss == NULL || sdp_error(server_ss) != 0) {
if (server_ss == NULL) {
log_err("failed to contact SDP server");
return;
}
}
memset(&p, 0, sizeof(p));
p.psm = l2cap_psm;
p.load_factor = (UINT8_MAX - server_count * UINT8_MAX / server_limit);
p.security_description = (l2cap_mode == 0 ? 0x0000 : 0x0001);
server_mkrecord();
if (server_handle)
rv = sdp_change_service(server_ss, server_handle,
(uint8_t *)&p, sizeof(p));
if (server_handle == 0)
rv = sdp_record_insert(server_ss, &local_bdaddr,
&server_handle, &server_record);
else
rv = sdp_register_service(server_ss, service_class,
&local_bdaddr, (uint8_t *)&p, sizeof(p), &server_handle);
rv = sdp_record_update(server_ss, server_handle,
&server_record);
if (rv != 0) {
errno = sdp_error(server_ss);
log_err("%s: %m", service_name);
if (!rv) {
log_err("%s: %m", service_type);
exit(EXIT_FAILURE);
}
}
static void
server_mkrecord(void)
{
static uint8_t data[256]; /* tis enough */
sdp_data_t buf;
size_t i;
buf.next = data;
buf.end = data + sizeof(data);
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, service_class);
sdp_put_uint16(&buf, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
sdp_put_seq(&buf, 8 + 10 + 3 * server_nproto);
sdp_put_seq(&buf, 6);
sdp_put_uuid16(&buf, SDP_UUID_PROTOCOL_L2CAP);
sdp_put_uint16(&buf, l2cap_psm);
sdp_put_seq(&buf, 8 + 3 * server_nproto);
sdp_put_uuid16(&buf, SDP_UUID_PROTOCOL_BNEP);
sdp_put_uint16(&buf, 0x0100); /* v1.0 */
sdp_put_seq(&buf, 3 * server_nproto);
for (i = 0; i < server_nproto; i++)
sdp_put_uint16(&buf, server_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, (UINT8_MAX - server_count * UINT8_MAX / server_limit));
sdp_put_uint16(&buf, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
sdp_put_seq(&buf, 8);
sdp_put_seq(&buf, 6);
sdp_put_uuid16(&buf, service_class);
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, service_name, -1);
sdp_put_uint16(&buf, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID
+ SDP_ATTR_SERVICE_DESCRIPTION_OFFSET);
sdp_put_str(&buf, service_desc, -1);
sdp_put_uint16(&buf, SDP_ATTR_SECURITY_DESCRIPTION);
sdp_put_uint16(&buf, (l2cap_mode == 0) ? 0x0000 : 0x0001);
if (service_class == SDP_SERVICE_CLASS_NAP) {
sdp_put_uint16(&buf, SDP_ATTR_NET_ACCESS_TYPE);
sdp_put_uint16(&buf, 0x0004); /* 10Mb Ethernet */
sdp_put_uint16(&buf, SDP_ATTR_MAX_NET_ACCESS_RATE);
sdp_put_uint32(&buf, 10000); /* 10Mb/s (?) */
}
if (service_class == SDP_SERVICE_CLASS_NAP
|| service_class == SDP_SERVICE_CLASS_GN) {
if (server_ipv4_subnet) {
sdp_put_uint16(&buf, SDP_ATTR_IPV4_SUBNET);
sdp_put_str(&buf, server_ipv4_subnet, -1);
}
if (server_ipv6_subnet) {
sdp_put_uint16(&buf, SDP_ATTR_IPV6_SUBNET);
sdp_put_str(&buf, server_ipv6_subnet, -1);
}
}
server_record.next = data;
server_record.end = buf.next;
}