Home | History | Annotate | Line # | Download | only in rfcomm_sppd
      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