rfcomm_sppd.c revision 1.8 1 /* $NetBSD: rfcomm_sppd.c,v 1.8 2007/04/21 10:39:30 dsl Exp $ */
2
3 /*-
4 * Copyright (c) 2006 Itronix Inc.
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 * 3. The name of Itronix Inc. may not be used to endorse
16 * or promote products derived from this software without specific
17 * prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31 /*
32 * rfcomm_sppd.c
33 *
34 * Copyright (c) 2007 Iain Hibbert
35 * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin (at) yahoo.com>
36 * All rights reserved.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57 * SUCH DAMAGE.
58 */
59
60 #include <sys/cdefs.h>
61 __COPYRIGHT("@(#) Copyright (c) 2007 Iain Hibbert\n"
62 "@(#) Copyright (c) 2006 Itronix, Inc.\n"
63 "@(#) Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin (at) yahoo.com>\n"
64 "All rights reserved.\n");
65 __RCSID("$NetBSD: rfcomm_sppd.c,v 1.8 2007/04/21 10:39:30 dsl Exp $");
66
67 #include <bluetooth.h>
68 #include <ctype.h>
69 #include <err.h>
70 #include <errno.h>
71 #include <fcntl.h>
72 #include <grp.h>
73 #include <limits.h>
74 #include <paths.h>
75 #include <sdp.h>
76 #include <signal.h>
77 #include <stdarg.h>
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <string.h>
81 #include <syslog.h>
82 #include <termios.h>
83 #include <unistd.h>
84
85 #include <netbt/rfcomm.h>
86
87 #include "rfcomm_sdp.h"
88
89 #define max(a, b) ((a) > (b) ? (a) : (b))
90
91 int open_tty(const char *);
92 int open_client(bdaddr_t *, bdaddr_t *, int, const char *);
93 int open_server(bdaddr_t *, uint8_t, int, const char *);
94 void copy_data(int, int);
95 void sighandler(int);
96 void usage(void);
97 void reset_tio(void);
98
99 int done; /* got a signal */
100 struct termios tio; /* stored termios for reset on exit */
101
102 struct service {
103 const char *name;
104 const char *description;
105 uint16_t class;
106 int pdulen;
107 } services[] = {
108 { "DUN", "Dialup Networking",
109 SDP_SERVICE_CLASS_DIALUP_NETWORKING,
110 sizeof(struct sdp_dun_profile)
111 },
112 { "LAN", "Lan access using PPP",
113 SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
114 sizeof(struct sdp_lan_profile)
115 },
116 { "SP", "Serial Port",
117 SDP_SERVICE_CLASS_SERIAL_PORT,
118 sizeof(struct sdp_sp_profile)
119 },
120 { NULL, NULL,
121 0,
122 0
123 }
124 };
125
126 int
127 main(int argc, char *argv[])
128 {
129 struct termios t;
130 bdaddr_t laddr, raddr;
131 fd_set rdset;
132 const char *service;
133 char *ep, *tty;
134 int lm, n, rfcomm, tty_in, tty_out;
135 uint8_t channel;
136
137 bdaddr_copy(&laddr, BDADDR_ANY);
138 bdaddr_copy(&raddr, BDADDR_ANY);
139 service = "SP";
140 tty = NULL;
141 channel = 0;
142 lm = 0;
143
144 /* Parse command line options */
145 while ((n = getopt(argc, argv, "a:c:d:hm:s:t:")) != -1) {
146 switch (n) {
147 case 'a': /* remote device address */
148 if (!bt_aton(optarg, &raddr)) {
149 struct hostent *he = NULL;
150
151 if ((he = bt_gethostbyname(optarg)) == NULL)
152 errx(EXIT_FAILURE, "%s: %s", optarg,
153 hstrerror(h_errno));
154
155 bdaddr_copy(&raddr, (bdaddr_t *)he->h_addr);
156 }
157 break;
158
159 case 'c': /* RFCOMM channel */
160 channel = strtoul(optarg, &ep, 10);
161 if (*ep != '\0' || channel < 1 || channel > 30)
162 errx(EXIT_FAILURE, "Invalid channel: %s", optarg);
163
164 break;
165
166 case 'd': /* local device address */
167 if (!bt_devaddr(optarg, &laddr))
168 err(EXIT_FAILURE, "%s", optarg);
169
170 break;
171
172 case 'm': /* Link Mode */
173 if (strcasecmp(optarg, "auth") == 0)
174 lm = RFCOMM_LM_AUTH;
175 else if (strcasecmp(optarg, "encrypt") == 0)
176 lm = RFCOMM_LM_ENCRYPT;
177 else if (strcasecmp(optarg, "secure") == 0)
178 lm = RFCOMM_LM_SECURE;
179 else
180 errx(EXIT_FAILURE, "%s: unknown mode", optarg);
181
182 break;
183
184 case 's': /* service class */
185 service = optarg;
186 break;
187
188 case 't': /* Slave TTY name */
189 if (optarg[0] != '/')
190 asprintf(&tty, "%s%s", _PATH_DEV, optarg);
191 else
192 tty = optarg;
193
194 break;
195
196 case 'h':
197 default:
198 usage();
199 /* NOT REACHED */
200 }
201 }
202
203 /*
204 * validate options:
205 * must have channel or remote address but not both
206 */
207 if ((channel == 0 && bdaddr_any(&raddr))
208 || (channel != 0 && !bdaddr_any(&raddr)))
209 usage();
210
211 /*
212 * grab ttys before we start the bluetooth
213 */
214 if (tty == NULL) {
215 tty_in = STDIN_FILENO;
216 tty_out = STDOUT_FILENO;
217 } else {
218 tty_in = open_tty(tty);
219 tty_out = tty_in;
220 }
221
222 /* open RFCOMM */
223 if (channel == 0)
224 rfcomm = open_client(&laddr, &raddr, lm, service);
225 else
226 rfcomm = open_server(&laddr, channel, lm, service);
227
228 /*
229 * now we are ready to go, so either detach or maybe turn
230 * off some input processing, so that rfcomm_sppd can
231 * be used directly with stdio
232 */
233 if (tty == NULL) {
234 if (tcgetattr(tty_in, &t) < 0)
235 err(EXIT_FAILURE, "tcgetattr");
236
237 memcpy(&tio, &t, sizeof(tio));
238 t.c_lflag &= ~(ECHO | ICANON);
239 t.c_iflag &= ~(ICRNL);
240
241 if (memcmp(&tio, &t, sizeof(tio))) {
242 if (tcsetattr(tty_in, TCSANOW, &t) < 0)
243 err(EXIT_FAILURE, "tcsetattr");
244
245 atexit(reset_tio);
246 }
247 } else {
248 if (daemon(0, 0) < 0)
249 err(EXIT_FAILURE, "daemon() failed");
250 }
251
252 /* catch signals */
253 done = 0;
254 (void)signal(SIGHUP, sighandler);
255 (void)signal(SIGINT, sighandler);
256 (void)signal(SIGPIPE, sighandler);
257 (void)signal(SIGTERM, sighandler);
258
259 openlog(getprogname(), LOG_PERROR | LOG_PID, LOG_DAEMON);
260 syslog(LOG_INFO, "Starting on %s...", (tty ? tty : "stdio"));
261
262 n = max(tty_in, rfcomm) + 1;
263 while (!done) {
264 FD_ZERO(&rdset);
265 FD_SET(tty_in, &rdset);
266 FD_SET(rfcomm, &rdset);
267
268 if (select(n, &rdset, NULL, NULL, NULL) < 0) {
269 if (errno == EINTR)
270 continue;
271
272 syslog(LOG_ERR, "select error: %m");
273 exit(EXIT_FAILURE);
274 }
275
276 if (FD_ISSET(tty_in, &rdset))
277 copy_data(tty_in, rfcomm);
278
279 if (FD_ISSET(rfcomm, &rdset))
280 copy_data(rfcomm, tty_out);
281 }
282
283 syslog(LOG_INFO, "Completed on %s", (tty ? tty : "stdio"));
284 exit(EXIT_SUCCESS);
285 }
286
287 int
288 open_tty(const char *tty)
289 {
290 char pty[PATH_MAX], *slash;
291 struct group *gr = NULL;
292 gid_t ttygid;
293 int master;
294
295 /*
296 * Construct master PTY name. The slave tty name must be less then
297 * PATH_MAX characters in length, must contain '/' character and
298 * must not end with '/'.
299 */
300 if (strlen(tty) >= sizeof(pty))
301 errx(EXIT_FAILURE, ": tty name too long");
302
303 strlcpy(pty, tty, sizeof(pty));
304 slash = strrchr(pty, '/');
305 if (slash == NULL || slash[1] == '\0')
306 errx(EXIT_FAILURE, "%s: invalid tty", tty);
307
308 slash[1] = 'p';
309 if (strcmp(pty, tty) == 0)
310 errx(EXIT_FAILURE, "Master and slave tty are the same (%s)", tty);
311
312 if ((master = open(pty, O_RDWR, 0)) < 0)
313 err(EXIT_FAILURE, "%s", pty);
314
315 /*
316 * Slave TTY
317 */
318
319 if ((gr = getgrnam("tty")) != NULL)
320 ttygid = gr->gr_gid;
321 else
322 ttygid = (gid_t)-1;
323
324 (void)chown(tty, getuid(), ttygid);
325 (void)chmod(tty, S_IRUSR | S_IWUSR | S_IWGRP);
326 (void)revoke(tty);
327
328 return master;
329 }
330
331 int
332 open_client(bdaddr_t *laddr, bdaddr_t *raddr, int lm, const char *service)
333 {
334 struct sockaddr_bt sa;
335 struct service *s;
336 struct linger l;
337 char *ep;
338 int fd;
339 uint8_t channel;
340
341 for (s = services ; ; s++) {
342 if (s->name == NULL) {
343 channel = strtoul(service, &ep, 10);
344 if (*ep != '\0' || channel < 1 || channel > 30)
345 errx(EXIT_FAILURE, "Invalid service: %s", service);
346
347 break;
348 }
349
350 if (strcasecmp(s->name, service) == 0) {
351 if (rfcomm_channel_lookup(laddr, raddr, s->class, &channel, &errno) < 0)
352 err(EXIT_FAILURE, "%s", s->name);
353
354 break;
355 }
356 }
357
358 memset(&sa, 0, sizeof(sa));
359 sa.bt_len = sizeof(sa);
360 sa.bt_family = AF_BLUETOOTH;
361 bdaddr_copy(&sa.bt_bdaddr, laddr);
362
363 fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
364 if (fd < 0)
365 err(EXIT_FAILURE, "socket()");
366
367 if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
368 err(EXIT_FAILURE, "bind(%s)", bt_ntoa(laddr, NULL));
369
370 memset(&l, 0, sizeof(l));
371 l.l_onoff = 1;
372 l.l_linger = 5;
373 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0)
374 err(EXIT_FAILURE, "linger()");
375
376 if (setsockopt(fd, BTPROTO_RFCOMM, SO_RFCOMM_LM, &lm, sizeof(lm)) < 0)
377 err(EXIT_FAILURE, "link mode");
378
379 sa.bt_channel = channel;
380 bdaddr_copy(&sa.bt_bdaddr, raddr);
381
382 if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
383 err(EXIT_FAILURE, "connect(%s, %d)", bt_ntoa(raddr, NULL),
384 channel);
385
386 return fd;
387 }
388
389 /*
390 * In all the profiles we currently support registering, the channel
391 * is the first octet in the PDU, and it seems all the rest can be
392 * zero, so we just use an array of uint8_t big enough to store the
393 * largest, currently LAN. See <sdp.h> for definitions..
394 */
395 #define pdu_len sizeof(struct sdp_lan_profile)
396
397 int
398 open_server(bdaddr_t *laddr, uint8_t channel, int lm, const char *service)
399 {
400 struct sockaddr_bt sa;
401 struct linger l;
402 socklen_t len;
403 void *ss;
404 int sv, fd, n;
405 uint8_t pdu[pdu_len];
406
407 memset(&sa, 0, sizeof(sa));
408 sa.bt_len = sizeof(sa);
409 sa.bt_family = AF_BLUETOOTH;
410 bdaddr_copy(&sa.bt_bdaddr, laddr);
411 sa.bt_channel = channel;
412
413 sv = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
414 if (sv < 0)
415 err(EXIT_FAILURE, "socket()");
416
417 if (bind(sv, (struct sockaddr *)&sa, sizeof(sa)) < 0)
418 err(EXIT_FAILURE, "bind(%s, %d)", bt_ntoa(laddr, NULL),
419 channel);
420
421 if (setsockopt(sv, BTPROTO_RFCOMM, SO_RFCOMM_LM, &lm, sizeof(lm)) < 0)
422 err(EXIT_FAILURE, "link mode");
423
424 if (listen(sv, 1) < 0)
425 err(EXIT_FAILURE, "listen()");
426
427 /* Register service with SDP server */
428 for (n = 0 ; ; n++) {
429 if (services[n].name == NULL)
430 usage();
431
432 if (strcasecmp(services[n].name, service) == 0)
433 break;
434 }
435
436 memset(pdu, 0, pdu_len);
437 pdu[0] = channel;
438
439 ss = sdp_open_local(NULL);
440 if (ss == NULL || (errno = sdp_error(ss)) != 0)
441 err(EXIT_FAILURE, "sdp_open_local");
442
443 if (sdp_register_service(ss, services[n].class, laddr,
444 pdu, services[n].pdulen, NULL) != 0) {
445 errno = sdp_error(ss);
446 err(EXIT_FAILURE, "sdp_register_service");
447 }
448
449 len = sizeof(sa);
450 fd = accept(sv, (struct sockaddr *)&sa, &len);
451 if (fd < 0)
452 err(EXIT_FAILURE, "accept");
453
454 memset(&l, 0, sizeof(l));
455 l.l_onoff = 1;
456 l.l_linger = 5;
457 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0)
458 err(EXIT_FAILURE, "linger()");
459
460 close(sv);
461 return fd;
462 }
463
464 void
465 copy_data(int src, int dst)
466 {
467 static char buf[BUFSIZ];
468 ssize_t nr, nw, off;
469
470 while ((nr = read(src, buf, sizeof(buf))) == -1) {
471 if (errno != EINTR) {
472 syslog(LOG_ERR, "read failed: %m");
473 exit(EXIT_FAILURE);
474 }
475 }
476
477 if (nr == 0) /* reached EOF */
478 done++;
479
480 for (off = 0 ; nr ; nr -= nw, off += nw) {
481 if ((nw = write(dst, buf + off, (size_t)nr)) == -1) {
482 syslog(LOG_ERR, "write failed: %m");
483 exit(EXIT_FAILURE);
484 }
485 }
486 }
487
488 void
489 sighandler(int s)
490 {
491
492 done++;
493 }
494
495 void
496 reset_tio(void)
497 {
498
499 tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
500 }
501
502 void
503 usage(void)
504 {
505 const char *cmd = getprogname();
506 struct service *s;
507
508 fprintf(stderr, "Usage: %s [-d device] [-m mode] [-s service] [-t tty]\n"
509 " %*s {-a bdaddr | -c channel}\n"
510 "\n"
511 "Where:\n"
512 "\t-a bdaddr remote device address\n"
513 "\t-c channel local RFCOMM channel\n"
514 "\t-d device local device address\n"
515 "\t-m mode link mode\n"
516 "\t-s service service class\n"
517 "\t-t tty run in background using pty\n"
518 "\n", cmd, (int)strlen(cmd), "");
519
520 fprintf(stderr, "Known service classes:\n");
521 for (s = services ; s->name != NULL ; s++)
522 fprintf(stderr, "\t%-13s%s\n", s->name, s->description);
523
524 exit(EXIT_FAILURE);
525 }
526