1 /* $NetBSD: client.c,v 1.3 2009/05/12 21:08:30 plunky Exp $ */
4 * Copyright (c) 2008-2009 Iain Hibbert
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include <sys/cdefs.h>
29 __RCSID("$NetBSD: client.c,v 1.3 2009/05/12 21:08:30 plunky Exp $");
31 #include <bluetooth.h>
39 static void client_down(channel_t
*);
40 static void client_query(void);
45 struct sockaddr_bt sa
;
51 if (bdaddr_any(&remote_bdaddr
))
57 fd
= socket(PF_BLUETOOTH
, SOCK_SEQPACKET
, BTPROTO_L2CAP
);
59 log_err("Could not open L2CAP socket: %m");
63 memset(&sa
, 0, sizeof(sa
));
64 sa
.bt_family
= AF_BLUETOOTH
;
65 sa
.bt_len
= sizeof(sa
);
66 bdaddr_copy(&sa
.bt_bdaddr
, &local_bdaddr
);
67 if (bind(fd
, (struct sockaddr
*)&sa
, sizeof(sa
)) == -1) {
68 log_err("Could not bind client socket: %m");
72 if (setsockopt(fd
, BTPROTO_L2CAP
, SO_L2CAP_LM
,
73 &l2cap_mode
, sizeof(l2cap_mode
)) == -1) {
74 log_err("Could not set link mode (0x%4.4x): %m", l2cap_mode
);
79 if (setsockopt(fd
, BTPROTO_L2CAP
, SO_L2CAP_IMTU
,
80 &mru
, sizeof(mru
)) == -1) {
81 log_err("Could not set L2CAP IMTU (%d): %m", mru
);
85 log_info("Opening connection to service 0x%4.4x at %s",
86 service_class
, bt_ntoa(&remote_bdaddr
, NULL
));
88 sa
.bt_psm
= l2cap_psm
;
89 bdaddr_copy(&sa
.bt_bdaddr
, &remote_bdaddr
);
90 if (connect(fd
, (struct sockaddr
*)&sa
, sizeof(sa
)) == -1) {
91 log_err("Could not connect: %m");
96 if (getsockopt(fd
, BTPROTO_L2CAP
, SO_L2CAP_IMTU
, &mru
, &len
) == -1) {
97 log_err("Could not get IMTU: %m");
100 if (mru
< BNEP_MTU_MIN
) {
101 log_err("L2CAP IMTU too small (%d)", mru
);
106 if (getsockopt(fd
, BTPROTO_L2CAP
, SO_L2CAP_OMTU
, &mtu
, &len
) == -1) {
107 log_err("Could not get L2CAP OMTU: %m");
110 if (mtu
< BNEP_MTU_MIN
) {
111 log_err("L2CAP OMTU too small (%d)", mtu
);
115 chan
= channel_alloc();
119 chan
->send
= bnep_send
;
120 chan
->recv
= bnep_recv
;
121 chan
->down
= client_down
;
124 b2eaddr(chan
->raddr
, &remote_bdaddr
);
125 b2eaddr(chan
->laddr
, &local_bdaddr
);
126 chan
->state
= CHANNEL_WAIT_CONNECT_RSP
;
127 channel_timeout(chan
, 10);
128 if (!channel_open(chan
, fd
))
131 bnep_send_control(chan
, BNEP_SETUP_CONNECTION_REQUEST
,
132 2, service_class
, SDP_SERVICE_CLASS_PANU
);
136 client_down(channel_t
*chan
)
139 log_err("Client connection shut down, exiting");
146 uint8_t buf
[12]; /* enough for SSP and AIL both */
148 sdp_data_t ssp
, ail
, rsp
, rec
, value
, pdl
, seq
;
153 ss
= sdp_open(&local_bdaddr
, &remote_bdaddr
);
155 log_err("%s: %m", service_type
);
159 log_info("Searching for %s service at %s",
160 service_type
, bt_ntoa(&remote_bdaddr
, NULL
));
163 seq
.end
= buf
+ sizeof(buf
);
166 * build ServiceSearchPattern (9 bytes)
168 * uuid16 "service_class"
173 sdp_put_uuid16(&seq
, service_class
);
174 sdp_put_uuid16(&seq
, SDP_UUID_PROTOCOL_L2CAP
);
175 sdp_put_uuid16(&seq
, SDP_UUID_PROTOCOL_BNEP
);
179 * build AttributeIDList (3 bytes)
181 * uint16 ProtocolDescriptorList
184 sdp_put_uint16(&seq
, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST
);
187 rv
= sdp_service_search_attribute(ss
, &ssp
, &ail
, &rsp
);
189 log_err("%s: %m", service_type
);
194 * we expect the response to contain a list of records
195 * containing a ProtocolDescriptorList. Find the first
196 * one containing L2CAP and BNEP protocols and extract
200 while (!rv
&& sdp_get_seq(&rsp
, &rec
)) {
201 if (!sdp_get_attr(&rec
, &attr
, &value
)
202 || attr
!= SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST
)
205 sdp_get_alt(&value
, &value
); /* drop any alt header */
206 while (!rv
&& sdp_get_seq(&value
, &pdl
)) {
207 if (sdp_get_seq(&pdl
, &seq
)
208 && sdp_match_uuid16(&seq
, SDP_UUID_PROTOCOL_L2CAP
)
209 && sdp_get_uint(&seq
, &psm
)
210 && sdp_get_seq(&pdl
, &seq
)
211 && sdp_match_uuid16(&seq
, SDP_UUID_PROTOCOL_BNEP
))
219 log_err("%s query failed", service_type
);
223 l2cap_psm
= (uint16_t)psm
;
224 log_info("Found PSM %u for service %s", l2cap_psm
, service_type
);