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