Initial commit of rfcommd. - rfcommd - RFCOMM daemon to run filters on clients. | |
git clone git://bitreich.org/rfcommd/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinw… | |
Log | |
Files | |
Refs | |
Tags | |
README | |
LICENSE | |
--- | |
commit 70f725160626e24b13cdd304ed86b1184783fc97 | |
Author: Christoph Lohmann <[email protected]> | |
Date: Fri, 25 Mar 2022 18:10:39 +0100 | |
Initial commit of rfcommd. | |
Diffstat: | |
A Makefile | 53 ++++++++++++++++++++++++++++++ | |
A arg.h | 46 +++++++++++++++++++++++++++++… | |
A etc/conf.d/rfcommd.gentoo | 6 ++++++ | |
A etc/init.d/rfcommd.gentoo | 29 +++++++++++++++++++++++++++++ | |
A filters/euroklavfilter | 40 +++++++++++++++++++++++++++++… | |
A filters/spirofilter | 39 +++++++++++++++++++++++++++++… | |
A rfcommd.c | 595 +++++++++++++++++++++++++++++… | |
7 files changed, 808 insertions(+), 0 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
@@ -0,0 +1,53 @@ | |
+# rfcomm-script - a rfcomm service script | |
+# See LICENSE file for copyright and license details. | |
+.POSIX: | |
+ | |
+NAME = rfcommd | |
+VERSION = 0.2 | |
+ | |
+PREFIX = /usr/local | |
+BINDIR = ${PREFIX}/sbin | |
+MANDIR = ${PREFIX}/share/man/man1 | |
+ | |
+RFCOMMD_CFLAGS = -D_GNU_SOURCE -Wall -I. -I/usr/include ${CFLAGS} | |
+RFCOMMD_LDFLAGS = -L/usr/lib -L. -lbluetooth ${LDFLAGS} | |
+ | |
+SRC = rfcommd.c | |
+OBJ = ${SRC:.c=.o} | |
+ | |
+all: ${NAME} | |
+ | |
+.c.o: | |
+ ${CC} ${RFCOMMD_CFLAGS} -c $< | |
+ | |
+${OBJ}: | |
+ | |
+${NAME}: ${OBJ} | |
+ ${CC} -o $@ ${OBJ} ${RFCOMMD_LDFLAGS} | |
+ | |
+clean: | |
+ rm -f ${NAME} ${OBJ} ${NAME}-${VERSION}.tar.gz | |
+ | |
+install: all | |
+ mkdir -p "${DESTDIR}${BINDIR}" | |
+ cp -f ${NAME} "${DESTDIR}${BINDIR}" | |
+ chmod 755 "${DESTDIR}${BINDIR}/${NAME}" | |
+ #mkdir -p "${DESTDIR}${MANDIR}" | |
+ #cp -f ${NAME}.1 "${DESTDIR}${MANDIR}" | |
+ #chmod 644 "${DESTDIR}${MANDIR}/${MANDIR}.1" | |
+ | |
+uninstall: | |
+ rm -f "${DESTDIR}${BINDIR}/${NAME}" | |
+ #rm -f "${DESTDIR}${MANDIR}/${NAME}.1" | |
+ | |
+dist: clean | |
+ mkdir -p ${NAME}-${VERSION} | |
+ cp -R *.c *.h Makefile \ | |
+ ${NAME}-${VERSION} | |
+ tar -cf ${NAME}-${VERSION}.tar ${NAME}-${VERSION} | |
+ gzip ${NAME}-${VERSION}.tar | |
+ mv ${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION}.tgz | |
+ rm -rf "${NAME}-${VERSION}" | |
+ | |
+.PHONY: all clean | |
+ | |
diff --git a/arg.h b/arg.h | |
@@ -0,0 +1,46 @@ | |
+/* | |
+ * Copy me if you can. | |
+ * by 20h | |
+ */ | |
+ | |
+#ifndef ARG_H__ | |
+#define ARG_H__ | |
+ | |
+/* use main(int argc, char *argv[]) */ | |
+#define ARGBEGIN(ARGV0) for (ARGV0 = *argv, argv++, argc--;\ | |
+ argv[0] && argv[0][0] == '-'\ | |
+ && argv[0][1];\ | |
+ argc--, argv++) {\ | |
+ char argc_;\ | |
+ char **argv_;\ | |
+ int brk_;\ | |
+ if (argv[0][1] == '-' && argv[0][2] == '\0') {\ | |
+ argv++;\ | |
+ argc--;\ | |
+ break;\ | |
+ }\ | |
+ for (brk_ = 0, argv[0]++, argv_ = argv;\ | |
+ argv[0][0] && !brk_;\ | |
+ argv[0]++) {\ | |
+ if (argv_ != argv)\ | |
+ break;\ | |
+ argc_ = argv[0][0];\ | |
+ switch (argc_) | |
+#define ARGEND }\ | |
+ } | |
+ | |
+#define ARGC() argc_ | |
+ | |
+#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ | |
+ ((x), abort(), (char *)0) :\ | |
+ (brk_ = 1, (argv[0][1] != '\0')?\ | |
+ (&argv[0][1]) :\ | |
+ (argc--, argv++, argv[0]))) | |
+ | |
+#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ | |
+ (char *)0 :\ | |
+ (brk_ = 1, (argv[0][1] != '\0')?\ | |
+ (&argv[0][1]) :\ | |
+ (argc--, argv++, argv[0]))) | |
+ | |
+#endif | |
diff --git a/etc/conf.d/rfcommd.gentoo b/etc/conf.d/rfcommd.gentoo | |
@@ -0,0 +1,6 @@ | |
+# | |
+# Parameters to be passed to rfcommd | |
+# | |
+ | |
+RFCOMMD_ARGS=(-d -f 10:00:E8:27:E3:B3 "sudo -u david /usr/local/bin/spirofilte… | |
+ | |
diff --git a/etc/init.d/rfcommd.gentoo b/etc/init.d/rfcommd.gentoo | |
@@ -0,0 +1,29 @@ | |
+#!/sbin/openrc-run | |
+# Copyright 1999-2016 Gentoo Foundation | |
+# Distributed under the terms of the GNU General Public License v2 | |
+# $Id$ | |
+ | |
+depend() { | |
+ need bluetooth | |
+} | |
+ | |
+start() { | |
+ ebegin "Starting rfcommd" | |
+ #[ -n "$RFCOMMD_ARGS" ] && RFCOMMD_ARGS="-- $RFCOMMD_ARGS" | |
+ start-stop-daemon -Sbimp /var/run/rfcommd.pid \ | |
+ -1 /var/log/rfcommd-stdout.log \ | |
+ -2 /var/log/rfcommd-stderr.log \ | |
+ -x /usr/sbin/rfcommd -- "${RFCOMMD_ARGS[@]}" | |
+ #start-stop-daemon -Sbimp /var/run/rfcommd.pid \ | |
+ # -1 /tmp/rfcommd-stdout.log \ | |
+ # -2 /tmp/rfcommd-stderr.log \ | |
+ # -x /usr/sbin/rfcommd ${RFCOMMD_ARGS} | |
+ eend $? "Failed to start rfcommd" | |
+} | |
+ | |
+stop() { | |
+ ebegin "Stopping rfcommd" | |
+ start-stop-daemon -Kip /var/run/rfcommd.pid | |
+ eend $? "Failed to stop rfcommd" | |
+} | |
+ | |
diff --git a/filters/euroklavfilter b/filters/euroklavfilter | |
@@ -0,0 +1,40 @@ | |
+#!/bin/sh | |
+ | |
+euroklavdb="/home/david/share/euroklav" | |
+dbpath="${euroklavdb}" | |
+ | |
+if [ $# -gt 0 ]; | |
+then | |
+ infile="$1" | |
+ case "${infile}" in | |
+ /dev/rfcomm*) | |
+ stty -F "${infile}" raw -echo -echoe -echok | |
+ ;; | |
+ esac | |
+else | |
+ infile="/dev/stdin" | |
+fi | |
+ | |
+cd "${dbpath}" | |
+datetime="$(date +%Y-%m-%d-%H-%M-%S)" | |
+outfile="euroklav-${datetime}.txt" | |
+cat "${infile}" \ | |
+| while read -r line; | |
+do | |
+ case "${line}" in | |
+ $'\003'*) | |
+ datetime="$(date +%Y-%m-%d-%H-%M-%S)" | |
+ outfile="euroklav-${datetime}.txt" | |
+ ;; | |
+ *" 4.07 4.08"*) | |
+ printf "%s\n" "${line}" >> ${outfile} | |
+ cat ${outfile} \ | |
+ | tr -d '\002\003' \ | |
+ | tr '\370' ' ' \ | |
+ | tr '\346' ' ' \ | |
+ | lpr | |
+ ;; | |
+ esac | |
+ printf "%s\n" "${line}" >> ${outfile} | |
+done | |
+ | |
diff --git a/filters/spirofilter b/filters/spirofilter | |
@@ -0,0 +1,39 @@ | |
+#!/bin/sh | |
+ | |
+tmpfile="$(mktemp)" | |
+spirometrydb="/home/david/share/spirometry" | |
+ | |
+datetime="$(date +%Y-%m-%d-%H-%M-%S)" | |
+if [ $# -gt 0 ]; | |
+then | |
+ infile="$1" | |
+ case "${infile}" in | |
+ /dev/rfcomm*) | |
+ stty -F "${infile}" raw -echo -echoe -echok | |
+ ;; | |
+ *) | |
+ datetime="$(echo $infile \ | |
+ | cut -d- -f 2- \ | |
+ | cut -d'.' -f 1)" | |
+ ;; | |
+ esac | |
+else | |
+ infile="/dev/stdin" | |
+fi | |
+ | |
+cat "${infile}" > "${tmpfile}" | |
+dbpath="${spirometrydb}" | |
+cd "${dbpath}" | |
+outfile="spirometry-$(cat "${tmpfile}" \ | |
+ | tr -d '\r' \ | |
+ | iconv -f iso8859-1 -t utf-8 \ | |
+ | grep -a "^NAME" \ | |
+ | sed 's,NAME \([^ ]*\)[ ]*\([^ ]*\)[ ]*#ID \([0-9]*\)[ ]*,\1-\2-\3,' \ | |
+ | sed 's, ,_,g; s,/,_,g;')-${datetime}.pcl" | |
+mv "${tmpfile}" "${outfile}" 2>/dev/null | |
+chown david:david "${outfile}" 2>/dev/null | |
+ | |
+cat "${outfile}" \ | |
+ | sed '/Daten von spirolab III Ver/d' \ | |
+ | lpr | |
+ | |
diff --git a/rfcommd.c b/rfcommd.c | |
@@ -0,0 +1,595 @@ | |
+/* | |
+ * RFCOMM DAEMON | |
+ * Copied from rfcomm.c in bluez. | |
+ * SDP code from pybluez. | |
+ * | |
+ * Copy me if you can. | |
+ * by 20h | |
+ */ | |
+ | |
+#include <stdio.h> | |
+#include <errno.h> | |
+#include <fcntl.h> | |
+#include <unistd.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <getopt.h> | |
+#include <signal.h> | |
+#include <libgen.h> | |
+#include <termios.h> | |
+#include <stdint.h> | |
+#include <poll.h> | |
+#include <stdarg.h> | |
+#include <sys/poll.h> | |
+#include <sys/param.h> | |
+#include <sys/ioctl.h> | |
+#include <sys/socket.h> | |
+#include <sys/wait.h> | |
+ | |
+#include <bluetooth/bluetooth.h> | |
+#include <bluetooth/hci.h> | |
+#include <bluetooth/hci_lib.h> | |
+#include <bluetooth/rfcomm.h> | |
+#include <bluetooth/sdp.h> | |
+#include <bluetooth/sdp_lib.h> | |
+ | |
+#include "arg.h" | |
+ | |
+volatile sig_atomic_t __io_canceled = 0; | |
+ | |
+int dodebug = 0; | |
+ | |
+void | |
+debug(char *fmt, ...) | |
+{ | |
+ va_list fmtargs; | |
+ | |
+ if (!dodebug) | |
+ return; | |
+ | |
+ va_start(fmtargs, fmt); | |
+ vfprintf(stderr, fmt, fmtargs); | |
+ va_end(fmtargs); | |
+} | |
+ | |
+void | |
+sig_hup(int sig) | |
+{ | |
+ return; | |
+} | |
+ | |
+void | |
+sig_term(int sig) | |
+{ | |
+ __io_canceled = 1; | |
+} | |
+ | |
+void | |
+setup_signals(void) | |
+{ | |
+ struct sigaction sa; | |
+ | |
+ memset(&sa, 0, sizeof(sa)); | |
+ sa.sa_flags = SA_NOCLDSTOP; | |
+ sa.sa_handler = SIG_IGN; | |
+ sigaction(SIGCHLD, &sa, NULL); | |
+ sigaction(SIGPIPE, &sa, NULL); | |
+ | |
+ sa.sa_handler = sig_term; | |
+ sigaction(SIGTERM, &sa, NULL); | |
+ sigaction(SIGINT, &sa, NULL); | |
+ | |
+ sa.sa_handler = sig_hup; | |
+ sigaction(SIGHUP, &sa, NULL); | |
+} | |
+ | |
+int | |
+_adv_available(struct hci_dev_info *di) | |
+{ | |
+ uint32_t *flags = &di->flags; | |
+ int dd; | |
+ | |
+ if (hci_test_bit(HCI_RAW, &flags) && !bacmp(&di->bdaddr, BDADDR_ANY)) { | |
+ dd = hci_open_dev(di->dev_id); | |
+ | |
+ if (dd < 0) | |
+ return -1; | |
+ hci_read_bd_addr(dd, &di->bdaddr, 1000); | |
+ hci_close_dev(dd); | |
+ } | |
+ | |
+ return (hci_test_bit(HCI_UP, flags) && | |
+ hci_test_bit(HCI_RUNNING, flags) && | |
+ hci_test_bit(HCI_PSCAN, flags) && | |
+ hci_test_bit(HCI_ISCAN, flags)) != 0 ? 0 : -1; | |
+} | |
+ | |
+int | |
+_any_adv_available(void) | |
+{ | |
+ struct hci_dev_list_req *dl = NULL; | |
+ struct hci_dev_req *dr = NULL; | |
+ struct hci_dev_info di = {0, }; | |
+ int result = -1; | |
+ int ctl = -1; | |
+ int i; | |
+ | |
+ if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) | |
+ return -1; | |
+ | |
+ if (!(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + | |
+ sizeof(uint16_t)))) { | |
+ goto CLEAN_UP_RETURN; | |
+ } | |
+ dl->dev_num = HCI_MAX_DEV; | |
+ dr = dl->dev_req; | |
+ | |
+ if (ioctl(ctl, HCIGETDEVLIST, (void *)dl) < 0) | |
+ goto CLEAN_UP_RETURN; | |
+ | |
+ for (i = 0; i < dl->dev_num; i++) { | |
+ di.dev_id = (dr+i)->dev_id; | |
+ if (ioctl(ctl, HCIGETDEVINFO, (void *)&di) < 0) | |
+ continue; | |
+ | |
+ if (_adv_available(&di) == 0) { | |
+ result = 0; | |
+ goto CLEAN_UP_RETURN; | |
+ } | |
+ } | |
+ | |
+CLEAN_UP_RETURN: | |
+ close(ctl); | |
+ free(dl); | |
+ | |
+ return result; | |
+} | |
+ | |
+int | |
+adv_available(int sock) | |
+{ | |
+ bdaddr_t ba = {{0, }}; | |
+ struct sockaddr addr = {0, }; | |
+ int dev_id = -1; | |
+ socklen_t alen = sizeof(addr); | |
+ struct sockaddr_rc const *addr_rc = (struct sockaddr_rc const *)&addr; | |
+ struct hci_dev_info di; | |
+ | |
+ if (getsockname(sock, &addr, &alen) < 0) | |
+ return -1; | |
+ | |
+ ba = addr_rc->rc_bdaddr; | |
+ | |
+ if (bacmp(&ba, BDADDR_ANY) == 0) { | |
+ dev_id = -1; | |
+ } else { | |
+ dev_id = hci_get_route(&ba); | |
+ } | |
+ | |
+ if (dev_id == -1) { | |
+ return _any_adv_available(); | |
+ } else { | |
+ if (hci_devinfo(dev_id, &di)) | |
+ return -1; | |
+ return _adv_available(&di); | |
+ } | |
+} | |
+ | |
+int | |
+str2uuid(char *uuidstr, uuid_t *uuid) | |
+{ | |
+ uint32_t uuid_int[4]; | |
+ int i; | |
+ char *endptr, buf[9] = { 0 }; | |
+ | |
+ if (strlen(uuidstr) == 36) { | |
+ if (uuidstr[8] != '-' && uuidstr[13] != '-' && | |
+ uuidstr[18] != '-' && uuidstr[23] != '-') { | |
+ return 1; | |
+ } | |
+ | |
+ strncpy(buf, uuidstr, 8); | |
+ uuid_int[0] = htonl(strtoul(buf, &endptr, 16)); | |
+ if (endptr != buf+8) | |
+ return 1; | |
+ | |
+ strncpy(buf, uuidstr+9, 4); | |
+ strncpy(buf+4, uuidstr+14, 4); | |
+ uuid_int[1] = htonl(strtoul(buf, &endptr, 16)); | |
+ if (endptr != buf+8) | |
+ return 1; | |
+ | |
+ strncpy(buf, uuidstr+19, 4); | |
+ strncpy(buf+4, uuidstr+24, 4); | |
+ uuid_int[2] = htonl(strtoul(buf, &endptr, 16)); | |
+ if (endptr != buf+8) | |
+ return 1; | |
+ | |
+ strncpy(buf, uuidstr+28, 4); | |
+ uuid_int[3] = htonl(strtoul(buf, &endptr, 16)); | |
+ if (endptr != buf+8) | |
+ return 1; | |
+ | |
+ if (uuid != NULL) | |
+ sdp_uuid128_create(uuid, uuid_int); | |
+ } else if (strlen(uuidstr) == 8) { | |
+ uuid_int[0] = strtoul(uuidstr, &endptr, 16); | |
+ if (endptr != uuidstr+8) | |
+ return 1; | |
+ if (uuid != NULL) | |
+ sdp_uuid32_create(uuid, uuid_int[0]); | |
+ } else if (strlen(uuidstr) == 4) { | |
+ i = strtol(uuidstr, &endptr, 16); | |
+ if (endptr != uuidstr+4) | |
+ return 1; | |
+ if (uuid != NULL) | |
+ sdp_uuid16_create(uuid, i); | |
+ } else { | |
+ return 1; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+int | |
+sdp_advertise_service(int sock, char *svcname, | |
+ char *svcid, int svc_class, int profiles, | |
+ char *svcprovider, char *svcdescription) | |
+{ | |
+ char addrbuf[256]; | |
+ int res, err = 0; | |
+ struct sockaddr *sockaddr; | |
+ uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_class_uuid, | |
+ svc_uuid; | |
+ sdp_profile_desc_t *profile_desc; | |
+ sdp_list_t *l2cap_list = NULL, *rfcomm_list = NULL, *root_list = NULL, | |
+ *proto_list = NULL, *profile_list = NULL, | |
+ *svc_class_list = NULL, *access_proto_list = NULL; | |
+ sdp_data_t *channel = 0; | |
+ sdp_record_t record; | |
+ sdp_session_t *session = 0; | |
+ uint8_t rfcomm_channel; | |
+ socklen_t addrlen = sizeof(struct sockaddr_rc); | |
+ | |
+ str2uuid(svcid, &svc_uuid); | |
+ sdp_uuid16_create(&svc_class_uuid, svc_class); | |
+ | |
+ memset(addrbuf, 0, sizeof(addrbuf)); | |
+ | |
+ if (adv_available(sock) < 0) | |
+ return -1; | |
+ | |
+ res = getsockname(sock, (struct sockaddr *)addrbuf, &addrlen); | |
+ if (res < 0) | |
+ return -1; | |
+ sockaddr = (struct sockaddr *)addrbuf; | |
+ | |
+ memset(&record, 0, sizeof(record)); | |
+ memset(&record.handle, 0xff, sizeof(record.handle)); | |
+ | |
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); | |
+ root_list = sdp_list_append(0, &root_uuid); | |
+ sdp_set_browse_groups(&record, root_list); | |
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); | |
+ l2cap_list = sdp_list_append(0, &l2cap_uuid); | |
+ proto_list = sdp_list_append(0, l2cap_list); | |
+ rfcomm_channel = ((struct sockaddr_rc *)sockaddr)->rc_channel; | |
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); | |
+ channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel); | |
+ rfcomm_list = sdp_list_append(0, &rfcomm_uuid); | |
+ sdp_list_append(rfcomm_list, channel); | |
+ sdp_list_append(proto_list, rfcomm_list); | |
+ access_proto_list = sdp_list_append(0, proto_list); | |
+ sdp_set_access_protos(&record, access_proto_list); | |
+ svc_class_list = sdp_list_append(svc_class_list, &svc_class_uuid); | |
+ sdp_set_service_classes(&record, svc_class_list); | |
+ | |
+ profile_desc = (sdp_profile_desc_t *)malloc(sizeof(sdp_profile_desc_t)… | |
+ if (profile_desc == NULL) | |
+ return -1; | |
+ sdp_uuid16_create(&profile_desc->uuid, profiles); | |
+ profile_list = sdp_list_append(profile_list, profile_desc); | |
+ sdp_set_profile_descs(&record, profile_list); | |
+ | |
+ sdp_set_info_attr(&record, svcname, svcprovider, svcdescription); | |
+ sdp_set_service_id(&record, svc_uuid); | |
+ | |
+ session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0); | |
+ if (!session) | |
+ return -1; | |
+ err = sdp_record_register(session, &record, 0); | |
+ | |
+ if (channel) | |
+ sdp_data_free(channel); | |
+ sdp_list_free(l2cap_list, 0); | |
+ sdp_list_free(rfcomm_list, 0); | |
+ sdp_list_free(root_list, 0); | |
+ sdp_list_free(access_proto_list, 0); | |
+ sdp_list_free(svc_class_list, 0); | |
+ sdp_list_free(profile_list, free); | |
+ | |
+ if (err) | |
+ return -1; | |
+ | |
+ return 0; | |
+} | |
+ | |
+void | |
+usage(char *argv0) | |
+{ | |
+ fprintf(stderr, "%s [-dhrAESM] [-i hciX|bdaddr] [-L linger] [-c channe… | |
+ basename(argv0)); | |
+ exit(1); | |
+} | |
+ | |
+int | |
+main(int argc, char *argv[]) | |
+{ | |
+ int rfcomm_raw_tty = 0, auth = 0, encryption = 0, | |
+ secure = 0, master = 0, linger = 0, sk, nsk, fd, lm , try = 30, | |
+ ctl, rc_channel = 1, filteri, dev; | |
+ char *argv0, *optarg, dst[18], devname[MAXPATHLEN], *replace, | |
+ *cmd, *oldcmd, *defaultcmd = NULL, *runcmd; | |
+ struct sockaddr_rc laddr, raddr; | |
+ struct rfcomm_dev_req req; | |
+ struct termios ti; | |
+ socklen_t alen; | |
+ bdaddr_t bdaddr; | |
+ struct linger l; | |
+ | |
+ char **cmds = NULL; | |
+ char **filteraddrs = NULL; | |
+ bdaddr_t **filterbds = NULL; | |
+ int filtern = 0; | |
+ | |
+ bacpy(&bdaddr, BDADDR_ANY); | |
+ | |
+ ARGBEGIN(argv0) { | |
+ case 'c': | |
+ rc_channel = atoi(EARGF(usage(argv0))); | |
+ break; | |
+ case 'd': | |
+ dodebug = 1; | |
+ break; | |
+ case 'i': | |
+ optarg = EARGF(usage(argv0)); | |
+ if (strncmp(optarg, "hci", 3) == 0) { | |
+ hci_devba(atoi(optarg + 3), &bdaddr); | |
+ } else { | |
+ str2ba(optarg, &bdaddr); | |
+ } | |
+ break; | |
+ case 'f': | |
+ ++filtern; | |
+ filteraddrs = realloc(filteraddrs, | |
+ filtern * sizeof(*filteraddrs)); | |
+ if (filteraddrs == NULL) | |
+ exit(1); | |
+ | |
+ cmds = realloc(cmds, filtern * sizeof(*cmds)); | |
+ if (cmds == NULL) | |
+ exit(1); | |
+ | |
+ filterbds = realloc(filterbds, filtern * sizeof(*filterbds)); | |
+ if (filterbds == NULL) | |
+ exit(1); | |
+ | |
+ filteraddrs[filtern-1] = EARGF(usage(argv0)); | |
+ argv++, argc--; | |
+ if (argc <= 0) | |
+ usage(argv0); | |
+ cmds[filtern-1] = argv[0]; | |
+ | |
+ filterbds[filtern-1] = malloc(sizeof(*filterbds)); | |
+ if (filterbds[filtern-1] == NULL) | |
+ exit(1); | |
+ str2ba(filteraddrs[filtern-1], filterbds[filtern-1]); | |
+ break; | |
+ case 'r': | |
+ rfcomm_raw_tty = 1; | |
+ break; | |
+ case 'A': | |
+ auth = 1; | |
+ break; | |
+ case 'E': | |
+ encryption = 1; | |
+ break; | |
+ case 'S': | |
+ secure = 1; | |
+ break; | |
+ case 'M': | |
+ master = 1; | |
+ break; | |
+ case 'L': | |
+ linger = atoi(EARGF(usage(argv0))); | |
+ break; | |
+ case 'h': | |
+ default: | |
+ usage(argv0); | |
+ } ARGEND; | |
+ | |
+ if (argc > 0) | |
+ defaultcmd = argv[0]; | |
+ if (defaultcmd == NULL && filtern < 0) | |
+ usage(argv[0]); | |
+ | |
+ for (filteri = 0; filteri < filtern; filteri++) { | |
+ ba2str(filterbds[filteri], dst); | |
+ debug("filter: %s (%s) -> %s\n", | |
+ filteraddrs[filteri], dst, cmds[filteri]); | |
+ } | |
+ debug("defaultcmd: %s\n", defaultcmd); | |
+ | |
+ setup_signals(); | |
+ | |
+ ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM); | |
+ if (ctl < 0) { | |
+ perror("Can't open RFCOMM control socket"); | |
+ return 1; | |
+ } | |
+ | |
+ laddr.rc_family = AF_BLUETOOTH; | |
+ bacpy(&laddr.rc_bdaddr, &bdaddr); | |
+ laddr.rc_channel = rc_channel; | |
+ | |
+ sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); | |
+ if (sk < 0) { | |
+ perror("Can't create RFCOMM socket"); | |
+ return 1; | |
+ } | |
+ | |
+ lm = 0; | |
+ if (master) | |
+ lm |= RFCOMM_LM_MASTER; | |
+ if (auth) | |
+ lm |= RFCOMM_LM_AUTH; | |
+ if (encryption) | |
+ lm |= RFCOMM_LM_ENCRYPT; | |
+ if (secure) | |
+ lm |= RFCOMM_LM_SECURE; | |
+ | |
+ if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { | |
+ perror("Can't set RFCOMM link mode"); | |
+ close(sk); | |
+ return 1; | |
+ } | |
+ | |
+ if (bind(sk, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) { | |
+ perror("Can't bind RFCOMM socket"); | |
+ close(sk); | |
+ return 1; | |
+ } | |
+ | |
+ debug("Waiting for connection on channel %d\n", laddr.rc_channel); | |
+ | |
+ listen(sk, 10); | |
+ | |
+ sdp_advertise_service(sk, | |
+ "SPP Printer", | |
+ "00001101-0000-1000-8000-00805F9B34FB", | |
+ SERIAL_PORT_SVCLASS_ID, | |
+ SERIAL_PORT_PROFILE_ID, | |
+ "SPP Printer Emulation", | |
+ "rfcommd"); | |
+ | |
+ while (!__io_canceled) { | |
+ alen = sizeof(raddr); | |
+ nsk = accept(sk, (struct sockaddr *)&raddr, &alen); | |
+ | |
+ if (fork() != 0) | |
+ continue; | |
+ | |
+ ba2str(&raddr.rc_bdaddr, dst); | |
+ debug("Accept from %s\n", dst); | |
+ | |
+ for (filteri = 0; filteri < filtern; filteri++) { | |
+ if (!bacmp(filterbds[filteri], &raddr.rc_bdaddr)) { | |
+ runcmd = cmds[filteri]; | |
+ debug("filter found: %s -> %s\n", | |
+ filteraddrs[filteri], | |
+ runcmd); | |
+ break; | |
+ } | |
+ } | |
+ if (filteri >= filtern) { | |
+ if (defaultcmd != NULL) { | |
+ debug("running defaultcmd = %s\n", | |
+ defaultcmd); | |
+ runcmd = defaultcmd; | |
+ } else { | |
+ close(nsk); | |
+ continue; | |
+ } | |
+ } | |
+ | |
+ alen = sizeof(laddr); | |
+ if (getsockname(nsk, (struct sockaddr *)&laddr, &alen) < 0) { | |
+ perror("Can't get RFCOMM socket name"); | |
+ close(nsk); | |
+ continue; | |
+ } | |
+ | |
+ if (linger) { | |
+ l.l_onoff = 1; | |
+ l.l_linger = linger; | |
+ | |
+ if (setsockopt(nsk, SOL_SOCKET, SO_LINGER, &l, sizeof(… | |
+ perror("Can't set linger option"); | |
+ close(nsk); | |
+ continue; | |
+ } | |
+ } | |
+ | |
+ memset(&req, 0, sizeof(req)); | |
+ req.dev_id = -1; | |
+ req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONH… | |
+ | |
+ bacpy(&req.src, &laddr.rc_bdaddr); | |
+ bacpy(&req.dst, &raddr.rc_bdaddr); | |
+ req.channel = raddr.rc_channel; | |
+ | |
+ dev = ioctl(nsk, RFCOMMCREATEDEV, &req); | |
+ if (dev < 0) { | |
+ perror("Can't create RFCOMM TTY"); | |
+ close(sk); | |
+ continue; | |
+ } | |
+ | |
+ snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev); | |
+ while ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) { | |
+ if (errno == EACCES) { | |
+ perror("Can't open RFCOMM device"); | |
+ goto release; | |
+ } | |
+ | |
+ snprintf(devname, MAXPATHLEN - 1, "/dev/bluetooth/rfco… | |
+ if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) { | |
+ if (try--) { | |
+ snprintf(devname, MAXPATHLEN - 1, "/de… | |
+ usleep(100 * 1000); | |
+ continue; | |
+ } | |
+ perror("Can't open RFCOMM device"); | |
+ goto release; | |
+ } | |
+ } | |
+ | |
+ if (rfcomm_raw_tty) { | |
+ tcflush(fd, TCIOFLUSH); | |
+ | |
+ cfmakeraw(&ti); | |
+ tcsetattr(fd, TCSANOW, &ti); | |
+ } | |
+ | |
+ ba2str(&req.dst, dst); | |
+ debug("Connection from %s to %s\n", dst, devname); | |
+ | |
+ /* Replace all occurences of '{}' with the rfcomm device path.… | |
+ asprintf(&oldcmd, "%s", runcmd); | |
+ while ((replace = strstr(oldcmd, "{}"))) { | |
+ replace[0] = '%'; | |
+ replace[1] = 's'; | |
+ asprintf(&cmd, oldcmd, devname); | |
+ free(oldcmd); | |
+ oldcmd = cmd; | |
+ } | |
+ | |
+ debug("Executing %s\n", cmd); | |
+ | |
+ system(cmd); | |
+ free(cmd); | |
+ | |
+ close(fd); | |
+ close(nsk); | |
+release: | |
+ memset(&req, 0, sizeof(req)); | |
+ req.dev_id = dev; | |
+ req.flags = (1 << RFCOMM_HANGUP_NOW); | |
+ ioctl(ctl, RFCOMMRELEASEDEV, &req); | |
+ } | |
+ | |
+ close(sk); | |
+ | |
+ return 0; | |
+} | |
+ |