From ab1f45acf711b6f3714e0c45ed70d99daf151ce9 Mon Sep 17 00:00:00 2001 From: plunky Date: Tue, 12 May 2009 21:50:38 +0000 Subject: [PATCH] update to use the new Service Discovery API --- usr.sbin/btpand/Makefile | 6 +- usr.sbin/btpand/btpand.c | 48 +++++---- usr.sbin/btpand/btpand.h | 6 +- usr.sbin/btpand/client.c | 116 +++++++++++++--------- usr.sbin/btpand/sdp.c | 207 --------------------------------------- usr.sbin/btpand/sdp.h | 36 ------- usr.sbin/btpand/server.c | 126 ++++++++++++++++++++---- 7 files changed, 211 insertions(+), 334 deletions(-) delete mode 100644 usr.sbin/btpand/sdp.c delete mode 100644 usr.sbin/btpand/sdp.h diff --git a/usr.sbin/btpand/Makefile b/usr.sbin/btpand/Makefile index ea870690c448..ea0cfda4e96b 100644 --- a/usr.sbin/btpand/Makefile +++ b/usr.sbin/btpand/Makefile @@ -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 diff --git a/usr.sbin/btpand/btpand.c b/usr.sbin/btpand/btpand.c index c21c3a74805a..cb7a1a45e8b4 100644 --- a/usr.sbin/btpand/btpand.c +++ b/usr.sbin/btpand/btpand.c @@ -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 -__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 @@ -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 */ const char * interface_name; /* -i */ -const char * service_name; /* -s */ +const char * service_type; /* -s */ uint16_t service_class; +const char * service_name; +const char * service_desc; bdaddr_t local_bdaddr; /* -d */ bdaddr_t remote_bdaddr; /* -a */ uint16_t l2cap_psm; /* -p */ int l2cap_mode; /* -m */ - int server_limit; /* -n */ 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); } diff --git a/usr.sbin/btpand/btpand.h b/usr.sbin/btpand/btpand.h index 72e2fbe98931..58d1b2911259 100644 --- a/usr.sbin/btpand/btpand.h +++ b/usr.sbin/btpand/btpand.h @@ -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; diff --git a/usr.sbin/btpand/client.c b/usr.sbin/btpand/client.c index 8d621c47bf8c..d8a7eaf98b13 100644 --- a/usr.sbin/btpand/client.c +++ b/usr.sbin/btpand/client.c @@ -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 -__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 #include @@ -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); } diff --git a/usr.sbin/btpand/sdp.c b/usr.sbin/btpand/sdp.c deleted file mode 100644 index 4306d88c3e46..000000000000 --- a/usr.sbin/btpand/sdp.c +++ /dev/null @@ -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 -__RCSID("$NetBSD: sdp.c,v 1.2 2008/12/06 20:01:14 plunky Exp $"); - -#include - -#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; -} diff --git a/usr.sbin/btpand/sdp.h b/usr.sbin/btpand/sdp.h deleted file mode 100644 index b2e466d273ff..000000000000 --- a/usr.sbin/btpand/sdp.h +++ /dev/null @@ -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 -#include -#include -#include - -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 *); diff --git a/usr.sbin/btpand/server.c b/usr.sbin/btpand/server.c index abd577f29713..9b0606e107b8 100644 --- a/usr.sbin/btpand/server.c +++ b/usr.sbin/btpand/server.c @@ -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 -__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 +#include + #include #include #include @@ -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; +}