186d7f5d3SJohn Marino /* $NetBSD: rfcomm_sdp.c,v 1.1 2006/06/19 15:44:56 gdamore Exp $ */
286d7f5d3SJohn Marino /* $DragonFly: src/usr.bin/rfcomm_sppd/rfcomm_sdp.c,v 1.1 2008/02/08 14:06:25 hasso Exp $ */
386d7f5d3SJohn Marino
486d7f5d3SJohn Marino /*-
586d7f5d3SJohn Marino * Copyright (c) 2006 Itronix Inc.
686d7f5d3SJohn Marino * All rights reserved.
786d7f5d3SJohn Marino *
886d7f5d3SJohn Marino * Redistribution and use in source and binary forms, with or without
986d7f5d3SJohn Marino * modification, are permitted provided that the following conditions
1086d7f5d3SJohn Marino * are met:
1186d7f5d3SJohn Marino * 1. Redistributions of source code must retain the above copyright
1286d7f5d3SJohn Marino * notice, this list of conditions and the following disclaimer.
1386d7f5d3SJohn Marino * 2. Redistributions in binary form must reproduce the above copyright
1486d7f5d3SJohn Marino * notice, this list of conditions and the following disclaimer in the
1586d7f5d3SJohn Marino * documentation and/or other materials provided with the distribution.
1686d7f5d3SJohn Marino * 3. The name of Itronix Inc. may not be used to endorse
1786d7f5d3SJohn Marino * or promote products derived from this software without specific
1886d7f5d3SJohn Marino * prior written permission.
1986d7f5d3SJohn Marino *
2086d7f5d3SJohn Marino * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
2186d7f5d3SJohn Marino * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2286d7f5d3SJohn Marino * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2386d7f5d3SJohn Marino * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
2486d7f5d3SJohn Marino * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2586d7f5d3SJohn Marino * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2686d7f5d3SJohn Marino * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2786d7f5d3SJohn Marino * ON ANY THEORY OF LIABILITY, WHETHER IN
2886d7f5d3SJohn Marino * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2986d7f5d3SJohn Marino * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3086d7f5d3SJohn Marino * POSSIBILITY OF SUCH DAMAGE.
3186d7f5d3SJohn Marino */
3286d7f5d3SJohn Marino /*
3386d7f5d3SJohn Marino * rfcomm_sdp.c
3486d7f5d3SJohn Marino *
3586d7f5d3SJohn Marino * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
3686d7f5d3SJohn Marino * All rights reserved.
3786d7f5d3SJohn Marino *
3886d7f5d3SJohn Marino * Redistribution and use in source and binary forms, with or without
3986d7f5d3SJohn Marino * modification, are permitted provided that the following conditions
4086d7f5d3SJohn Marino * are met:
4186d7f5d3SJohn Marino * 1. Redistributions of source code must retain the above copyright
4286d7f5d3SJohn Marino * notice, this list of conditions and the following disclaimer.
4386d7f5d3SJohn Marino * 2. Redistributions in binary form must reproduce the above copyright
4486d7f5d3SJohn Marino * notice, this list of conditions and the following disclaimer in the
4586d7f5d3SJohn Marino * documentation and/or other materials provided with the distribution.
4686d7f5d3SJohn Marino *
4786d7f5d3SJohn Marino * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
4886d7f5d3SJohn Marino * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4986d7f5d3SJohn Marino * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5086d7f5d3SJohn Marino * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
5186d7f5d3SJohn Marino * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5286d7f5d3SJohn Marino * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5386d7f5d3SJohn Marino * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5486d7f5d3SJohn Marino * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5586d7f5d3SJohn Marino * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5686d7f5d3SJohn Marino * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5786d7f5d3SJohn Marino * SUCH DAMAGE.
5886d7f5d3SJohn Marino *
5986d7f5d3SJohn Marino * $Id: rfcomm_sdp.c,v 1.1 2006/06/19 15:44:56 gdamore Exp $
6086d7f5d3SJohn Marino * $FreeBSD: src/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sdp.c,v 1.2 2004/04/09 23:26:16 emax Exp $
6186d7f5d3SJohn Marino */
6286d7f5d3SJohn Marino
6386d7f5d3SJohn Marino #include <bluetooth.h>
6486d7f5d3SJohn Marino #include <errno.h>
6586d7f5d3SJohn Marino #include <sdp.h>
6686d7f5d3SJohn Marino #include <stdio.h>
6786d7f5d3SJohn Marino
6886d7f5d3SJohn Marino #include "rfcomm_sdp.h"
6986d7f5d3SJohn Marino
7086d7f5d3SJohn Marino #undef PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE
7186d7f5d3SJohn Marino #define PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE 256
7286d7f5d3SJohn Marino
7386d7f5d3SJohn Marino #undef PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE
7486d7f5d3SJohn Marino #define PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE 12
7586d7f5d3SJohn Marino
7686d7f5d3SJohn Marino static int rfcomm_proto_list_parse (uint8_t const *start, uint8_t const *end,
7786d7f5d3SJohn Marino uint8_t *channel, int *error);
7886d7f5d3SJohn Marino
7986d7f5d3SJohn Marino /*
8086d7f5d3SJohn Marino * Lookup RFCOMM channel number in the Protocol Descriptor List
8186d7f5d3SJohn Marino */
8286d7f5d3SJohn Marino
8386d7f5d3SJohn Marino #undef rfcomm_channel_lookup_exit
8486d7f5d3SJohn Marino #define rfcomm_channel_lookup_exit(e) { \
8586d7f5d3SJohn Marino if (error != NULL) \
8686d7f5d3SJohn Marino *error = (e); \
8786d7f5d3SJohn Marino if (ss != NULL) { \
8886d7f5d3SJohn Marino sdp_close(ss); \
8986d7f5d3SJohn Marino ss = NULL; \
9086d7f5d3SJohn Marino } \
9186d7f5d3SJohn Marino return (((e) == 0)? 0 : -1); \
9286d7f5d3SJohn Marino }
9386d7f5d3SJohn Marino
9486d7f5d3SJohn Marino int
rfcomm_channel_lookup(bdaddr_t const * local,bdaddr_t const * remote,int service,uint8_t * channel,int * error)9586d7f5d3SJohn Marino rfcomm_channel_lookup(bdaddr_t const *local, bdaddr_t const *remote,
9686d7f5d3SJohn Marino int service, uint8_t *channel, int *error)
9786d7f5d3SJohn Marino {
9886d7f5d3SJohn Marino uint8_t buffer[PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE];
9986d7f5d3SJohn Marino void *ss = NULL;
10086d7f5d3SJohn Marino uint16_t serv = (uint16_t) service;
10186d7f5d3SJohn Marino uint32_t attr = SDP_ATTR_RANGE(
10286d7f5d3SJohn Marino SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
10386d7f5d3SJohn Marino SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
10486d7f5d3SJohn Marino sdp_attr_t proto = { SDP_ATTR_INVALID,0,sizeof(buffer),buffer };
10586d7f5d3SJohn Marino uint32_t type, len;
10686d7f5d3SJohn Marino
10786d7f5d3SJohn Marino if (local == NULL)
10886d7f5d3SJohn Marino local = BDADDR_ANY;
10986d7f5d3SJohn Marino if (remote == NULL || channel == NULL)
11086d7f5d3SJohn Marino rfcomm_channel_lookup_exit(EINVAL);
11186d7f5d3SJohn Marino
11286d7f5d3SJohn Marino if ((ss = sdp_open(local, remote)) == NULL)
11386d7f5d3SJohn Marino rfcomm_channel_lookup_exit(ENOMEM);
11486d7f5d3SJohn Marino if (sdp_error(ss) != 0)
11586d7f5d3SJohn Marino rfcomm_channel_lookup_exit(sdp_error(ss));
11686d7f5d3SJohn Marino
11786d7f5d3SJohn Marino if (sdp_search(ss, 1, &serv, 1, &attr, 1, &proto) != 0)
11886d7f5d3SJohn Marino rfcomm_channel_lookup_exit(sdp_error(ss));
11986d7f5d3SJohn Marino if (proto.flags != SDP_ATTR_OK)
12086d7f5d3SJohn Marino rfcomm_channel_lookup_exit(ENOATTR);
12186d7f5d3SJohn Marino
12286d7f5d3SJohn Marino sdp_close(ss);
12386d7f5d3SJohn Marino ss = NULL;
12486d7f5d3SJohn Marino
12586d7f5d3SJohn Marino /*
12686d7f5d3SJohn Marino * If it is possible for more than one kind of protocol stack to be
12786d7f5d3SJohn Marino * used to gain access to the service, the ProtocolDescriptorList
12886d7f5d3SJohn Marino * takes the form of a data element alternative. We always use the
12986d7f5d3SJohn Marino * first protocol stack.
13086d7f5d3SJohn Marino *
13186d7f5d3SJohn Marino * A minimal Protocol Descriptor List for RFCOMM based service would
13286d7f5d3SJohn Marino * look like
13386d7f5d3SJohn Marino *
13486d7f5d3SJohn Marino * seq8 len8 - 2 bytes
13586d7f5d3SJohn Marino * seq8 len8 - 2 bytes
13686d7f5d3SJohn Marino * uuid16 value16 - 3 bytes L2CAP
13786d7f5d3SJohn Marino * seq8 len8 - 2 bytes
13886d7f5d3SJohn Marino * uuid16 value16 - 3 bytes RFCOMM
13986d7f5d3SJohn Marino * uint8 value8 - 2 bytes RFCOMM param #1
14086d7f5d3SJohn Marino * =========
14186d7f5d3SJohn Marino * 14 bytes
14286d7f5d3SJohn Marino *
14386d7f5d3SJohn Marino * Lets not count first [seq8 len8] wrapper, so the minimal size of
14486d7f5d3SJohn Marino * the Protocol Descriptor List (the data we are actually interested
14586d7f5d3SJohn Marino * in) for RFCOMM based service would be 12 bytes.
14686d7f5d3SJohn Marino */
14786d7f5d3SJohn Marino
14886d7f5d3SJohn Marino if (proto.vlen < PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE)
14986d7f5d3SJohn Marino rfcomm_channel_lookup_exit(EINVAL);
15086d7f5d3SJohn Marino
15186d7f5d3SJohn Marino SDP_GET8(type, proto.value);
15286d7f5d3SJohn Marino
15386d7f5d3SJohn Marino if (type == SDP_DATA_ALT8) {
15486d7f5d3SJohn Marino SDP_GET8(len, proto.value);
15586d7f5d3SJohn Marino } else if (type == SDP_DATA_ALT16) {
15686d7f5d3SJohn Marino SDP_GET16(len, proto.value);
15786d7f5d3SJohn Marino } else if (type == SDP_DATA_ALT32) {
15886d7f5d3SJohn Marino SDP_GET32(len, proto.value);
15986d7f5d3SJohn Marino } else
16086d7f5d3SJohn Marino len = 0;
16186d7f5d3SJohn Marino
16286d7f5d3SJohn Marino if (len > 0)
16386d7f5d3SJohn Marino SDP_GET8(type, proto.value);
16486d7f5d3SJohn Marino
16586d7f5d3SJohn Marino switch (type) {
16686d7f5d3SJohn Marino case SDP_DATA_SEQ8:
16786d7f5d3SJohn Marino SDP_GET8(len, proto.value);
16886d7f5d3SJohn Marino break;
16986d7f5d3SJohn Marino
17086d7f5d3SJohn Marino case SDP_DATA_SEQ16:
17186d7f5d3SJohn Marino SDP_GET16(len, proto.value);
17286d7f5d3SJohn Marino break;
17386d7f5d3SJohn Marino
17486d7f5d3SJohn Marino case SDP_DATA_SEQ32:
17586d7f5d3SJohn Marino SDP_GET32(len, proto.value);
17686d7f5d3SJohn Marino break;
17786d7f5d3SJohn Marino
17886d7f5d3SJohn Marino default:
17986d7f5d3SJohn Marino rfcomm_channel_lookup_exit(ENOATTR);
18086d7f5d3SJohn Marino /* NOT REACHED */
18186d7f5d3SJohn Marino }
18286d7f5d3SJohn Marino
18386d7f5d3SJohn Marino if (len < PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE)
18486d7f5d3SJohn Marino rfcomm_channel_lookup_exit(EINVAL);
18586d7f5d3SJohn Marino
18686d7f5d3SJohn Marino return (rfcomm_proto_list_parse(proto.value,
18786d7f5d3SJohn Marino buffer + proto.vlen, channel, error));
18886d7f5d3SJohn Marino }
18986d7f5d3SJohn Marino
19086d7f5d3SJohn Marino /*
19186d7f5d3SJohn Marino * Parse protocol descriptor list
19286d7f5d3SJohn Marino *
19386d7f5d3SJohn Marino * The ProtocolDescriptorList attribute describes one or more protocol
19486d7f5d3SJohn Marino * stacks that may be used to gain access to the service described by
19586d7f5d3SJohn Marino * the service record. If the ProtocolDescriptorList describes a single
19686d7f5d3SJohn Marino * stack, it takes the form of a data element sequence in which each
19786d7f5d3SJohn Marino * element of the sequence is a protocol descriptor.
19886d7f5d3SJohn Marino */
19986d7f5d3SJohn Marino
20086d7f5d3SJohn Marino #undef rfcomm_proto_list_parse_exit
20186d7f5d3SJohn Marino #define rfcomm_proto_list_parse_exit(e) { \
20286d7f5d3SJohn Marino if (error != NULL) \
20386d7f5d3SJohn Marino *error = (e); \
20486d7f5d3SJohn Marino return (((e) == 0)? 0 : -1); \
20586d7f5d3SJohn Marino }
20686d7f5d3SJohn Marino
20786d7f5d3SJohn Marino static int
rfcomm_proto_list_parse(uint8_t const * start,uint8_t const * end,uint8_t * channel,int * error)20886d7f5d3SJohn Marino rfcomm_proto_list_parse(uint8_t const *start, uint8_t const *end,
20986d7f5d3SJohn Marino uint8_t *channel, int *error)
21086d7f5d3SJohn Marino {
21186d7f5d3SJohn Marino int type, len, value;
21286d7f5d3SJohn Marino
21386d7f5d3SJohn Marino while (start < end) {
21486d7f5d3SJohn Marino
21586d7f5d3SJohn Marino /*
21686d7f5d3SJohn Marino * Parse protocol descriptor
21786d7f5d3SJohn Marino *
21886d7f5d3SJohn Marino * A protocol descriptor identifies a communications protocol
21986d7f5d3SJohn Marino * and provides protocol specific parameters. A protocol
22086d7f5d3SJohn Marino * descriptor is represented as a data element sequence. The
22186d7f5d3SJohn Marino * first data element in the sequence must be the UUID that
22286d7f5d3SJohn Marino * identifies the protocol. Additional data elements optionally
22386d7f5d3SJohn Marino * provide protocol specific information, such as the L2CAP
22486d7f5d3SJohn Marino * protocol/service multiplexer (PSM) and the RFCOMM server
22586d7f5d3SJohn Marino * channel number (CN).
22686d7f5d3SJohn Marino */
22786d7f5d3SJohn Marino
22886d7f5d3SJohn Marino /* We must have at least one byte (type) */
22986d7f5d3SJohn Marino if (end - start < 1)
23086d7f5d3SJohn Marino rfcomm_proto_list_parse_exit(EINVAL)
23186d7f5d3SJohn Marino
23286d7f5d3SJohn Marino SDP_GET8(type, start);
23386d7f5d3SJohn Marino switch (type) {
23486d7f5d3SJohn Marino case SDP_DATA_SEQ8:
23586d7f5d3SJohn Marino SDP_GET8(len, start);
23686d7f5d3SJohn Marino break;
23786d7f5d3SJohn Marino
23886d7f5d3SJohn Marino case SDP_DATA_SEQ16:
23986d7f5d3SJohn Marino SDP_GET16(len, start);
24086d7f5d3SJohn Marino break;
24186d7f5d3SJohn Marino
24286d7f5d3SJohn Marino case SDP_DATA_SEQ32:
24386d7f5d3SJohn Marino SDP_GET32(len, start);
24486d7f5d3SJohn Marino break;
24586d7f5d3SJohn Marino
24686d7f5d3SJohn Marino default:
24786d7f5d3SJohn Marino rfcomm_proto_list_parse_exit(ENOATTR)
24886d7f5d3SJohn Marino /* NOT REACHED */
24986d7f5d3SJohn Marino }
25086d7f5d3SJohn Marino
25186d7f5d3SJohn Marino /* We must have at least 3 bytes (type + UUID16) */
25286d7f5d3SJohn Marino if (end - start < 3)
25386d7f5d3SJohn Marino rfcomm_proto_list_parse_exit(EINVAL);
25486d7f5d3SJohn Marino
25586d7f5d3SJohn Marino /* Get protocol UUID */
25686d7f5d3SJohn Marino SDP_GET8(type, start); len -= sizeof(uint8_t);
25786d7f5d3SJohn Marino switch (type) {
25886d7f5d3SJohn Marino case SDP_DATA_UUID16:
25986d7f5d3SJohn Marino SDP_GET16(value, start); len -= sizeof(uint16_t);
26086d7f5d3SJohn Marino if (value != SDP_UUID_PROTOCOL_RFCOMM)
26186d7f5d3SJohn Marino goto next_protocol;
26286d7f5d3SJohn Marino break;
26386d7f5d3SJohn Marino
26486d7f5d3SJohn Marino case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
26586d7f5d3SJohn Marino case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
26686d7f5d3SJohn Marino default:
26786d7f5d3SJohn Marino rfcomm_proto_list_parse_exit(ENOATTR);
26886d7f5d3SJohn Marino /* NOT REACHED */
26986d7f5d3SJohn Marino }
27086d7f5d3SJohn Marino
27186d7f5d3SJohn Marino /*
27286d7f5d3SJohn Marino * First protocol specific parameter for RFCOMM procotol must
27386d7f5d3SJohn Marino * be uint8 that represents RFCOMM channel number. So we must
27486d7f5d3SJohn Marino * have at least two bytes.
27586d7f5d3SJohn Marino */
27686d7f5d3SJohn Marino
27786d7f5d3SJohn Marino if (end - start < 2)
27886d7f5d3SJohn Marino rfcomm_proto_list_parse_exit(EINVAL);
27986d7f5d3SJohn Marino
28086d7f5d3SJohn Marino SDP_GET8(type, start);
28186d7f5d3SJohn Marino if (type != SDP_DATA_UINT8)
28286d7f5d3SJohn Marino rfcomm_proto_list_parse_exit(ENOATTR);
28386d7f5d3SJohn Marino
28486d7f5d3SJohn Marino SDP_GET8(*channel, start);
28586d7f5d3SJohn Marino
28686d7f5d3SJohn Marino rfcomm_proto_list_parse_exit(0);
28786d7f5d3SJohn Marino /* NOT REACHED */
28886d7f5d3SJohn Marino next_protocol:
28986d7f5d3SJohn Marino start += len;
29086d7f5d3SJohn Marino }
29186d7f5d3SJohn Marino
29286d7f5d3SJohn Marino /*
29386d7f5d3SJohn Marino * If we got here then it means we could not find RFCOMM protocol
29486d7f5d3SJohn Marino * descriptor, but the reply format was actually valid.
29586d7f5d3SJohn Marino */
29686d7f5d3SJohn Marino
29786d7f5d3SJohn Marino rfcomm_proto_list_parse_exit(ENOATTR);
29886d7f5d3SJohn Marino }
299