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