rfcomm_sppd.c revision 1.14 1 1.14 plunky /* $NetBSD: rfcomm_sppd.c,v 1.14 2011/07/18 15:44:17 plunky 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.14 plunky __RCSID("$NetBSD: rfcomm_sppd.c,v 1.14 2011/07/18 15:44:17 plunky 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.1 gdamore #include <stdio.h>
81 1.1 gdamore #include <stdlib.h>
82 1.1 gdamore #include <string.h>
83 1.1 gdamore #include <syslog.h>
84 1.1 gdamore #include <termios.h>
85 1.1 gdamore #include <unistd.h>
86 1.1 gdamore
87 1.7 plunky #include <netbt/rfcomm.h>
88 1.7 plunky
89 1.2 plunky int open_tty(const char *);
90 1.12 plunky int open_client(bdaddr_t *, bdaddr_t *, int, uintmax_t, const char *);
91 1.12 plunky int open_server(bdaddr_t *, uint16_t, uint8_t, int, const char *);
92 1.2 plunky void copy_data(int, int);
93 1.12 plunky int service_search(const bdaddr_t *, const bdaddr_t *, uint16_t, uintmax_t *, uintmax_t *);
94 1.2 plunky void sighandler(int);
95 1.2 plunky void usage(void);
96 1.2 plunky void reset_tio(void);
97 1.2 plunky
98 1.2 plunky int done; /* got a signal */
99 1.2 plunky struct termios tio; /* stored termios for reset on exit */
100 1.2 plunky
101 1.2 plunky struct service {
102 1.10 plunky const char * name;
103 1.10 plunky const char * description;
104 1.2 plunky uint16_t class;
105 1.2 plunky } services[] = {
106 1.2 plunky { "DUN", "Dialup Networking",
107 1.10 plunky SDP_SERVICE_CLASS_DIALUP_NETWORKING },
108 1.10 plunky { "LAN", "LAN access using PPP",
109 1.10 plunky SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP },
110 1.2 plunky { "SP", "Serial Port",
111 1.10 plunky SDP_SERVICE_CLASS_SERIAL_PORT },
112 1.10 plunky { NULL, NULL, 0 }
113 1.2 plunky };
114 1.1 gdamore
115 1.1 gdamore int
116 1.1 gdamore main(int argc, char *argv[])
117 1.1 gdamore {
118 1.2 plunky struct termios t;
119 1.2 plunky bdaddr_t laddr, raddr;
120 1.2 plunky fd_set rdset;
121 1.6 plunky const char *service;
122 1.6 plunky char *ep, *tty;
123 1.7 plunky int lm, n, rfcomm, tty_in, tty_out;
124 1.12 plunky uint16_t psm;
125 1.2 plunky uint8_t channel;
126 1.1 gdamore
127 1.1 gdamore bdaddr_copy(&laddr, BDADDR_ANY);
128 1.1 gdamore bdaddr_copy(&raddr, BDADDR_ANY);
129 1.3 plunky service = "SP";
130 1.3 plunky tty = NULL;
131 1.13 plunky channel = RFCOMM_CHANNEL_ANY;
132 1.12 plunky psm = L2CAP_PSM_RFCOMM;
133 1.7 plunky lm = 0;
134 1.1 gdamore
135 1.1 gdamore /* Parse command line options */
136 1.12 plunky while ((n = getopt(argc, argv, "a:c:d:hm:p:s:t:")) != -1) {
137 1.1 gdamore switch (n) {
138 1.2 plunky case 'a': /* remote device address */
139 1.1 gdamore if (!bt_aton(optarg, &raddr)) {
140 1.1 gdamore struct hostent *he = NULL;
141 1.1 gdamore
142 1.1 gdamore if ((he = bt_gethostbyname(optarg)) == NULL)
143 1.2 plunky errx(EXIT_FAILURE, "%s: %s", optarg,
144 1.2 plunky hstrerror(h_errno));
145 1.1 gdamore
146 1.1 gdamore bdaddr_copy(&raddr, (bdaddr_t *)he->h_addr);
147 1.1 gdamore }
148 1.1 gdamore break;
149 1.1 gdamore
150 1.1 gdamore case 'c': /* RFCOMM channel */
151 1.1 gdamore channel = strtoul(optarg, &ep, 10);
152 1.13 plunky if (*ep != '\0'
153 1.13 plunky || channel < RFCOMM_CHANNEL_MIN
154 1.13 plunky || channel > RFCOMM_CHANNEL_MAX)
155 1.3 plunky errx(EXIT_FAILURE, "Invalid channel: %s", optarg);
156 1.3 plunky
157 1.1 gdamore break;
158 1.1 gdamore
159 1.2 plunky case 'd': /* local device address */
160 1.2 plunky if (!bt_devaddr(optarg, &laddr))
161 1.2 plunky err(EXIT_FAILURE, "%s", optarg);
162 1.2 plunky
163 1.1 gdamore break;
164 1.1 gdamore
165 1.7 plunky case 'm': /* Link Mode */
166 1.7 plunky if (strcasecmp(optarg, "auth") == 0)
167 1.7 plunky lm = RFCOMM_LM_AUTH;
168 1.7 plunky else if (strcasecmp(optarg, "encrypt") == 0)
169 1.7 plunky lm = RFCOMM_LM_ENCRYPT;
170 1.7 plunky else if (strcasecmp(optarg, "secure") == 0)
171 1.7 plunky lm = RFCOMM_LM_SECURE;
172 1.7 plunky else
173 1.7 plunky errx(EXIT_FAILURE, "%s: unknown mode", optarg);
174 1.7 plunky
175 1.7 plunky break;
176 1.7 plunky
177 1.12 plunky case 'p': /* PSM */
178 1.12 plunky psm = strtoul(optarg, &ep, 0);
179 1.12 plunky if (*ep != '\0' || L2CAP_PSM_INVALID(psm))
180 1.12 plunky errx(EXIT_FAILURE, "Invalid PSM: %s", optarg);
181 1.12 plunky
182 1.12 plunky break;
183 1.12 plunky
184 1.3 plunky case 's': /* service class */
185 1.2 plunky service = optarg;
186 1.1 gdamore break;
187 1.1 gdamore
188 1.1 gdamore case 't': /* Slave TTY name */
189 1.1 gdamore if (optarg[0] != '/')
190 1.1 gdamore asprintf(&tty, "%s%s", _PATH_DEV, optarg);
191 1.1 gdamore else
192 1.1 gdamore tty = optarg;
193 1.2 plunky
194 1.1 gdamore break;
195 1.1 gdamore
196 1.1 gdamore case 'h':
197 1.1 gdamore default:
198 1.1 gdamore usage();
199 1.1 gdamore /* NOT REACHED */
200 1.1 gdamore }
201 1.1 gdamore }
202 1.1 gdamore
203 1.2 plunky /*
204 1.2 plunky * validate options:
205 1.13 plunky * cannot have remote address if channel was given
206 1.2 plunky */
207 1.13 plunky if (channel != RFCOMM_CHANNEL_ANY && !bdaddr_any(&raddr))
208 1.1 gdamore usage();
209 1.1 gdamore
210 1.2 plunky /*
211 1.2 plunky * grab ttys before we start the bluetooth
212 1.2 plunky */
213 1.1 gdamore if (tty == NULL) {
214 1.2 plunky tty_in = STDIN_FILENO;
215 1.2 plunky tty_out = STDOUT_FILENO;
216 1.2 plunky } else {
217 1.2 plunky tty_in = open_tty(tty);
218 1.2 plunky tty_out = tty_in;
219 1.2 plunky }
220 1.1 gdamore
221 1.2 plunky /* open RFCOMM */
222 1.13 plunky if (!bdaddr_any(&raddr))
223 1.12 plunky rfcomm = open_client(&laddr, &raddr, lm, psm, service);
224 1.2 plunky else
225 1.12 plunky rfcomm = open_server(&laddr, psm, channel, lm, service);
226 1.1 gdamore
227 1.2 plunky /*
228 1.4 plunky * now we are ready to go, so either detach or maybe turn
229 1.2 plunky * off some input processing, so that rfcomm_sppd can
230 1.2 plunky * be used directly with stdio
231 1.2 plunky */
232 1.2 plunky if (tty == NULL) {
233 1.14 plunky if (isatty(tty_in)) {
234 1.14 plunky if (tcgetattr(tty_in, &t) < 0)
235 1.14 plunky err(EXIT_FAILURE, "tcgetattr");
236 1.14 plunky
237 1.14 plunky memcpy(&tio, &t, sizeof(tio));
238 1.14 plunky t.c_lflag &= ~(ECHO | ICANON);
239 1.14 plunky t.c_iflag &= ~(ICRNL);
240 1.14 plunky
241 1.14 plunky if (memcmp(&tio, &t, sizeof(tio))) {
242 1.14 plunky if (tcsetattr(tty_in, TCSANOW, &t) < 0)
243 1.14 plunky err(EXIT_FAILURE, "tcsetattr");
244 1.1 gdamore
245 1.14 plunky atexit(reset_tio);
246 1.14 plunky }
247 1.4 plunky }
248 1.2 plunky } else {
249 1.2 plunky if (daemon(0, 0) < 0)
250 1.2 plunky err(EXIT_FAILURE, "daemon() failed");
251 1.1 gdamore }
252 1.1 gdamore
253 1.2 plunky /* catch signals */
254 1.2 plunky done = 0;
255 1.2 plunky (void)signal(SIGHUP, sighandler);
256 1.2 plunky (void)signal(SIGINT, sighandler);
257 1.2 plunky (void)signal(SIGPIPE, sighandler);
258 1.2 plunky (void)signal(SIGTERM, sighandler);
259 1.2 plunky
260 1.2 plunky openlog(getprogname(), LOG_PERROR | LOG_PID, LOG_DAEMON);
261 1.2 plunky syslog(LOG_INFO, "Starting on %s...", (tty ? tty : "stdio"));
262 1.2 plunky
263 1.11 plunky n = MAX(tty_in, rfcomm) + 1;
264 1.2 plunky while (!done) {
265 1.2 plunky FD_ZERO(&rdset);
266 1.2 plunky FD_SET(tty_in, &rdset);
267 1.2 plunky FD_SET(rfcomm, &rdset);
268 1.1 gdamore
269 1.2 plunky if (select(n, &rdset, NULL, NULL, NULL) < 0) {
270 1.1 gdamore if (errno == EINTR)
271 1.1 gdamore continue;
272 1.1 gdamore
273 1.2 plunky syslog(LOG_ERR, "select error: %m");
274 1.1 gdamore exit(EXIT_FAILURE);
275 1.1 gdamore }
276 1.1 gdamore
277 1.2 plunky if (FD_ISSET(tty_in, &rdset))
278 1.2 plunky copy_data(tty_in, rfcomm);
279 1.1 gdamore
280 1.2 plunky if (FD_ISSET(rfcomm, &rdset))
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.2 plunky exit(EXIT_SUCCESS);
286 1.2 plunky }
287 1.1 gdamore
288 1.2 plunky 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.1 gdamore * Construct master PTY name. The slave tty name must be less then
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.2 plunky if (strlen(tty) >= sizeof(pty))
302 1.2 plunky errx(EXIT_FAILURE, ": tty name too long");
303 1.1 gdamore
304 1.1 gdamore strlcpy(pty, tty, sizeof(pty));
305 1.1 gdamore slash = strrchr(pty, '/');
306 1.2 plunky if (slash == NULL || slash[1] == '\0')
307 1.2 plunky errx(EXIT_FAILURE, "%s: invalid tty", tty);
308 1.1 gdamore
309 1.1 gdamore slash[1] = 'p';
310 1.2 plunky if (strcmp(pty, tty) == 0)
311 1.2 plunky errx(EXIT_FAILURE, "Master and slave tty are the same (%s)", tty);
312 1.1 gdamore
313 1.2 plunky if ((master = open(pty, O_RDWR, 0)) < 0)
314 1.2 plunky err(EXIT_FAILURE, "%s", pty);
315 1.1 gdamore
316 1.1 gdamore /*
317 1.1 gdamore * Slave TTY
318 1.1 gdamore */
319 1.1 gdamore
320 1.1 gdamore if ((gr = getgrnam("tty")) != NULL)
321 1.1 gdamore ttygid = gr->gr_gid;
322 1.1 gdamore else
323 1.1 gdamore ttygid = (gid_t)-1;
324 1.1 gdamore
325 1.2 plunky (void)chown(tty, getuid(), ttygid);
326 1.2 plunky (void)chmod(tty, S_IRUSR | S_IWUSR | S_IWGRP);
327 1.2 plunky (void)revoke(tty);
328 1.2 plunky
329 1.2 plunky return master;
330 1.2 plunky }
331 1.1 gdamore
332 1.2 plunky int
333 1.12 plunky open_client(bdaddr_t *laddr, bdaddr_t *raddr, int lm, uintmax_t psm, const char *service)
334 1.2 plunky {
335 1.2 plunky struct sockaddr_bt sa;
336 1.3 plunky struct service *s;
337 1.2 plunky struct linger l;
338 1.3 plunky char *ep;
339 1.10 plunky int fd, error;
340 1.10 plunky uintmax_t channel;
341 1.3 plunky
342 1.3 plunky for (s = services ; ; s++) {
343 1.3 plunky if (s->name == NULL) {
344 1.5 plunky channel = strtoul(service, &ep, 10);
345 1.10 plunky if (*ep != '\0')
346 1.10 plunky errx(EXIT_FAILURE, "Unknown service: %s", service);
347 1.3 plunky
348 1.3 plunky break;
349 1.3 plunky }
350 1.3 plunky
351 1.3 plunky if (strcasecmp(s->name, service) == 0) {
352 1.12 plunky error = service_search(laddr, raddr, s->class, &psm, &channel);
353 1.10 plunky if (error != 0)
354 1.10 plunky errx(EXIT_FAILURE, "%s: %s", s->name, strerror(error));
355 1.3 plunky
356 1.3 plunky break;
357 1.3 plunky }
358 1.3 plunky }
359 1.1 gdamore
360 1.10 plunky if (channel < RFCOMM_CHANNEL_MIN || channel > RFCOMM_CHANNEL_MAX)
361 1.10 plunky errx(EXIT_FAILURE, "Invalid channel %"PRIuMAX, channel);
362 1.10 plunky
363 1.12 plunky if (L2CAP_PSM_INVALID(psm))
364 1.12 plunky errx(EXIT_FAILURE, "Invalid PSM 0x%04"PRIxMAX, psm);
365 1.12 plunky
366 1.2 plunky memset(&sa, 0, sizeof(sa));
367 1.2 plunky sa.bt_len = sizeof(sa);
368 1.2 plunky sa.bt_family = AF_BLUETOOTH;
369 1.2 plunky bdaddr_copy(&sa.bt_bdaddr, laddr);
370 1.2 plunky
371 1.2 plunky fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
372 1.2 plunky if (fd < 0)
373 1.2 plunky err(EXIT_FAILURE, "socket()");
374 1.2 plunky
375 1.2 plunky if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
376 1.2 plunky err(EXIT_FAILURE, "bind(%s)", bt_ntoa(laddr, NULL));
377 1.2 plunky
378 1.2 plunky memset(&l, 0, sizeof(l));
379 1.2 plunky l.l_onoff = 1;
380 1.2 plunky l.l_linger = 5;
381 1.2 plunky if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0)
382 1.2 plunky err(EXIT_FAILURE, "linger()");
383 1.2 plunky
384 1.7 plunky if (setsockopt(fd, BTPROTO_RFCOMM, SO_RFCOMM_LM, &lm, sizeof(lm)) < 0)
385 1.7 plunky err(EXIT_FAILURE, "link mode");
386 1.7 plunky
387 1.12 plunky sa.bt_psm = psm;
388 1.2 plunky sa.bt_channel = channel;
389 1.2 plunky bdaddr_copy(&sa.bt_bdaddr, raddr);
390 1.2 plunky
391 1.2 plunky if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
392 1.12 plunky err(EXIT_FAILURE, "connect(%s, 0x%04"PRIxMAX", %"PRIuMAX")",
393 1.12 plunky bt_ntoa(raddr, NULL), psm, channel);
394 1.1 gdamore
395 1.2 plunky return fd;
396 1.2 plunky }
397 1.1 gdamore
398 1.2 plunky int
399 1.12 plunky open_server(bdaddr_t *laddr, uint16_t psm, uint8_t channel, int lm, const char *service)
400 1.1 gdamore {
401 1.10 plunky uint8_t buffer[256];
402 1.2 plunky struct sockaddr_bt sa;
403 1.10 plunky struct service *s;
404 1.2 plunky struct linger l;
405 1.2 plunky socklen_t len;
406 1.10 plunky sdp_session_t ss;
407 1.10 plunky sdp_data_t rec;
408 1.10 plunky int sv, fd;
409 1.10 plunky
410 1.10 plunky for (s = services; ; s++) {
411 1.10 plunky if (s->name == NULL)
412 1.10 plunky usage();
413 1.1 gdamore
414 1.10 plunky if (strcasecmp(s->name, service) == 0)
415 1.10 plunky break;
416 1.10 plunky }
417 1.2 plunky
418 1.10 plunky /* Open server socket */
419 1.2 plunky sv = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
420 1.2 plunky if (sv < 0)
421 1.2 plunky err(EXIT_FAILURE, "socket()");
422 1.2 plunky
423 1.10 plunky memset(&sa, 0, sizeof(sa));
424 1.10 plunky sa.bt_len = sizeof(sa);
425 1.10 plunky sa.bt_family = AF_BLUETOOTH;
426 1.12 plunky sa.bt_psm = psm;
427 1.10 plunky sa.bt_channel = channel;
428 1.10 plunky bdaddr_copy(&sa.bt_bdaddr, laddr);
429 1.2 plunky if (bind(sv, (struct sockaddr *)&sa, sizeof(sa)) < 0)
430 1.12 plunky err(EXIT_FAILURE, "bind(%s, 0x%04x, %d)",
431 1.12 plunky bt_ntoa(laddr, NULL), psm, channel);
432 1.2 plunky
433 1.7 plunky if (setsockopt(sv, BTPROTO_RFCOMM, SO_RFCOMM_LM, &lm, sizeof(lm)) < 0)
434 1.7 plunky err(EXIT_FAILURE, "link mode");
435 1.7 plunky
436 1.2 plunky if (listen(sv, 1) < 0)
437 1.2 plunky err(EXIT_FAILURE, "listen()");
438 1.2 plunky
439 1.13 plunky len = sizeof(sa);
440 1.13 plunky if (getsockname(sv, (struct sockaddr *)&sa, &len) < 0)
441 1.13 plunky err(EXIT_FAILURE, "getsockname()");
442 1.13 plunky if (len != sizeof(sa))
443 1.13 plunky errx(EXIT_FAILURE, "getsockname()");
444 1.13 plunky
445 1.10 plunky /* Build SDP record */
446 1.10 plunky rec.next = buffer;
447 1.10 plunky rec.end = buffer + sizeof(buffer);
448 1.10 plunky
449 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_SERVICE_RECORD_HANDLE);
450 1.10 plunky sdp_put_uint32(&rec, 0x00000000);
451 1.10 plunky
452 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_SERVICE_CLASS_ID_LIST);
453 1.10 plunky sdp_put_seq(&rec, 3);
454 1.10 plunky sdp_put_uuid16(&rec, s->class);
455 1.10 plunky
456 1.12 plunky len = (psm == L2CAP_PSM_RFCOMM ? 0 : 3);
457 1.12 plunky
458 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
459 1.12 plunky sdp_put_seq(&rec, 12 + len);
460 1.12 plunky sdp_put_seq(&rec, 3 + len);
461 1.10 plunky sdp_put_uuid16(&rec, SDP_UUID_PROTOCOL_L2CAP);
462 1.12 plunky if (len > 0)
463 1.12 plunky sdp_put_uint16(&rec, psm);
464 1.10 plunky sdp_put_seq(&rec, 5);
465 1.10 plunky sdp_put_uuid16(&rec, SDP_UUID_PROTOCOL_RFCOMM);
466 1.13 plunky sdp_put_uint8(&rec, sa.bt_channel);
467 1.10 plunky
468 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_BROWSE_GROUP_LIST);
469 1.10 plunky sdp_put_seq(&rec, 3);
470 1.10 plunky sdp_put_uuid16(&rec, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);
471 1.10 plunky
472 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
473 1.10 plunky sdp_put_seq(&rec, 9);
474 1.10 plunky sdp_put_uint16(&rec, 0x656e); /* "en" */
475 1.10 plunky sdp_put_uint16(&rec, 106); /* UTF-8 */
476 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID);
477 1.10 plunky
478 1.10 plunky if (s->class == SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP) {
479 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_SERVICE_AVAILABILITY);
480 1.10 plunky sdp_put_uint8(&rec, 0x00);
481 1.10 plunky }
482 1.10 plunky
483 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
484 1.10 plunky sdp_put_seq(&rec, 8);
485 1.10 plunky sdp_put_seq(&rec, 6);
486 1.10 plunky sdp_put_uuid16(&rec, s->class);
487 1.10 plunky sdp_put_uint16(&rec, 0x0100); /* v1.0 */
488 1.10 plunky
489 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID
490 1.10 plunky + SDP_ATTR_SERVICE_NAME_OFFSET);
491 1.10 plunky sdp_put_str(&rec, s->description, -1);
492 1.10 plunky
493 1.10 plunky if (s->class == SDP_SERVICE_CLASS_DIALUP_NETWORKING) {
494 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_AUDIO_FEEDBACK_SUPPORT);
495 1.10 plunky sdp_put_bool(&rec, false);
496 1.10 plunky }
497 1.1 gdamore
498 1.10 plunky #if 0
499 1.10 plunky if (s->class == SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP) {
500 1.10 plunky sdp_put_uint16(&rec, SDP_ATTR_IP_SUBNET); /* TODO */
501 1.10 plunky sdp_put_str(&rec, "0.0.0.0/0", -1);
502 1.1 gdamore }
503 1.10 plunky #endif
504 1.1 gdamore
505 1.10 plunky rec.end = rec.next;
506 1.10 plunky rec.next = buffer;
507 1.2 plunky
508 1.10 plunky /* Register service with SDP server */
509 1.2 plunky ss = sdp_open_local(NULL);
510 1.10 plunky if (ss == NULL)
511 1.2 plunky err(EXIT_FAILURE, "sdp_open_local");
512 1.2 plunky
513 1.10 plunky if (!sdp_record_insert(ss, laddr, NULL, &rec))
514 1.10 plunky err(EXIT_FAILURE, "sdp_record_insert");
515 1.2 plunky
516 1.10 plunky /* Accept client connection */
517 1.2 plunky len = sizeof(sa);
518 1.2 plunky fd = accept(sv, (struct sockaddr *)&sa, &len);
519 1.2 plunky if (fd < 0)
520 1.2 plunky err(EXIT_FAILURE, "accept");
521 1.2 plunky
522 1.2 plunky memset(&l, 0, sizeof(l));
523 1.2 plunky l.l_onoff = 1;
524 1.2 plunky l.l_linger = 5;
525 1.2 plunky if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0)
526 1.2 plunky err(EXIT_FAILURE, "linger()");
527 1.2 plunky
528 1.2 plunky close(sv);
529 1.2 plunky return fd;
530 1.2 plunky }
531 1.1 gdamore
532 1.2 plunky void
533 1.2 plunky copy_data(int src, int dst)
534 1.1 gdamore {
535 1.2 plunky static char buf[BUFSIZ];
536 1.2 plunky ssize_t nr, nw, off;
537 1.1 gdamore
538 1.2 plunky while ((nr = read(src, buf, sizeof(buf))) == -1) {
539 1.2 plunky if (errno != EINTR) {
540 1.2 plunky syslog(LOG_ERR, "read failed: %m");
541 1.2 plunky exit(EXIT_FAILURE);
542 1.2 plunky }
543 1.2 plunky }
544 1.1 gdamore
545 1.3 plunky if (nr == 0) /* reached EOF */
546 1.3 plunky done++;
547 1.3 plunky
548 1.2 plunky for (off = 0 ; nr ; nr -= nw, off += nw) {
549 1.2 plunky if ((nw = write(dst, buf + off, (size_t)nr)) == -1) {
550 1.2 plunky syslog(LOG_ERR, "write failed: %m");
551 1.2 plunky exit(EXIT_FAILURE);
552 1.1 gdamore }
553 1.1 gdamore }
554 1.2 plunky }
555 1.1 gdamore
556 1.10 plunky int
557 1.12 plunky service_search(bdaddr_t const *laddr, bdaddr_t const *raddr,
558 1.12 plunky uint16_t class, uintmax_t *psm, uintmax_t *channel)
559 1.10 plunky {
560 1.10 plunky uint8_t buffer[6]; /* SSP (3 bytes) + AIL (3 bytes) */
561 1.10 plunky sdp_session_t ss;
562 1.10 plunky sdp_data_t ail, ssp, rsp, rec, value, pdl, seq;
563 1.10 plunky uint16_t attr;
564 1.10 plunky bool rv;
565 1.10 plunky
566 1.10 plunky seq.next = buffer;
567 1.10 plunky seq.end = buffer + sizeof(buffer);
568 1.10 plunky
569 1.10 plunky /*
570 1.10 plunky * build ServiceSearchPattern (3 bytes)
571 1.10 plunky */
572 1.10 plunky ssp.next = seq.next;
573 1.10 plunky sdp_put_uuid16(&seq, class);
574 1.10 plunky ssp.end = seq.next;
575 1.10 plunky
576 1.10 plunky /*
577 1.10 plunky * build AttributeIDList (3 bytes)
578 1.10 plunky */
579 1.10 plunky ail.next = seq.next;
580 1.10 plunky sdp_put_uint16(&seq, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
581 1.10 plunky ail.end = seq.next;
582 1.10 plunky
583 1.10 plunky ss = sdp_open(laddr, raddr);
584 1.10 plunky if (ss == NULL)
585 1.10 plunky return errno;
586 1.10 plunky
587 1.10 plunky rv = sdp_service_search_attribute(ss, &ssp, &ail, &rsp);
588 1.10 plunky if (!rv) {
589 1.10 plunky sdp_close(ss);
590 1.10 plunky return errno;
591 1.10 plunky }
592 1.10 plunky
593 1.10 plunky /*
594 1.10 plunky * The response will be a list of records that matched our
595 1.10 plunky * ServiceSearchPattern, where each record is a sequence
596 1.10 plunky * containing a single ProtocolDescriptorList attribute and
597 1.10 plunky * value
598 1.10 plunky *
599 1.10 plunky * seq
600 1.10 plunky * uint16 ProtocolDescriptorList
601 1.10 plunky * value
602 1.10 plunky * seq
603 1.10 plunky * uint16 ProtocolDescriptorList
604 1.10 plunky * value
605 1.10 plunky *
606 1.10 plunky * If the ProtocolDescriptorList describes a single stack,
607 1.10 plunky * the attribute value takes the form of a single Data Element
608 1.10 plunky * Sequence where each member is a protocol descriptor.
609 1.10 plunky *
610 1.10 plunky * seq
611 1.10 plunky * list
612 1.10 plunky *
613 1.10 plunky * If it is possible for more than one kind of protocol
614 1.10 plunky * stack to be used to gain access to the service, the
615 1.10 plunky * ProtocolDescriptorList takes the form of a Data Element
616 1.10 plunky * Alternative where each member is a Data Element Sequence
617 1.10 plunky * describing an alternative protocol stack.
618 1.10 plunky *
619 1.10 plunky * alt
620 1.10 plunky * seq
621 1.10 plunky * list
622 1.10 plunky * seq
623 1.10 plunky * list
624 1.10 plunky *
625 1.10 plunky * Each protocol stack description contains a sequence for each
626 1.10 plunky * protocol, where each sequence contains the protocol UUID as
627 1.10 plunky * the first element, and any ProtocolSpecificParameters. We are
628 1.12 plunky * interested in the L2CAP psm if provided, and the RFCOMM channel
629 1.12 plunky * number, stored as parameter#1 in each case.
630 1.10 plunky *
631 1.10 plunky * seq
632 1.10 plunky * uuid L2CAP
633 1.10 plunky * uint16 psm
634 1.10 plunky * seq
635 1.10 plunky * uuid RFCOMM
636 1.10 plunky * uint8 channel
637 1.10 plunky */
638 1.10 plunky
639 1.10 plunky rv = false;
640 1.10 plunky while (!rv && sdp_get_seq(&rsp, &rec)) {
641 1.10 plunky if (!sdp_get_attr(&rec, &attr, &value)
642 1.10 plunky || attr != SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST)
643 1.10 plunky continue;
644 1.10 plunky
645 1.10 plunky sdp_get_alt(&value, &value); /* strip any alt container */
646 1.10 plunky while (!rv && sdp_get_seq(&value, &pdl)) {
647 1.12 plunky *psm = L2CAP_PSM_RFCOMM;
648 1.10 plunky if (sdp_get_seq(&pdl, &seq)
649 1.10 plunky && sdp_match_uuid16(&seq, SDP_UUID_PROTOCOL_L2CAP)
650 1.12 plunky && (sdp_get_uint(&seq, psm) || true)
651 1.10 plunky && sdp_get_seq(&pdl, &seq)
652 1.10 plunky && sdp_match_uuid16(&seq, SDP_UUID_PROTOCOL_RFCOMM)
653 1.10 plunky && sdp_get_uint(&seq, channel))
654 1.10 plunky rv = true;
655 1.10 plunky }
656 1.10 plunky }
657 1.10 plunky
658 1.10 plunky sdp_close(ss);
659 1.10 plunky return (rv) ? 0 : ENOATTR;
660 1.10 plunky }
661 1.10 plunky
662 1.2 plunky void
663 1.2 plunky sighandler(int s)
664 1.2 plunky {
665 1.1 gdamore
666 1.2 plunky done++;
667 1.2 plunky }
668 1.2 plunky
669 1.2 plunky void
670 1.2 plunky reset_tio(void)
671 1.1 gdamore {
672 1.1 gdamore
673 1.2 plunky tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
674 1.2 plunky }
675 1.2 plunky
676 1.2 plunky void
677 1.1 gdamore usage(void)
678 1.1 gdamore {
679 1.7 plunky const char *cmd = getprogname();
680 1.2 plunky struct service *s;
681 1.2 plunky
682 1.12 plunky fprintf(stderr, "Usage: %s [-d device] [-m mode] [-p psm] [-s service] [-t tty]\n"
683 1.13 plunky " %*s {-a bdaddr | [-c channel]}\n"
684 1.2 plunky "\n"
685 1.2 plunky "Where:\n"
686 1.2 plunky "\t-a bdaddr remote device address\n"
687 1.3 plunky "\t-c channel local RFCOMM channel\n"
688 1.2 plunky "\t-d device local device address\n"
689 1.7 plunky "\t-m mode link mode\n"
690 1.12 plunky "\t-p psm protocol/service multiplexer\n"
691 1.3 plunky "\t-s service service class\n"
692 1.2 plunky "\t-t tty run in background using pty\n"
693 1.8 dsl "\n", cmd, (int)strlen(cmd), "");
694 1.2 plunky
695 1.3 plunky fprintf(stderr, "Known service classes:\n");
696 1.2 plunky for (s = services ; s->name != NULL ; s++)
697 1.3 plunky fprintf(stderr, "\t%-13s%s\n", s->name, s->description);
698 1.1 gdamore
699 1.1 gdamore exit(EXIT_FAILURE);
700 1.2 plunky }
701