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