client.c revision 1.3 1 /* $NetBSD: client.c,v 1.3 2009/05/12 21:08:30 plunky Exp $ */
2
3 /*-
4 * Copyright (c) 2008 Iain Hibbert
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
15 *
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.
26 */
27
28 #include <sys/cdefs.h>
29 __RCSID("$NetBSD: client.c,v 1.3 2009/05/12 21:08:30 plunky Exp $");
30
31 #include <bluetooth.h>
32 #include <errno.h>
33 #include <sdp.h>
34 #include <unistd.h>
35
36 #include "btpand.h"
37 #include "bnep.h"
38 #include "sdp.h"
39
40 static void client_down(channel_t *);
41 static void client_query(void);
42
43 void
44 client_init(void)
45 {
46 struct sockaddr_bt sa;
47 channel_t *chan;
48 socklen_t len;
49 int fd;
50 uint16_t mru, mtu;
51
52 if (bdaddr_any(&remote_bdaddr))
53 return;
54
55 if (service_name)
56 client_query();
57
58 fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
59 if (fd == -1) {
60 log_err("Could not open L2CAP socket: %m");
61 exit(EXIT_FAILURE);
62 }
63
64 memset(&sa, 0, sizeof(sa));
65 sa.bt_family = AF_BLUETOOTH;
66 sa.bt_len = sizeof(sa);
67 bdaddr_copy(&sa.bt_bdaddr, &local_bdaddr);
68 if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
69 log_err("Could not bind client socket: %m");
70 exit(EXIT_FAILURE);
71 }
72
73 if (setsockopt(fd, BTPROTO_L2CAP, SO_L2CAP_LM,
74 &l2cap_mode, sizeof(l2cap_mode)) == -1) {
75 log_err("Could not set link mode (0x%4.4x): %m", l2cap_mode);
76 exit(EXIT_FAILURE);
77 }
78
79 mru = BNEP_MTU_MIN;
80 if (setsockopt(fd, BTPROTO_L2CAP, SO_L2CAP_IMTU,
81 &mru, sizeof(mru)) == -1) {
82 log_err("Could not set L2CAP IMTU (%d): %m", mru);
83 exit(EXIT_FAILURE);
84 }
85
86 log_info("Opening connection to service 0x%4.4x at %s",
87 service_class, bt_ntoa(&remote_bdaddr, NULL));
88
89 sa.bt_psm = l2cap_psm;
90 bdaddr_copy(&sa.bt_bdaddr, &remote_bdaddr);
91 if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
92 log_err("Could not connect: %m");
93 exit(EXIT_FAILURE);
94 }
95
96 len = sizeof(mru);
97 if (getsockopt(fd, BTPROTO_L2CAP, SO_L2CAP_IMTU, &mru, &len) == -1) {
98 log_err("Could not get IMTU: %m");
99 exit(EXIT_FAILURE);
100 }
101 if (mru < BNEP_MTU_MIN) {
102 log_err("L2CAP IMTU too small (%d)", mru);
103 exit(EXIT_FAILURE);
104 }
105
106 len = sizeof(mtu);
107 if (getsockopt(fd, BTPROTO_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) {
108 log_err("Could not get L2CAP OMTU: %m");
109 exit(EXIT_FAILURE);
110 }
111 if (mtu < BNEP_MTU_MIN) {
112 log_err("L2CAP OMTU too small (%d)", mtu);
113 exit(EXIT_FAILURE);
114 }
115
116 chan = channel_alloc();
117 if (chan == NULL)
118 exit(EXIT_FAILURE);
119
120 chan->send = bnep_send;
121 chan->recv = bnep_recv;
122 chan->down = client_down;
123 chan->mru = mru;
124 chan->mtu = mtu;
125 b2eaddr(chan->raddr, &remote_bdaddr);
126 b2eaddr(chan->laddr, &local_bdaddr);
127 chan->state = CHANNEL_WAIT_CONNECT_RSP;
128 channel_timeout(chan, 10);
129 if (!channel_open(chan, fd))
130 exit(EXIT_FAILURE);
131
132 bnep_send_control(chan, BNEP_SETUP_CONNECTION_REQUEST,
133 2, service_class, SDP_SERVICE_CLASS_PANU);
134 }
135
136 static void
137 client_down(channel_t *chan)
138 {
139
140 log_err("Client connection shut down, exiting");
141 exit(EXIT_FAILURE);
142 }
143
144 static void
145 client_query(void)
146 {
147 uint8_t buffer[512];
148 sdp_attr_t attr;
149 uint32_t range;
150 void *ss;
151 int rv;
152 uint8_t *seq0, *seq1;
153
154 attr.flags = SDP_ATTR_INVALID;
155 attr.attr = 0;
156 attr.vlen = sizeof(buffer);
157 attr.value = buffer;
158
159 range = SDP_ATTR_RANGE(SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
160 SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
161
162 ss = sdp_open(&local_bdaddr, &remote_bdaddr);
163 if (ss == NULL || (errno = sdp_error(ss)) != 0) {
164 log_err("%s: %m", service_name);
165 exit(EXIT_FAILURE);
166 }
167
168 log_info("Searching for %s service at %s",
169 service_name, bt_ntoa(&remote_bdaddr, NULL));
170
171 rv = sdp_search(ss, 1, &service_class, 1, &range, 1, &attr);
172 if (rv != 0) {
173 log_err("%s: %s", service_name, strerror(sdp_error(ss)));
174 exit(EXIT_FAILURE);
175 }
176
177 sdp_close(ss);
178
179 if (attr.flags != SDP_ATTR_OK
180 || attr.attr != SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST) {
181 log_err("%s service not found", service_name);
182 exit(EXIT_FAILURE);
183 }
184
185 /*
186 * we expect the following protocol descriptor list
187 *
188 * seq len
189 * seq len
190 * uuid value == L2CAP
191 * uint16 value16 => PSM
192 * seq len
193 * uuid value == BNEP
194 */
195 if (_sdp_get_seq(&attr.value, attr.value + attr.vlen, &seq0)
196 && _sdp_get_seq(&seq0, attr.value, &seq1)
197 && _sdp_match_uuid16(&seq1, seq0, SDP_UUID_PROTOCOL_L2CAP)
198 && _sdp_get_uint16(&seq1, seq0, &l2cap_psm)
199 && _sdp_get_seq(&seq0, attr.value, &seq1)
200 && _sdp_match_uuid16(&seq1, seq0, SDP_UUID_PROTOCOL_BNEP)) {
201 log_info("Found PSM %d for service %s", l2cap_psm, service_name);
202 return;
203 }
204
205 log_err("%s query failed", service_name);
206 exit(EXIT_FAILURE);
207 }
208