1 /* $NetBSD: rfcomm_sppd.c,v 1.11 2009/05/21 14:44:01 plunky Exp $ */
4 * Copyright (c) 2006 Itronix Inc.
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.
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.
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.
32 * Copyright (c) 2009 The NetBSD Foundation, Inc.
33 * Copyright (c) 2007 Iain Hibbert
34 * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
35 * All rights reserved.
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in the
44 * documentation and/or other materials provided with the distribution.
46 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 #include <sys/cdefs.h>
60 __COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc.\
61 Copyright (c) 2007 Iain Hibbert.\
62 Copyright (c) 2006 Itronix, Inc.\
63 Copyright (c) 2003 Maksim Yevmenkin m_evmenkin@yahoo.com.\
64 All rights reserved.");
65 __RCSID("$NetBSD: rfcomm_sppd.c,v 1.11 2009/05/21 14:44:01 plunky Exp $");
67 #include <sys/param.h>
69 #include <bluetooth.h>
87 #include <netbt/rfcomm.h>
89 int open_tty(const char *);
90 int open_client(bdaddr_t
*, bdaddr_t
*, int, uintmax_t, const char *);
91 int open_server(bdaddr_t
*, uint16_t, uint8_t, int, const char *);
92 void copy_data(int, int);
93 int service_search(const bdaddr_t
*, const bdaddr_t
*, uint16_t, uintmax_t *, uintmax_t *);
98 int done
; /* got a signal */
99 struct termios tio
; /* stored termios for reset on exit */
103 const char * description
;
106 { "DUN", "Dialup Networking",
107 SDP_SERVICE_CLASS_DIALUP_NETWORKING
},
108 { "LAN", "LAN access using PPP",
109 SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP
},
110 { "SP", "Serial Port",
111 SDP_SERVICE_CLASS_SERIAL_PORT
},
116 main(int argc
, char *argv
[])
119 bdaddr_t laddr
, raddr
;
123 int lm
, n
, rfcomm
, tty_in
, tty_out
;
127 bdaddr_copy(&laddr
, BDADDR_ANY
);
128 bdaddr_copy(&raddr
, BDADDR_ANY
);
132 psm
= L2CAP_PSM_RFCOMM
;
135 /* Parse command line options */
136 while ((n
= getopt(argc
, argv
, "a:c:d:hm:p:s:t:")) != -1) {
138 case 'a': /* remote device address */
139 if (!bt_aton(optarg
, &raddr
)) {
140 struct hostent
*he
= NULL
;
142 if ((he
= bt_gethostbyname(optarg
)) == NULL
)
143 errx(EXIT_FAILURE
, "%s: %s", optarg
,
146 bdaddr_copy(&raddr
, (bdaddr_t
*)he
->h_addr
);
150 case 'c': /* RFCOMM channel */
151 channel
= strtoul(optarg
, &ep
, 10);
152 if (*ep
!= '\0' || channel
< 1 || channel
> 30)
153 errx(EXIT_FAILURE
, "Invalid channel: %s", optarg
);
157 case 'd': /* local device address */
158 if (!bt_devaddr(optarg
, &laddr
))
159 err(EXIT_FAILURE
, "%s", optarg
);
163 case 'm': /* Link Mode */
164 if (strcasecmp(optarg
, "auth") == 0)
166 else if (strcasecmp(optarg
, "encrypt") == 0)
167 lm
= RFCOMM_LM_ENCRYPT
;
168 else if (strcasecmp(optarg
, "secure") == 0)
169 lm
= RFCOMM_LM_SECURE
;
171 errx(EXIT_FAILURE
, "%s: unknown mode", optarg
);
176 psm
= strtoul(optarg
, &ep
, 0);
177 if (*ep
!= '\0' || L2CAP_PSM_INVALID(psm
))
178 errx(EXIT_FAILURE
, "Invalid PSM: %s", optarg
);
182 case 's': /* service class */
186 case 't': /* Slave TTY name */
187 if (optarg
[0] != '/')
188 asprintf(&tty
, "%s%s", _PATH_DEV
, optarg
);
203 * must have channel or remote address but not both
205 if ((channel
== 0 && bdaddr_any(&raddr
))
206 || (channel
!= 0 && !bdaddr_any(&raddr
)))
210 * grab ttys before we start the bluetooth
213 tty_in
= STDIN_FILENO
;
214 tty_out
= STDOUT_FILENO
;
216 tty_in
= open_tty(tty
);
222 rfcomm
= open_client(&laddr
, &raddr
, lm
, psm
, service
);
224 rfcomm
= open_server(&laddr
, psm
, channel
, lm
, service
);
227 * now we are ready to go, so either detach or maybe turn
228 * off some input processing, so that rfcomm_sppd can
229 * be used directly with stdio
232 if (tcgetattr(tty_in
, &t
) < 0)
233 err(EXIT_FAILURE
, "tcgetattr");
235 memcpy(&tio
, &t
, sizeof(tio
));
236 t
.c_lflag
&= ~(ECHO
| ICANON
);
237 t
.c_iflag
&= ~(ICRNL
);
239 if (memcmp(&tio
, &t
, sizeof(tio
))) {
240 if (tcsetattr(tty_in
, TCSANOW
, &t
) < 0)
241 err(EXIT_FAILURE
, "tcsetattr");
246 if (daemon(0, 0) < 0)
247 err(EXIT_FAILURE
, "daemon() failed");
252 (void)signal(SIGHUP
, sighandler
);
253 (void)signal(SIGINT
, sighandler
);
254 (void)signal(SIGPIPE
, sighandler
);
255 (void)signal(SIGTERM
, sighandler
);
257 openlog(getprogname(), LOG_PERROR
| LOG_PID
, LOG_DAEMON
);
258 syslog(LOG_INFO
, "Starting on %s...", (tty
? tty
: "stdio"));
260 n
= MAX(tty_in
, rfcomm
) + 1;
263 FD_SET(tty_in
, &rdset
);
264 FD_SET(rfcomm
, &rdset
);
266 if (select(n
, &rdset
, NULL
, NULL
, NULL
) < 0) {
270 syslog(LOG_ERR
, "select error: %m");
274 if (FD_ISSET(tty_in
, &rdset
))
275 copy_data(tty_in
, rfcomm
);
277 if (FD_ISSET(rfcomm
, &rdset
))
278 copy_data(rfcomm
, tty_out
);
281 syslog(LOG_INFO
, "Completed on %s", (tty
? tty
: "stdio"));
286 open_tty(const char *tty
)
288 char pty
[PATH_MAX
], *slash
;
289 struct group
*gr
= NULL
;
294 * Construct master PTY name. The slave tty name must be less then
295 * PATH_MAX characters in length, must contain '/' character and
296 * must not end with '/'.
298 if (strlen(tty
) >= sizeof(pty
))
299 errx(EXIT_FAILURE
, ": tty name too long");
301 strlcpy(pty
, tty
, sizeof(pty
));
302 slash
= strrchr(pty
, '/');
303 if (slash
== NULL
|| slash
[1] == '\0')
304 errx(EXIT_FAILURE
, "%s: invalid tty", tty
);
307 if (strcmp(pty
, tty
) == 0)
308 errx(EXIT_FAILURE
, "Master and slave tty are the same (%s)", tty
);
310 if ((master
= open(pty
, O_RDWR
, 0)) < 0)
311 err(EXIT_FAILURE
, "%s", pty
);
317 if ((gr
= getgrnam("tty")) != NULL
)
322 (void)chown(tty
, getuid(), ttygid
);
323 (void)chmod(tty
, S_IRUSR
| S_IWUSR
| S_IWGRP
);
330 open_client(bdaddr_t
*laddr
, bdaddr_t
*raddr
, int lm
, uintmax_t psm
, const char *service
)
332 struct sockaddr_bt sa
;
339 for (s
= services
; ; s
++) {
340 if (s
->name
== NULL
) {
341 channel
= strtoul(service
, &ep
, 10);
343 errx(EXIT_FAILURE
, "Unknown service: %s", service
);
348 if (strcasecmp(s
->name
, service
) == 0) {
349 error
= service_search(laddr
, raddr
, s
->class, &psm
, &channel
);
351 errx(EXIT_FAILURE
, "%s: %s", s
->name
, strerror(error
));
357 if (channel
< RFCOMM_CHANNEL_MIN
|| channel
> RFCOMM_CHANNEL_MAX
)
358 errx(EXIT_FAILURE
, "Invalid channel %"PRIuMAX
, channel
);
360 if (L2CAP_PSM_INVALID(psm
))
361 errx(EXIT_FAILURE
, "Invalid PSM 0x%04"PRIxMAX
, psm
);
363 memset(&sa
, 0, sizeof(sa
));
364 sa
.bt_len
= sizeof(sa
);
365 sa
.bt_family
= AF_BLUETOOTH
;
366 bdaddr_copy(&sa
.bt_bdaddr
, laddr
);
368 fd
= socket(PF_BLUETOOTH
, SOCK_STREAM
, BTPROTO_RFCOMM
);
370 err(EXIT_FAILURE
, "socket()");
372 if (bind(fd
, (struct sockaddr
*)&sa
, sizeof(sa
)) < 0)
373 err(EXIT_FAILURE
, "bind(%s)", bt_ntoa(laddr
, NULL
));
375 memset(&l
, 0, sizeof(l
));
378 if (setsockopt(fd
, SOL_SOCKET
, SO_LINGER
, &l
, sizeof(l
)) < 0)
379 err(EXIT_FAILURE
, "linger()");
381 if (setsockopt(fd
, BTPROTO_RFCOMM
, SO_RFCOMM_LM
, &lm
, sizeof(lm
)) < 0)
382 err(EXIT_FAILURE
, "link mode");
385 sa
.bt_channel
= channel
;
386 bdaddr_copy(&sa
.bt_bdaddr
, raddr
);
388 if (connect(fd
, (struct sockaddr
*)&sa
, sizeof(sa
)) < 0)
389 err(EXIT_FAILURE
, "connect(%s, 0x%04"PRIxMAX
", %"PRIuMAX
")",
390 bt_ntoa(raddr
, NULL
), psm
, channel
);
396 open_server(bdaddr_t
*laddr
, uint16_t psm
, uint8_t channel
, int lm
, const char *service
)
399 struct sockaddr_bt sa
;
407 for (s
= services
; ; s
++) {
411 if (strcasecmp(s
->name
, service
) == 0)
415 /* Open server socket */
416 sv
= socket(PF_BLUETOOTH
, SOCK_STREAM
, BTPROTO_RFCOMM
);
418 err(EXIT_FAILURE
, "socket()");
420 memset(&sa
, 0, sizeof(sa
));
421 sa
.bt_len
= sizeof(sa
);
422 sa
.bt_family
= AF_BLUETOOTH
;
424 sa
.bt_channel
= channel
;
425 bdaddr_copy(&sa
.bt_bdaddr
, laddr
);
426 if (bind(sv
, (struct sockaddr
*)&sa
, sizeof(sa
)) < 0)
427 err(EXIT_FAILURE
, "bind(%s, 0x%04x, %d)",
428 bt_ntoa(laddr
, NULL
), psm
, channel
);
430 if (setsockopt(sv
, BTPROTO_RFCOMM
, SO_RFCOMM_LM
, &lm
, sizeof(lm
)) < 0)
431 err(EXIT_FAILURE
, "link mode");
433 if (listen(sv
, 1) < 0)
434 err(EXIT_FAILURE
, "listen()");
436 /* Build SDP record */
438 rec
.end
= buffer
+ sizeof(buffer
);
440 sdp_put_uint16(&rec
, SDP_ATTR_SERVICE_RECORD_HANDLE
);
441 sdp_put_uint32(&rec
, 0x00000000);
443 sdp_put_uint16(&rec
, SDP_ATTR_SERVICE_CLASS_ID_LIST
);
444 sdp_put_seq(&rec
, 3);
445 sdp_put_uuid16(&rec
, s
->class);
447 len
= (psm
== L2CAP_PSM_RFCOMM
? 0 : 3);
449 sdp_put_uint16(&rec
, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST
);
450 sdp_put_seq(&rec
, 12 + len
);
451 sdp_put_seq(&rec
, 3 + len
);
452 sdp_put_uuid16(&rec
, SDP_UUID_PROTOCOL_L2CAP
);
454 sdp_put_uint16(&rec
, psm
);
455 sdp_put_seq(&rec
, 5);
456 sdp_put_uuid16(&rec
, SDP_UUID_PROTOCOL_RFCOMM
);
457 sdp_put_uint8(&rec
, channel
);
459 sdp_put_uint16(&rec
, SDP_ATTR_BROWSE_GROUP_LIST
);
460 sdp_put_seq(&rec
, 3);
461 sdp_put_uuid16(&rec
, SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP
);
463 sdp_put_uint16(&rec
, SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST
);
464 sdp_put_seq(&rec
, 9);
465 sdp_put_uint16(&rec
, 0x656e); /* "en" */
466 sdp_put_uint16(&rec
, 106); /* UTF-8 */
467 sdp_put_uint16(&rec
, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID
);
469 if (s
->class == SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP
) {
470 sdp_put_uint16(&rec
, SDP_ATTR_SERVICE_AVAILABILITY
);
471 sdp_put_uint8(&rec
, 0x00);
474 sdp_put_uint16(&rec
, SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST
);
475 sdp_put_seq(&rec
, 8);
476 sdp_put_seq(&rec
, 6);
477 sdp_put_uuid16(&rec
, s
->class);
478 sdp_put_uint16(&rec
, 0x0100); /* v1.0 */
480 sdp_put_uint16(&rec
, SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID
481 + SDP_ATTR_SERVICE_NAME_OFFSET
);
482 sdp_put_str(&rec
, s
->description
, -1);
484 if (s
->class == SDP_SERVICE_CLASS_DIALUP_NETWORKING
) {
485 sdp_put_uint16(&rec
, SDP_ATTR_AUDIO_FEEDBACK_SUPPORT
);
486 sdp_put_bool(&rec
, false);
490 if (s
->class == SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP
) {
491 sdp_put_uint16(&rec
, SDP_ATTR_IP_SUBNET
); /* TODO */
492 sdp_put_str(&rec
, "0.0.0.0/0", -1);
499 /* Register service with SDP server */
500 ss
= sdp_open_local(NULL
);
502 err(EXIT_FAILURE
, "sdp_open_local");
504 if (!sdp_record_insert(ss
, laddr
, NULL
, &rec
))
505 err(EXIT_FAILURE
, "sdp_record_insert");
507 /* Accept client connection */
509 fd
= accept(sv
, (struct sockaddr
*)&sa
, &len
);
511 err(EXIT_FAILURE
, "accept");
513 memset(&l
, 0, sizeof(l
));
516 if (setsockopt(fd
, SOL_SOCKET
, SO_LINGER
, &l
, sizeof(l
)) < 0)
517 err(EXIT_FAILURE
, "linger()");
524 copy_data(int src
, int dst
)
526 static char buf
[BUFSIZ
];
529 while ((nr
= read(src
, buf
, sizeof(buf
))) == -1) {
530 if (errno
!= EINTR
) {
531 syslog(LOG_ERR
, "read failed: %m");
536 if (nr
== 0) /* reached EOF */
539 for (off
= 0 ; nr
; nr
-= nw
, off
+= nw
) {
540 if ((nw
= write(dst
, buf
+ off
, (size_t)nr
)) == -1) {
541 syslog(LOG_ERR
, "write failed: %m");
548 service_search(bdaddr_t
const *laddr
, bdaddr_t
const *raddr
,
549 uint16_t class, uintmax_t *psm
, uintmax_t *channel
)
551 uint8_t buffer
[6]; /* SSP (3 bytes) + AIL (3 bytes) */
553 sdp_data_t ail
, ssp
, rsp
, rec
, value
, pdl
, seq
;
558 seq
.end
= buffer
+ sizeof(buffer
);
561 * build ServiceSearchPattern (3 bytes)
564 sdp_put_uuid16(&seq
, class);
568 * build AttributeIDList (3 bytes)
571 sdp_put_uint16(&seq
, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST
);
574 ss
= sdp_open(laddr
, raddr
);
578 rv
= sdp_service_search_attribute(ss
, &ssp
, &ail
, &rsp
);
585 * The response will be a list of records that matched our
586 * ServiceSearchPattern, where each record is a sequence
587 * containing a single ProtocolDescriptorList attribute and
591 * uint16 ProtocolDescriptorList
594 * uint16 ProtocolDescriptorList
597 * If the ProtocolDescriptorList describes a single stack,
598 * the attribute value takes the form of a single Data Element
599 * Sequence where each member is a protocol descriptor.
604 * If it is possible for more than one kind of protocol
605 * stack to be used to gain access to the service, the
606 * ProtocolDescriptorList takes the form of a Data Element
607 * Alternative where each member is a Data Element Sequence
608 * describing an alternative protocol stack.
616 * Each protocol stack description contains a sequence for each
617 * protocol, where each sequence contains the protocol UUID as
618 * the first element, and any ProtocolSpecificParameters. We are
619 * interested in the L2CAP psm if provided, and the RFCOMM channel
620 * number, stored as parameter#1 in each case.
631 while (!rv
&& sdp_get_seq(&rsp
, &rec
)) {
632 if (!sdp_get_attr(&rec
, &attr
, &value
)
633 || attr
!= SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST
)
636 sdp_get_alt(&value
, &value
); /* strip any alt container */
637 while (!rv
&& sdp_get_seq(&value
, &pdl
)) {
638 *psm
= L2CAP_PSM_RFCOMM
;
639 if (sdp_get_seq(&pdl
, &seq
)
640 && sdp_match_uuid16(&seq
, SDP_UUID_PROTOCOL_L2CAP
)
641 && (sdp_get_uint(&seq
, psm
) || true)
642 && sdp_get_seq(&pdl
, &seq
)
643 && sdp_match_uuid16(&seq
, SDP_UUID_PROTOCOL_RFCOMM
)
644 && sdp_get_uint(&seq
, channel
))
650 return (rv
) ? 0 : ENOATTR
;
664 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &tio
);
670 const char *cmd
= getprogname();
673 fprintf(stderr
, "Usage: %s [-d device] [-m mode] [-p psm] [-s service] [-t tty]\n"
674 " %*s {-a bdaddr | -c channel}\n"
677 "\t-a bdaddr remote device address\n"
678 "\t-c channel local RFCOMM channel\n"
679 "\t-d device local device address\n"
680 "\t-m mode link mode\n"
681 "\t-p psm protocol/service multiplexer\n"
682 "\t-s service service class\n"
683 "\t-t tty run in background using pty\n"
684 "\n", cmd
, (int)strlen(cmd
), "");
686 fprintf(stderr
, "Known service classes:\n");
687 for (s
= services
; s
->name
!= NULL
; s
++)
688 fprintf(stderr
, "\t%-13s%s\n", s
->name
, s
->description
);