xref: /netbsd-src/usr.sbin/btpand/btpand.c (revision db203c8401d4119c621968413a441a456c83bf80)
1*db203c84Splunky /*	$NetBSD: btpand.c,v 1.9 2024/06/04 06:24:58 plunky Exp $	*/
21fc74d21Splunky 
31fc74d21Splunky /*-
4ab1f45acSplunky  * Copyright (c) 2008-2009 Iain Hibbert
51fc74d21Splunky  * All rights reserved.
61fc74d21Splunky  *
71fc74d21Splunky  * Redistribution and use in source and binary forms, with or without
81fc74d21Splunky  * modification, are permitted provided that the following conditions
91fc74d21Splunky  * are met:
101fc74d21Splunky  * 1. Redistributions of source code must retain the above copyright
111fc74d21Splunky  *    notice, this list of conditions and the following disclaimer.
121fc74d21Splunky  * 2. Redistributions in binary form must reproduce the above copyright
131fc74d21Splunky  *    notice, this list of conditions and the following disclaimer in the
141fc74d21Splunky  *    documentation and/or other materials provided with the distribution.
151fc74d21Splunky  *
161fc74d21Splunky  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
171fc74d21Splunky  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
181fc74d21Splunky  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
191fc74d21Splunky  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
201fc74d21Splunky  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
211fc74d21Splunky  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
221fc74d21Splunky  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
231fc74d21Splunky  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
241fc74d21Splunky  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
251fc74d21Splunky  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
261fc74d21Splunky  */
271fc74d21Splunky 
281fc74d21Splunky #include <sys/cdefs.h>
29ab1f45acSplunky __COPYRIGHT("@(#) Copyright (c) 2008-2009 Iain Hibbert. All rights reserved.");
30*db203c84Splunky __RCSID("$NetBSD: btpand.c,v 1.9 2024/06/04 06:24:58 plunky Exp $");
311fc74d21Splunky 
321fc74d21Splunky #include <sys/wait.h>
331fc74d21Splunky 
341fc74d21Splunky #include <bluetooth.h>
351fc74d21Splunky #include <err.h>
361fc74d21Splunky #include <fcntl.h>
371fc74d21Splunky #include <paths.h>
381fc74d21Splunky #include <sdp.h>
391fc74d21Splunky #include <stdio.h>
401fc74d21Splunky #include <signal.h>
411fc74d21Splunky #include <stdlib.h>
421fc74d21Splunky #include <string.h>
431fc74d21Splunky #include <unistd.h>
441fc74d21Splunky 
451fc74d21Splunky #include "btpand.h"
461fc74d21Splunky 
471fc74d21Splunky /* global variables */
481fc74d21Splunky const char *	control_path;		/* -c <path> */
491fc74d21Splunky const char *	interface_name;		/* -i <ifname> */
50ab1f45acSplunky const char *	service_type;		/* -s <service> */
511fc74d21Splunky uint16_t	service_class;
52ab1f45acSplunky const char *	service_name;
53ab1f45acSplunky const char *	service_desc;
541fc74d21Splunky 
551fc74d21Splunky bdaddr_t	local_bdaddr;		/* -d <addr> */
561fc74d21Splunky bdaddr_t	remote_bdaddr;		/* -a <addr> */
571fc74d21Splunky uint16_t	l2cap_psm;		/* -p <psm> */
581fc74d21Splunky int		l2cap_mode;		/* -m <mode> */
591fc74d21Splunky int		server_limit;		/* -n <limit> */
601fc74d21Splunky 
611fc74d21Splunky static const struct {
62ab1f45acSplunky 	const char *	type;
631fc74d21Splunky 	uint16_t	class;
64ab1f45acSplunky 	const char *	name;
651fc74d21Splunky 	const char *	desc;
661fc74d21Splunky } services[] = {
67ab1f45acSplunky 	{ "PANU", SDP_SERVICE_CLASS_PANU,
68ab1f45acSplunky 	  "Personal Ad-hoc User Service",
69ab1f45acSplunky 	  "Personal Ad-hoc User Service"
70ab1f45acSplunky 	},
71ab1f45acSplunky 	{ "NAP",  SDP_SERVICE_CLASS_NAP,
72d339aae0Sandvar 	  "Network Access Point",
73ab1f45acSplunky 	  "Personal Ad-hoc Network Service"
74ab1f45acSplunky 	},
75ab1f45acSplunky 	{ "GN",	  SDP_SERVICE_CLASS_GN,
76ab1f45acSplunky 	  "Group Ad-hoc Network",
77ab1f45acSplunky 	  "Personal Group Ad-hoc Network Service"
78ab1f45acSplunky 	},
791fc74d21Splunky };
801fc74d21Splunky 
81dccf569eSjoerg __dead static void main_exit(int);
821fc74d21Splunky static void main_detach(void);
83dccf569eSjoerg __dead static void usage(void);
841fc74d21Splunky 
851fc74d21Splunky int
main(int argc,char * argv[])861fc74d21Splunky main(int argc, char *argv[])
871fc74d21Splunky {
881fc74d21Splunky 	unsigned long	ul;
891fc74d21Splunky 	char *		ep;
901fc74d21Splunky 	int		ch, status;
911fc74d21Splunky 
921fc74d21Splunky 	while ((ch = getopt(argc, argv, "a:c:d:i:l:m:p:S:s:")) != -1) {
931fc74d21Splunky 		switch (ch) {
941fc74d21Splunky 		case 'a': /* remote address */
951fc74d21Splunky 			if (!bt_aton(optarg, &remote_bdaddr)) {
961fc74d21Splunky 				struct hostent  *he;
971fc74d21Splunky 
981fc74d21Splunky 				if ((he = bt_gethostbyname(optarg)) == NULL)
991fc74d21Splunky 					errx(EXIT_FAILURE, "%s: %s",
1001fc74d21Splunky 					    optarg, hstrerror(h_errno));
1011fc74d21Splunky 
1021fc74d21Splunky 				bdaddr_copy(&remote_bdaddr, (bdaddr_t *)he->h_addr);
1031fc74d21Splunky 			}
1041fc74d21Splunky 
1051fc74d21Splunky 			break;
1061fc74d21Splunky 
1071fc74d21Splunky 		case 'c': /* control socket path */
1081fc74d21Splunky 			control_path = optarg;
1091fc74d21Splunky 			break;
1101fc74d21Splunky 
1111fc74d21Splunky 		case 'd': /* local address */
1121fc74d21Splunky 			if (!bt_devaddr(optarg, &local_bdaddr))
1131fc74d21Splunky 				err(EXIT_FAILURE, "%s", optarg);
1141fc74d21Splunky 
1151fc74d21Splunky 			break;
1161fc74d21Splunky 
1171fc74d21Splunky 		case 'i': /* tap interface name */
1181fc74d21Splunky 			if (strchr(optarg, '/') == NULL) {
1191fc74d21Splunky 				asprintf(&ep, "/dev/%s", optarg);
1201fc74d21Splunky 				interface_name = ep;
1211fc74d21Splunky 			} else {
1221fc74d21Splunky 				interface_name = optarg;
1231fc74d21Splunky 			}
1241fc74d21Splunky 
1251fc74d21Splunky 			break;
1261fc74d21Splunky 
1271fc74d21Splunky 		case 'l': /* limit server sessions */
1281fc74d21Splunky 			ul = strtoul(optarg, &ep, 10);
1291fc74d21Splunky 			if (*optarg == '\0' || *ep != '\0' || ul == 0)
1301fc74d21Splunky 				errx(EXIT_FAILURE, "%s: invalid session limit", optarg);
1311fc74d21Splunky 
1321fc74d21Splunky 			server_limit = ul;
1331fc74d21Splunky 			break;
1341fc74d21Splunky 
1351fc74d21Splunky 		case 'm': /* link mode */
1361fc74d21Splunky 			if (strcasecmp(optarg, "auth") == 0)
1371fc74d21Splunky 				l2cap_mode = L2CAP_LM_AUTH;
1381fc74d21Splunky 			else if (strcasecmp(optarg, "encrypt") == 0)
1391fc74d21Splunky 				l2cap_mode = L2CAP_LM_ENCRYPT;
1401fc74d21Splunky 			else if (strcasecmp(optarg, "secure") == 0)
1411fc74d21Splunky 				l2cap_mode = L2CAP_LM_SECURE;
1421fc74d21Splunky 			else if (strcasecmp(optarg, "none"))
1431fc74d21Splunky 				errx(EXIT_FAILURE, "%s: unknown mode", optarg);
1441fc74d21Splunky 
1451fc74d21Splunky 			break;
1461fc74d21Splunky 
1471fc74d21Splunky 		case 'p': /* protocol/service multiplexer */
1481fc74d21Splunky 			ul = strtoul(optarg, &ep, 0);
1491fc74d21Splunky 			if (*optarg == '\0' || *ep != '\0'
1501fc74d21Splunky 			    || ul > 0xffff || L2CAP_PSM_INVALID(ul))
1511fc74d21Splunky 				errx(EXIT_FAILURE, "%s: invalid PSM", optarg);
1521fc74d21Splunky 
153ab1f45acSplunky 			l2cap_psm = (uint16_t)ul;
1541fc74d21Splunky 			break;
1551fc74d21Splunky 
1561fc74d21Splunky 		case 's': /* service */
1571fc74d21Splunky 		case 'S': /* service (no SDP) */
158*db203c84Splunky 			for (ul = 0; ul < __arraycount(services); ul++) {
159*db203c84Splunky 				if (strcasecmp(optarg, services[ul].type) == 0)
160*db203c84Splunky 					break;
161*db203c84Splunky 			}
162*db203c84Splunky 
1631fc74d21Splunky 			if (ul == __arraycount(services))
1641fc74d21Splunky 				errx(EXIT_FAILURE, "%s: unknown service", optarg);
1651fc74d21Splunky 
166ab1f45acSplunky 			if (ch == 's') {
167ab1f45acSplunky 				service_type = services[ul].type;
1681fc74d21Splunky 				service_name = services[ul].name;
169ab1f45acSplunky 				service_desc = services[ul].desc;
170ab1f45acSplunky 			}
1711fc74d21Splunky 
1721fc74d21Splunky 			service_class = services[ul].class;
1731fc74d21Splunky 			break;
1741fc74d21Splunky 
1751fc74d21Splunky 		default:
1761fc74d21Splunky 			usage();
1771fc74d21Splunky 			/* NOTREACHED */
1781fc74d21Splunky 		}
1791fc74d21Splunky 	}
1801fc74d21Splunky 
1811fc74d21Splunky 	argc -= optind;
1821fc74d21Splunky 	argv += optind;
1831fc74d21Splunky 
1841fc74d21Splunky 	/* validate options */
1851fc74d21Splunky 	if (bdaddr_any(&local_bdaddr) || service_class == 0)
1861fc74d21Splunky 		usage();
1871fc74d21Splunky 
1881fc74d21Splunky 	if (!bdaddr_any(&remote_bdaddr) && (server_limit != 0 ||
189ab1f45acSplunky 	    control_path != 0 || (service_type != NULL && l2cap_psm != 0)))
1901fc74d21Splunky 		usage();
1911fc74d21Splunky 
1921fc74d21Splunky 	/* default options */
1931fc74d21Splunky 	if (interface_name == NULL)
1941fc74d21Splunky 		interface_name = "/dev/tap";
1951fc74d21Splunky 
1961fc74d21Splunky 	if (l2cap_psm == 0)
1971fc74d21Splunky 		l2cap_psm = L2CAP_PSM_BNEP;
1981fc74d21Splunky 
1991fc74d21Splunky 	if (bdaddr_any(&remote_bdaddr) && server_limit == 0) {
2001fc74d21Splunky 		if (service_class == SDP_SERVICE_CLASS_PANU)
2011fc74d21Splunky 			server_limit = 1;
2021fc74d21Splunky 		else
2031fc74d21Splunky 			server_limit = 7;
2041fc74d21Splunky 	}
2051fc74d21Splunky 
2061fc74d21Splunky #ifdef L2CAP_LM_MASTER
2071fc74d21Splunky 	if (server_limit > 1 && service_class != SDP_SERVICE_CLASS_PANU)
2081fc74d21Splunky 		l2cap_mode |= L2CAP_LM_MASTER;
2091fc74d21Splunky #endif
2101fc74d21Splunky 
2111fc74d21Splunky 	/*
2121fc74d21Splunky 	 * fork() now so that the setup can be done in the child process
2131fc74d21Splunky 	 * (as kqueue is not inherited) but block in the parent until the
2141fc74d21Splunky 	 * setup is finished so we can return an error if necessary.
2151fc74d21Splunky 	 */
2161fc74d21Splunky 	switch(fork()) {
2171fc74d21Splunky 	case -1: /* bad */
2181fc74d21Splunky 		err(EXIT_FAILURE, "fork() failed");
2192cab0740Schristos 		/*NOTREACHED*/
2201fc74d21Splunky 
2211fc74d21Splunky 	case 0:	/* child */
2221fc74d21Splunky 		openlog(getprogname(), LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_DAEMON);
2231fc74d21Splunky 
2241fc74d21Splunky 		channel_init();
2251fc74d21Splunky 		event_init();
226493f204dSplunky 		server_init();
2271fc74d21Splunky 		client_init();
2281fc74d21Splunky 		tap_init();
2291fc74d21Splunky 
2301fc74d21Splunky 		main_detach();
2311fc74d21Splunky 
2321fc74d21Splunky 		event_dispatch();
2331fc74d21Splunky 		break;
2341fc74d21Splunky 
2351fc74d21Splunky 	default: /* parent */
2361fc74d21Splunky 		signal(SIGUSR1, main_exit);
2371fc74d21Splunky 		wait(&status);
2381fc74d21Splunky 
2391fc74d21Splunky 		if (WIFEXITED(status))
2401fc74d21Splunky 			exit(WEXITSTATUS(status));
2411fc74d21Splunky 
2421fc74d21Splunky 		break;
2431fc74d21Splunky 	}
2441fc74d21Splunky 
2451fc74d21Splunky 	err(EXIT_FAILURE, "exiting");
2461fc74d21Splunky }
2471fc74d21Splunky 
2481fc74d21Splunky static void
main_exit(int s)2491fc74d21Splunky main_exit(int s)
2501fc74d21Splunky {
2511fc74d21Splunky 
2521fc74d21Splunky 	/* child is all grown up */
2531fc74d21Splunky 	_exit(EXIT_SUCCESS);
2541fc74d21Splunky }
2551fc74d21Splunky 
2561fc74d21Splunky static void
main_detach(void)2571fc74d21Splunky main_detach(void)
2581fc74d21Splunky {
2591fc74d21Splunky 	int fd;
2601fc74d21Splunky 
2611fc74d21Splunky 	if (kill(getppid(), SIGUSR1) == -1)
2621fc74d21Splunky 		log_err("Could not signal main process: %m");
2631fc74d21Splunky 
2641fc74d21Splunky 	if (setsid() == -1)
2651fc74d21Splunky 		log_err("setsid() failed");
2661fc74d21Splunky 
2671fc74d21Splunky 	fd = open(_PATH_DEVNULL, O_RDWR, 0);
2681fc74d21Splunky 	if (fd == -1) {
2691fc74d21Splunky 		log_err("Could not open %s", _PATH_DEVNULL);
2701fc74d21Splunky 	} else {
2711fc74d21Splunky 		(void)dup2(fd, STDIN_FILENO);
2721fc74d21Splunky 		(void)dup2(fd, STDOUT_FILENO);
2731fc74d21Splunky 		(void)dup2(fd, STDERR_FILENO);
2741fc74d21Splunky 		close(fd);
2751fc74d21Splunky 	}
2761fc74d21Splunky }
2771fc74d21Splunky 
2781fc74d21Splunky static void
usage(void)2791fc74d21Splunky usage(void)
2801fc74d21Splunky {
2811fc74d21Splunky 	const char *p = getprogname();
282ab1f45acSplunky 	size_t n = strlen(p);
2831fc74d21Splunky 
2841fc74d21Splunky 	fprintf(stderr,
2851fc74d21Splunky 	    "usage: %s [-i ifname] [-m mode] -a address -d device\n"
2861fc74d21Splunky 	    "       %*s {-s service | -S service [-p psm]}\n"
2871fc74d21Splunky 	    "       %s [-c path] [-i ifname] [-l limit] [-m mode] [-p psm] -d device\n"
2881fc74d21Splunky 	    "       %*s {-s service | -S service}\n"
2891fc74d21Splunky 	    "\n"
2901fc74d21Splunky 	    "Where:\n"
2911fc74d21Splunky 	    "\t-a address  remote bluetooth device\n"
2921fc74d21Splunky 	    "\t-c path     SDP server socket\n"
2931fc74d21Splunky 	    "\t-d device   local bluetooth device\n"
2941fc74d21Splunky 	    "\t-i ifname   tap interface\n"
2951fc74d21Splunky 	    "\t-l limit    limit server sessions\n"
2961fc74d21Splunky 	    "\t-m mode     L2CAP link mode\n"
2971fc74d21Splunky 	    "\t-p psm      L2CAP PSM\n"
2981fc74d21Splunky 	    "\t-S service  service name (no SDP)\n"
2991fc74d21Splunky 	    "\t-s service  service name\n"
3001fc74d21Splunky 	    "\n"
3011fc74d21Splunky 	    "Known services:\n"
302a1776a6dSchristos 	    "", p, (int)n, "", p, (int)n, "");
3031fc74d21Splunky 
304ab1f45acSplunky 	for (n = 0; n < __arraycount(services); n++)
305ab1f45acSplunky 		fprintf(stderr, "\t%s\t%s\n", services[n].type, services[n].name);
3061fc74d21Splunky 
3071fc74d21Splunky 	exit(EXIT_FAILURE);
3081fc74d21Splunky }
309