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