mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-16 00:34:39 +08:00
remove compat daemons
This commit is contained in:
parent
3e68d12a4f
commit
c31c93c351
@ -223,42 +223,3 @@ EXTRA_DIST += test/sap-client test/hsplay test/hsmicro \
|
||||
test/service-record.dtd test/service-did.xml \
|
||||
test/service-spp.xml test/service-opp.xml test/service-ftp.xml \
|
||||
test/simple-player test/test-nap
|
||||
|
||||
if HIDD
|
||||
bin_PROGRAMS += compat/hidd
|
||||
|
||||
compat_hidd_SOURCES = compat/hidd.c compat/hidd.h src/uinput.h \
|
||||
compat/sdp.h compat/sdp.c compat/fakehid.c \
|
||||
src/textfile.h src/textfile.c
|
||||
compat_hidd_LDADD = -lm lib/libbluetooth-private.la
|
||||
|
||||
dist_man_MANS += compat/hidd.1
|
||||
else
|
||||
EXTRA_DIST += compat/hidd.1
|
||||
endif
|
||||
|
||||
if PAND
|
||||
bin_PROGRAMS += compat/pand
|
||||
|
||||
compat_pand_SOURCES = compat/pand.c compat/pand.h \
|
||||
compat/bnep.c compat/sdp.h compat/sdp.c \
|
||||
src/textfile.h src/textfile.c
|
||||
compat_pand_LDADD = lib/libbluetooth-private.la
|
||||
|
||||
dist_man_MANS += compat/pand.1
|
||||
else
|
||||
EXTRA_DIST += compat/pand.1
|
||||
endif
|
||||
|
||||
if DUND
|
||||
bin_PROGRAMS += compat/dund
|
||||
|
||||
compat_dund_SOURCES = compat/dund.c compat/dund.h compat/lib.h \
|
||||
compat/sdp.h compat/sdp.c compat/dun.c compat/msdun.c \
|
||||
src/textfile.h src/textfile.c
|
||||
compat_dund_LDADD = lib/libbluetooth-private.la
|
||||
|
||||
dist_man_MANS += compat/dund.1
|
||||
else
|
||||
EXTRA_DIST += compat/dund.1
|
||||
endif
|
||||
|
18
acinclude.m4
18
acinclude.m4
@ -179,9 +179,6 @@ AC_DEFUN([AC_ARG_BLUEZ], [
|
||||
health_enable=no
|
||||
pnat_enable=no
|
||||
tools_enable=yes
|
||||
hidd_enable=no
|
||||
pand_enable=no
|
||||
dund_enable=no
|
||||
cups_enable=no
|
||||
test_enable=no
|
||||
bccmd_enable=no
|
||||
@ -273,18 +270,6 @@ AC_DEFUN([AC_ARG_BLUEZ], [
|
||||
dfutool_enable=${enableval}
|
||||
])
|
||||
|
||||
AC_ARG_ENABLE(hidd, AC_HELP_STRING([--enable-hidd], [install HID daemon]), [
|
||||
hidd_enable=${enableval}
|
||||
])
|
||||
|
||||
AC_ARG_ENABLE(pand, AC_HELP_STRING([--enable-pand], [install PAN daemon]), [
|
||||
pand_enable=${enableval}
|
||||
])
|
||||
|
||||
AC_ARG_ENABLE(dund, AC_HELP_STRING([--enable-dund], [install DUN daemon]), [
|
||||
dund_enable=${enableval}
|
||||
])
|
||||
|
||||
AC_ARG_ENABLE(cups, AC_HELP_STRING([--enable-cups], [install CUPS backend support]), [
|
||||
cups_enable=${enableval}
|
||||
])
|
||||
@ -369,9 +354,6 @@ AC_DEFUN([AC_ARG_BLUEZ], [
|
||||
AM_CONDITIONAL(HAL, test "${hal_enable}" = "yes")
|
||||
AM_CONDITIONAL(READLINE, test "${readline_found}" = "yes")
|
||||
AM_CONDITIONAL(PNATPLUGIN, test "${pnat_enable}" = "yes")
|
||||
AM_CONDITIONAL(HIDD, test "${hidd_enable}" = "yes")
|
||||
AM_CONDITIONAL(PAND, test "${pand_enable}" = "yes")
|
||||
AM_CONDITIONAL(DUND, test "${dund_enable}" = "yes")
|
||||
AM_CONDITIONAL(CUPS, test "${cups_enable}" = "yes")
|
||||
AM_CONDITIONAL(TEST, test "${test_enable}" = "yes" && test "${check_found}" = "yes")
|
||||
AM_CONDITIONAL(TOOLS, test "${tools_enable}" = "yes")
|
||||
|
@ -21,9 +21,6 @@ fi
|
||||
--enable-bccmd \
|
||||
--enable-dfutool \
|
||||
--enable-hid2hci \
|
||||
--enable-hidd \
|
||||
--enable-pand \
|
||||
--enable-dund \
|
||||
--enable-test \
|
||||
--enable-cups \
|
||||
--enable-dbusoob \
|
||||
|
339
compat/bnep.c
339
compat/bnep.c
@ -1,339 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
* Copyright (C) 2002-2010 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/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];
|
||||
unsigned 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++) {
|
||||
char addr[18];
|
||||
ba2str((bdaddr_t *) ci[i].dst, addr);
|
||||
printf("%s %s %s\n", ci[i].device,
|
||||
addr, 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];
|
||||
unsigned 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];
|
||||
ssize_t r;
|
||||
|
||||
r = recv(sk, pkt, BNEP_MTU, 0);
|
||||
if (r <= 0)
|
||||
return -1;
|
||||
|
||||
errno = EPROTO;
|
||||
|
||||
if ((size_t) r < sizeof(*req))
|
||||
return -1;
|
||||
|
||||
req = (void *) pkt;
|
||||
|
||||
/* Highest known Control command ID
|
||||
* is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */
|
||||
if (req->type == BNEP_CONTROL &&
|
||||
req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
|
||||
uint8_t pkt[3];
|
||||
|
||||
pkt[0] = BNEP_CONTROL;
|
||||
pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
|
||||
pkt[2] = req->ctrl;
|
||||
|
||||
send(sk, pkt, sizeof(pkt), 0);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
struct timeval timeo;
|
||||
unsigned char pkt[BNEP_MTU];
|
||||
ssize_t 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);
|
||||
|
||||
memset(&timeo, 0, sizeof(timeo));
|
||||
timeo.tv_sec = 30;
|
||||
|
||||
setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
|
||||
|
||||
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;
|
||||
|
||||
memset(&timeo, 0, sizeof(timeo));
|
||||
timeo.tv_sec = 0;
|
||||
|
||||
setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
|
||||
|
||||
errno = EPROTO;
|
||||
|
||||
if ((size_t) 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);
|
||||
}
|
334
compat/dun.c
334
compat/dun.c
@ -1,334 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
* Copyright (C) 2002-2010 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);
|
||||
free(dl);
|
||||
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, PATH_MAX);
|
||||
if (len > 0) {
|
||||
link[len] = 0;
|
||||
if (strstr(link, dev)) {
|
||||
closedir(dir);
|
||||
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;
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
.\" 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
|
645
compat/dund.c
645
compat/dund.c
@ -1,645 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
* Copyright (C) 2002-2010 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);
|
||||
snprintf(ch, sizeof(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();
|
||||
}
|
||||
|
||||
static void sig_hup(int sig)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static 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 const char *main_sopts = "hsc:k:Kr:i:lnp::DQ::AESMP:C::P:Xam:u";
|
||||
|
||||
static const char *main_help =
|
||||
"Bluetooth LAP (LAN Access over PPP) daemon version %s\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, VERSION);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
/* The rest is pppd options */
|
||||
if (argc > 0) {
|
||||
for (opt = 3; argc && opt < DUN_MAX_PPP_OPTS - 1;
|
||||
argc--, opt++)
|
||||
pppd_opts[opt] = *argv++;
|
||||
pppd_opts[opt] = NULL;
|
||||
}
|
||||
|
||||
io_init();
|
||||
|
||||
if (dun_init()) {
|
||||
free(dst);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check non daemon modes first */
|
||||
switch (mode) {
|
||||
case SHOW:
|
||||
do_show();
|
||||
free(dst);
|
||||
return 0;
|
||||
|
||||
case KILL:
|
||||
do_kill(dst);
|
||||
free(dst);
|
||||
return 0;
|
||||
|
||||
case NONE:
|
||||
printf(main_help, VERSION);
|
||||
free(dst);
|
||||
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);
|
||||
free(dst);
|
||||
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;
|
||||
}
|
||||
|
||||
free(dst);
|
||||
return 0;
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
* Copyright (C) 2002-2010 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);
|
669
compat/fakehid.c
669
compat/fakehid.c
@ -1,669 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2003-2010 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 int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
|
||||
{
|
||||
struct uinput_event event;
|
||||
|
||||
if (fd <= fileno(stderr))
|
||||
return -EINVAL;
|
||||
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.type = type;
|
||||
event.code = code;
|
||||
event.value = value;
|
||||
|
||||
return 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 - 1);
|
||||
|
||||
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;
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
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.
|
@ -1,41 +0,0 @@
|
||||
.\" 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
|
848
compat/hidd.c
848
compat/hidd.c
@ -1,848 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2003-2010 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 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);
|
||||
}
|
||||
|
||||
err = ioctl(ctl, HIDPCONNADD, &req);
|
||||
|
||||
error:
|
||||
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];
|
||||
unsigned 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));
|
||||
name[0] = '\0';
|
||||
|
||||
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];
|
||||
unsigned 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;
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2003-2010 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);
|
86
compat/lib.h
86
compat/lib.h
@ -1,86 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
* Copyright (C) 2002-2010 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;
|
||||
}
|
153
compat/msdun.c
153
compat/msdun.c
@ -1,153 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
* Copyright (C) 2002-2010 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"
|
||||
#include "dund.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];
|
||||
unsigned len = 0;
|
||||
int r;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
.\" 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.
|
811
compat/pand.c
811
compat/pand.c
@ -1,811 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
* Copyright (C) 2002-2010 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) {
|
||||
bdaddr_t *ba = strtoba(dst);
|
||||
bnep_kill_connection((void *) ba);
|
||||
free(ba);
|
||||
} 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 const char *main_sopts = "hsc:k:Kr:d:e:i:lnp::DQ::AESMC::P:u:o:z";
|
||||
|
||||
static const char *main_help =
|
||||
"Bluetooth PAN daemon version %s\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, VERSION);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
optind = 0;
|
||||
|
||||
if (bnep_init()) {
|
||||
free(dst);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check non daemon modes first */
|
||||
switch (mode) {
|
||||
case SHOW:
|
||||
do_show();
|
||||
free(dst);
|
||||
return 0;
|
||||
|
||||
case KILL:
|
||||
do_kill(dst);
|
||||
free(dst);
|
||||
return 0;
|
||||
|
||||
case NONE:
|
||||
printf(main_help, VERSION);
|
||||
free(dst);
|
||||
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);
|
||||
free(dst);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (pidfile && write_pidfile()) {
|
||||
free(dst);
|
||||
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;
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
||||
* Copyright (C) 2002-2010 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);
|
706
compat/sdp.c
706
compat/sdp.c
@ -1,706 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2003-2010 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 <limits.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 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 };
|
||||
unsigned 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);
|
||||
|
||||
|
||||
req->vendor = vendor;
|
||||
req->product = product;
|
||||
req->version = version;
|
||||
req->subclass = subclass;
|
||||
req->country = country;
|
||||
req->parser = parser;
|
||||
|
||||
snprintf(req->name, 128, "%s", str + pos);
|
||||
|
||||
free(str);
|
||||
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;
|
||||
|
||||
pdlist2 = sdp_data_get(rec, 0x0100);
|
||||
if (pdlist2)
|
||||
strncpy(req->name, pdlist2->val.str, sizeof(req->name) - 1);
|
||||
else {
|
||||
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) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
sdp_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;
|
||||
}
|
39
compat/sdp.h
39
compat/sdp.h
@ -1,39 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2003-2010 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);
|
Loading…
Reference in New Issue
Block a user