rfcomm_sppd.c revision 1.16 1 1.16 wiz /* $NetBSD: rfcomm_sppd.c,v 1.16 2013/12/09 09:35:17 wiz Exp $ */
2 1.1 gdamore
3 1.1 gdamore /*-
4 1.1 gdamore * Copyright (c) 2006 Itronix Inc.
5 1.1 gdamore * All rights reserved.
6 1.1 gdamore *
7 1.1 gdamore * Redistribution and use in source and binary forms, with or without
8 1.1 gdamore * modification, are permitted provided that the following conditions
9 1.1 gdamore * are met:
10 1.1 gdamore * 1. Redistributions of source code must retain the above copyright
11 1.1 gdamore * notice, this list of conditions and the following disclaimer.
12 1.1 gdamore * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 gdamore * notice, this list of conditions and the following disclaimer in the
14 1.1 gdamore * documentation and/or other materials provided with the distribution.
15 1.1 gdamore * 3. The name of Itronix Inc. may not be used to endorse
16 1.1 gdamore * or promote products derived from this software without specific
17 1.1 gdamore * prior written permission.
18 1.1 gdamore *
19 1.1 gdamore * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
20 1.1 gdamore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 gdamore * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 gdamore * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
23 1.1 gdamore * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 1.1 gdamore * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 1.1 gdamore * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 1.1 gdamore * ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 gdamore * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 gdamore * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 gdamore * POSSIBILITY OF SUCH DAMAGE.
30 1.1 gdamore */
31 1.1 gdamore /*
32 1.10 plunky * Copyright (c) 2009 The NetBSD Foundation, Inc.
33 1.2 plunky * Copyright (c) 2007 Iain Hibbert
34 1.1 gdamore * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin (at) yahoo.com>
35 1.1 gdamore * All rights reserved.
36 1.1 gdamore *
37 1.1 gdamore * Redistribution and use in source and binary forms, with or without
38 1.1 gdamore * modification, are permitted provided that the following conditions
39 1.1 gdamore * are met:
40 1.1 gdamore * 1. Redistributions of source code must retain the above copyright
41 1.1 gdamore * notice, this list of conditions and the following disclaimer.
42 1.1 gdamore * 2. Redistributions in binary form must reproduce the above copyright
43 1.1 gdamore * notice, this list of conditions and the following disclaimer in the
44 1.1 gdamore * documentation and/or other materials provided with the distribution.
45 1.1 gdamore *
46 1.1 gdamore * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
47 1.1 gdamore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 1.1 gdamore * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 1.1 gdamore * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
50 1.1 gdamore * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 1.1 gdamore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 1.1 gdamore * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 1.1 gdamore * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 1.1 gdamore * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 1.1 gdamore * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 1.1 gdamore * SUCH DAMAGE.
57 1.1 gdamore */
58 1.1 gdamore
59 1.1 gdamore #include <sys/cdefs.h>
60 1.10 plunky __COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc.\
61 1.10 plunky Copyright (c) 2007 Iain Hibbert.\
62 1.9 lukem Copyright (c) 2006 Itronix, Inc.\
63 1.9 lukem Copyright (c) 2003 Maksim Yevmenkin m_evmenkin (at) yahoo.com.\
64 1.9 lukem All rights reserved.");
65 1.16 wiz __RCSID("$NetBSD: rfcomm_sppd.c,v 1.16 2013/12/09 09:35:17 wiz Exp $");
66 1.11 plunky
67 1.11 plunky #include <sys/param.h>
68 1.1 gdamore
69 1.1 gdamore #include <bluetooth.h>
70 1.1 gdamore #include <ctype.h>
71 1.1 gdamore #include <err.h>
72 1.1 gdamore #include <errno.h>
73 1.1 gdamore #include <fcntl.h>
74 1.1 gdamore #include <grp.h>
75 1.1 gdamore #include <limits.h>
76 1.1 gdamore #include <paths.h>
77 1.1 gdamore #include <sdp.h>
78 1.1 gdamore #include <signal.h>
79 1.1 gdamore #include <stdarg.h>
80 1.15 christos #include <poll.h>
81 1.1 gdamore #include <stdio.h>
82 1.1 gdamore #include <stdlib.h>
83 1.1 gdamore #include <string.h>
84 1.1 gdamore #include <syslog.h>
85 1.1 gdamore #include <termios.h>
86 1.1 gdamore #include <unistd.h>
87 1.1 gdamore
88 1.7 plunky #include <netbt/rfcomm.h>
89 1.7 plunky
90 1.15 christos static int open_tty(const char *);
91 1.15 christos static int open_client(bdaddr_t *, bdaddr_t *, int, uintmax_t, const char *);
92 1.15 christos static int open_server(bdaddr_t *, uint16_t, uint8_t, int, const char *);
93 1.15 christos static void copy_data(int, int);
94 1.15 christos static int service_search(const bdaddr_t *, const bdaddr_t *, uint16_t,
95 1.15 christos uintmax_t *, uintmax_t *);
96 1.15 christos static void sighandler(int);
97 1.15 christos static void usage(void) __attribute__((__noreturn__));
98 1.15 christos static void reset_tio(void);
99 1.2 plunky
100 1.15 christos static sig_atomic_t done; /* got a signal */
101 1.15 christos static struct termios tio; /* stored termios for reset on exit */
102 1.2 plunky
103 1.15 christos static const struct service {
104 1.10 plunky const char * name;
105 1.10 plunky const char * description;
106 1.2 plunky uint16_t class;
107 1.2 plunky } services[] = {
108 1.2 plunky { "DUN", "Dialup Networking",
109 1.10 plunky SDP_SERVICE_CLASS_DIALUP_NETWORKING },
110 1.10 plunky { "LAN", "LAN access using PPP",
111 1.10 plunky SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP },
112 1.2 plunky { "SP", "Serial Port",
113 1.10 plunky SDP_SERVICE_CLASS_SERIAL_PORT },
114 1.10 plunky { NULL, NULL, 0 }
115 1.2 plunky };
116 1.1 gdamore
117 1.1 gdamore int
118 1.1 gdamore main(int argc, char *argv[])
119 1.1 gdamore {
120 1.2 plunky struct termios t;
121 1.2 plunky bdaddr_t laddr, raddr;
122 1.15 christos struct pollfd pfd[2];
123 1.6 plunky const char *service;
124 1.6 plunky char *ep, *tty;
125 1.15 christos int n, lm, rfcomm, tty_in, tty_out;
126 1.12 plunky uint16_t psm;
127 1.2 plunky uint8_t channel;
128 1.1 gdamore
129 1.15 christos setprogname(argv[0]);
130 1.1 gdamore bdaddr_copy(&laddr, BDADDR_ANY);
131 1.1 gdamore bdaddr_copy(&raddr, BDADDR_ANY);
132 1.3 plunky service = "SP";
133 1.3 plunky tty = NULL;
134 1.13 plunky channel = RFCOMM_CHANNEL_ANY;
135 1.12 plunky psm = L2CAP_PSM_RFCOMM;
136 1.7 plunky lm = 0;
137 1.1 gdamore
138 1.1 gdamore /* Parse command line options */
139 1.12 plunky while ((n = getopt(argc, argv, "a:c:d:hm:p:s:t:")) != -1) {
140 1.1 gdamore switch (n) {
141 1.2 plunky case 'a': /* remote device address */
142 1.1 gdamore if (!bt_aton(optarg, &raddr)) {
143 1.1 gdamore struct hostent *he = NULL;
144 1.1 gdamore
145 1.1 gdamore if ((he = bt_gethostbyname(optarg)) == NULL)
146 1.2 plunky errx(EXIT_FAILURE, "%s: %s", optarg,
147 1.2 plunky hstrerror(h_errno));
148 1.1 gdamore
149 1.1 gdamore bdaddr_copy(&raddr, (bdaddr_t *)he->h_addr);
150 1.1 gdamore }
151 1.1 gdamore break;
152 1.1 gdamore
153 1.1 gdamore case 'c': /* RFCOMM channel */
154 1.1 gdamore channel = strtoul(optarg, &ep, 10);
155 1.13 plunky if (*ep != '\0'
156 1.13 plunky || channel < RFCOMM_CHANNEL_MIN
157 1.13 plunky || channel > RFCOMM_CHANNEL_MAX)
158 1.15 christos errx(EXIT_FAILURE, "Invalid channel: %s",
159 1.15 christos optarg);
160 1.3 plunky
161 1.1 gdamore break;
162 1.1 gdamore
163 1.2 plunky case 'd': /* local device address */
164 1.2 plunky if (!bt_devaddr(optarg, &laddr))
165 1.2 plunky err(EXIT_FAILURE, "%s", optarg);
166 1.2 plunky
167 1.1 gdamore break;
168 1.1 gdamore
169 1.7 plunky case 'm': /* Link Mode */
170 1.7 plunky if (strcasecmp(optarg, "auth") == 0)
171 1.7 plunky lm = RFCOMM_LM_AUTH;
172 1.7 plunky else if (strcasecmp(optarg, "encrypt") == 0)
173 1.7 plunky lm = RFCOMM_LM_ENCRYPT;
174 1.7 plunky else if (strcasecmp(optarg, "secure") == 0)
175 1.7 plunky lm = RFCOMM_LM_SECURE;
176 1.7 plunky else
177 1.15 christos errx(EXIT_FAILURE, "Unknown mode: %s", optarg);
178 1.7 plunky
179 1.7 plunky break;
180 1.7 plunky
181 1.12 plunky case 'p': /* PSM */
182 1.12 plunky psm = strtoul(optarg, &ep, 0);
183 1.12 plunky if (*ep != '\0' || L2CAP_PSM_INVALID(psm))
184 1.12 plunky errx(EXIT_FAILURE, "Invalid PSM: %s", optarg);
185 1.12 plunky
186 1.12 plunky break;
187 1.12 plunky
188 1.3 plunky case 's': /* service class */
189 1.2 plunky service = optarg;
190 1.1 gdamore break;
191 1.1 gdamore
192 1.1 gdamore case 't': /* Slave TTY name */
193 1.1 gdamore if (optarg[0] != '/')
194 1.1 gdamore asprintf(&tty, "%s%s", _PATH_DEV, optarg);
195 1.1 gdamore else
196 1.1 gdamore tty = optarg;
197 1.2 plunky
198 1.1 gdamore break;
199 1.1 gdamore
200 1.1 gdamore case 'h':
201 1.1 gdamore default:
202 1.1 gdamore usage();
203 1.1 gdamore /* NOT REACHED */
204 1.1 gdamore }
205 1.1 gdamore }
206 1.1 gdamore
207 1.2 plunky /*
208 1.2 plunky * validate options:
209 1.13 plunky * cannot have remote address if channel was given
210 1.2 plunky */
211 1.13 plunky if (channel != RFCOMM_CHANNEL_ANY && !bdaddr_any(&raddr))
212 1.1 gdamore usage();
213 1.1 gdamore
214 1.2 plunky /*
215 1.2 plunky * grab ttys before we start the bluetooth
216 1.2 plunky */
217 1.1 gdamore if (tty == NULL) {
218 1.2 plunky tty_in = STDIN_FILENO;
219 1.2 plunky tty_out = STDOUT_FILENO;
220 1.2 plunky } else {
221 1.2 plunky tty_in = open_tty(tty);
222 1.2 plunky tty_out = tty_in;
223 1.2 plunky }
224 1.1 gdamore
225 1.2 plunky /* open RFCOMM */
226 1.13 plunky if (!bdaddr_any(&raddr))
227 1.12 plunky rfcomm = open_client(&laddr, &raddr, lm, psm, service);
228 1.2 plunky else
229 1.12 plunky rfcomm = open_server(&laddr, psm, channel, lm, service);
230 1.1 gdamore
231 1.2 plunky /*
232 1.4 plunky * now we are ready to go, so either detach or maybe turn
233 1.2 plunky * off some input processing, so that rfcomm_sppd can
234 1.2 plunky * be used directly with stdio
235 1.2 plunky */
236 1.2 plunky if (tty == NULL) {
237 1.15 christos if (tcgetattr(tty_in, &t) != -1) {
238 1.15 christos tio = t;
239 1.14 plunky t.c_lflag &= ~(ECHO | ICANON);
240 1.14 plunky t.c_iflag &= ~(ICRNL);
241 1.14 plunky
242 1.15 christos if (tio.c_lflag != t.c_lflag ||
243 1.15 christos tio.c_iflag != t.c_iflag) {
244 1.15 christos if (tcsetattr(tty_in, TCSANOW, &t) == -1)
245 1.14 plunky err(EXIT_FAILURE, "tcsetattr");
246 1.1 gdamore
247 1.14 plunky atexit(reset_tio);
248 1.14 plunky }
249 1.4 plunky }
250 1.2 plunky } else {
251 1.15 christos if (daemon(0, 0) == -1)
252 1.2 plunky err(EXIT_FAILURE, "daemon() failed");
253 1.1 gdamore }
254 1.1 gdamore
255 1.2 plunky /* catch signals */
256 1.2 plunky done = 0;
257 1.2 plunky (void)signal(SIGHUP, sighandler);
258 1.2 plunky (void)signal(SIGINT, sighandler);
259 1.2 plunky (void)signal(SIGPIPE, sighandler);
260 1.2 plunky (void)signal(SIGTERM, sighandler);
261 1.2 plunky
262 1.2 plunky openlog(getprogname(), LOG_PERROR | LOG_PID, LOG_DAEMON);
263 1.2 plunky syslog(LOG_INFO, "Starting on %s...", (tty ? tty : "stdio"));
264 1.2 plunky
265 1.15 christos pfd[0].fd = tty_in;
266 1.15 christos pfd[1].fd = rfcomm;
267 1.15 christos pfd[0].events = POLLIN|POLLRDNORM;
268 1.15 christos pfd[1].events = POLLIN|POLLRDNORM;
269 1.15 christos
270 1.2 plunky while (!done) {
271 1.15 christos if (poll(pfd, 2, INFTIM) == -1) {
272 1.1 gdamore if (errno == EINTR)
273 1.1 gdamore continue;
274 1.1 gdamore
275 1.15 christos syslog(LOG_ERR, "poll error: %m");
276 1.1 gdamore }
277 1.15 christos if (pfd[0].revents & (POLLIN|POLLRDNORM))
278 1.2 plunky copy_data(tty_in, rfcomm);
279 1.1 gdamore
280 1.15 christos if (pfd[1].revents & (POLLIN|POLLRDNORM))
281 1.2 plunky copy_data(rfcomm, tty_out);
282 1.2 plunky }
283 1.1 gdamore
284 1.2 plunky syslog(LOG_INFO, "Completed on %s", (tty ? tty : "stdio"));
285 1.15 christos return EXIT_SUCCESS;
286 1.2 plunky }
287 1.1 gdamore
288 1.15 christos static int
289 1.2 plunky open_tty(const char *tty)
290 1.1 gdamore {
291 1.1 gdamore char pty[PATH_MAX], *slash;
292 1.1 gdamore struct group *gr = NULL;
293 1.1 gdamore gid_t ttygid;
294 1.2 plunky int master;
295 1.1 gdamore
296 1.1 gdamore /*
297 1.16 wiz * Construct master PTY name. The slave tty name must be less than
298 1.1 gdamore * PATH_MAX characters in length, must contain '/' character and
299 1.1 gdamore * must not end with '/'.
300 1.1 gdamore */
301 1.15 christos if (strlcpy(pty, tty, sizeof(pty)) >= sizeof(pty))
302 1.15 christos errx(EXIT_FAILURE, "Tty name too long `%s'", tty);
303 1.1 gdamore
304 1.1 gdamore slash = strrchr(pty, '/');
305 1.2 plunky if (slash == NULL || slash[1] == '\0')
306 1.15 christos errx(EXIT_FAILURE, "Invalid tty `%s'", tty);
307 1.1 gdamore
308 1.1 gdamore slash[1] = 'p';
309 1.2 plunky if (strcmp(pty, tty) == 0)
310 1.15 christos errx(EXIT_FAILURE, "Master and slave tty are the same (%s)",
311 1.15 christos tty);
312 1.1 gdamore
313 1.15 christos if ((master = open(pty, O_RDWR)) == -1)
314 1.15 christos err(EXIT_FAILURE, "Cannot open `%s'", pty);
315 1.1 gdamore
316 1.1 gdamore /*
317 1.1 gdamore * Slave TTY
318 1.1 gdamore */
319 1.1 gdamore if ((gr = getgrnam("tty")) != NULL)
320 1.1 gdamore ttygid = gr->gr_gid;
321 1.1 gdamore else
322 1.1 gdamore ttygid = (gid_t)-1;
323 1.1 gdamore
324 1.15 christos if (chown(tty, getuid(), ttygid) == -1)
325 1.15 christos err(EXIT_FAILURE, "Cannot chown `%s'", pty);
326 1.15 christos if (chmod(tty, S_IRUSR | S_IWUSR | S_IWGRP) == -1)
327 1.15 christos err(EXIT_FAILURE, "Cannot chmod `%s'", pty);
328 1.15 christos if (revoke(tty) == -1)
329 1.15 christos err(EXIT_FAILURE, "Cannot revoke `%s'", pty);
330 1.2 plunky
331 1.2 plunky return master;
332 1.2 plunky }
333 1.1 gdamore
334 1.15 christos static int
335 1.15 christos open_client(bdaddr_t *laddr, bdaddr_t *raddr, int lm, uintmax_t psm,
336 1.15 christos const char *service)
337 1.2 plunky {
338 1.2 plunky struct sockaddr_bt sa;
339 1.15 christos const struct service *s;
340 1.2 plunky struct linger l;
341 1.3 plunky char *ep;
342 1.15 christos int fd;
343 1.10 plunky uintmax_t channel;
344 1.3 plunky
345 1.3 plunky for (s = services ; ; s++) {
346 1.3 plunky if (s->name == NULL) {
347 1.15 christos errno = 0;
348 1.5 plunky channel = strtoul(service, &ep, 10);
349 1.15 christos if (service == ep || *ep != '\0')
350 1.15 christos errx(EXIT_FAILURE, "Unknown service `%s'",
351 1.15 christos service);
352 1.15 christos if (channel == ULONG_MAX && errno == ERANGE)
353 1.15 christos err(EXIT_FAILURE, "Service `%s'",
354 1.15 christos service);
355 1.3 plunky
356 1.3 plunky break;
357 1.3 plunky }
358 1.3 plunky
359 1.3 plunky if (strcasecmp(s->name, service) == 0) {
360 1.15 christos if (service_search(laddr, raddr, s->class, &psm,
361 1.15 christos &channel) == -1)
362 1.15 christos err(EXIT_FAILURE, "%s", s->name);
363 1.3 plunky
364 1.3 plunky break;
365 1.3 plunky }
366 1.3 plunky }
367 1.1 gdamore
368 1.10 plunky if (channel < RFCOMM_CHANNEL_MIN || channel > RFCOMM_CHANNEL_MAX)
369 1.10 plunky errx(EXIT_FAILURE, "Invalid channel %"PRIuMAX, channel);
370 1.10 plunky
371 1.12 plunky if (L2CAP_PSM_INVALID(psm))
372 1.12 plunky errx(EXIT_FAILURE, "Invalid PSM 0x%04"PRIxMAX, psm);
373 1.12 plunky
374 1.2 plunky memset(&sa, 0, sizeof(sa));
375 1.2 plunky sa.bt_len = sizeof(sa);
376 1.2 plunky sa.bt_family = AF_BLUETOOTH;
377 1.2 plunky bdaddr_copy(&sa.bt_bdaddr, laddr);
378 1.2 plunky
379 1.2 plunky fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
380 1.15 christos if (fd == -1)
381 1.2 plunky err(EXIT_FAILURE, "socket()");
382 1.2 plunky
383 1.15 christos if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1)
384 1.2 plunky err(EXIT_FAILURE, "bind(%s)", bt_ntoa(laddr, NULL));
385 1.2 plunky
386 1.2 plunky memset(&l, 0, sizeof(l));
387 1.2 plunky l.l_onoff = 1;
388 1.2 plunky l.l_linger = 5;
389 1.15 christos if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) == -1)
390 1.2 plunky err(EXIT_FAILURE, "linger()");
391 1.2 plunky
392 1.15 christos if (setsockopt(fd, BTPROTO_RFCOMM, SO_RFCOMM_LM, &lm, sizeof(lm)) == -1)
393 1.7 plunky err(EXIT_FAILURE, "link mode");
394 1.7 plunky
395 1.12 plunky sa.bt_psm = psm;
396 1.2 plunky sa.bt_channel = channel;
397 1.2 plunky bdaddr_copy(&sa.bt_bdaddr, raddr);
398 1.2 plunky
399 1.15 christos if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1)
400 1.12 plunky err(EXIT_FAILURE, "connect(%s, 0x%04"PRIxMAX", %"PRIuMAX")",
401 1.12 plunky bt_ntoa(raddr, NULL), psm, channel);
402 1.1 gdamore
403 1.2 plunky return fd;
404 1.2 plunky }
405 1.1 gdamore
406 1.15 christos static int
407 1.15 christos open_server(bdaddr_t *laddr, uint16_t psm, uint8_t channel, int lm,
408 1.15 christos const char *service)
409 1.1 gdamore {
410 1.10 plunky uint8_t buffer[256];
411 1.2 plunky struct sockaddr_bt sa;
412 1.15 christos const struct service *s;
413 1.2 plunky struct linger l;
414 1.2 plunky socklen_t len;
415 1.10 plunky sdp_session_t ss;
416 1.10 plunky sdp_data_t rec;
417 1.10 plunky int sv, fd;
418 1.10 plunky
419 1.10 plunky for (s = services; ; s++) {
420 1.10 plunky if (s->name == NULL)
421 1.10 plunky usage();
422 1.1 gdamore
423 1.10 plunky if (strcasecmp(s->name, service) == 0)
424 1.10 plunky break;
425 1.10 plunky }
426 1.2 plunky
427 1.10 plunky /* Open server socket */
428 1.2 plunky sv = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
429 1.15 christos if (sv == -1)
430 1.2 plunky err(EXIT_FAILURE, "socket()");
431 1.2 plunky
432 1.10 plunky memset(&sa, 0, sizeof(sa));
433 1.10 plunky sa.bt_len = sizeof(sa);
434 1.10 plunky sa.bt_family = AF_BLUETOOTH;
435 1.12 plunky sa.bt_psm = psm;
436 1.10 plunky sa.bt_channel = channel;
437 1.10 plunky bdaddr_copy(&sa.bt_bdaddr, laddr);
438 1.15 christos if (bind(sv, (struct sockaddr *)&sa, sizeof(sa)) == -1)
439 1.12 plunky err(EXIT_FAILURE, "bind(%s, 0x%04x, %d)",
440 1.12 plunky bt_ntoa(laddr, NULL), psm, channel);
441 1.2 plunky
442 1.15 christos if (setsockopt(sv, BTPROTO_RFCOMM, SO_RFCOMM_LM, &lm, sizeof(lm)) == -1)
443 1.7 plunky err(EXIT_FAILURE, "link mode");
444 1.7 plunky
445 1.15 christos if (listen(sv, 1) == -1)
446 1.2 plunky err(EXIT_FAILURE, "listen()");
447 1.2 plunky
448 1.13 plunky len = sizeof(sa);
449 1.15 christos if (getsockname(sv, (struct sockaddr *)&sa, &len) == -1)
450 1.13 plunky err(EXIT_FAILURE, "getsockname()");
451 1.13 plunky if (len != sizeof(sa))
452 1.13 plunky errx(EXIT_FAILURE, "getsockname()");
453 1.13 plunky
454 1.10 plunky /* Build SDP record */
455 1.10 plunky rec.next = buffer;
456 1.10 plunky rec.end = buffer + sizeof(buffer);
457 1.10 plunky
458 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_SERVICE_RECORD_HANDLE);
459 1.10 plunky sdp_put_uint32(&rec, 0x00000000);
460 1.10 plunky
461 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_SERVICE_CLASS_ID_LIST);
462 1.10 plunky sdp_put_seq(&rec, 3);
463 1.10 plunky sdp_put_uuid16(&rec, s->class);
464 1.10 plunky
465 1.12 plunky len = (psm == L2CAP_PSM_RFCOMM ? 0 : 3);
466 1.12 plunky
467 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
468 1.12 plunky sdp_put_seq(&rec, 12 + len);
469 1.12 plunky sdp_put_seq(&rec, 3 + len);
470 1.10 plunky sdp_put_uuid16(&rec, SDP_UUID_PROTOCOL_L2CAP);
471 1.12 plunky if (len > 0)
472 1.12 plunky sdp_put_uint16(&rec, psm);
473 1.10 plunky sdp_put_seq(&rec, 5);
474 1.10 plunky sdp_put_uuid16(&rec, SDP_UUID_PROTOCOL_RFCOMM);
475 1.13 plunky sdp_put_uint8(&rec, sa.bt_channel);
476 1.10 plunky
477 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_BROWSE_GROUP_LIST);
478 1.10 plunky sdp_put_seq(&rec, 3);
479 1.10 plunky sdp_put_uuid16(&rec, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);
480 1.10 plunky
481 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
482 1.10 plunky sdp_put_seq(&rec, 9);
483 1.10 plunky sdp_put_uint16(&rec, 0x656e); /* "en" */
484 1.10 plunky sdp_put_uint16(&rec, 106); /* UTF-8 */
485 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);
486 1.10 plunky
487 1.10 plunky if (s->class == SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP) {
488 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_SERVICE_AVAILABILITY);
489 1.10 plunky sdp_put_uint8(&rec, 0x00);
490 1.10 plunky }
491 1.10 plunky
492 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
493 1.10 plunky sdp_put_seq(&rec, 8);
494 1.10 plunky sdp_put_seq(&rec, 6);
495 1.10 plunky sdp_put_uuid16(&rec, s->class);
496 1.10 plunky sdp_put_uint16(&rec, 0x0100); /* v1.0 */
497 1.10 plunky
498 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID
499 1.10 plunky + SDP_ATTR_SERVICE_NAME_OFFSET);
500 1.10 plunky sdp_put_str(&rec, s->description, -1);
501 1.10 plunky
502 1.10 plunky if (s->class == SDP_SERVICE_CLASS_DIALUP_NETWORKING) {
503 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_AUDIO_FEEDBACK_SUPPORT);
504 1.10 plunky sdp_put_bool(&rec, false);
505 1.10 plunky }
506 1.1 gdamore
507 1.10 plunky #if 0
508 1.10 plunky if (s->class == SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP) {
509 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_IP_SUBNET); /* TODO */
510 1.10 plunky sdp_put_str(&rec, "0.0.0.0/0", -1);
511 1.1 gdamore }
512 1.10 plunky #endif
513 1.1 gdamore
514 1.10 plunky rec.end = rec.next;
515 1.10 plunky rec.next = buffer;
516 1.2 plunky
517 1.10 plunky /* Register service with SDP server */
518 1.2 plunky ss = sdp_open_local(NULL);
519 1.10 plunky if (ss == NULL)
520 1.2 plunky err(EXIT_FAILURE, "sdp_open_local");
521 1.2 plunky
522 1.10 plunky if (!sdp_record_insert(ss, laddr, NULL, &rec))
523 1.10 plunky err(EXIT_FAILURE, "sdp_record_insert");
524 1.2 plunky
525 1.10 plunky /* Accept client connection */
526 1.2 plunky len = sizeof(sa);
527 1.2 plunky fd = accept(sv, (struct sockaddr *)&sa, &len);
528 1.15 christos if (fd == -1)
529 1.2 plunky err(EXIT_FAILURE, "accept");
530 1.2 plunky
531 1.2 plunky memset(&l, 0, sizeof(l));
532 1.2 plunky l.l_onoff = 1;
533 1.2 plunky l.l_linger = 5;
534 1.15 christos if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) == -1)
535 1.2 plunky err(EXIT_FAILURE, "linger()");
536 1.2 plunky
537 1.2 plunky close(sv);
538 1.2 plunky return fd;
539 1.2 plunky }
540 1.1 gdamore
541 1.15 christos static void
542 1.2 plunky copy_data(int src, int dst)
543 1.1 gdamore {
544 1.2 plunky static char buf[BUFSIZ];
545 1.2 plunky ssize_t nr, nw, off;
546 1.1 gdamore
547 1.2 plunky while ((nr = read(src, buf, sizeof(buf))) == -1) {
548 1.2 plunky if (errno != EINTR) {
549 1.2 plunky syslog(LOG_ERR, "read failed: %m");
550 1.2 plunky exit(EXIT_FAILURE);
551 1.2 plunky }
552 1.2 plunky }
553 1.1 gdamore
554 1.3 plunky if (nr == 0) /* reached EOF */
555 1.3 plunky done++;
556 1.3 plunky
557 1.2 plunky for (off = 0 ; nr ; nr -= nw, off += nw) {
558 1.2 plunky if ((nw = write(dst, buf + off, (size_t)nr)) == -1) {
559 1.2 plunky syslog(LOG_ERR, "write failed: %m");
560 1.2 plunky exit(EXIT_FAILURE);
561 1.1 gdamore }
562 1.1 gdamore }
563 1.2 plunky }
564 1.1 gdamore
565 1.15 christos static int
566 1.12 plunky service_search(bdaddr_t const *laddr, bdaddr_t const *raddr,
567 1.12 plunky uint16_t class, uintmax_t *psm, uintmax_t *channel)
568 1.10 plunky {
569 1.10 plunky uint8_t buffer[6]; /* SSP (3 bytes) + AIL (3 bytes) */
570 1.10 plunky sdp_session_t ss;
571 1.10 plunky sdp_data_t ail, ssp, rsp, rec, value, pdl, seq;
572 1.10 plunky uint16_t attr;
573 1.10 plunky bool rv;
574 1.10 plunky
575 1.10 plunky seq.next = buffer;
576 1.10 plunky seq.end = buffer + sizeof(buffer);
577 1.10 plunky
578 1.10 plunky /*
579 1.10 plunky * build ServiceSearchPattern (3 bytes)
580 1.10 plunky */
581 1.10 plunky ssp.next = seq.next;
582 1.10 plunky sdp_put_uuid16(&seq, class);
583 1.10 plunky ssp.end = seq.next;
584 1.10 plunky
585 1.10 plunky /*
586 1.10 plunky * build AttributeIDList (3 bytes)
587 1.10 plunky */
588 1.10 plunky ail.next = seq.next;
589 1.10 plunky sdp_put_uint16(&seq, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
590 1.10 plunky ail.end = seq.next;
591 1.10 plunky
592 1.10 plunky ss = sdp_open(laddr, raddr);
593 1.10 plunky if (ss == NULL)
594 1.15 christos return -1;
595 1.10 plunky
596 1.10 plunky rv = sdp_service_search_attribute(ss, &ssp, &ail, &rsp);
597 1.10 plunky if (!rv) {
598 1.10 plunky sdp_close(ss);
599 1.15 christos return -1;
600 1.10 plunky }
601 1.10 plunky
602 1.10 plunky /*
603 1.10 plunky * The response will be a list of records that matched our
604 1.10 plunky * ServiceSearchPattern, where each record is a sequence
605 1.10 plunky * containing a single ProtocolDescriptorList attribute and
606 1.10 plunky * value
607 1.10 plunky *
608 1.10 plunky * seq
609 1.10 plunky * uint16 ProtocolDescriptorList
610 1.10 plunky * value
611 1.10 plunky * seq
612 1.10 plunky * uint16 ProtocolDescriptorList
613 1.10 plunky * value
614 1.10 plunky *
615 1.10 plunky * If the ProtocolDescriptorList describes a single stack,
616 1.10 plunky * the attribute value takes the form of a single Data Element
617 1.10 plunky * Sequence where each member is a protocol descriptor.
618 1.10 plunky *
619 1.10 plunky * seq
620 1.10 plunky * list
621 1.10 plunky *
622 1.10 plunky * If it is possible for more than one kind of protocol
623 1.10 plunky * stack to be used to gain access to the service, the
624 1.10 plunky * ProtocolDescriptorList takes the form of a Data Element
625 1.10 plunky * Alternative where each member is a Data Element Sequence
626 1.10 plunky * describing an alternative protocol stack.
627 1.10 plunky *
628 1.10 plunky * alt
629 1.10 plunky * seq
630 1.10 plunky * list
631 1.10 plunky * seq
632 1.10 plunky * list
633 1.10 plunky *
634 1.10 plunky * Each protocol stack description contains a sequence for each
635 1.10 plunky * protocol, where each sequence contains the protocol UUID as
636 1.10 plunky * the first element, and any ProtocolSpecificParameters. We are
637 1.12 plunky * interested in the L2CAP psm if provided, and the RFCOMM channel
638 1.12 plunky * number, stored as parameter#1 in each case.
639 1.10 plunky *
640 1.10 plunky * seq
641 1.10 plunky * uuid L2CAP
642 1.10 plunky * uint16 psm
643 1.10 plunky * seq
644 1.10 plunky * uuid RFCOMM
645 1.10 plunky * uint8 channel
646 1.10 plunky */
647 1.10 plunky
648 1.10 plunky rv = false;
649 1.10 plunky while (!rv && sdp_get_seq(&rsp, &rec)) {
650 1.10 plunky if (!sdp_get_attr(&rec, &attr, &value)
651 1.10 plunky || attr != SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST)
652 1.10 plunky continue;
653 1.10 plunky
654 1.10 plunky sdp_get_alt(&value, &value); /* strip any alt container */
655 1.10 plunky while (!rv && sdp_get_seq(&value, &pdl)) {
656 1.12 plunky *psm = L2CAP_PSM_RFCOMM;
657 1.10 plunky if (sdp_get_seq(&pdl, &seq)
658 1.10 plunky && sdp_match_uuid16(&seq, SDP_UUID_PROTOCOL_L2CAP)
659 1.12 plunky && (sdp_get_uint(&seq, psm) || true)
660 1.10 plunky && sdp_get_seq(&pdl, &seq)
661 1.10 plunky && sdp_match_uuid16(&seq, SDP_UUID_PROTOCOL_RFCOMM)
662 1.10 plunky && sdp_get_uint(&seq, channel))
663 1.10 plunky rv = true;
664 1.10 plunky }
665 1.10 plunky }
666 1.10 plunky
667 1.10 plunky sdp_close(ss);
668 1.15 christos if (rv)
669 1.15 christos return 0;
670 1.15 christos errno = ENOATTR;
671 1.15 christos return -1;
672 1.10 plunky }
673 1.10 plunky
674 1.15 christos static void
675 1.2 plunky sighandler(int s)
676 1.2 plunky {
677 1.1 gdamore
678 1.2 plunky done++;
679 1.2 plunky }
680 1.2 plunky
681 1.15 christos static void
682 1.2 plunky reset_tio(void)
683 1.1 gdamore {
684 1.1 gdamore
685 1.2 plunky tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
686 1.2 plunky }
687 1.2 plunky
688 1.15 christos static void
689 1.1 gdamore usage(void)
690 1.1 gdamore {
691 1.7 plunky const char *cmd = getprogname();
692 1.15 christos const struct service *s;
693 1.2 plunky
694 1.15 christos fprintf(stderr, "Usage: %s [-d device] [-m mode] [-p psm] [-s service]"
695 1.15 christos " [-t tty]\n"
696 1.15 christos " %*s {-a bdaddr | [-c channel]}\n"
697 1.15 christos "\n"
698 1.15 christos "Where:\n"
699 1.15 christos "\t-a bdaddr remote device address\n"
700 1.15 christos "\t-c channel local RFCOMM channel\n"
701 1.15 christos "\t-d device local device address\n"
702 1.15 christos "\t-m mode link mode\n"
703 1.15 christos "\t-p psm protocol/service multiplexer\n"
704 1.15 christos "\t-s service service class\n"
705 1.15 christos "\t-t tty run in background using pty\n"
706 1.15 christos "\n", cmd, (int)strlen(cmd), "");
707 1.2 plunky
708 1.3 plunky fprintf(stderr, "Known service classes:\n");
709 1.2 plunky for (s = services ; s->name != NULL ; s++)
710 1.3 plunky fprintf(stderr, "\t%-13s%s\n", s->name, s->description);
711 1.1 gdamore
712 1.1 gdamore exit(EXIT_FAILURE);
713 1.2 plunky }
714