diff options
Diffstat (limited to 'compat')
| -rw-r--r-- | compat/Makefile.am | 47 | ||||
| -rw-r--r-- | compat/bnep.c | 311 | ||||
| -rw-r--r-- | compat/dun.c | 331 | ||||
| -rw-r--r-- | compat/dund.1 | 72 | ||||
| -rw-r--r-- | compat/dund.c | 637 | ||||
| -rw-r--r-- | compat/dund.h | 40 | ||||
| -rw-r--r-- | compat/fakehid.c | 669 | ||||
| -rw-r--r-- | compat/fakehid.txt | 134 | ||||
| -rw-r--r-- | compat/hidd.1 | 41 | ||||
| -rw-r--r-- | compat/hidd.c | 861 | ||||
| -rw-r--r-- | compat/hidd.h | 30 | ||||
| -rw-r--r-- | compat/lib.h | 86 | ||||
| -rw-r--r-- | compat/msdun.c | 151 | ||||
| -rw-r--r-- | compat/pand.1 | 77 | ||||
| -rw-r--r-- | compat/pand.c | 800 | ||||
| -rw-r--r-- | compat/pand.h | 36 | ||||
| -rw-r--r-- | compat/sdp.c | 718 | ||||
| -rw-r--r-- | compat/sdp.h | 39 | 
18 files changed, 5080 insertions, 0 deletions
| diff --git a/compat/Makefile.am b/compat/Makefile.am new file mode 100644 index 00000000..4054b3e7 --- /dev/null +++ b/compat/Makefile.am @@ -0,0 +1,47 @@ + +bin_PROGRAMS = +man_MANS = + +if HIDD +bin_PROGRAMS += hidd + +hidd_SOURCES = hidd.c hidd.h sdp.c fakehid.c + +hidd_LDADD = $(top_builddir)/common/libhelper.a @BLUEZ_LIBS@ -lm + +if MANPAGES +man_MANS += hidd.1 +endif +endif + +if PAND +bin_PROGRAMS += pand + +pand_SOURCES = pand.c pand.h bnep.c sdp.c + +pand_LDADD = $(top_builddir)/common/libhelper.a @BLUEZ_LIBS@ +endif + +if MANPAGES +man_MANS += pand.1 +endif + +if DUND +bin_PROGRAMS += dund + +dund_SOURCES = dund.c dund.h lib.h sdp.c dun.c msdun.c + +dund_LDADD = $(top_builddir)/common/libhelper.a @BLUEZ_LIBS@ +endif + +if MANPAGES +man_MANS += dund.1 +endif + +AM_CFLAGS = @BLUEZ_CFLAGS@ + +INCLUDES = -I$(top_srcdir)/common + +EXTRA_DIST = fakehid.txt hidd.1 pand.1 dund.1 + +MAINTAINERCLEANFILES = Makefile.in diff --git a/compat/bnep.c b/compat/bnep.c new file mode 100644 index 00000000..604ed552 --- /dev/null +++ b/compat/bnep.c @@ -0,0 +1,311 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com> + *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/l2cap.h> +#include <bluetooth/bnep.h> + +#include <netinet/in.h> + +#include "pand.h" + +static int ctl; + +/* Compatibility with old ioctls */ +#define OLD_BNEPCONADD      1 +#define OLD_BNEPCONDEL      2 +#define OLD_BNEPGETCONLIST  3 +#define OLD_BNEPGETCONINFO  4 + +static unsigned long bnepconnadd; +static unsigned long bnepconndel; +static unsigned long bnepgetconnlist; +static unsigned long bnepgetconninfo; + +static struct { +	char     *str; +	uint16_t uuid; +} __svc[] = { +	{ "PANU", BNEP_SVC_PANU }, +	{ "NAP",  BNEP_SVC_NAP  }, +	{ "GN",   BNEP_SVC_GN   }, +	{ NULL } +}; + +int bnep_str2svc(char *svc, uint16_t *uuid) +{ +	int i; +	for (i = 0; __svc[i].str; i++) +		if (!strcasecmp(svc, __svc[i].str)) { +			*uuid = __svc[i].uuid; +			return 0; +		} +	return -1; +} + +char *bnep_svc2str(uint16_t uuid) +{ +	int i; +	for (i = 0; __svc[i].str; i++) +		if (__svc[i].uuid == uuid) +			return __svc[i].str; +	return NULL; +} + +int bnep_init(void) +{ +	ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP); +	if (ctl < 0) { +		perror("Failed to open control socket"); +		return 1; +	} + +	/* Temporary ioctl compatibility hack */ +	{  +		struct bnep_connlist_req req; +		struct bnep_conninfo ci[1]; + +		req.cnum = 1; +		req.ci   = ci; + +		if (!ioctl(ctl, BNEPGETCONNLIST, &req)) { +			/* New ioctls */ +			bnepconnadd     = BNEPCONNADD; +			bnepconndel     = BNEPCONNDEL; +			bnepgetconnlist = BNEPGETCONNLIST; +			bnepgetconninfo = BNEPGETCONNINFO; +		} else { +			/* Old ioctls */ +			bnepconnadd     = OLD_BNEPCONADD; +			bnepconndel     = OLD_BNEPCONDEL; +			bnepgetconnlist = OLD_BNEPGETCONLIST; +			bnepgetconninfo = OLD_BNEPGETCONINFO; +		} +	} + +	return 0; +} + +int bnep_cleanup(void) +{ +	close(ctl); +	return 0; +} + +int bnep_show_connections(void) +{ +	struct bnep_connlist_req req; +	struct bnep_conninfo ci[48]; +	int i; + +	req.cnum = 48; +	req.ci   = ci; +	if (ioctl(ctl, bnepgetconnlist, &req)) { +		perror("Failed to get connection list"); +		return -1; +	} + +	for (i=0; i < req.cnum; i++) { +		printf("%s %s %s\n", ci[i].device, +			batostr((bdaddr_t *) ci[i].dst), +			bnep_svc2str(ci[i].role)); +	} +	return 0; +} + +int bnep_kill_connection(uint8_t *dst) +{ +	struct bnep_conndel_req req; + +	memcpy(req.dst, dst, ETH_ALEN); +	req.flags = 0; +	if (ioctl(ctl, bnepconndel, &req)) { +		perror("Failed to kill connection"); +		return -1; +	} +	return 0; +} + +int bnep_kill_all_connections(void) +{ +	struct bnep_connlist_req req; +	struct bnep_conninfo ci[48]; +	int i; + +	req.cnum = 48; +	req.ci   = ci; +	if (ioctl(ctl, bnepgetconnlist, &req)) { +		perror("Failed to get connection list"); +		return -1; +	} + +	for (i=0; i < req.cnum; i++) { +		struct bnep_conndel_req req; +		memcpy(req.dst, ci[i].dst, ETH_ALEN); +		req.flags = 0; +		ioctl(ctl, bnepconndel, &req); +	} +	return 0; +} + +static int bnep_connadd(int sk, uint16_t role, char *dev) +{ +	struct bnep_connadd_req req; + +	strncpy(req.device, dev, 16); +	req.device[15] = '\0'; +	req.sock = sk; +	req.role = role; +	if (ioctl(ctl, bnepconnadd, &req)) +		return -1; +	strncpy(dev, req.device, 16); +	return 0; +} + +struct __service_16 {  +	uint16_t dst; +	uint16_t src; +} __attribute__ ((packed)); + +struct __service_32 {  +	uint16_t unused1; +	uint16_t dst; +	uint16_t unused2; +	uint16_t src; +} __attribute__ ((packed)); + +struct __service_128 {  +	uint16_t unused1; +	uint16_t dst; +	uint16_t unused2[8]; +	uint16_t src; +	uint16_t unused3[7]; +} __attribute__ ((packed)); + +int bnep_accept_connection(int sk, uint16_t role, char *dev) +{ +	struct bnep_setup_conn_req *req; +	struct bnep_control_rsp *rsp; +	unsigned char pkt[BNEP_MTU]; +	int r; + +	r = recv(sk, pkt, BNEP_MTU, 0); +	if (r <= 0) +		return -1; + +	errno = EPROTO; + +	if (r < sizeof(*req)) +		return -1; + +	req = (void *) pkt; +	if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ) +		return -1; + +	/* FIXME: Check role UUIDs */ + +	rsp = (void *) pkt; +	rsp->type = BNEP_CONTROL; +	rsp->ctrl = BNEP_SETUP_CONN_RSP; +	rsp->resp = htons(BNEP_SUCCESS); +	if (send(sk, rsp, sizeof(*rsp), 0) < 0) +		return -1; + +	return bnep_connadd(sk, role, dev); +} + +/* Create BNEP connection  + * sk      - Connect L2CAP socket + * role    - Local role + * service - Remote service + * dev     - Network device (contains actual dev name on return) + */ +int bnep_create_connection(int sk, uint16_t role, uint16_t svc, char *dev) +{ +	struct bnep_setup_conn_req *req; +	struct bnep_control_rsp *rsp; +	struct __service_16 *s; +	unsigned char pkt[BNEP_MTU]; +	int r; + +	/* Send request */ +	req = (void *) pkt; +	req->type = BNEP_CONTROL; +	req->ctrl = BNEP_SETUP_CONN_REQ; +	req->uuid_size = 2;	/* 16bit UUID */ +	s = (void *) req->service; +	s->dst = htons(svc); +	s->src = htons(role); + +	if (send(sk, pkt, sizeof(*req) + sizeof(*s), 0) < 0) +		return -1; + +receive: +	/* Get response */ +	r = recv(sk, pkt, BNEP_MTU, 0); +	if (r <= 0) +		return -1; + +	errno = EPROTO; + +	if (r < sizeof(*rsp)) +		return -1; +	 +	rsp = (void *) pkt; +	if (rsp->type != BNEP_CONTROL) +		return -1; + +	if (rsp->ctrl != BNEP_SETUP_CONN_RSP) +		goto receive; + +	r = ntohs(rsp->resp); + +	switch (r) { +	case BNEP_SUCCESS: +		break; + +	case BNEP_CONN_INVALID_DST: +	case BNEP_CONN_INVALID_SRC: +	case BNEP_CONN_INVALID_SVC: +		errno = EPROTO; +		return -1; + +	case BNEP_CONN_NOT_ALLOWED: +		errno = EACCES; +		return -1; +	} + +	return bnep_connadd(sk, role, dev); +} diff --git a/compat/dun.c b/compat/dun.c new file mode 100644 index 00000000..de437cf4 --- /dev/null +++ b/compat/dun.c @@ -0,0 +1,331 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com> + *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <syslog.h> +#include <dirent.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> + +#include "dund.h" +#include "lib.h" + +#define PROC_BASE  "/proc" + +static int for_each_port(int (*func)(struct rfcomm_dev_info *, unsigned long), unsigned long arg) +{ +	struct rfcomm_dev_list_req *dl; +	struct rfcomm_dev_info *di; +	long r = 0; +	int  sk, i; + +	sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM); +	if (sk < 0 ) { +		perror("Can't open RFCOMM control socket"); +		exit(1); +	} + +	dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di)); +	if (!dl) { +		perror("Can't allocate request memory"); +		close(sk); +		exit(1); +	} + +	dl->dev_num = RFCOMM_MAX_DEV; +	di = dl->dev_info; + +	if (ioctl(sk, RFCOMMGETDEVLIST, (void *) dl) < 0) { +		perror("Can't get device list"); +		exit(1); +	} + +	for (i = 0; i < dl->dev_num; i++) { +		r = func(di + i, arg); +		if (r) break; +	} + +	close(sk); +	return r; +} + +static int uses_rfcomm(char *path, char *dev) +{ +	struct dirent *de; +	DIR   *dir; + +	dir = opendir(path); +	if (!dir) +		return 0; + +	if (chdir(path) < 0) +		return 0; + +	while ((de = readdir(dir)) != NULL) { +		char link[PATH_MAX + 1]; +		int  len = readlink(de->d_name, link, sizeof(link)); +		if (len > 0) { +			link[len] = 0; +			if (strstr(link, dev)) +				return 1; +		} +	} + +	closedir(dir); + +	return 0; +} + +static int find_pppd(int id, pid_t *pid) +{ +	struct dirent *de; +	char  path[PATH_MAX + 1]; +	char  dev[10]; +	int   empty = 1; +	DIR   *dir; + +	dir = opendir(PROC_BASE); +	if (!dir) { +		perror(PROC_BASE); +		return -1; +	} + +	sprintf(dev, "rfcomm%d", id); + +	*pid = 0; +	while ((de = readdir(dir)) != NULL) { +		empty = 0; +		if (isdigit(de->d_name[0])) { +			sprintf(path, "%s/%s/fd", PROC_BASE, de->d_name); +			if (uses_rfcomm(path, dev)) { +				*pid = atoi(de->d_name); +				break; +			} +		} +	} +	closedir(dir); + +	if (empty) +		fprintf(stderr, "%s is empty (not mounted ?)\n", PROC_BASE); + +	return *pid != 0; +} + +static int dun_exec(char *tty, char *prog, char **args) +{ +	int pid = fork(); +	int fd; +	 +	switch (pid) { +	case -1: +		return -1; + +	case 0: +		break; + +	default: +		return pid; +	} + +	setsid(); + +	/* Close all FDs */ +	for (fd = 3; fd < 20; fd++) +		close(fd); + +	execvp(prog, args); + +	syslog(LOG_ERR, "Error while executing %s", prog); + +	exit(1); +} + +static int dun_create_tty(int sk, char *tty, int size) +{ +	struct sockaddr_rc sa; +	struct stat st; +	socklen_t alen; +	int id, try = 30; + +	struct rfcomm_dev_req req = { +		flags:   (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP), +		dev_id:  -1 +	}; + +	alen = sizeof(sa); +	if (getpeername(sk, (struct sockaddr *) &sa, &alen) < 0) +		return -1; +	bacpy(&req.dst, &sa.rc_bdaddr); + +	alen = sizeof(sa); +	if (getsockname(sk, (struct sockaddr *) &sa, &alen) < 0) +		return -1; +	bacpy(&req.src, &sa.rc_bdaddr); +	req.channel = sa.rc_channel; + +	id = ioctl(sk, RFCOMMCREATEDEV, &req); +	if (id < 0) +		return id; + +	snprintf(tty, size, "/dev/rfcomm%d", id); +	while (stat(tty, &st) < 0) { +		snprintf(tty, size, "/dev/bluetooth/rfcomm/%d", id); +		if (stat(tty, &st) < 0) { +			snprintf(tty, size, "/dev/rfcomm%d", id); +			if (try--) { +				usleep(100 * 1000); +				continue; +			} + +			memset(&req, 0, sizeof(req)); +			req.dev_id = id; +			ioctl(sk, RFCOMMRELEASEDEV, &req); + +			return -1; +		} +	} + +	return id; +} + +int dun_init(void) +{ +	return 0; +} + +int dun_cleanup(void) +{ +	return 0; +} + +static int show_conn(struct rfcomm_dev_info *di, unsigned long arg) +{ +	pid_t pid; +	 +	if (di->state == BT_CONNECTED && +		(di->flags & (1<<RFCOMM_REUSE_DLC)) && +		(di->flags & (1<<RFCOMM_TTY_ATTACHED)) && +		(di->flags & (1<<RFCOMM_RELEASE_ONHUP))) { + +		if (find_pppd(di->id, &pid)) { +			char dst[18]; +			ba2str(&di->dst, dst); + +			printf("rfcomm%d: %s channel %d pppd pid %d\n", +					di->id, dst, di->channel, pid); +		} +	} +	return 0; +} + +static int kill_conn(struct rfcomm_dev_info *di, unsigned long arg) +{ +	bdaddr_t *dst = (bdaddr_t *) arg; +	pid_t pid; + +	if (di->state == BT_CONNECTED && +		(di->flags & (1<<RFCOMM_REUSE_DLC)) && +		(di->flags & (1<<RFCOMM_TTY_ATTACHED)) && +		(di->flags & (1<<RFCOMM_RELEASE_ONHUP))) { + +		if (dst && bacmp(&di->dst, dst)) +			return 0; + +		if (find_pppd(di->id, &pid)) { +			if (kill(pid, SIGINT) < 0) +				perror("Kill"); + +			if (!dst) +				return 0; +			return 1; +		} +	} +	return 0; +} + +int dun_show_connections(void) +{ +	for_each_port(show_conn, 0); +	return 0; +} + +int dun_kill_connection(uint8_t *dst) +{ +	for_each_port(kill_conn, (unsigned long) dst); +	return 0; +} + +int dun_kill_all_connections(void) +{ +	for_each_port(kill_conn, 0); +	return 0; +} + +int dun_open_connection(int sk, char *pppd, char **args, int wait) +{ +	char tty[100]; +	int  pid; + +	if (dun_create_tty(sk, tty, sizeof(tty) - 1) < 0) { +		syslog(LOG_ERR, "RFCOMM TTY creation failed. %s(%d)", strerror(errno), errno); +		return -1; +	} + +	args[0] = "pppd"; +	args[1] = tty; +	args[2] = "nodetach"; + +	pid = dun_exec(tty, pppd, args); +	if (pid < 0) { +		syslog(LOG_ERR, "Exec failed. %s(%d)", strerror(errno), errno); +		return -1; +	} + +	if (wait) { +		int status; +		waitpid(pid, &status, 0); +		/* FIXME: Check for waitpid errors */ +	} + +	return 0; +} diff --git a/compat/dund.1 b/compat/dund.1 new file mode 100644 index 00000000..09fb7f75 --- /dev/null +++ b/compat/dund.1 @@ -0,0 +1,72 @@ +.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.29. +.TH BlueZ "1" "February 2003" "DUN daemon" "User Commands" +.SH NAME +dund \- BlueZ Bluetooth dial-up networking daemon +.SH DESCRIPTION +DUN daemon +.SH SYNOPSIS +dund <options> [pppd options] +.SH OPTIONS +.TP +\fB\-\-show\fR \fB\-\-list\fR \fB\-l\fR +Show active DUN connections +.TP +\fB\-\-listen\fR \fB\-s\fR +Listen for DUN connections +.TP +\fB\-\-dialup\fR \fB\-u\fR +Listen for dialup/telephone connections +.TP +\fB\-\-connect\fR \fB\-c\fR <bdaddr> +Create DUN connection +.TP +\fB\-\-mrouter\fR \fB\-m\fR <bdaddr> +Create mRouter connection +.TP +\fB\-\-search\fR \fB\-Q[duration]\fR +Search and connect +.TP +\fB\-\-kill\fR \fB\-k\fR <bdaddr> +Kill DUN connection +.TP +\fB\-\-killall\fR \fB\-K\fR +Kill all DUN connections +.TP +\fB\-\-channel\fR \fB\-C\fR <channel> +RFCOMM channel +.TP +\fB\-\-device\fR \fB\-i\fR <bdaddr> +Source bdaddr +.TP +\fB\-\-nosdp\fR \fB\-D\fR +Disable SDP +.TP +\fB\-\-auth\fR \fB\-A\fR +Enable authentification +.TP +\fB\-\-encrypt\fR \fB\-E\fR +Enable encryption +.TP +\fB\-\-secure\fR \fB\-S\fR +Secure connection +.TP +\fB\-\-master\fR \fB\-M\fR +Become the master of a piconet +.TP +\fB\-\-nodetach\fR \fB\-n\fR +Do not become a daemon +.TP +\fB\-\-persist\fR \fB\-p[interval]\fR +Persist mode +.TP +\fB\-\-pppd\fR \fB\-d\fR <pppd> +Location of the PPP daemon (pppd) +.TP +\fB\-\-msdun\fR \fB\-X\fR [timeo] +Enable Microsoft dialup networking support +.TP +\fB\-\-activesync\fR \fB\-a\fR +Enable Microsoft ActiveSync networking +.TP +\fB\-\-cache\fR \fB\-C\fR [valid] +Enable address cache diff --git a/compat/dund.c b/compat/dund.c new file mode 100644 index 00000000..dea945d2 --- /dev/null +++ b/compat/dund.c @@ -0,0 +1,637 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com> + *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <signal.h> +#include <getopt.h> + +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/rfcomm.h> +#include <bluetooth/hidp.h> + +#include "sdp.h" +#include "dund.h" +#include "lib.h" + +volatile sig_atomic_t __io_canceled; + +/* MS dialup networking support (i.e. CLIENT / CLIENTSERVER thing) */ +static int msdun = 0; + +static char *pppd = "/usr/sbin/pppd"; +static char *pppd_opts[DUN_MAX_PPP_OPTS] = +	{ +		/* First 3 are reserved */ +		"", "", "", +		"noauth", +		"noipdefault", +		NULL +	}; + +static int  detach = 1; +static int  persist; +static int  use_sdp = 1; +static int  auth; +static int  encrypt; +static int  secure; +static int  master; +static int  type = LANACCESS; +static int  search_duration = 10; +static uint use_cache; + +static int  channel; + +static struct { +	uint     valid; +	char     dst[40]; +	bdaddr_t bdaddr; +	int      channel; +} cache; + +static bdaddr_t src_addr = *BDADDR_ANY; +static int src_dev = -1; + +volatile int terminate; + +enum { +	NONE, +	SHOW, +	LISTEN, +	CONNECT, +	KILL +} modes; + +static int create_connection(char *dst, bdaddr_t *bdaddr, int mrouter); + +static int do_listen(void) +{ +	struct sockaddr_rc sa; +	int sk, lm; + +	if (type == MROUTER) { +		if (!cache.valid) +			return -1; + +		if (create_connection(cache.dst, &cache.bdaddr, type) < 0) { +			syslog(LOG_ERR, "Cannot connect to mRouter device. %s(%d)", +								strerror(errno), errno); +			return -1; +		} +	} + +	if (!channel) +		channel = DUN_DEFAULT_CHANNEL; + +	if (use_sdp) +		dun_sdp_register(&src_addr, channel, type); + +	if (type == MROUTER) +		syslog(LOG_INFO, "Waiting for mRouter callback on channel %d", channel); + +	/* Create RFCOMM socket */ +	sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); +	if (sk < 0) { +		syslog(LOG_ERR, "Cannot create RFCOMM socket. %s(%d)", +				strerror(errno), errno); +		return -1; +	} + +	sa.rc_family  = AF_BLUETOOTH; +	sa.rc_channel = channel; +	sa.rc_bdaddr  = src_addr; + +	if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) { +		syslog(LOG_ERR, "Bind failed. %s(%d)", strerror(errno), errno); +		return -1; +	} + +	/* Set link mode */ +	lm = 0; +	if (master) +		lm |= RFCOMM_LM_MASTER; +	if (auth) +		lm |= RFCOMM_LM_AUTH; +	if (encrypt) +		lm |= RFCOMM_LM_ENCRYPT; +	if (secure) +		lm |= RFCOMM_LM_SECURE; + +	if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { +		syslog(LOG_ERR, "Failed to set link mode. %s(%d)", strerror(errno), errno); +		return -1; +	} + +	listen(sk, 10); + +	while (!terminate) { +		socklen_t alen = sizeof(sa); +		int nsk; +		char ba[40]; +		char ch[10]; + +		nsk = accept(sk, (struct sockaddr *) &sa, &alen); +		if (nsk < 0) { +			syslog(LOG_ERR, "Accept failed. %s(%d)", strerror(errno), errno); +			continue; +		} + +		switch (fork()) { +		case 0: +			break; +		case -1: +			syslog(LOG_ERR, "Fork failed. %s(%d)", strerror(errno), errno); +		default: +			close(nsk); +			if (type == MROUTER) { +				close(sk); +				terminate = 1; +			} +			continue; +		} + +		close(sk); + +		if (msdun && ms_dun(nsk, 1, msdun) < 0) { +			syslog(LOG_ERR, "MSDUN failed. %s(%d)", strerror(errno), errno); +			exit(0); +		} + +		ba2str(&sa.rc_bdaddr, ba); +		sprintf(ch, "%d", channel); + +		/* Setup environment */ +		setenv("DUN_BDADDR",  ba, 1); +		setenv("DUN_CHANNEL", ch, 1); + +		if (!dun_open_connection(nsk, pppd, pppd_opts, 0)) +			syslog(LOG_INFO, "New connection from %s", ba); + +		close(nsk); +		exit(0); +	} + +	if (use_sdp) +		dun_sdp_unregister(); +	return 0; +} + +/* Connect and initiate RFCOMM session + * Returns: + *   -1 - critical error (exit persist mode) + *   1  - non critical error + *   0  - success + */ +static int create_connection(char *dst, bdaddr_t *bdaddr, int mrouter) +{ +	struct sockaddr_rc sa; +	int sk, err = 0, ch; + +	if (use_cache && cache.valid && cache.channel) { +		/* Use cached channel */ +		ch = cache.channel; + +	} else if (!channel) { +		syslog(LOG_INFO, "Searching for %s on %s", mrouter ? "SP" : "LAP", dst); + +		if (dun_sdp_search(&src_addr, bdaddr, &ch, mrouter) <= 0) +			return 0; +	} else +		ch = channel; + +	syslog(LOG_INFO, "Connecting to %s channel %d", dst, ch); + +	sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); +	if (sk < 0) { +		syslog(LOG_ERR, "Cannot create RFCOMM socket. %s(%d)", +		     strerror(errno), errno); +		return -1; +	} + +	sa.rc_family  = AF_BLUETOOTH; +	sa.rc_channel = 0; +	sa.rc_bdaddr  = src_addr; + +	if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) +		syslog(LOG_ERR, "Bind failed. %s(%d)",  +			strerror(errno), errno); + +	sa.rc_channel = ch; +	sa.rc_bdaddr  = *bdaddr; + +	if (!connect(sk, (struct sockaddr *) &sa, sizeof(sa)) ) { +		if (mrouter) { +			sleep(1); +			close(sk); +			return 0; +		} + +		syslog(LOG_INFO, "Connection established"); + +		if (msdun && ms_dun(sk, 0, msdun) < 0) { +			syslog(LOG_ERR, "MSDUN failed. %s(%d)", strerror(errno), errno); +			err = 1; +			goto out; +		} + +		if (!dun_open_connection(sk, pppd, pppd_opts, (persist > 0))) +			err = 0; +		else +			err = 1; +	} else { +		syslog(LOG_ERR, "Connect to %s failed. %s(%d)", +				dst, strerror(errno), errno); +		err = 1; +	} + +out: +	if (use_cache) { +		if (!err) { +			/* Succesesful connection, validate cache */ +			strcpy(cache.dst, dst); +			bacpy(&cache.bdaddr, bdaddr); +			cache.channel = ch; +			cache.valid   = use_cache; +		} else { +			cache.channel = 0; +			cache.valid--; +		} +	} + +	close(sk); +	return err; +} + +/* Search and connect + * Returns: + *   -1 - critical error (exit persist mode) + *   1  - non critical error + *   0  - success + */ +static int do_connect(void) +{ +	inquiry_info *ii; +	int reconnect = 0; +	int i, n, r = 0; + +	do { +		if (reconnect) +			sleep(persist); +		reconnect = 1; + +		if (cache.valid) { +			/* Use cached bdaddr */ +			r = create_connection(cache.dst, &cache.bdaddr, 0); +			if (r < 0) { +				terminate = 1; +				break; +			} +			continue; +		} + +		syslog(LOG_INFO, "Inquiring"); + +		/* FIXME: Should we use non general LAP here ? */ + +		ii = NULL; +		n  = hci_inquiry(src_dev, search_duration, 0, NULL, &ii, 0); +		if (n < 0) { +			syslog(LOG_ERR, "Inquiry failed. %s(%d)", strerror(errno), errno); +			continue; +		} + +		for (i = 0; i < n; i++) { +			char dst[40]; +			ba2str(&ii[i].bdaddr, dst); +			 +			r = create_connection(dst, &ii[i].bdaddr, 0); +			if (r < 0) { +				terminate = 1; +				break; +			} +		} +		bt_free(ii); +	} while (!terminate && persist); + +	return r; +} + +static void do_show(void) +{ +	dun_show_connections(); +} + +static void do_kill(char *dst) +{ +	if (dst) { +		bdaddr_t ba; +		str2ba(dst, &ba); +		dun_kill_connection((void *) &ba); +	} else +		dun_kill_all_connections(); +} + +void sig_hup(int sig) +{ +	return; +} + +void sig_term(int sig) +{ +	io_cancel(); +	terminate = 1; +} + +static struct option main_lopts[] = { +	{ "help",	0, 0, 'h' }, +	{ "listen",	0, 0, 's' }, +	{ "connect",	1, 0, 'c' }, +	{ "search",	2, 0, 'Q' }, +	{ "kill",	1, 0, 'k' }, +	{ "killall",	0, 0, 'K' }, +	{ "channel",	1, 0, 'P' }, +	{ "device",	1, 0, 'i' }, +	{ "nosdp",	0, 0, 'D' }, +	{ "list",	0, 0, 'l' }, +	{ "show",	0, 0, 'l' }, +	{ "nodetach",	0, 0, 'n' }, +	{ "persist",	2, 0, 'p' }, +	{ "auth",	0, 0, 'A' }, +	{ "encrypt",	0, 0, 'E' }, +	{ "secure",	0, 0, 'S' }, +	{ "master",	0, 0, 'M' }, +	{ "cache",	0, 0, 'C' }, +	{ "pppd",	1, 0, 'd' }, +	{ "msdun",	2, 0, 'X' }, +	{ "activesync",	0, 0, 'a' }, +	{ "mrouter",	1, 0, 'm' }, +	{ "dialup",	0, 0, 'u' }, +	{ 0, 0, 0, 0 } +}; + +static char main_sopts[] = "hsc:k:Kr:i:lnp::DQ::AESMP:C::P:Xam:u"; + +static char main_help[] =  +	"Bluetooth LAP (LAN Access over PPP) daemon version " VERSION " \n" +	"Usage:\n" +	"\tdund <options> [pppd options]\n" +	"Options:\n" +	"\t--show --list -l          Show active LAP connections\n" +	"\t--listen -s               Listen for LAP connections\n" +	"\t--dialup -u               Pretend to be a dialup/telephone\n" +	"\t--connect -c <bdaddr>     Create LAP connection\n" +	"\t--mrouter -m <bdaddr>     Create mRouter connection\n" +	"\t--search -Q[duration]     Search and connect\n" +	"\t--kill -k <bdaddr>        Kill LAP connection\n" +	"\t--killall -K              Kill all LAP connections\n" +	"\t--channel -P <channel>    RFCOMM channel\n" +	"\t--device -i <bdaddr>      Source bdaddr\n" +	"\t--nosdp -D                Disable SDP\n" +	"\t--auth -A                 Enable authentication\n" +	"\t--encrypt -E              Enable encryption\n" +	"\t--secure -S               Secure connection\n" +	"\t--master -M               Become the master of a piconet\n" +	"\t--nodetach -n             Do not become a daemon\n" +	"\t--persist -p[interval]    Persist mode\n" +	"\t--pppd -d <pppd>          Location of the PPP daemon (pppd)\n" +	"\t--msdun -X[timeo]         Enable Microsoft dialup networking support\n" +	"\t--activesync -a           Enable Microsoft ActiveSync networking\n" +	"\t--cache -C[valid]         Enable address cache\n"; + +int main(int argc, char *argv[]) +{ +	char *dst = NULL, *src = NULL; +	struct sigaction sa; +	int mode = NONE; +	int opt; + +	while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) { +		switch(opt) { +		case 'l': +			mode = SHOW; +			detach = 0; +			break; + +		case 's': +			mode = LISTEN; +			type = LANACCESS; +			break; + +		case 'c': +			mode = CONNECT; +			dst  = strdup(optarg); +			break; + +		case 'Q': +			mode = CONNECT; +			dst  = NULL; +			if (optarg) +				search_duration = atoi(optarg); +			break; + +		case 'k': +			mode = KILL; +			detach = 0; +			dst  = strdup(optarg); +			break; + +		case 'K': +			mode = KILL; +			detach = 0; +			dst  = NULL; +			break; + +		case 'P': +			channel = atoi(optarg); +			break; + +		case 'i': +			src = strdup(optarg); +			break; + +		case 'D': +			use_sdp = 0; +			break; + +		case 'A': +			auth = 1; +			break; + +		case 'E': +			encrypt = 1; +			break; + +		case 'S': +			secure = 1; +			break; + +		case 'M': +			master = 1; +			break; + +		case 'n': +			detach = 0; +			break; + +		case 'p': +			if (optarg) +				persist = atoi(optarg); +			else +				persist = 5; +			break; + +		case 'C': +			if (optarg) +				use_cache = atoi(optarg); +			else +				use_cache = 2; +			break; + +		case 'd': +			pppd  = strdup(optarg); +			break; + +		case 'X': +			if (optarg) +				msdun = atoi(optarg); +			else +				msdun = 10; +			break; + +		case 'a': +			msdun = 10; +			type = ACTIVESYNC; +			break; + +		case 'm': +			mode = LISTEN; +			dst  = strdup(optarg); +			type = MROUTER; +			break; + +		case 'u': +			mode = LISTEN; +			type = DIALUP; +			break; + +		case 'h': +		default: +			printf(main_help); +			exit(0); +		} +	} + +	argc -= optind; +	argv += optind; + +	/* The rest is pppd options */ +	if (argc > 0) { +		for (opt = 3; argc && opt < DUN_MAX_PPP_OPTS; argc--, opt++) +			pppd_opts[opt] = *argv++; +		pppd_opts[opt] = NULL; +	} + +	io_init(); + +	if (dun_init()) +		return -1; + +	/* Check non daemon modes first */ +	switch (mode) { +	case SHOW: +		do_show(); +		return 0; + +	case KILL: +		do_kill(dst); +		return 0; + +	case NONE: +		printf(main_help); +		return 0; +	} + +	/* Initialize signals */ +	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); + +	if (detach && daemon(0, 0)) { +		perror("Can't start daemon"); +		exit(1); +	} + +	openlog("dund", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON); +	syslog(LOG_INFO, "Bluetooth DUN daemon version %s", VERSION); + +	if (src) { +		src_dev = hci_devid(src); +		if (src_dev < 0 || hci_devba(src_dev, &src_addr) < 0) { +			syslog(LOG_ERR, "Invalid source. %s(%d)", strerror(errno), errno); +			return -1; +		} +	} + +	if (dst) { +		strncpy(cache.dst, dst, sizeof(cache.dst) - 1); +		str2ba(dst, &cache.bdaddr); + +		/* Disable cache invalidation */ +		use_cache = cache.valid = ~0; +	} + +	switch (mode) { +	case CONNECT: +		do_connect(); +		break; + +	case LISTEN: +		do_listen(); +		break; +	} + +	return 0; +} diff --git a/compat/dund.h b/compat/dund.h new file mode 100644 index 00000000..c3308e1e --- /dev/null +++ b/compat/dund.h @@ -0,0 +1,40 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com> + *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#define DUN_CONFIG_DIR	"/etc/bluetooth/dun" + +#define DUN_DEFAULT_CHANNEL	1 + +#define DUN_MAX_PPP_OPTS	40 + +int dun_init(void); +int dun_cleanup(void); + +int dun_show_connections(void); +int dun_kill_connection(uint8_t *dst); +int dun_kill_all_connections(void); + +int dun_open_connection(int sk, char *pppd, char **pppd_opts, int wait); + +int ms_dun(int fd, int server, int timeo); diff --git a/compat/fakehid.c b/compat/fakehid.c new file mode 100644 index 00000000..feefe2a6 --- /dev/null +++ b/compat/fakehid.c @@ -0,0 +1,669 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2003-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#define _GNU_SOURCE +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/poll.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> +#include <bluetooth/hidp.h> + +#include "hidd.h" +#include "uinput.h" + +#include <math.h> + +#ifdef NEED_PPOLL +#include "ppoll.h" +#endif + +static volatile sig_atomic_t __io_canceled = 0; + +static void sig_hup(int sig) +{ +} + +static void sig_term(int sig) +{ +	__io_canceled = 1; +} + +static void send_event(int fd, uint16_t type, uint16_t code, int32_t value) +{ +	struct uinput_event event; +	int len; + +	if (fd <= fileno(stderr)) +		return; + +	memset(&event, 0, sizeof(event)); +	event.type = type; +	event.code = code; +	event.value = value; + +	len = write(fd, &event, sizeof(event)); +} + +static int uinput_create(char *name, int keyboard, int mouse) +{ +	struct uinput_dev dev; +	int fd, aux; + +	fd = open("/dev/uinput", O_RDWR); +	if (fd < 0) { +		fd = open("/dev/input/uinput", O_RDWR); +		if (fd < 0) { +			fd = open("/dev/misc/uinput", O_RDWR); +			if (fd < 0) { +				fprintf(stderr, "Can't open input device: %s (%d)\n", +							strerror(errno), errno); +				return -1; +			} +		} +	} + +	memset(&dev, 0, sizeof(dev)); + +	if (name) +		strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE); + +	dev.id.bustype = BUS_BLUETOOTH; +	dev.id.vendor  = 0x0000; +	dev.id.product = 0x0000; +	dev.id.version = 0x0000; + +	if (write(fd, &dev, sizeof(dev)) < 0) { +		fprintf(stderr, "Can't write device information: %s (%d)\n", +							strerror(errno), errno); +		close(fd); +		return -1; +	} + +	if (mouse) { +		ioctl(fd, UI_SET_EVBIT, EV_REL); + +		for (aux = REL_X; aux <= REL_MISC; aux++) +			ioctl(fd, UI_SET_RELBIT, aux); +	} + +	if (keyboard) { +		ioctl(fd, UI_SET_EVBIT, EV_KEY); +		ioctl(fd, UI_SET_EVBIT, EV_LED); +		ioctl(fd, UI_SET_EVBIT, EV_REP); + +		for (aux = KEY_RESERVED; aux <= KEY_UNKNOWN; aux++) +			ioctl(fd, UI_SET_KEYBIT, aux); + +		//for (aux = LED_NUML; aux <= LED_MISC; aux++) +		//	ioctl(fd, UI_SET_LEDBIT, aux); +	} + +	if (mouse) { +		ioctl(fd, UI_SET_EVBIT, EV_KEY); + +		for (aux = BTN_LEFT; aux <= BTN_BACK; aux++) +			ioctl(fd, UI_SET_KEYBIT, aux); +	} + +	ioctl(fd, UI_DEV_CREATE); + +	return fd; +} + +static int rfcomm_connect(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel) +{ +	struct sockaddr_rc addr; +	int sk; + +	sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); +	if (sk < 0) { +		fprintf(stderr, "Can't create socket: %s (%d)\n", +							strerror(errno), errno); +		return -1; +	} + +	memset(&addr, 0, sizeof(addr)); +	addr.rc_family = AF_BLUETOOTH; +	bacpy(&addr.rc_bdaddr, src); + +	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { +		fprintf(stderr, "Can't bind socket: %s (%d)\n", +							strerror(errno), errno); +		close(sk); +		return -1; +	} + +	memset(&addr, 0, sizeof(addr)); +	addr.rc_family = AF_BLUETOOTH; +	bacpy(&addr.rc_bdaddr, dst); +	addr.rc_channel = channel; + +	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { +		fprintf(stderr, "Can't connect: %s (%d)\n", +							strerror(errno), errno); +		close(sk); +		return -1; +	} + +	return sk; +} + +static void func(int fd) +{ +} + +static void back(int fd) +{ +} + +static void next(int fd) +{ +} + +static void button(int fd, unsigned int button, int is_press) +{ +	switch (button) { +	case 1: +		send_event(fd, EV_KEY, BTN_LEFT, is_press); +		break; +	case 3: +		send_event(fd, EV_KEY, BTN_RIGHT, is_press); +		break; +	} + +	send_event(fd, EV_SYN, SYN_REPORT, 0); +} + +static void move(int fd, unsigned int direction) +{ +	double angle; +	int32_t x, y; + +	angle = (direction * 22.5) * 3.1415926 / 180; +	x = (int) (sin(angle) * 8); +	y = (int) (cos(angle) * -8); + +	send_event(fd, EV_REL, REL_X, x); +	send_event(fd, EV_REL, REL_Y, y); + +	send_event(fd, EV_SYN, SYN_REPORT, 0); +} + +static inline void epox_decode(int fd, unsigned char event) +{ +	switch (event) { +	case 48: +		func(fd); break; +	case 55: +		back(fd); break; +	case 56: +		next(fd); break; +	case 53: +		button(fd, 1, 1); break; +	case 121: +		button(fd, 1, 0); break; +	case 113: +		break; +	case 54: +		button(fd, 3, 1); break; +	case 120: +		button(fd, 3, 0); break; +	case 112: +		break; +	case 51: +		move(fd, 0); break; +	case 97: +		move(fd, 1); break; +	case 65: +		move(fd, 2); break; +	case 98: +		move(fd, 3); break; +	case 50: +		move(fd, 4); break; +	case 99: +		move(fd, 5); break; +	case 67: +		move(fd, 6); break; +	case 101: +		move(fd, 7); break; +	case 52: +		move(fd, 8); break; +	case 100: +		move(fd, 9); break; +	case 66: +		move(fd, 10); break; +	case 102: +		move(fd, 11); break; +	case 49: +		move(fd, 12); break; +	case 103: +		move(fd, 13); break; +	case 57: +		move(fd, 14); break; +	case 104: +		move(fd, 15); break; +	case 69: +		break; +	default: +		printf("Unknown event code %d\n", event); +		break; +	} +} + +int epox_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel) +{ +	unsigned char buf[16]; +	struct sigaction sa; +	struct pollfd p; +	sigset_t sigs; +	char addr[18]; +	int i, fd, sk, len; + +	sk = rfcomm_connect(src, dst, channel); +	if (sk < 0) +		return -1; + +	fd = uinput_create("Bluetooth Presenter", 0, 1); +	if (fd < 0) { +		close(sk); +		return -1; +	} + +	ba2str(dst, addr); + +	printf("Connected to %s on channel %d\n", addr, channel); +	printf("Press CTRL-C for hangup\n"); + +	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); + +	sigfillset(&sigs); +	sigdelset(&sigs, SIGCHLD); +	sigdelset(&sigs, SIGPIPE); +	sigdelset(&sigs, SIGTERM); +	sigdelset(&sigs, SIGINT); +	sigdelset(&sigs, SIGHUP); + +	p.fd = sk; +	p.events = POLLIN | POLLERR | POLLHUP; + +	while (!__io_canceled) { +		p.revents = 0; +		if (ppoll(&p, 1, NULL, &sigs) < 1) +			continue; + +		len = read(sk, buf, sizeof(buf)); +		if (len < 0) +			break; + +		for (i = 0; i < len; i++) +			epox_decode(fd, buf[i]); +	} + +	printf("Disconnected\n"); + +	ioctl(fd, UI_DEV_DESTROY); + +	close(fd); +	close(sk); + +	return 0; +} + +int headset_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel) +{ +	printf("Not implemented\n"); +	return -1; +} + +/* The strange meta key close to Ctrl has been assigned to Esc, +   Fn key to CtrlR and the left space to Alt*/ + +static unsigned char jthree_keycodes[63] = { +	KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, +	KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, +	KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, +	KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, +	KEY_LEFTALT, KEY_TAB, KEY_CAPSLOCK, KEY_ESC, +	KEY_7, KEY_8, KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, +	KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE, +	KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_ENTER, +	KEY_N, KEY_M, KEY_COMMA, KEY_DOT, KEY_SLASH, KEY_UP, +	KEY_SPACE, KEY_COMPOSE, KEY_LEFT, KEY_DOWN, KEY_RIGHT, +	KEY_LEFTCTRL, KEY_RIGHTSHIFT, KEY_LEFTSHIFT, KEY_DELETE, KEY_RIGHTCTRL, KEY_RIGHTALT, +}; + +static inline void jthree_decode(int fd, unsigned char event) +{ +	if (event > 63) +		send_event(fd, EV_KEY, jthree_keycodes[event & 0x3f], 0); +	else +		send_event(fd, EV_KEY, jthree_keycodes[event - 1], 1); +} + +int jthree_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel) +{ +	unsigned char buf[16]; +	struct sigaction sa; +	struct pollfd p; +	sigset_t sigs; +	char addr[18]; +	int i, fd, sk, len; + +	sk = rfcomm_connect(src, dst, channel); +	if (sk < 0) +		return -1; + +	fd = uinput_create("J-Three Keyboard", 1, 0); +	if (fd < 0) { +		close(sk); +		return -1; +	} + +	ba2str(dst, addr); + +	printf("Connected to %s on channel %d\n", addr, channel); +	printf("Press CTRL-C for hangup\n"); + +	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); + +	sigfillset(&sigs); +	sigdelset(&sigs, SIGCHLD); +	sigdelset(&sigs, SIGPIPE); +	sigdelset(&sigs, SIGTERM); +	sigdelset(&sigs, SIGINT); +	sigdelset(&sigs, SIGHUP); + +	p.fd = sk; +	p.events = POLLIN | POLLERR | POLLHUP; + +	while (!__io_canceled) { +		p.revents = 0; +		if (ppoll(&p, 1, NULL, &sigs) < 1) +			continue; + +		len = read(sk, buf, sizeof(buf)); +		if (len < 0) +			break; + +		for (i = 0; i < len; i++) +			jthree_decode(fd, buf[i]); +	} + +	printf("Disconnected\n"); + +	ioctl(fd, UI_DEV_DESTROY); + +	close(fd); +	close(sk); + +	return 0; +} + +static const int celluon_xlate_num[10] = { +	KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9 +}; + +static const int celluon_xlate_char[26] = { +	KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, +	KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, +	KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z +}; + +static int celluon_xlate(int c) +{ +	if (c >= '0' && c <= '9') +		return celluon_xlate_num[c - '0']; + +	if (c >= 'A' && c <= 'Z') +		return celluon_xlate_char[c - 'A']; + +	switch (c) { +	case 0x08: +		return KEY_BACKSPACE; +	case 0x09: +		return KEY_TAB; +	case 0x0d: +		return KEY_ENTER; +	case 0x11: +		return KEY_LEFTCTRL; +	case 0x14: +		return KEY_CAPSLOCK; +	case 0x20: +		return KEY_SPACE; +	case 0x25: +		return KEY_LEFT; +	case 0x26: +		return KEY_UP; +	case 0x27: +		return KEY_RIGHT; +	case 0x28: +		return KEY_DOWN; +	case 0x2e: +		return KEY_DELETE; +	case 0x5b: +		return KEY_MENU; +	case 0xa1: +		return KEY_RIGHTSHIFT; +	case 0xa0: +		return KEY_LEFTSHIFT; +	case 0xba: +		return KEY_SEMICOLON; +	case 0xbd: +		return KEY_MINUS; +	case 0xbc: +		return KEY_COMMA; +	case 0xbb: +		return KEY_EQUAL; +	case 0xbe: +		return KEY_DOT; +	case 0xbf: +		return KEY_SLASH; +	case 0xc0: +		return KEY_GRAVE; +	case 0xdb: +		return KEY_LEFTBRACE; +	case 0xdc: +		return KEY_BACKSLASH; +	case 0xdd: +		return KEY_RIGHTBRACE; +	case 0xde: +		return KEY_APOSTROPHE; +	case 0xff03: +		return KEY_HOMEPAGE; +	case 0xff04: +		return KEY_TIME; +	case 0xff06: +		return KEY_OPEN; +	case 0xff07: +		return KEY_LIST; +	case 0xff08: +		return KEY_MAIL; +	case 0xff30: +		return KEY_CALC; +	case 0xff1a: /* Map FN to ALT */ +		return KEY_LEFTALT; +	case 0xff2f: +		return KEY_INFO; +	default: +		printf("Unknown key %x\n", c); +		return c; +	} +} + +struct celluon_state { +	int len;	/* Expected length of current packet */ +	int count;	/* Number of bytes received */ +	int action; +	int key; +}; + +static void celluon_decode(int fd, struct celluon_state *s, uint8_t c) +{ +	if (s->count < 2 && c != 0xa5) { +		/* Lost Sync */ +		s->count = 0; +		return; +	} + +	switch (s->count) { +	case 0: +		/* New packet - Reset state */ +		s->len = 30; +		s->key = 0; +		break; +	case 1: +		break; +	case 6: +		s->action = c; +		break; +	case 28: +		s->key = c; +		if (c == 0xff) +			s->len = 31; +		break; +	case 29: +	case 30: +		if (s->count == s->len - 1) { +			/* TODO: Verify checksum */ +			if (s->action < 2) { +				send_event(fd, EV_KEY, celluon_xlate(s->key), +								s->action); +			} +			s->count = -1; +		} else { +			s->key = (s->key << 8) | c; +		} +		break; +	} + +	s->count++; + +	return; +} + +int celluon_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel) +{ +	unsigned char buf[16]; +	struct sigaction sa; +	struct pollfd p; +	sigset_t sigs; +	char addr[18]; +	int i, fd, sk, len; +	struct celluon_state s; + +	sk = rfcomm_connect(src, dst, channel); +	if (sk < 0) +		return -1; + +	fd = uinput_create("Celluon Keyboard", 1, 0); +	if (fd < 0) { +		close(sk); +		return -1; +	} + +	ba2str(dst, addr); + +	printf("Connected to %s on channel %d\n", addr, channel); +	printf("Press CTRL-C for hangup\n"); + +	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); + +	sigfillset(&sigs); +	sigdelset(&sigs, SIGCHLD); +	sigdelset(&sigs, SIGPIPE); +	sigdelset(&sigs, SIGTERM); +	sigdelset(&sigs, SIGINT); +	sigdelset(&sigs, SIGHUP); + +	p.fd = sk; +	p.events = POLLIN | POLLERR | POLLHUP; + +	memset(&s, 0, sizeof(s)); + +	while (!__io_canceled) { +		p.revents = 0; +		if (ppoll(&p, 1, NULL, &sigs) < 1) +			continue; + +		len = read(sk, buf, sizeof(buf)); +		if (len < 0) +			break; + +		for (i = 0; i < len; i++) +			celluon_decode(fd, &s, buf[i]); +	} + +	printf("Disconnected\n"); + +	ioctl(fd, UI_DEV_DESTROY); + +	close(fd); +	close(sk); + +	return 0; +} diff --git a/compat/fakehid.txt b/compat/fakehid.txt new file mode 100644 index 00000000..000d0ee2 --- /dev/null +++ b/compat/fakehid.txt @@ -0,0 +1,134 @@ +EPox Presenter +============== + +# hcitool inq +Inquiring ... +        00:04:61:aa:bb:cc       clock offset: 0x1ded    class: 0x004000 + +# hcitool info 00:04:61:aa:bb:cc +Requesting information ... +        BD Address:  00:04:61:aa:bb:cc +        OUI Company: EPOX Computer Co., Ltd. (00-04-61) +        Device Name: EPox BT-PM01B aabbcc +        LMP Version: 1.1 (0x1) LMP Subversion: 0xf78 +        Manufacturer: Cambridge Silicon Radio (10) +        Features: 0xff 0xff 0x0f 0x00 0x00 0x00 0x00 0x00 +                <3-slot packets> <5-slot packets> <encryption> <slot offset>  +                <timing accuracy> <role switch> <hold mode> <sniff mode>  +                <park state> <RSSI> <channel quality> <SCO link> <HV2 packets>  +                <HV3 packets> <u-law log> <A-law log> <CVSD> <paging scheme>  +                <power control> <transparent SCO>  + +# sdptool records --raw 00:04:61:aa:bb:cc +Sequence +        Attribute 0x0000 - ServiceRecordHandle +                UINT32 0x00010000 +        Attribute 0x0001 - ServiceClassIDList +                Sequence +                        UUID16 0x1101 - SerialPort +        Attribute 0x0004 - ProtocolDescriptorList +                Sequence +                        Sequence +                                UUID16 0x0100 - L2CAP +                        Sequence +                                UUID16 0x0003 - RFCOMM +                                UINT8 0x01 +        Attribute 0x0100 +                String Cable Replacement + + +J-Three Keyboard +================ + +# hcitool inq +Inquiring ... +        00:0A:3A:aa:bb:cc       clock offset: 0x3039    class: 0x001f00 + +# hcitool info 00:0A:3A:aa:bb:cc +Password: +Requesting information ... +        BD Address:  00:0A:3A:aa:bb:cc +        OUI Company: J-THREE INTERNATIONAL Holding Co., Ltd. (00-0A-3A) +        Device Name: KEYBOARD +        LMP Version: 1.1 (0x1) LMP Subversion: 0x2c2 +        Manufacturer: Cambridge Silicon Radio (10) +        Features: 0xbc 0x06 0x07 0x00 0x00 0x00 0x00 0x00 +                <encryption> <slot offset> <timing accuracy> <role switch>  +                <sniff mode> <RSSI> <channel quality> <CVSD> <paging scheme>  +                <power control>  + +# sdptool records --raw 00:0A:3A:aa:bb:cc +Sequence +        Attribute 0x0000 - ServiceRecordHandle +                UINT32 0x00010000 +        Attribute 0x0001 - ServiceClassIDList +                Sequence +                        UUID16 0x1101 - SerialPort +        Attribute 0x0004 - ProtocolDescriptorList +                Sequence +                        Sequence +                                UUID16 0x0100 - L2CAP +                        Sequence +                                UUID16 0x0003 - RFCOMM +                                UINT8 0x01 +        Attribute 0x0006 - LanguageBaseAttributeIDList +                Sequence +                        UINT16 0x656e +                        UINT16 0x006a +                        UINT16 0x0100 +        Attribute 0x0100 +                String SPP slave + + +Celluon Laserkey Keyboard +========================= + +# hcitool inq +Inquiring ... +       00:0B:24:aa:bb:cc       clock offset: 0x3ab6    class: 0x400210 + +# hcitool info 00:0B:24:aa:bb:cc +Requesting information ... +       BD Address:  00:0B:24:aa:bb:cc +       OUI Company: AirLogic (00-0B-24) +       Device Name: CL800BT +       LMP Version: 1.1 (0x1) LMP Subversion: 0x291 +       Manufacturer: Cambridge Silicon Radio (10) +       Features: 0xff 0xff 0x0f 0x00 0x00 0x00 0x00 0x00 +               <3-slot packets> <5-slot packets> <encryption> <slot offset> +               <timing accuracy> <role switch> <hold mode> <sniff mode> +               <park state> <RSSI> <channel quality> <SCO link> <HV2 packets> +               <HV3 packets> <u-law log> <A-law log> <CVSD> <paging scheme> +               <power control> <transparent SCO> + +# sdptool records --raw 00:0B:24:aa:bb:cc +Sequence +         Attribute 0x0000 - ServiceRecordHandle +                 UINT32 0x00010000 +         Attribute 0x0001 - ServiceClassIDList +                 Sequence +                         UUID16 0x1101 - SerialPort +         Attribute 0x0004 - ProtocolDescriptorList +                 Sequence +                         Sequence +                                 UUID16 0x0100 - L2CAP +                         Sequence +                                 UUID16 0x0003 - RFCOMM +                                 UINT8 0x01 +         Attribute 0x0100 +                 String Serial Port + +Packet format is as follows (all fields little-endian): +     0 uint16  magic            # 0x5a5a +     2 uint32  unknown          # ??? +     6 uint8   action           # 0 = keyup, 1 = keydown, 2 = repeat +                                # 3, 4, 5, 6 = ??? (Mouse mode) +     7 uint8   unknown[9]       # ??? +    16 uint8   action2          # ??? same as action +    17 uint16  x                # Horizontal coordinate +    19 uint16  y                # Vertical coordinate +    21 uint16  time             # Some sort of timestamp +    23 uint8   unknown[5]       # ??? +    28 uint8   key[]            # single byte keycode or 0xff byte +                                # follwed by special keycode byte. +    Each packet followed by a checksum byte. diff --git a/compat/hidd.1 b/compat/hidd.1 new file mode 100644 index 00000000..b186ac24 --- /dev/null +++ b/compat/hidd.1 @@ -0,0 +1,41 @@ +.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.33. +.TH HIDD "1" "May 2004" "hidd - Bluetooth HID daemon" "User Commands" +.SH NAME +hidd \- Bluetooth HID daemon +.SH DESCRIPTION +hidd - Bluetooth HID daemon +.SS "Usage:" +.IP +hidd [options] [commands] +.SH OPTIONS +.TP +\fB\-i\fR <hciX|bdaddr> +Local HCI device or BD Address +.TP +\fB\-t\fR <timeout> +Set idle timeout (in minutes) +.TP +\fB\-n\fR, \fB\-\-nodaemon\fR +Don't fork daemon to background +.TP +\fB\-h\fR, \fB\-\-help\fR +Display help +.SS "Commands:" +.TP +\fB\-\-server\fR +Start HID server +.TP +\fB\-\-search\fR +Search for HID devices +.TP +\fB\-\-connect\fR <bdaddr> +Connect remote HID device +.TP +\fB\-\-kill\fR <bdaddr> +Terminate HID connection +.TP +\fB\-\-killall\fR +Terminate all connections +.TP +\fB\-\-show\fR +List current HID connections diff --git a/compat/hidd.c b/compat/hidd.c new file mode 100644 index 00000000..6a0058c4 --- /dev/null +++ b/compat/hidd.c @@ -0,0 +1,861 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2003-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#define _GNU_SOURCE +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <signal.h> +#include <getopt.h> +#include <sys/poll.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/l2cap.h> +#include <bluetooth/sdp.h> +#include <bluetooth/hidp.h> + +#include "sdp.h" +#include "hidd.h" + +#ifdef NEED_PPOLL +#include "ppoll.h" +#endif + +enum { +	NONE, +	SHOW, +	SERVER, +	SEARCH, +	CONNECT, +	KILL +}; + +static volatile sig_atomic_t __io_canceled = 0; + +static void sig_hup(int sig) +{ +} + +static void sig_term(int sig) +{ +	__io_canceled = 1; +} + +static int l2cap_connect(bdaddr_t *src, bdaddr_t *dst, unsigned short psm) +{ +	struct sockaddr_l2 addr; +	struct l2cap_options opts; +	int sk; + +	if ((sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) +		return -1; + +	memset(&addr, 0, sizeof(addr)); +	addr.l2_family  = AF_BLUETOOTH; +	bacpy(&addr.l2_bdaddr, src); + +	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { +		close(sk); +		return -1; +	} + +	memset(&opts, 0, sizeof(opts)); +	opts.imtu = HIDP_DEFAULT_MTU; +	opts.omtu = HIDP_DEFAULT_MTU; +	opts.flush_to = 0xffff; + +	setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)); + +	memset(&addr, 0, sizeof(addr)); +	addr.l2_family  = AF_BLUETOOTH; +	bacpy(&addr.l2_bdaddr, dst); +	addr.l2_psm = htobs(psm); + +	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { +		close(sk); +		return -1; +	} + +	return sk; +} + +static int l2cap_listen(const bdaddr_t *bdaddr, unsigned short psm, int lm, int backlog) +{ +	struct sockaddr_l2 addr; +	struct l2cap_options opts; +	int sk; + +	if ((sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) +		return -1; + +	memset(&addr, 0, sizeof(addr)); +	addr.l2_family = AF_BLUETOOTH; +	bacpy(&addr.l2_bdaddr, bdaddr); +	addr.l2_psm = htobs(psm); + +	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { +		close(sk); +		return -1; +	} + +	setsockopt(sk, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm)); + +	memset(&opts, 0, sizeof(opts)); +	opts.imtu = HIDP_DEFAULT_MTU; +	opts.omtu = HIDP_DEFAULT_MTU; +	opts.flush_to = 0xffff; + +	setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)); + +	if (listen(sk, backlog) < 0) { +		close(sk); +		return -1; +	} + +	return sk; +} + +static int l2cap_accept(int sk, bdaddr_t *bdaddr) +{ +	struct sockaddr_l2 addr; +	socklen_t addrlen; +	int nsk; + +	memset(&addr, 0, sizeof(addr)); +	addrlen = sizeof(addr); + +	if ((nsk = accept(sk, (struct sockaddr *) &addr, &addrlen)) < 0) +		return -1; + +	if (bdaddr) +		bacpy(bdaddr, &addr.l2_bdaddr); + +	return nsk; +} + +static int request_authentication(bdaddr_t *src, bdaddr_t *dst) +{ +	struct hci_conn_info_req *cr; +	char addr[18]; +	int err, dd, dev_id; + +	ba2str(src, addr); +	dev_id = hci_devid(addr); +	if (dev_id < 0) +		return dev_id; + +	dd = hci_open_dev(dev_id); +	if (dd < 0) +		return dd; + +	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); +	if (!cr) +		return -ENOMEM; + +	bacpy(&cr->bdaddr, dst); +	cr->type = ACL_LINK; +	err = ioctl(dd, HCIGETCONNINFO, (unsigned long) cr); +	if (err < 0) { +		free(cr); +		hci_close_dev(dd); +		return err; +	} + +	err = hci_authenticate_link(dd, htobs(cr->conn_info->handle), 25000); + +	free(cr); +	hci_close_dev(dd); + +	return err; +} + +static int request_encryption(bdaddr_t *src, bdaddr_t *dst) +{ +	struct hci_conn_info_req *cr; +	char addr[18]; +	int err, dd, dev_id; + +	ba2str(src, addr); +	dev_id = hci_devid(addr); +	if (dev_id < 0) +		return dev_id; + +	dd = hci_open_dev(dev_id); +	if (dd < 0) +		return dd; + +	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); +	if (!cr) +		return -ENOMEM; + +	bacpy(&cr->bdaddr, dst); +	cr->type = ACL_LINK; +	err = ioctl(dd, HCIGETCONNINFO, (unsigned long) cr); +	if (err < 0) { +		free(cr); +		hci_close_dev(dd); +		return err; +	} + +	err = hci_encrypt_link(dd, htobs(cr->conn_info->handle), 1, 25000); + +	free(cr); +	hci_close_dev(dd); + +	return err; +} + +static void enable_sixaxis(int csk) +{ +	const unsigned char buf[] = { +		0x53 /*HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE*/, +		0xf4,  0x42, 0x03, 0x00, 0x00 }; +	int err; + +	err = write(csk, buf, sizeof(buf)); +} + +static int create_device(int ctl, int csk, int isk, uint8_t subclass, int nosdp, int nocheck, int bootonly, int encrypt, int timeout) +{ +	struct hidp_connadd_req req; +	struct sockaddr_l2 addr; +	socklen_t addrlen; +	bdaddr_t src, dst; +	char bda[18]; +	int err; + +	memset(&addr, 0, sizeof(addr)); +	addrlen = sizeof(addr); + +	if (getsockname(csk, (struct sockaddr *) &addr, &addrlen) < 0) +		return -1; + +	bacpy(&src, &addr.l2_bdaddr); + +	memset(&addr, 0, sizeof(addr)); +	addrlen = sizeof(addr); + +	if (getpeername(csk, (struct sockaddr *) &addr, &addrlen) < 0) +		return -1; + +	bacpy(&dst, &addr.l2_bdaddr); + +	memset(&req, 0, sizeof(req)); +	req.ctrl_sock = csk; +	req.intr_sock = isk; +	req.flags     = 0; +	req.idle_to   = timeout * 60; + +	err = get_stored_device_info(&src, &dst, &req); +	if (!err) +		goto create; + +	if (!nocheck) { +		ba2str(&dst, bda); +		syslog(LOG_ERR, "Rejected connection from unknown device %s", bda); +		/* Return no error to avoid run_server() complaining too */ +		return 0; +	} + +	if (!nosdp) { +		err = get_sdp_device_info(&src, &dst, &req); +		if (err < 0) +			goto error; +	} else { +		struct l2cap_conninfo conn; +		socklen_t size; +		uint8_t class[3]; + +		memset(&conn, 0, sizeof(conn)); +		size = sizeof(conn); +		if (getsockopt(csk, SOL_L2CAP, L2CAP_CONNINFO, &conn, &size) < 0) +			memset(class, 0, 3); +		else +			memcpy(class, conn.dev_class, 3); + +		if (class[1] == 0x25 && (class[2] == 0x00 || class[2] == 0x01)) +			req.subclass = class[0]; +		else +			req.subclass = 0xc0; +	} + +create: +	if (subclass != 0x00) +		req.subclass = subclass; + +	ba2str(&dst, bda); +	syslog(LOG_INFO, "New HID device %s (%s)", bda, req.name); + +	if (encrypt && (req.subclass & 0x40)) { +		err = request_authentication(&src, &dst); +		if (err < 0) { +			syslog(LOG_ERR, "Authentication for %s failed", bda); +			goto error; +		} + +		err = request_encryption(&src, &dst); +		if (err < 0) +			syslog(LOG_ERR, "Encryption for %s failed", bda); +	} + +	if (bootonly) { +		req.rd_size = 0; +		req.flags |= (1 << HIDP_BOOT_PROTOCOL_MODE); +	} + +	if (req.vendor == 0x054c && req.product == 0x0268) +		enable_sixaxis(csk); + +	err = ioctl(ctl, HIDPCONNADD, &req); + +error: +	if (req.rd_data) +		free(req.rd_data); + +	return err; +} + +static void run_server(int ctl, int csk, int isk, uint8_t subclass, int nosdp, int nocheck, int bootonly, int encrypt, int timeout) +{ +	struct pollfd p[2]; +	sigset_t sigs; +	short events; +	int err, ncsk, nisk; + +	sigfillset(&sigs); +	sigdelset(&sigs, SIGCHLD); +	sigdelset(&sigs, SIGPIPE); +	sigdelset(&sigs, SIGTERM); +	sigdelset(&sigs, SIGINT); +	sigdelset(&sigs, SIGHUP); + +	p[0].fd = csk; +	p[0].events = POLLIN | POLLERR | POLLHUP; + +	p[1].fd = isk; +	p[1].events = POLLIN | POLLERR | POLLHUP; + +	while (!__io_canceled) { +		p[0].revents = 0; +		p[1].revents = 0; + +		if (ppoll(p, 2, NULL, &sigs) < 1) +			continue; + +		events = p[0].revents | p[1].revents; + +		if (events & POLLIN) { +			ncsk = l2cap_accept(csk, NULL); +			nisk = l2cap_accept(isk, NULL); + +			err = create_device(ctl, ncsk, nisk, subclass, nosdp, nocheck, bootonly, encrypt, timeout); +			if (err < 0) +				syslog(LOG_ERR, "HID create error %d (%s)", +						errno, strerror(errno)); + +			close(nisk); +			sleep(1); +			close(ncsk); +		} +	} +} + +static char *hidp_state[] = { +	"unknown", +	"connected", +	"open", +	"bound", +	"listening", +	"connecting", +	"connecting", +	"config", +	"disconnecting", +	"closed" +}; + +static char *hidp_flagstostr(uint32_t flags) +{ +	static char str[100]; +	str[0] = 0; + +	strcat(str, "["); + +	if (flags & (1 << HIDP_BOOT_PROTOCOL_MODE)) +		strcat(str, "boot-protocol"); + +	strcat(str, "]"); + +	return str; +} + +static void do_show(int ctl) +{ +	struct hidp_connlist_req req; +	struct hidp_conninfo ci[16]; +	char addr[18]; +	int i; + +	req.cnum = 16; +	req.ci   = ci; + +	if (ioctl(ctl, HIDPGETCONNLIST, &req) < 0) { +		perror("Can't get connection list"); +		close(ctl); +		exit(1); +	} + +	for (i = 0; i < req.cnum; i++) { +		ba2str(&ci[i].bdaddr, addr); +		printf("%s %s [%04x:%04x] %s %s\n", addr, ci[i].name, +			ci[i].vendor, ci[i].product, hidp_state[ci[i].state], +			ci[i].flags ? hidp_flagstostr(ci[i].flags) : ""); +	} +} + +static void do_connect(int ctl, bdaddr_t *src, bdaddr_t *dst, uint8_t subclass, int fakehid, int bootonly, int encrypt, int timeout) +{ +	struct hidp_connadd_req req; +	uint16_t uuid = HID_SVCLASS_ID; +	uint8_t channel = 0; +	char name[256]; +	int csk, isk, err; + +	memset(&req, 0, sizeof(req)); + +	err = get_sdp_device_info(src, dst, &req); +	if (err < 0 && fakehid) +		err = get_alternate_device_info(src, dst, +				&uuid, &channel, name, sizeof(name) - 1); + +	if (err < 0) { +		perror("Can't get device information"); +		close(ctl); +		exit(1); +	} + +	switch (uuid) { +	case HID_SVCLASS_ID: +		goto connect; + +	case SERIAL_PORT_SVCLASS_ID: +		if (subclass == 0x40 || !strcmp(name, "Cable Replacement")) { +			if (epox_presenter(src, dst, channel) < 0) { +				close(ctl); +				exit(1); +			} +			break; +		} +		if (subclass == 0x1f || !strcmp(name, "SPP slave")) { +			if (jthree_keyboard(src, dst, channel) < 0) { +				close(ctl); +				exit(1); +			} +			break; +		} +		if (subclass == 0x02 || !strcmp(name, "Serial Port")) { +			if (celluon_keyboard(src, dst, channel) < 0) { +				close(ctl); +				exit(1); +			} +			break; +		} +		break; + +	case HEADSET_SVCLASS_ID: +	case HANDSFREE_SVCLASS_ID: +		if (headset_presenter(src, dst, channel) < 0) { +			close(ctl); +			exit(1); +		} +		break; +	} + +	return; + +connect: +	csk = l2cap_connect(src, dst, L2CAP_PSM_HIDP_CTRL); +	if (csk < 0) { +		perror("Can't create HID control channel"); +		close(ctl); +		exit(1); +	} + +	isk = l2cap_connect(src, dst, L2CAP_PSM_HIDP_INTR); +	if (isk < 0) { +		perror("Can't create HID interrupt channel"); +		close(csk); +		close(ctl); +		exit(1); +	} + +	err = create_device(ctl, csk, isk, subclass, 1, 1, bootonly, encrypt, timeout); +	if (err < 0) { +		fprintf(stderr, "HID create error %d (%s)\n", +						errno, strerror(errno)); +		close(isk); +		sleep(1); +		close(csk); +		close(ctl); +		exit(1); +	} +} + +static void do_search(int ctl, bdaddr_t *bdaddr, uint8_t subclass, int fakehid, int bootonly, int encrypt, int timeout) +{ +	inquiry_info *info = NULL; +	bdaddr_t src, dst; +	int i, dev_id, num_rsp, length, flags; +	char addr[18]; +	uint8_t class[3]; + +	ba2str(bdaddr, addr); +	dev_id = hci_devid(addr); +	if (dev_id < 0) { +		dev_id = hci_get_route(NULL); +		hci_devba(dev_id, &src); +	} else +		bacpy(&src, bdaddr); + +	length  = 8;	/* ~10 seconds */ +	num_rsp = 0; +	flags   = IREQ_CACHE_FLUSH; + +	printf("Searching ...\n"); + +	num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags); + +	for (i = 0; i < num_rsp; i++) { +		memcpy(class, (info+i)->dev_class, 3); +		if (class[1] == 0x25 && (class[2] == 0x00 || class[2] == 0x01)) { +			bacpy(&dst, &(info+i)->bdaddr); +			ba2str(&dst, addr); + +			printf("\tConnecting to device %s\n", addr); +			do_connect(ctl, &src, &dst, subclass, fakehid, bootonly, encrypt, timeout); +		} +	} + +	if (!fakehid) +		goto done; + +	for (i = 0; i < num_rsp; i++) { +		memcpy(class, (info+i)->dev_class, 3); +		if ((class[0] == 0x00 && class[2] == 0x00 &&  +				(class[1] == 0x40 || class[1] == 0x1f)) || +				(class[0] == 0x10 && class[1] == 0x02 && class[2] == 0x40)) { +			bacpy(&dst, &(info+i)->bdaddr); +			ba2str(&dst, addr); + +			printf("\tConnecting to device %s\n", addr); +			do_connect(ctl, &src, &dst, subclass, 1, bootonly, 0, timeout); +		} +	} + +done: +	bt_free(info); + +	if (!num_rsp) { +		fprintf(stderr, "\tNo devices in range or visible\n"); +		close(ctl); +		exit(1); +	} +} + +static void do_kill(int ctl, bdaddr_t *bdaddr, uint32_t flags) +{ +	struct hidp_conndel_req req; +	struct hidp_connlist_req cl; +	struct hidp_conninfo ci[16]; +	int i; + +	if (!bacmp(bdaddr, BDADDR_ALL)) { +		cl.cnum = 16; +		cl.ci   = ci; + +		if (ioctl(ctl, HIDPGETCONNLIST, &cl) < 0) { +			perror("Can't get connection list"); +			close(ctl); +			exit(1); +		} + +		for (i = 0; i < cl.cnum; i++) { +			bacpy(&req.bdaddr, &ci[i].bdaddr); +			req.flags = flags; + +			if (ioctl(ctl, HIDPCONNDEL, &req) < 0) { +				perror("Can't release connection"); +				close(ctl); +				exit(1); +			} +		} + +	} else { +		bacpy(&req.bdaddr, bdaddr); +		req.flags = flags; + +		if (ioctl(ctl, HIDPCONNDEL, &req) < 0) { +			perror("Can't release connection"); +			close(ctl); +			exit(1); +		} +	} +} + +static void usage(void) +{ +	printf("hidd - Bluetooth HID daemon version %s\n\n", VERSION); + +	printf("Usage:\n" +		"\thidd [options] [commands]\n" +		"\n"); + +	printf("Options:\n" +		"\t-i <hciX|bdaddr>     Local HCI device or BD Address\n" +		"\t-t <timeout>         Set idle timeout (in minutes)\n" +		"\t-b <subclass>        Overwrite the boot mode subclass\n" +		"\t-n, --nodaemon       Don't fork daemon to background\n" +		"\t-h, --help           Display help\n" +		"\n"); + +	printf("Commands:\n" +		"\t--server             Start HID server\n" +		"\t--search             Search for HID devices\n" +		"\t--connect <bdaddr>   Connect remote HID device\n" +		"\t--unplug <bdaddr>    Unplug the HID connection\n" +		"\t--kill <bdaddr>      Terminate HID connection\n" +		"\t--killall            Terminate all connections\n" +		"\t--show               List current HID connections\n" +		"\n"); +} + +static struct option main_options[] = { +	{ "help",	0, 0, 'h' }, +	{ "nodaemon",	0, 0, 'n' }, +	{ "subclass",	1, 0, 'b' }, +	{ "timeout",	1, 0, 't' }, +	{ "device",	1, 0, 'i' }, +	{ "master",	0, 0, 'M' }, +	{ "encrypt",	0, 0, 'E' }, +	{ "nosdp",	0, 0, 'D' }, +	{ "nocheck",	0, 0, 'Z' }, +	{ "bootonly",	0, 0, 'B' }, +	{ "hidonly",	0, 0, 'H' }, +	{ "show",	0, 0, 'l' }, +	{ "list",	0, 0, 'l' }, +	{ "server",	0, 0, 'd' }, +	{ "listen",	0, 0, 'd' }, +	{ "search",	0, 0, 's' }, +	{ "create",	1, 0, 'c' }, +	{ "connect",	1, 0, 'c' }, +	{ "disconnect",	1, 0, 'k' }, +	{ "terminate",	1, 0, 'k' }, +	{ "release",	1, 0, 'k' }, +	{ "kill",	1, 0, 'k' }, +	{ "killall",	0, 0, 'K' }, +	{ "unplug",	1, 0, 'u' }, +	{ 0, 0, 0, 0 } +}; + +int main(int argc, char *argv[]) +{ +	struct sigaction sa; +	bdaddr_t bdaddr, dev; +	uint32_t flags = 0; +	uint8_t subclass = 0x00; +	char addr[18]; +	int log_option = LOG_NDELAY | LOG_PID; +	int opt, ctl, csk, isk; +	int mode = SHOW, detach = 1, nosdp = 0, nocheck = 0, bootonly = 0; +	int fakehid = 1, encrypt = 0, timeout = 30, lm = 0; + +	bacpy(&bdaddr, BDADDR_ANY); + +	while ((opt = getopt_long(argc, argv, "+i:nt:b:MEDZBHldsc:k:Ku:h", main_options, NULL)) != -1) { +		switch(opt) { +		case 'i': +			if (!strncasecmp(optarg, "hci", 3)) +				hci_devba(atoi(optarg + 3), &bdaddr); +			else +				str2ba(optarg, &bdaddr); +			break; +		case 'n': +			detach = 0; +			break; +		case 't': +			timeout = atoi(optarg); +			break; +		case 'b': +			if (!strncasecmp(optarg, "0x", 2)) +				subclass = (uint8_t) strtol(optarg, NULL, 16); +			else +				subclass = atoi(optarg); +			break; +		case 'M': +			lm |= L2CAP_LM_MASTER; +			break; +		case 'E': +			encrypt = 1; +			break; +		case 'D': +			nosdp = 1; +			break; +		case 'Z': +			nocheck = 1; +			break; +		case 'B': +			bootonly = 1; +			break; +		case 'H': +			fakehid = 0; +			break; +		case 'l': +			mode = SHOW; +			break; +		case 'd': +			mode = SERVER; +			break; +		case 's': +			mode = SEARCH; +			break; +		case 'c': +			str2ba(optarg, &dev); +			mode = CONNECT; +			break; +		case 'k': +			str2ba(optarg, &dev); +			mode = KILL; +			break; +		case 'K': +			bacpy(&dev, BDADDR_ALL); +			mode = KILL; +			break; +		case 'u': +			str2ba(optarg, &dev); +			flags = (1 << HIDP_VIRTUAL_CABLE_UNPLUG); +			mode = KILL; +			break; +		case 'h': +			usage(); +			exit(0); +		default: +			exit(0); +		} +	} + +	ba2str(&bdaddr, addr); + +	ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP); +	if (ctl < 0) { +		perror("Can't open HIDP control socket"); +		exit(1); +	} + +	switch (mode) { +	case SERVER: +		csk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_CTRL, lm, 10); +		if (csk < 0) { +			perror("Can't listen on HID control channel"); +			close(ctl); +			exit(1); +		} + +		isk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_INTR, lm, 10); +		if (isk < 0) { +			perror("Can't listen on HID interrupt channel"); +			close(ctl); +			close(csk); +			exit(1); +		} +		break; + +	case SEARCH: +		do_search(ctl, &bdaddr, subclass, fakehid, bootonly, encrypt, timeout); +		close(ctl); +		exit(0); + +	case CONNECT: +		do_connect(ctl, &bdaddr, &dev, subclass, fakehid, bootonly, encrypt, timeout); +		close(ctl); +		exit(0); + +	case KILL: +		do_kill(ctl, &dev, flags); +		close(ctl); +		exit(0); + +	default: +		do_show(ctl); +		close(ctl); +		exit(0); +	} + +        if (detach) { +		if (daemon(0, 0)) { +			perror("Can't start daemon"); +        	        exit(1); +		} +	} else +		log_option |= LOG_PERROR; + +	openlog("hidd", log_option, LOG_DAEMON); + +	if (bacmp(&bdaddr, BDADDR_ANY)) +		syslog(LOG_INFO, "Bluetooth HID daemon (%s)", addr); +	else +		syslog(LOG_INFO, "Bluetooth HID daemon"); + +	memset(&sa, 0, sizeof(sa)); +	sa.sa_flags = SA_NOCLDSTOP; + +	sa.sa_handler = sig_term; +	sigaction(SIGTERM, &sa, NULL); +	sigaction(SIGINT,  &sa, NULL); +	sa.sa_handler = sig_hup; +	sigaction(SIGHUP, &sa, NULL); + +	sa.sa_handler = SIG_IGN; +	sigaction(SIGCHLD, &sa, NULL); +	sigaction(SIGPIPE, &sa, NULL); + +	run_server(ctl, csk, isk, subclass, nosdp, nocheck, bootonly, encrypt, timeout); + +	syslog(LOG_INFO, "Exit"); + +	close(csk); +	close(isk); +	close(ctl); + +	return 0; +} diff --git a/compat/hidd.h b/compat/hidd.h new file mode 100644 index 00000000..ba94bcf4 --- /dev/null +++ b/compat/hidd.h @@ -0,0 +1,30 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2003-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#define L2CAP_PSM_HIDP_CTRL 0x11 +#define L2CAP_PSM_HIDP_INTR 0x13 + +int epox_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel); +int headset_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel); +int jthree_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel); +int celluon_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel); diff --git a/compat/lib.h b/compat/lib.h new file mode 100644 index 00000000..b070ee40 --- /dev/null +++ b/compat/lib.h @@ -0,0 +1,86 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com> + *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#include <sys/types.h> +#include <errno.h> +#include <signal.h> + +#ifndef min +#define min(a,b)    ( (a)<(b) ? (a):(b) ) +#endif + +/* IO cancelation */ +extern volatile sig_atomic_t __io_canceled; + +static inline void io_init(void) +{ +	__io_canceled = 0; +} + +static inline void io_cancel(void) +{ +	__io_canceled = 1; +} + +/* Read exactly len bytes (Signal safe)*/ +static inline int read_n(int fd, char *buf, int len) +{ +	register int t = 0, w; + +	while (!__io_canceled && len > 0) { +		if ((w = read(fd, buf, len)) < 0) { +			if (errno == EINTR || errno == EAGAIN) +				continue; +			return -1; +		} +		if (!w) +			return 0; +		len -= w; +		buf += w; +		t += w; +	} + +	return t; +} + +/* Write exactly len bytes (Signal safe)*/ +static inline int write_n(int fd, char *buf, int len) +{ +	register int t = 0, w; + +	while (!__io_canceled && len > 0) { +		if ((w = write(fd, buf, len)) < 0) { +			if (errno == EINTR || errno == EAGAIN) +				continue; +			return -1; +		} +		if (!w) +			return 0; +		len -= w; +		buf += w; +		t += w; +	} + +	return t; +} diff --git a/compat/msdun.c b/compat/msdun.c new file mode 100644 index 00000000..1ef559f1 --- /dev/null +++ b/compat/msdun.c @@ -0,0 +1,151 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com> + *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdint.h> +#include <stdlib.h> +#include <syslog.h> +#include <setjmp.h> +#include <string.h> + +#include "lib.h" + +#define MS_PPP      2 +#define MS_SUCCESS  1 +#define MS_FAILED  -1 +#define MS_TIMEOUT -2 + +static sigjmp_buf jmp; +static int        retry; +static int        timeout; + +static void sig_alarm(int sig) +{ +	siglongjmp(jmp, MS_TIMEOUT); +} + +static int w4_str(int fd, char *str) +{ +	char buf[40]; +	int  r, len = 0; +	 +	while (1) { +		r = read(fd, buf + len, sizeof(buf) - len - 1); +		if (r < 0) { +			if (errno == EINTR || errno == EAGAIN) +				continue; +			break; +		} +		if (!r) +			break; + +		len += r; + +		if (len < strlen(str)) +			continue; +		buf[len] = 0; + +		if (strstr(buf, str)) +			return MS_SUCCESS; + +		/* Detect PPP */ +		if (strchr(buf, '~')) +			return MS_PPP; +	} +	return MS_FAILED; +} + +static int ms_server(int fd) +{ +	switch (w4_str(fd, "CLIENT")) { +	case MS_SUCCESS: +		write_n(fd, "CLIENTSERVER", 12); +	case MS_PPP: +		return MS_SUCCESS; +	default:	 +		return MS_FAILED; +	} +} + +static int ms_client(int fd) +{ +	write_n(fd, "CLIENT", 6); +	return w4_str(fd, "CLIENTSERVER"); +} + +int ms_dun(int fd, int server, int timeo) +{ +	sig_t osig; + +	retry    = 4; +	timeout  = timeo; + +	if (!server) +		timeout /= retry; + +	osig = signal(SIGALRM, sig_alarm); + +	while (1) { +		int r = sigsetjmp(jmp, 1); +		if (r) { +			if (r == MS_TIMEOUT && !server && --retry) +				continue; + +			alarm(0); +			signal(SIGALRM, osig); +		 +			switch (r) { +			case MS_SUCCESS: +			case MS_PPP: +				errno = 0; +				return 0; + +			case MS_FAILED: +				errno = EPROTO; +				break; + +			case MS_TIMEOUT: +				errno = ETIMEDOUT; +				break; +			} +			return -1; +		} + +		alarm(timeout); +		 +		if (server) +			r = ms_server(fd); +		else +			r = ms_client(fd); + +		siglongjmp(jmp, r); +	} +} diff --git a/compat/pand.1 b/compat/pand.1 new file mode 100644 index 00000000..4603b8bf --- /dev/null +++ b/compat/pand.1 @@ -0,0 +1,77 @@ +.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.29. +.TH BlueZ "1" "February 2003" "PAN daemon" "User Commands" +.SH NAME +pand \- BlueZ Bluetooth PAN daemon +.SH DESCRIPTION +The pand PAN daemon allows your computer to connect to ethernet +networks using Bluetooth. +.SH SYNPOSIS +pand <options> +.SH OPTIONS +.TP +\fB\-\-show\fR \fB\-\-list\fR \fB\-l\fR +Show active PAN connections +.TP +\fB\-\-listen\fR \fB\-s\fR +Listen for PAN connections +.TP +\fB\-\-connect\fR \fB\-c\fR <bdaddr> +Create PAN connection +.TP +\fB\-\-search\fR \fB\-Q[duration]\fR +Search and connect +.TP +\fB\-\-kill\fR \fB\-k\fR <bdaddr> +Kill PAN connection +.TP +\fB\-\-killall\fR \fB\-K\fR +Kill all PAN connections +.TP +\fB\-\-role\fR \fB\-r\fR <role> +Local PAN role (PANU, NAP, GN) +.TP +\fB\-\-service\fR \fB\-d\fR <role> +Remote PAN service (PANU, NAP, GN) +.TP +\fB\-\-ethernet\fR \fB\-e\fR <name> +Network interface name +.TP +\fB\-\-device\fR \fB\-i\fR <bdaddr> +Source bdaddr +.TP +\fB\-\-nosdp\fR \fB\-D\fR +Disable SDP +.TP +\fB\-\-encrypt\fR \fB\-E\fR +Enable encryption +.TP +\fB\-\-secure\fR \fB\-S\fR +Secure connection +.TP +\fB\-\-master\fR \fB\-M\fR +Become the master of a piconet +.TP +\fB\-\-nodetach\fR \fB\-n\fR +Do not become a daemon +.TP +\fB\-\-persist\fR \fB\-p[interval]\fR +Persist mode +.TP +\fB\-\-cache\fR \fB\-C[valid]\fR +Cache addresses +.TP +\fB\-\-pidfile\fR \fB\-P <pidfile>\fR +Create PID file +.TP +\fB\-\-devup\fR \fB\-u <script>\fR +Script to run when interface comes up +.TP +\fB\-\-devdown\fR \fB\-o <script>\fR +Script to run when interface comes down +.TP +\fB\-\-autozap\fR \fB\-z\fR +Disconnect automatically on exit + +.SH SCRIPTS +The devup/devdown script will be called with bluetooth device as first argument +and bluetooth destination address as second argument. diff --git a/compat/pand.c b/compat/pand.c new file mode 100644 index 00000000..ed80c7a5 --- /dev/null +++ b/compat/pand.c @@ -0,0 +1,800 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com> + *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#define _GNU_SOURCE +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <signal.h> +#include <getopt.h> +#include <sys/poll.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/l2cap.h> +#include <bluetooth/bnep.h> +#include <bluetooth/hidp.h> + +#include "sdp.h" +#include "pand.h" + +#ifdef NEED_PPOLL +#include "ppoll.h" +#endif + +static uint16_t role    = BNEP_SVC_PANU;	/* Local role (ie service) */ +static uint16_t service = BNEP_SVC_NAP;		/* Remote service */ + +static int detach = 1; +static int persist; +static int use_sdp = 1; +static int use_cache; +static int link_mode = 0; +static int cleanup; +static int search_duration = 10; + +static struct { +	int      valid; +	char     dst[40]; +	bdaddr_t bdaddr; +} cache; + +static char netdev[16] = "bnep%d"; +static char *pidfile = NULL; +static char *devupcmd = NULL; +static char *devdowncmd = NULL; + +static bdaddr_t src_addr = *BDADDR_ANY; +static int src_dev = -1; + +static volatile int terminate; + +static void do_kill(char *dst); + +enum { +	NONE, +	SHOW, +	LISTEN, +	CONNECT, +	KILL +} modes; + +struct script_arg { +	char	dev[20]; +	char	dst[20]; +	int	sk; +	int	nsk; +}; + +static void run_script(char *script, char *dev, char *dst, int sk, int nsk) +{ +	char *argv[4]; +	struct sigaction sa; + +	if (!script) +		return; + +	if (access(script, R_OK | X_OK)) +		return; + +	if (fork()) +		return; + +	if (sk >= 0) +		close(sk); + +	if (nsk >= 0) +		close(nsk); + +	memset(&sa, 0, sizeof(sa)); +	sa.sa_handler = SIG_DFL; +	sigaction(SIGCHLD, &sa, NULL); +	sigaction(SIGPIPE, &sa, NULL); + +	argv[0] = script; +	argv[1] = dev; +	argv[2] = dst; +	argv[3] = NULL; + +	execv(script, argv); + +	exit(1); +} + +/* Wait for disconnect or error condition on the socket */ +static int w4_hup(int sk, struct script_arg *down_cmd) +{ +	struct pollfd pf; +	sigset_t sigs; +	int n; + +	sigfillset(&sigs); +	sigdelset(&sigs, SIGCHLD); +	sigdelset(&sigs, SIGPIPE); +	sigdelset(&sigs, SIGTERM); +	sigdelset(&sigs, SIGINT); +	sigdelset(&sigs, SIGHUP); + +	while (!terminate) { +		pf.fd = sk; +		pf.events = POLLERR | POLLHUP; + +		n = ppoll(&pf, 1, NULL, &sigs); + +		if (n < 0) { +			if (errno == EINTR || errno == EAGAIN) +				continue; + +			syslog(LOG_ERR, "Poll failed. %s(%d)", +						strerror(errno), errno); + +			return 1; +		} + +		if (n) { +			int err = 0; +			socklen_t olen = sizeof(err); + +			getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &olen); + +			syslog(LOG_INFO, "%s disconnected%s%s", netdev, +				err ? " : " : "", err ? strerror(err) : ""); + +			if (down_cmd) +				run_script(devdowncmd, +						down_cmd->dev, down_cmd->dst, +						down_cmd->sk, down_cmd->nsk); + +			close(sk); + +			return 0; +		} +	} + +	return 0; +} + +static int do_listen(void) +{ +	struct l2cap_options l2o; +	struct sockaddr_l2 l2a; +	socklen_t olen; +	int sk, lm; + +	if (use_sdp) +		bnep_sdp_register(&src_addr, role); + +	/* Create L2CAP socket and bind it to PSM BNEP */ +	sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); +	if (sk < 0) { +		syslog(LOG_ERR, "Cannot create L2CAP socket. %s(%d)", +						strerror(errno), errno); +		return -1; +	} + +	memset(&l2a, 0, sizeof(l2a)); +	l2a.l2_family = AF_BLUETOOTH; +	bacpy(&l2a.l2_bdaddr, &src_addr); +	l2a.l2_psm = htobs(BNEP_PSM); + +	if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a))) { +		syslog(LOG_ERR, "Bind failed. %s(%d)", +						strerror(errno), errno); +		return -1; +	} + +	/* Setup L2CAP options according to BNEP spec */ +	memset(&l2o, 0, sizeof(l2o)); +	olen = sizeof(l2o); +	if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen) < 0) { +		syslog(LOG_ERR, "Failed to get L2CAP options. %s(%d)", +						strerror(errno), errno); +		return -1; +	} + +	l2o.imtu = l2o.omtu = BNEP_MTU; +	if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) { +		syslog(LOG_ERR, "Failed to set L2CAP options. %s(%d)", +						strerror(errno), errno); +		return -1; +	} + +	/* Set link mode */ +	lm = link_mode; +	if (lm && setsockopt(sk, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm)) < 0) { +		syslog(LOG_ERR, "Failed to set link mode. %s(%d)", +						strerror(errno), errno); +		return -1; +	} + +	listen(sk, 10); + +	while (!terminate) { +		socklen_t alen = sizeof(l2a); +		char devname[16]; +		int nsk; + +		nsk = accept(sk, (struct sockaddr *) &l2a, &alen); +		if (nsk < 0) { +			syslog(LOG_ERR, "Accept failed. %s(%d)", +						strerror(errno), errno); +			continue; +		} + +		switch (fork()) { +		case 0: +			break; +		case -1: +			syslog(LOG_ERR, "Fork failed. %s(%d)", +						strerror(errno), errno); +		default: +			close(nsk); +			continue; +		} + +		strncpy(devname, netdev, 16); +		devname[15] = '\0'; + +		if (!bnep_accept_connection(nsk, role, devname)) { +			char str[40]; +			struct script_arg down_cmd; + +			ba2str(&l2a.l2_bdaddr, str); + +			syslog(LOG_INFO, "New connection from %s at %s", +								str, devname); + +			run_script(devupcmd, devname, str, sk, nsk); + +			memset(&down_cmd, 0, sizeof(struct script_arg)); +			strncpy(down_cmd.dev, devname, strlen(devname) + 1); +			strncpy(down_cmd.dst, str, strlen(str) + 1); +			down_cmd.sk = sk; +			down_cmd.nsk = nsk; +			w4_hup(nsk, &down_cmd); +		} else { +			syslog(LOG_ERR, "Connection failed. %s(%d)", +						strerror(errno), errno); +		} + +		close(nsk); +		exit(0); +	} + +	if (use_sdp) +		bnep_sdp_unregister(); + +	return 0; +} + +/* Connect and initiate BNEP session + * Returns: + *   -1 - critical error (exit persist mode) + *   1  - non critical error + *   0  - success + */ +static int create_connection(char *dst, bdaddr_t *bdaddr) +{ +	struct l2cap_options l2o; +	struct sockaddr_l2 l2a; +	socklen_t olen; +	int sk, r = 0; +	struct script_arg down_cmd; + +	syslog(LOG_INFO, "Connecting to %s", dst); + +	sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); +	if (sk < 0) { +		syslog(LOG_ERR, "Cannot create L2CAP socket. %s(%d)", +						strerror(errno), errno); +		return -1; +	} + +	/* Setup L2CAP options according to BNEP spec */ +	memset(&l2o, 0, sizeof(l2o)); +	olen = sizeof(l2o); +	getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen); +	l2o.imtu = l2o.omtu = BNEP_MTU; +	setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)); + +	memset(&l2a, 0, sizeof(l2a)); +	l2a.l2_family = AF_BLUETOOTH; +	bacpy(&l2a.l2_bdaddr, &src_addr); + +	if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a))) +		syslog(LOG_ERR, "Bind failed. %s(%d)", +						strerror(errno), errno); + +	memset(&l2a, 0, sizeof(l2a)); +	l2a.l2_family = AF_BLUETOOTH; +	bacpy(&l2a.l2_bdaddr, bdaddr); +	l2a.l2_psm = htobs(BNEP_PSM); + +	if (!connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) &&  +			!bnep_create_connection(sk, role, service, netdev)) { + +		syslog(LOG_INFO, "%s connected", netdev); + +		run_script(devupcmd, netdev, dst, sk, -1); + +		if (persist || devdowncmd) { +				memset(&down_cmd, 0, sizeof(struct script_arg)); +				strncpy(down_cmd.dev, netdev, strlen(netdev) + 1); +				strncpy(down_cmd.dst, dst, strlen(dst) + 1); +				down_cmd.sk = sk; +				down_cmd.nsk = -1; +				w4_hup(sk, &down_cmd); + +			if (terminate && cleanup) { +				syslog(LOG_INFO, "Disconnecting from %s.", dst); +				do_kill(dst); +			} +		} + +		r = 0; +	} else { +		syslog(LOG_ERR, "Connect to %s failed. %s(%d)", +						dst, strerror(errno), errno); +		r = 1; +	} + +	close(sk); + +	if (use_cache) { +		if (!r) { +			/* Succesesful connection, validate cache */ +			strcpy(cache.dst, dst); +			bacpy(&cache.bdaddr, bdaddr); +			cache.valid = use_cache; +		} else +			cache.valid--; +	} + +	return r; +} + +/* Search and connect + * Returns: + *   -1 - critical error (exit persist mode) + *   1  - non critical error + *   0  - success + */ +static int do_connect(void) +{ +	inquiry_info *ii; +	int reconnect = 0; +	int i, n, r = 0; + +	do { +		if (reconnect) +			sleep(persist); +		reconnect = 1; + +		if (cache.valid > 0) { +			/* Use cached bdaddr */ +			r = create_connection(cache.dst, &cache.bdaddr); +			if (r < 0) { +				terminate = 1; +				break; +			} +			continue; +		} + +		syslog(LOG_INFO, "Inquiring"); + +		/* FIXME: Should we use non general LAP here ? */ + +		ii = NULL; +		n  = hci_inquiry(src_dev, search_duration, 0, NULL, &ii, 0); +		if (n < 0) { +			syslog(LOG_ERR, "Inquiry failed. %s(%d)", +						strerror(errno), errno); +			continue; +		} + +		for (i = 0; i < n; i++) { +			char dst[40]; +			ba2str(&ii[i].bdaddr, dst); + +			if (use_sdp) { +				syslog(LOG_INFO, "Searching for %s on %s",  +						bnep_svc2str(service), dst); + +				if (bnep_sdp_search(&src_addr, &ii[i].bdaddr, service) <= 0) +					continue; +			} + +			r = create_connection(dst, &ii[i].bdaddr); +			if (r < 0) { +				terminate = 1; +				break; +			} +		} +		bt_free(ii); +	} while (!terminate && persist); + +	return r; +} + +static void do_show(void) +{ +	bnep_show_connections(); +} + +static void do_kill(char *dst) +{ +	if (dst) +		bnep_kill_connection((void *) strtoba(dst)); +	else +		bnep_kill_all_connections(); +} + +static void sig_hup(int sig) +{ +	return; +} + +static void sig_term(int sig) +{ +	terminate = 1; +} + +static int write_pidfile(void) +{ +	int fd; +	FILE *f; +	pid_t pid; + +	do { +		fd = open(pidfile, O_WRONLY|O_TRUNC|O_CREAT|O_EXCL, 0644); +		if (fd == -1) { +			/* Try to open the file for read. */ +			fd = open(pidfile, O_RDONLY); +			if (fd < 0) { +				syslog(LOG_ERR, "Could not read old pidfile: %s(%d)", +							strerror(errno), errno); +				return -1; +			} +			 +			/* We're already running; send a SIGHUP (we presume that they +			 * are calling ifup for a reason, so they probably want to +			 * rescan) and then exit cleanly and let things go on in the +			 * background.  Muck with the filename so that we don't go +			 * deleting the pid file for the already-running instance. +			 */ +			f = fdopen(fd, "r"); +			if (!f) { +				syslog(LOG_ERR, "Could not fdopen old pidfile: %s(%d)", +							strerror(errno), errno); +				close(fd); +				return -1; +			} + +			pid = 0; +			if (fscanf(f, "%d", &pid) != 1) +				pid = 0; +			fclose(f); + +			if (pid) { +				/* Try to kill it. */ +				if (kill(pid, SIGHUP) == -1) { +					/* No such pid; remove the bogus pid file. */ +					syslog(LOG_INFO, "Removing stale pidfile"); +					unlink(pidfile); +					fd = -1; +				} else { +					/* Got it.  Don't mess with the pid file on +					 * our way out. */ +					syslog(LOG_INFO, "Signalling existing process %d and exiting\n", pid); +					pidfile = NULL; +					return -1; +				} +			} +		} +	} while(fd == -1); + +	f = fdopen(fd, "w"); +	if (!f) { +		syslog(LOG_ERR, "Could not fdopen new pidfile: %s(%d)", +						strerror(errno), errno); +		close(fd); +		unlink(pidfile); +		return -1; +	} + +	fprintf(f, "%d\n", getpid()); +	fclose(f); + +	return 0; +} + +static struct option main_lopts[] = { +	{ "help",     0, 0, 'h' }, +	{ "listen",   0, 0, 's' }, +	{ "connect",  1, 0, 'c' }, +	{ "search",   2, 0, 'Q' }, +	{ "kill",     1, 0, 'k' }, +	{ "killall",  0, 0, 'K' }, +	{ "role",     1, 0, 'r' }, +	{ "service",  1, 0, 'd' }, +	{ "ethernet", 1, 0, 'e' }, +	{ "device",   1, 0, 'i' }, +	{ "nosdp",    0, 0, 'D' }, +	{ "list",     0, 0, 'l' }, +	{ "show",     0, 0, 'l' }, +	{ "nodetach", 0, 0, 'n' }, +	{ "persist",  2, 0, 'p' }, +	{ "auth",     0, 0, 'A' }, +	{ "encrypt",  0, 0, 'E' }, +	{ "secure",   0, 0, 'S' }, +	{ "master",   0, 0, 'M' }, +	{ "cache",    0, 0, 'C' }, +	{ "pidfile",  1, 0, 'P' }, +	{ "devup",    1, 0, 'u' }, +	{ "devdown",  1, 0, 'o' }, +	{ "autozap",  0, 0, 'z' }, +	{ 0, 0, 0, 0 } +}; + +static char main_sopts[] = "hsc:k:Kr:d:e:i:lnp::DQ::AESMC::P:u:o:z"; + +static char main_help[] =  +	"Bluetooth PAN daemon version " VERSION " \n" +	"Usage:\n" +	"\tpand <options>\n" +	"Options:\n" +	"\t--show --list -l          Show active PAN connections\n" +	"\t--listen -s               Listen for PAN connections\n" +	"\t--connect -c <bdaddr>     Create PAN connection\n" +	"\t--autozap -z              Disconnect automatically on exit\n" +	"\t--search -Q[duration]     Search and connect\n" +	"\t--kill -k <bdaddr>        Kill PAN connection\n" +	"\t--killall -K              Kill all PAN connections\n" +	"\t--role -r <role>          Local PAN role (PANU, NAP, GN)\n" +	"\t--service -d <role>       Remote PAN service (PANU, NAP, GN)\n" +	"\t--ethernet -e <name>      Network interface name\n" +	"\t--device -i <bdaddr>      Source bdaddr\n" +	"\t--nosdp -D                Disable SDP\n" +	"\t--auth -A                 Enable authentication\n" +	"\t--encrypt -E              Enable encryption\n" +	"\t--secure -S               Secure connection\n" +	"\t--master -M               Become the master of a piconet\n" +	"\t--nodetach -n             Do not become a daemon\n" +	"\t--persist -p[interval]    Persist mode\n" +	"\t--cache -C[valid]         Cache addresses\n" +	"\t--pidfile -P <pidfile>    Create PID file\n" +	"\t--devup -u <script>       Script to run when interface comes up\n" +	"\t--devdown -o <script>     Script to run when interface comes down\n"; + +int main(int argc, char *argv[]) +{ +	char *dst = NULL, *src = NULL; +	struct sigaction sa; +	int mode = NONE; +	int opt; + +	while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) { +		switch(opt) { +		case 'l': +			mode = SHOW; +			detach = 0; +			break; + +		case 's': +			mode = LISTEN; +			break; + +		case 'c': +			mode = CONNECT; +			dst  = strdup(optarg); +			break; + +		case 'Q': +			mode = CONNECT; +			if (optarg) +				search_duration = atoi(optarg); +			break; + +		case 'k': +			mode = KILL; +			detach = 0; +			dst  = strdup(optarg); +			break; + +		case 'K': +			mode = KILL; +			detach = 0; +			break; + +		case 'i': +			src = strdup(optarg); +			break; + +		case 'r': +			bnep_str2svc(optarg, &role); +			break; + +		case 'd': +			bnep_str2svc(optarg, &service); +			break; + +		case 'D': +			use_sdp = 0; +			break; + +		case 'A': +			link_mode |= L2CAP_LM_AUTH; +			break; + +		case 'E': +			link_mode |= L2CAP_LM_ENCRYPT; +			break; + +		case 'S': +			link_mode |= L2CAP_LM_SECURE; +			break; + +		case 'M': +			link_mode |= L2CAP_LM_MASTER; +			break; + +		case 'e': +			strncpy(netdev, optarg, 16); +			netdev[15] = '\0'; +			break; + +		case 'n': +			detach = 0; +			break; + +		case 'p': +			if (optarg) +				persist = atoi(optarg); +			else +				persist = 5; +			break; + +		case 'C': +			if (optarg) +				use_cache = atoi(optarg); +			else +				use_cache = 2; +			break; + +		case 'P': +			pidfile = strdup(optarg); +			break; + +		case 'u': +			devupcmd = strdup(optarg); +			break; + +		case 'o': +			devdowncmd = strdup(optarg); +			break; + +		case 'z': +			cleanup = 1; +			break; + +		case 'h': +		default: +			printf(main_help); +			exit(0); +		} +	} + +	argc -= optind; +	argv += optind; +	optind = 0; + +	if (bnep_init()) +		return -1; + +	/* Check non daemon modes first */ +	switch (mode) { +	case SHOW: +		do_show(); +		return 0; + +	case KILL: +		do_kill(dst); +		return 0; + +	case NONE: +		printf(main_help); +		return 0; +	} + +	/* Initialize signals */ +	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_hup; +	sigaction(SIGHUP, &sa, NULL); + +	sa.sa_handler = sig_term; +	sigaction(SIGTERM, &sa, NULL); +	sigaction(SIGINT,  &sa, NULL); + +	if (detach && daemon(0, 0)) { +		perror("Can't start daemon"); +		exit(1); +	} + +	openlog("pand", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON); +	syslog(LOG_INFO, "Bluetooth PAN daemon version %s", VERSION); + +	if (src) { +		src_dev = hci_devid(src); +		if (src_dev < 0 || hci_devba(src_dev, &src_addr) < 0) { +			syslog(LOG_ERR, "Invalid source. %s(%d)", +						strerror(errno), errno); +			return -1; +		} +	} + +	if (pidfile && write_pidfile()) +		return -1; + +	if (dst) { +		/* Disable cache invalidation */ +		use_cache = 0; + +		strncpy(cache.dst, dst, sizeof(cache.dst) - 1); +		str2ba(dst, &cache.bdaddr); +		cache.valid = 1; +		free(dst); +	} + +	switch (mode) { +	case CONNECT: +		do_connect(); +		break; + +	case LISTEN: +		do_listen(); +		break; +	} + +	if (pidfile) +		unlink(pidfile); + +	return 0; +} diff --git a/compat/pand.h b/compat/pand.h new file mode 100644 index 00000000..90f26ed6 --- /dev/null +++ b/compat/pand.h @@ -0,0 +1,36 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com> + *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +int bnep_init(void); +int bnep_cleanup(void); + +int bnep_str2svc(char *svc, uint16_t *uuid); +char *bnep_svc2str(uint16_t uuid); + +int bnep_show_connections(void); +int bnep_kill_connection(uint8_t *dst); +int bnep_kill_all_connections(void); + +int bnep_accept_connection(int sk, uint16_t role, char *dev); +int bnep_create_connection(int sk, uint16_t role, uint16_t svc, char *dev); diff --git a/compat/sdp.c b/compat/sdp.c new file mode 100644 index 00000000..a08ace90 --- /dev/null +++ b/compat/sdp.c @@ -0,0 +1,718 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2003-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <sys/stat.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/l2cap.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> +#include <bluetooth/hidp.h> +#include <bluetooth/bnep.h> + +#include "textfile.h" +#include "sdp.h" + +static sdp_record_t *record = NULL; +static sdp_session_t *session = NULL; + +static void add_lang_attr(sdp_record_t *r) +{ +	sdp_lang_attr_t base_lang; +	sdp_list_t *langs = 0; + +	/* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */ +	base_lang.code_ISO639 = (0x65 << 8) | 0x6e; +	base_lang.encoding = 106; +	base_lang.base_offset = SDP_PRIMARY_LANG_BASE; +	langs = sdp_list_append(0, &base_lang); +	sdp_set_lang_attr(r, langs); +	sdp_list_free(langs, 0); +} + +static void epox_endian_quirk(unsigned char *data, int size) +{ +	/* USAGE_PAGE (Keyboard)	05 07 +	 * USAGE_MINIMUM (0)		19 00 +	 * USAGE_MAXIMUM (65280)	2A 00 FF   <= must be FF 00 +	 * LOGICAL_MINIMUM (0)		15 00 +	 * LOGICAL_MAXIMUM (65280)	26 00 FF   <= must be FF 00 +	 */ +	unsigned char pattern[] = { 0x05, 0x07, 0x19, 0x00, 0x2a, 0x00, 0xff, +						0x15, 0x00, 0x26, 0x00, 0xff }; +	int i; + +	if (!data) +		return; + +	for (i = 0; i < size - sizeof(pattern); i++) { +		if (!memcmp(data + i, pattern, sizeof(pattern))) { +			data[i + 5] = 0xff; +			data[i + 6] = 0x00; +			data[i + 10] = 0xff; +			data[i + 11] = 0x00; +		} +	} +} + +static int store_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req) +{ +	char filename[PATH_MAX + 1], addr[18], *str, *desc; +	int i, err, size; + +	ba2str(src, addr); +	create_name(filename, PATH_MAX, STORAGEDIR, addr, "hidd"); + +	size = 15 + 3 + 3 + 5 + (req->rd_size * 2) + 1 + 9 + strlen(req->name) + 2; +	str = malloc(size); +	if (!str) +		return -ENOMEM; + +	desc = malloc((req->rd_size * 2) + 1); +	if (!desc) { +		free(str); +		return -ENOMEM; +	} + +	memset(desc, 0, (req->rd_size * 2) + 1); +	for (i = 0; i < req->rd_size; i++) +		sprintf(desc + (i * 2), "%2.2X", req->rd_data[i]); + +	snprintf(str, size - 1, "%04X:%04X:%04X %02X %02X %04X %s %08X %s", +			req->vendor, req->product, req->version, +			req->subclass, req->country, req->parser, desc, +			req->flags, req->name); + +	free(desc); + +	create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + +	ba2str(dst, addr); +	err = textfile_put(filename, addr, str); + +	free(str); + +	return err; +} + +int get_stored_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req) +{ +	char filename[PATH_MAX + 1], addr[18], tmp[3], *str, *desc; +	unsigned int vendor, product, version, subclass, country, parser, pos; +	int i; + +	desc = malloc(4096); +	if (!desc) +		return -ENOMEM; + +	memset(desc, 0, 4096); + +	ba2str(src, addr); +	create_name(filename, PATH_MAX, STORAGEDIR, addr, "hidd"); + +	ba2str(dst, addr); +	str = textfile_get(filename, addr); +	if (!str) { +		free(desc); +		return -EIO; +	} + +	sscanf(str, "%04X:%04X:%04X %02X %02X %04X %4095s %08X %n", +			&vendor, &product, &version, &subclass, &country, +			&parser, desc, &req->flags, &pos); + +	free(str); + +	req->vendor   = vendor; +	req->product  = product; +	req->version  = version; +	req->subclass = subclass; +	req->country  = country; +	req->parser   = parser; + +	snprintf(req->name, 128, str + pos); + +	req->rd_size = strlen(desc) / 2; +	req->rd_data = malloc(req->rd_size); +	if (!req->rd_data) { +		free(desc); +		return -ENOMEM; +	} + +	memset(tmp, 0, sizeof(tmp)); +	for (i = 0; i < req->rd_size; i++) { +		memcpy(tmp, desc + (i * 2), 2); +		req->rd_data[i] = (uint8_t) strtol(tmp, NULL, 16); +	} + +	free(desc); + +	return 0; +} + +int get_sdp_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req) +{ +	struct sockaddr_l2 addr; +	socklen_t addrlen; +	bdaddr_t bdaddr; +	uint32_t range = 0x0000ffff; +	sdp_session_t *s; +	sdp_list_t *search, *attrid, *pnp_rsp, *hid_rsp; +	sdp_record_t *rec; +	sdp_data_t *pdlist, *pdlist2; +	uuid_t svclass; +	int err; + +	s = sdp_connect(src, dst, SDP_RETRY_IF_BUSY | SDP_WAIT_ON_CLOSE); +	if (!s) +		return -1; + +	sdp_uuid16_create(&svclass, PNP_INFO_SVCLASS_ID); +	search = sdp_list_append(NULL, &svclass); +	attrid = sdp_list_append(NULL, &range); + +	err = sdp_service_search_attr_req(s, search, +					SDP_ATTR_REQ_RANGE, attrid, &pnp_rsp); + +	sdp_list_free(search, NULL); +	sdp_list_free(attrid, NULL); + +	sdp_uuid16_create(&svclass, HID_SVCLASS_ID); +	search = sdp_list_append(NULL, &svclass); +	attrid = sdp_list_append(NULL, &range); + +	err = sdp_service_search_attr_req(s, search, +					SDP_ATTR_REQ_RANGE, attrid, &hid_rsp); + +	sdp_list_free(search, NULL); +	sdp_list_free(attrid, NULL); + +	memset(&addr, 0, sizeof(addr)); +	addrlen = sizeof(addr); + +	if (getsockname(s->sock, (struct sockaddr *) &addr, &addrlen) < 0) +		bacpy(&bdaddr, src); +	else +		bacpy(&bdaddr, &addr.l2_bdaddr); + +	sdp_close(s); + +	if (err || !hid_rsp) +		return -1; + +	if (pnp_rsp) { +		rec = (sdp_record_t *) pnp_rsp->data; + +		pdlist = sdp_data_get(rec, 0x0201); +		req->vendor = pdlist ? pdlist->val.uint16 : 0x0000; + +		pdlist = sdp_data_get(rec, 0x0202); +		req->product = pdlist ? pdlist->val.uint16 : 0x0000; + +		pdlist = sdp_data_get(rec, 0x0203); +		req->version = pdlist ? pdlist->val.uint16 : 0x0000; + +		sdp_record_free(rec); +	} + +	rec = (sdp_record_t *) hid_rsp->data; + +	pdlist = sdp_data_get(rec, 0x0101); +	pdlist2 = sdp_data_get(rec, 0x0102); +	if (pdlist) { +		if (pdlist2) { +			if (strncmp(pdlist->val.str, pdlist2->val.str, 5)) { +				strncpy(req->name, pdlist2->val.str, sizeof(req->name) - 1); +				strcat(req->name, " "); +			} +			strncat(req->name, pdlist->val.str, +					sizeof(req->name) - strlen(req->name)); +		} else +			strncpy(req->name, pdlist->val.str, sizeof(req->name)); +	} else { +		pdlist2 = sdp_data_get(rec, 0x0100); +		if (pdlist2) +			strncpy(req->name, pdlist2->val.str, sizeof(req->name)); +	} + +	pdlist = sdp_data_get(rec, 0x0201); +	req->parser = pdlist ? pdlist->val.uint16 : 0x0100; + +	pdlist = sdp_data_get(rec, 0x0202); +	req->subclass = pdlist ? pdlist->val.uint8 : 0; + +	pdlist = sdp_data_get(rec, 0x0203); +	req->country = pdlist ? pdlist->val.uint8 : 0; + +	pdlist = sdp_data_get(rec, 0x0206); +	if (pdlist) { +		pdlist = pdlist->val.dataseq; +		pdlist = pdlist->val.dataseq; +		pdlist = pdlist->next; + +		req->rd_data = malloc(pdlist->unitSize); +		if (req->rd_data) { +			memcpy(req->rd_data, (unsigned char *) pdlist->val.str, pdlist->unitSize); +			req->rd_size = pdlist->unitSize; +			epox_endian_quirk(req->rd_data, req->rd_size); +		} +	} + +	sdp_record_free(rec); + +	if (bacmp(&bdaddr, BDADDR_ANY)) +		store_device_info(&bdaddr, dst, req); + +	return 0; +} + +int get_alternate_device_info(const bdaddr_t *src, const bdaddr_t *dst, uint16_t *uuid, uint8_t *channel, char *name, size_t len) +{ +	uint16_t attr1 = SDP_ATTR_PROTO_DESC_LIST; +	uint16_t attr2 = SDP_ATTR_SVCNAME_PRIMARY; +	sdp_session_t *s; +	sdp_list_t *search, *attrid, *rsp; +	uuid_t svclass; +	int err; + +	s = sdp_connect(src, dst, SDP_RETRY_IF_BUSY | SDP_WAIT_ON_CLOSE); +	if (!s) +		return -1; + +	sdp_uuid16_create(&svclass, HEADSET_SVCLASS_ID); +	search = sdp_list_append(NULL, &svclass); +	attrid = sdp_list_append(NULL, &attr1); +	attrid = sdp_list_append(attrid, &attr2); + +	err = sdp_service_search_attr_req(s, search, +					SDP_ATTR_REQ_INDIVIDUAL, attrid, &rsp); + +	sdp_list_free(search, NULL); +	sdp_list_free(attrid, NULL); + +	if (err <= 0) { +		sdp_uuid16_create(&svclass, SERIAL_PORT_SVCLASS_ID); +		search = sdp_list_append(NULL, &svclass); +		attrid = sdp_list_append(NULL, &attr1); +		attrid = sdp_list_append(attrid, &attr2); + +		err = sdp_service_search_attr_req(s, search, +					SDP_ATTR_REQ_INDIVIDUAL, attrid, &rsp); + +		sdp_list_free(search, NULL); +		sdp_list_free(attrid, NULL); + +		if (err < 0) { +			sdp_close(s); +			return err; +		} + +		if (uuid) +			*uuid = SERIAL_PORT_SVCLASS_ID; +	} else { +		if (uuid) +			*uuid = HEADSET_SVCLASS_ID; +	} + +	sdp_close(s); + +	for (; rsp; rsp = rsp->next) { +		sdp_record_t *rec = (sdp_record_t *) rsp->data; +		sdp_list_t *protos; + +		sdp_get_service_name(rec, name, len); + +		if (!sdp_get_access_protos(rec, &protos)) { +			uint8_t ch = sdp_get_proto_port(protos, RFCOMM_UUID); +			if (ch > 0) { +				if (channel) +					*channel = ch; +				return 0; +			} +		} + +		sdp_record_free(rec); +	} + +	return -EIO; +} + +void bnep_sdp_unregister(void)  +{ +	if (record && sdp_record_unregister(session, record)) +		syslog(LOG_ERR, "Service record unregistration failed."); + +	sdp_close(session); +} + +int bnep_sdp_register(bdaddr_t *device, uint16_t role) +{ +	sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto; +	uuid_t root_uuid, pan, l2cap, bnep; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *proto[2]; +	sdp_data_t *v, *p; +	uint16_t psm = 15, version = 0x0100; +	uint16_t security_desc = 0; +	uint16_t net_access_type = 0xfffe; +	uint32_t max_net_access_rate = 0; +	char *name = "BlueZ PAN"; +	char *desc = "BlueZ PAN Service"; +	int status; + +	session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0); +	if (!session) { +		syslog(LOG_ERR, "Failed to connect to the local SDP server. %s(%d)", +							strerror(errno), errno); +		return -1; +	} + +	record = sdp_record_alloc(); +	if (!record) { +		syslog(LOG_ERR, "Failed to allocate service record %s(%d)", +							strerror(errno), errno); +		sdp_close(session); +		return -1; +	} + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(record, root); +	sdp_list_free(root, 0); + +	sdp_uuid16_create(&l2cap, L2CAP_UUID); +	proto[0] = sdp_list_append(NULL, &l2cap); +	p = sdp_data_alloc(SDP_UINT16, &psm); +	proto[0] = sdp_list_append(proto[0], p); +	apseq    = sdp_list_append(NULL, proto[0]); + +	sdp_uuid16_create(&bnep, BNEP_UUID); +	proto[1] = sdp_list_append(NULL, &bnep); +	v = sdp_data_alloc(SDP_UINT16, &version); +	proto[1] = sdp_list_append(proto[1], v); + +	/* Supported protocols */ +	{ +		uint16_t ptype[4] = {  +			0x0800,  /* IPv4 */ +			0x0806,  /* ARP */ +		}; +		sdp_data_t *head, *pseq; +		int p; + +		for (p = 0, head = NULL; p < 2; p++) { +			sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]); +			if (head) +				sdp_seq_append(head, data); +			else +				head = data; +		} +		pseq = sdp_data_alloc(SDP_SEQ16, head); +		proto[1] = sdp_list_append(proto[1], pseq); +	} + +	apseq = sdp_list_append(apseq, proto[1]); + +	aproto = sdp_list_append(NULL, apseq); +	sdp_set_access_protos(record, aproto); + +	add_lang_attr(record); + +	sdp_list_free(proto[0], NULL); +	sdp_list_free(proto[1], NULL); +	sdp_list_free(apseq, NULL); +	sdp_list_free(aproto, NULL); +	sdp_data_free(p); +	sdp_data_free(v); +	sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security_desc); + +	switch (role) { +	case BNEP_SVC_NAP: +		sdp_uuid16_create(&pan, NAP_SVCLASS_ID); +		svclass = sdp_list_append(NULL, &pan); +		sdp_set_service_classes(record, svclass); + +		sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID); +		profile[0].version = 0x0100; +		pfseq = sdp_list_append(NULL, &profile[0]); +		sdp_set_profile_descs(record, pfseq); + +		sdp_set_info_attr(record, "Network Access Point", name, desc); + +		sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &net_access_type); +		sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE, SDP_UINT32, &max_net_access_rate); +		break; + +	case BNEP_SVC_GN: +		sdp_uuid16_create(&pan, GN_SVCLASS_ID); +		svclass = sdp_list_append(NULL, &pan); +		sdp_set_service_classes(record, svclass); + +		sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID); +		profile[0].version = 0x0100; +		pfseq = sdp_list_append(NULL, &profile[0]); +		sdp_set_profile_descs(record, pfseq); +		 +		sdp_set_info_attr(record, "Group Network Service", name, desc); +		break; + +	case BNEP_SVC_PANU: +		sdp_uuid16_create(&pan, PANU_SVCLASS_ID); +		svclass = sdp_list_append(NULL, &pan); +		sdp_set_service_classes(record, svclass); +		sdp_list_free(svclass, 0); + +		sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID); +		profile[0].version = 0x0100; +		pfseq = sdp_list_append(NULL, &profile[0]); +		sdp_set_profile_descs(record, pfseq); +		sdp_list_free(pfseq, 0); + +		sdp_set_info_attr(record, "PAN User", name, desc); +		break; +	} + +	status = sdp_device_record_register(session, device, record, 0); +	if (status) { +		syslog(LOG_ERR, "SDP registration failed."); +		sdp_record_free(record); record = NULL; +		sdp_close(session); +		return -1; +	} + +	return 0; +} + +/* Search for PAN service. + * Returns 1 if service is found and 0 otherwise. */ +int bnep_sdp_search(bdaddr_t *src, bdaddr_t *dst, uint16_t service) +{ +	sdp_list_t *srch, *rsp = NULL; +	sdp_session_t *s; +	uuid_t svclass; +	int err; + +	switch (service) { +	case BNEP_SVC_PANU: +		sdp_uuid16_create(&svclass, PANU_SVCLASS_ID); +		break; +	case BNEP_SVC_NAP: +		sdp_uuid16_create(&svclass, NAP_SVCLASS_ID); +		break; +	case BNEP_SVC_GN: +		sdp_uuid16_create(&svclass, GN_SVCLASS_ID); +		break; +	} + +	srch = sdp_list_append(NULL, &svclass); + +	s = sdp_connect(src, dst, 0); +	if (!s) { +		syslog(LOG_ERR, "Failed to connect to the SDP server. %s(%d)", +							strerror(errno), errno); +		return 0; +	} + +	err = sdp_service_search_req(s, srch, 1, &rsp); +	sdp_close(s); + +	/* Assume that search is successeful +	 * if at least one record is found */ +	if (!err && sdp_list_len(rsp)) +		return 1; + +	return 0; +} + +static unsigned char async_uuid[] = {	0x03, 0x50, 0x27, 0x8F, 0x3D, 0xCA, 0x4E, 0x62, +					0x83, 0x1D, 0xA4, 0x11, 0x65, 0xFF, 0x90, 0x6C }; + +void dun_sdp_unregister(void)  +{ +	if (record && sdp_record_unregister(session, record)) +		syslog(LOG_ERR, "Service record unregistration failed."); +	sdp_close(session); +} + +int dun_sdp_register(bdaddr_t *device, uint8_t channel, int type) +{ +	sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto; +	uuid_t root_uuid, l2cap, rfcomm, dun; +	sdp_profile_desc_t profile[1]; +	sdp_list_t *proto[2]; +	int status; + +	session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0); +	if (!session) { +		syslog(LOG_ERR, "Failed to connect to the local SDP server. %s(%d)",  +				strerror(errno), errno); +		return -1; +	} + +	record = sdp_record_alloc(); +	if (!record) { +		syslog(LOG_ERR, "Failed to alloc service record"); +		return -1; +	} + +	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); +	root = sdp_list_append(NULL, &root_uuid); +	sdp_set_browse_groups(record, root); + +	sdp_uuid16_create(&l2cap, L2CAP_UUID); +	proto[0] = sdp_list_append(NULL, &l2cap); +	apseq    = sdp_list_append(NULL, proto[0]); + +	sdp_uuid16_create(&rfcomm, RFCOMM_UUID); +	proto[1] = sdp_list_append(NULL, &rfcomm); +	proto[1] = sdp_list_append(proto[1], sdp_data_alloc(SDP_UINT8, &channel)); +	apseq    = sdp_list_append(apseq, proto[1]); + +	aproto   = sdp_list_append(NULL, apseq); +	sdp_set_access_protos(record, aproto); + +	switch (type) { +	case MROUTER: +		sdp_uuid16_create(&dun, SERIAL_PORT_SVCLASS_ID); +		break; +	case ACTIVESYNC: +		sdp_uuid128_create(&dun, (void *) async_uuid); +		break; +	case DIALUP: +		sdp_uuid16_create(&dun, DIALUP_NET_SVCLASS_ID); +		break; +	default: +		sdp_uuid16_create(&dun, LAN_ACCESS_SVCLASS_ID); +		break; +	} + +	svclass = sdp_list_append(NULL, &dun); +	sdp_set_service_classes(record, svclass); + +	switch (type) { +	case LANACCESS: +		sdp_uuid16_create(&profile[0].uuid, LAN_ACCESS_PROFILE_ID); +		profile[0].version = 0x0100; +		pfseq = sdp_list_append(NULL, &profile[0]); +		sdp_set_profile_descs(record, pfseq); +		break; +	case DIALUP: +		sdp_uuid16_create(&profile[0].uuid, DIALUP_NET_PROFILE_ID); +		profile[0].version = 0x0100; +		pfseq = sdp_list_append(NULL, &profile[0]); +		sdp_set_profile_descs(record, pfseq); +		break; +	} + +	switch (type) { +	case MROUTER: +		sdp_set_info_attr(record, "mRouter", NULL, NULL); +		break; +	case ACTIVESYNC: +		sdp_set_info_attr(record, "ActiveSync", NULL, NULL); +		break; +	case DIALUP: +		sdp_set_info_attr(record, "Dialup Networking", NULL, NULL); +		break; +	default: +		sdp_set_info_attr(record, "LAN Access Point", NULL, NULL); +		break; +	} + +	status = sdp_device_record_register(session, device, record, 0); +	if (status) { +		syslog(LOG_ERR, "SDP registration failed."); +		sdp_record_free(record); +		record = NULL; +		return -1; +	} +	return 0; +} + +int dun_sdp_search(bdaddr_t *src, bdaddr_t *dst, int *channel, int type) +{ +	sdp_session_t *s; +	sdp_list_t *srch, *attrs, *rsp; +	uuid_t svclass; +	uint16_t attr; +	int err; + +	s = sdp_connect(src, dst, 0); +	if (!s) { +		syslog(LOG_ERR, "Failed to connect to the SDP server. %s(%d)",  +				strerror(errno), errno); +		return -1; +	} + +	switch (type) { +	case MROUTER: +		sdp_uuid16_create(&svclass, SERIAL_PORT_SVCLASS_ID); +		break; +	case ACTIVESYNC: +		sdp_uuid128_create(&svclass, (void *) async_uuid); +		break; +	case DIALUP: +		sdp_uuid16_create(&svclass, DIALUP_NET_SVCLASS_ID); +		break; +	default: +		sdp_uuid16_create(&svclass, LAN_ACCESS_SVCLASS_ID); +		break; +	} + +	srch  = sdp_list_append(NULL, &svclass); + +	attr  = SDP_ATTR_PROTO_DESC_LIST; +	attrs = sdp_list_append(NULL, &attr); + +	err = sdp_service_search_attr_req(s, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp); + +	sdp_close(s); + +	if (err) +		return 0; + +	for(; rsp; rsp = rsp->next) { +		sdp_record_t *rec = (sdp_record_t *) rsp->data; +		sdp_list_t *protos; + +		if (!sdp_get_access_protos(rec, &protos)) { +			int ch = sdp_get_proto_port(protos, RFCOMM_UUID); +			if (ch > 0) { +				*channel = ch; +				return 1; +			} +		} +	} + +	return 0; +} diff --git a/compat/sdp.h b/compat/sdp.h new file mode 100644 index 00000000..7ff75d68 --- /dev/null +++ b/compat/sdp.h @@ -0,0 +1,39 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2003-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#define LANACCESS	0 +#define MROUTER		1 +#define ACTIVESYNC	2 +#define DIALUP		3 + +int get_stored_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req); +int get_sdp_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req); +int get_alternate_device_info(const bdaddr_t *src, const bdaddr_t *dst, uint16_t *uuid, uint8_t *channel, char *name, size_t len); + +int bnep_sdp_search(bdaddr_t *src, bdaddr_t *dst, uint16_t service); +int bnep_sdp_register(bdaddr_t *device, uint16_t role); +void bnep_sdp_unregister(void); + +int dun_sdp_search(bdaddr_t *src, bdaddr_t *dst, int *channel, int type); +int dun_sdp_register(bdaddr_t *device, uint8_t channel, int type); +void dun_sdp_unregister(void); | 
