diff options
| -rw-r--r-- | Makefile.am | 2 | ||||
| -rw-r--r-- | configure.in | 2 | ||||
| -rw-r--r-- | pand/Makefile.am | 15 | ||||
| -rw-r--r-- | pand/bnep.c | 305 | ||||
| -rw-r--r-- | pand/main.c | 604 | ||||
| -rw-r--r-- | pand/pand.1 | 59 | ||||
| -rw-r--r-- | pand/pand.h | 44 | ||||
| -rw-r--r-- | pand/sdp.c | 201 | 
8 files changed, 1230 insertions, 2 deletions
| diff --git a/Makefile.am b/Makefile.am index 1824ac2d..6a87b06c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@  #  $Id$  # -SUBDIRS := hcid tools rfcomm sdpd test scripts pcmcia +SUBDIRS := hcid tools rfcomm sdpd pand test scripts pcmcia  DISTCLEANFILES = conftest.c conftest diff --git a/configure.in b/configure.in index c9c11be5..c2868a9d 100644 --- a/configure.in +++ b/configure.in @@ -25,4 +25,4 @@ AC_PATH_BLUEZ  AC_PATH_DBUS -AC_OUTPUT(Makefile hcid/Makefile tools/Makefile rfcomm/Makefile sdpd/Makefile test/Makefile scripts/Makefile pcmcia/Makefile) +AC_OUTPUT(Makefile hcid/Makefile tools/Makefile rfcomm/Makefile sdpd/Makefile pand/Makefile test/Makefile scripts/Makefile pcmcia/Makefile) diff --git a/pand/Makefile.am b/pand/Makefile.am new file mode 100644 index 00000000..20b99270 --- /dev/null +++ b/pand/Makefile.am @@ -0,0 +1,15 @@ +# +#  $Id$ +# + +bin_PROGRAMS = pand + +pand_SOURCES = main.c pand.h bnep.c sdp.c + +LDFLAGS = @BLUEZ_LIBS@ + +INCLUDES = @BLUEZ_INCLUDES@ + +man_MANS = pand.1 + +EXTRA_DIST = $(man_MANS) diff --git a/pand/bnep.c b/pand/bnep.c new file mode 100644 index 00000000..6833f941 --- /dev/null +++ b/pand/bnep.c @@ -0,0 +1,305 @@ +/* +  pand - Bluetooth PAN daemon for BlueZ +  Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> + +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License, version 2, as +  published by the Free Software Foundation. + +  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA +*/ + +/* + * $Id$ + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +#include <sys/socket.h> +#include <sys/ioctl.h> + +#include <netinet/in.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/l2cap.h> +#include <bluetooth/bnep.h> + +#include "pand.h" + +static int ctl; + +/* Compatibility with old ioctls */ +#define OLD_BNEPCONADD      1 +#define OLD_BNEPCONDEL      2 +#define OLD_BNEPGETCONLIST  3 +#define OLD_BNEPGETCONINFO  4 + +static unsigned long bnepconnadd; +static unsigned long bnepconndel; +static unsigned long bnepgetconnlist; +static unsigned long bnepgetconninfo; + +static struct { +	char     *str; +	uint16_t uuid; +} __svc[] = { +	{ "PANU", BNEP_SVC_PANU }, +	{ "NAP",  BNEP_SVC_NAP  }, +	{ "GN",   BNEP_SVC_GN   }, +	{ NULL } +}; + +int bnep_str2svc(char *svc, uint16_t *uuid) +{ +	int i; +	for (i=0; __svc[i].str; i++) +		if (!strcasecmp(svc, __svc[i].str)) { +			*uuid = __svc[i].uuid; +			return 0; +		} +	return -1; +} + +char *bnep_svc2str(uint16_t uuid) +{ +	int i; +	for (i=0; __svc[i].str; i++) +		if (__svc[i].uuid == uuid) +			return __svc[i].str; +	return NULL; +} + +int bnep_init(void) +{ +	ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP); +	if (ctl < 0) { +		perror("Failed to open control socket"); +		return 1; +	} + +	/* Temporary ioctl compatibility hack */ +	{  +		struct bnep_connlist_req req; +		struct bnep_conninfo ci[1]; + +		req.cnum = 1; +		req.ci   = ci; + +		if (!ioctl(ctl, BNEPGETCONNLIST, &req)) { +			/* New ioctls */ +			bnepconnadd     = BNEPCONNADD; +			bnepconndel     = BNEPCONNDEL; +			bnepgetconnlist = BNEPGETCONNLIST; +			bnepgetconninfo = BNEPGETCONNINFO; +		} else { +			/* Old ioctls */ +			bnepconnadd     = OLD_BNEPCONADD; +			bnepconndel     = OLD_BNEPCONDEL; +			bnepgetconnlist = OLD_BNEPGETCONLIST; +			bnepgetconninfo = OLD_BNEPGETCONINFO; +		} +	} + +	return 0; +} + +int bnep_cleanup(void) +{ +	close(ctl); +	return 0; +} + +int bnep_show_connections(void) +{ +	struct bnep_connlist_req req; +	struct bnep_conninfo ci[48]; +	int i; + +	req.cnum = 48; +	req.ci   = ci; +	if (ioctl(ctl, bnepgetconnlist, &req)) { +		perror("Failed to get connection list"); +		return -1; +	} + +	for (i=0; i < req.cnum; i++) { +		printf("%s %s %s\n", ci[i].device, +			batostr((bdaddr_t *) ci[i].dst), +			bnep_svc2str(ci[i].role)); +	} +	return 0; +} + +int bnep_kill_connection(uint8_t *dst) +{ +	struct bnep_conndel_req req; + +	memcpy(req.dst, dst, ETH_ALEN); +	req.flags = 0; +	if (ioctl(ctl, bnepconndel, &req)) { +		perror("Failed to kill connection"); +		return -1; +	} +	return 0; +} + +int bnep_kill_all_connections(void) +{ +	struct bnep_connlist_req req; +	struct bnep_conninfo ci[48]; +	int i; + +	req.cnum = 48; +	req.ci   = ci; +	if (ioctl(ctl, bnepgetconnlist, &req)) { +		perror("Failed to get connection list"); +		return -1; +	} + +	for (i=0; i < req.cnum; i++) { +		struct bnep_conndel_req req; +		memcpy(req.dst, ci[i].dst, ETH_ALEN); +		req.flags = 0; +		ioctl(ctl, bnepconndel, &req); +	} +	return 0; +} + +static int bnep_connadd(int sk, uint16_t role, char *dev) +{ +	struct bnep_connadd_req req; + +	strcpy(req.device, dev); +	req.sock = sk; +	req.role = role; +	if (ioctl(ctl, bnepconnadd, &req)) +		return -1; +	strcpy(dev, req.device); +	return 0; +} + +struct __service_16 {  +	uint16_t dst; +	uint16_t src; +} __attribute__ ((packed)); + +struct __service_32 {  +	uint16_t unused1; +	uint16_t dst; +	uint16_t unused2; +	uint16_t src; +} __attribute__ ((packed)); + +struct __service_128 {  +	uint16_t unused1; +	uint16_t dst; +	uint16_t unused2[8]; +	uint16_t src; +	uint16_t unused3[7]; +} __attribute__ ((packed)); + +int bnep_accept_connection(int sk, uint16_t role, char *dev) +{ +	struct bnep_setup_conn_req *req; +	struct bnep_control_rsp *rsp; +	unsigned char pkt[BNEP_MTU]; +	int r; + +	r = recv(sk, pkt, BNEP_MTU, 0); +	if (r <= 0) +		return -1; + +	errno = EPROTO; + +	if (r < sizeof(*req)) +		return -1; + +	req = (void *) pkt; +	if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ) +		return -1; + +	/* FIXME: Check role UUIDs */ + +	rsp = (void *) pkt; +	rsp->type = BNEP_CONTROL; +	rsp->ctrl = BNEP_SETUP_CONN_RSP; +	rsp->resp = htons(BNEP_SUCCESS); +	if (send(sk, rsp, sizeof(*rsp), 0) < 0) +		return -1; + +	return bnep_connadd(sk, role, dev); +} + +/* Create BNEP connection  + * sk      - Connect L2CAP socket + * role    - Local role + * service - Remote service + * dev     - Network device (contains actual dev name on return) + */ +int bnep_create_connection(int sk, uint16_t role, uint16_t svc, char *dev) +{ +	struct bnep_setup_conn_req *req; +	struct bnep_control_rsp *rsp; +	struct __service_16 *s; +	unsigned char pkt[BNEP_MTU]; +	int r; + +	/* Send request */ +	req = (void *) pkt; +	req->type = BNEP_CONTROL; +	req->ctrl = BNEP_SETUP_CONN_REQ; +	req->uuid_size = 2;	//16bit UUID +	s = (void *) req->service; +	s->dst = htons(svc); +	s->src = htons(role); + +	if (send(sk, pkt, sizeof(*req) + sizeof(*s), 0) < 0) +		return -1; + +receive: +	/* Get response */ +	r = recv(sk, pkt, BNEP_MTU, 0); +	if (r <= 0) +		return -1; + +	errno = EPROTO; + +	if (r < sizeof(*rsp)) +		return -1; +	 +	rsp = (void *) pkt; +	if (rsp->type != BNEP_CONTROL) +		return -1; + +	if (rsp->ctrl != BNEP_SETUP_CONN_RSP) +		goto receive; + +	r = ntohs(rsp->resp); + +	switch (r) { +	case BNEP_SUCCESS: +		break; + +	case BNEP_CONN_INVALID_DST: +	case BNEP_CONN_INVALID_SRC: +	case BNEP_CONN_INVALID_SVC: +		errno = EPROTO; +		return -1; + +	case BNEP_CONN_NOT_ALLOWED: +		errno = EACCES; +		return -1; +	} + +	return bnep_connadd(sk, role, dev); +} diff --git a/pand/main.c b/pand/main.c new file mode 100644 index 00000000..91a9a92c --- /dev/null +++ b/pand/main.c @@ -0,0 +1,604 @@ +/* +  pand - Bluetooth PAN daemon for BlueZ +  Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> + +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License, version 2, as +  published by the Free Software Foundation. + +  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA +*/ + +/* + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <syslog.h> +#include <signal.h> +#include <errno.h> +#include <getopt.h> + +#include <sys/socket.h> +#include <sys/poll.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/l2cap.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/bnep.h> + +#include "pand.h" + +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  encrypt; +static int  master; +static int  search_duration = 10; + +static struct { +	int      valid; +	char     dst[40]; +	bdaddr_t bdaddr; +} cache; + +static char netdev[16] = "bnep%d"; + +static bdaddr_t src_addr = *BDADDR_ANY; +static int src_dev = -1; + +volatile int terminate; + +enum { +	NONE, +	SHOW, +	LISTEN, +	CONNECT, +	KILL +} modes; + +static void run_devup(char *dev, char *dst) +{ +	char *argv[4], prog[40]; + +	sprintf(prog, "%s/%s", PAND_CONFIG_DIR, PAND_DEVUP_CMD); + +	if (access(prog, R_OK | X_OK)) +		return; + +	if (fork()) +		return; + +	argv[0] = prog; +	argv[1] = dev; +	argv[2] = dst; +	argv[3] = NULL; +	execv(prog, argv); +	exit(1); +} + +static int do_listen(void) +{ +	struct l2cap_options l2o; +	struct sockaddr_l2 l2a; +	int sk, olen, lm; + +	if (use_sdp) +		bnep_sdp_register(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; +	} + +	l2a.l2_family = AF_BLUETOOTH; +	l2a.l2_psm    = htobs(BNEP_PSM); +	l2a.l2_bdaddr = src_addr; + +	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 */ +	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 = 0; +	if (master) +		lm |= L2CAP_LM_MASTER; + +	if (encrypt) +		lm |= L2CAP_LM_ENCRYPT; + +	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) { +		int alen = sizeof(l2a); +		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; +		} + +		if (!bnep_accept_connection(nsk, role, netdev)) { +			char str[40]; +			ba2str(&l2a.l2_bdaddr, str); + +			syslog(LOG_INFO, "New connection from %s %s", str, netdev); + +			run_devup(netdev, str); +		} else { +			syslog(LOG_ERR, "Connection failed. %s(%d)", +					strerror(errno), errno); +		} + +		close(nsk); +		exit(0); +	} + +	if (use_sdp) +		bnep_sdp_unregister(); +	return 0; +} + +/* Wait for disconnect or error condition on the socket */ +static int w4_hup(int sk) +{ +	struct pollfd pf; +	int n; + +	while (!terminate) { +		pf.fd = sk; +		pf.events = POLLERR | POLLHUP; +		n = poll(&pf, 1, -1); +		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, olen = sizeof(err); +			getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &olen); +			syslog(LOG_INFO, "%s disconnected%s%s", netdev, +				err ? " : " : "", err ? strerror(err) : ""); + +			close(sk); +			return 0; +		} +	} +	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; +	int sk, olen, r = 0; + +	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 */ +	getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen); +	l2o.imtu = l2o.omtu = BNEP_MTU; +	setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)); + +	l2a.l2_family = AF_BLUETOOTH; + +	/* Set local address */ +	l2a.l2_psm = 0; +	l2a.l2_bdaddr = src_addr; + +	if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a))) +		syslog(LOG_ERR, "Bind failed. %s(%d)",  +				strerror(errno), errno); + +	l2a.l2_psm = htobs(BNEP_PSM); +	l2a.l2_bdaddr = *bdaddr; + +	if (!connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) &&  +			!bnep_create_connection(sk, role, service, netdev)) { + +		syslog(LOG_INFO, "%s connected", netdev); + +		run_devup(netdev, dst); + +		if (persist) +			w4_hup(sk); + +		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, 10, 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; +			} +		} +		free(ii); +	} while (!terminate && persist); + +	return r; +} + +static void do_show(void) +{ +	bnep_show_connections(); +} + +static void do_kill(char *dst) +{ +	if (dst) +		bnep_kill_connection((void *) strtoba(dst)); +	else +		bnep_kill_all_connections(); +} + +void sig_hup(int sig) +{ +	return; +} + +void sig_term(int sig) +{ +	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' }, +	{ "role",     1, 0, 'r' }, +	{ "service",  1, 0, 'd' }, +	{ "device",   1, 0, 'i' }, +	{ "source",   1, 0, 'S' }, +	{ "nosdp",    0, 0, 'D' }, +	{ "list",     0, 0, 'l' }, +	{ "show",     0, 0, 'l' }, +	{ "nodetach", 0, 0, 'n' }, +	{ "persist",  2, 0, 'p' }, +	{ "encrypt",  0, 0, 'E' }, +	{ "master",   0, 0, 'M' }, +	{ "cache",    0, 0, 'C' }, +	{ 0, 0, 0, 0 } +}; + +static char main_sopts[] = "hsc:k:Kr:i:S:lnp::DQ::EMC::"; + +static char main_help[] =  +	"PAN daemon version " VERSION " \n" +	"Usage:\n" +	"\tpand <options>\n" +	"Options:\n" +	"\t--show --list -l          Show active PAN connections\n" +	"\t--listen -s               Listen for PAN connections\n" +	"\t--connect -c <bdaddr>     Create PAN connection\n" +	"\t--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--device -i <name>        Network interface name\n" +	"\t--source -S <bdaddr>      Source bdaddr\n" +	"\t--nosdp -D                Disable SDP\n" +	"\t--encrypt -E              Enable encryption\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"; + +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; +			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 'S': +			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 'E': +			encrypt = 1; +			break; + +		case 'M': +			master = 1; +			break; + +		case 'i': +			strcpy(netdev, optarg); +			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 'h': +		default: +			printf(main_help); +			exit(0); +		} +	} + +	argc -= optind; +	argv += optind; +	optind = 0; + +	if (bnep_init()) +		return -1; + +	/* Check non daemon modes first */ +	switch (mode) { +	case SHOW: +		do_show(); +		return 0; + +	case KILL: +		do_kill(dst); +		return 0; +	 +	case NONE: +		printf(main_help); +		return 0; +	} + +	/* Initialize signals */ +	memset(&sa, 0, sizeof(sa)); +	sa.sa_flags   = SA_NOCLDSTOP; +	sa.sa_handler = SIG_IGN; +	sigaction(SIGCHLD, &sa, NULL); +	sigaction(SIGPIPE, &sa, NULL); + +	sa.sa_handler = sig_hup; +	sigaction(SIGHUP, &sa, NULL); + +	sa.sa_handler = sig_term; +	sigaction(SIGTERM, &sa, NULL); +	sigaction(SIGINT,  &sa, NULL); + +	if (detach) { +		if (fork()) exit(0); + +		/* Direct stdin,stdout,stderr to '/dev/null' */ +		{ +			int fd = open("/dev/null", O_RDWR); +			dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); +			close(fd); +		} + +		setsid(); +		chdir("/"); +	} + +	openlog("pand", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON); +	syslog(LOG_INFO, "PAN daemon ver %s", VERSION); + +	if (src) { +		src_dev = hci_devid(src); +		if (src_dev < 0 || hci_devba(src_dev, &src_addr) < 0) { +			syslog(LOG_ERR, "Invalid source. %s(%d)", strerror(errno), errno); +			return -1; +		} +	} + +	if (dst) { +		/* Disable cache invalidation */ +		use_cache = 0; + +		strncpy(cache.dst, dst, sizeof(cache.dst) - 1); +		str2ba(dst, &cache.bdaddr); +		cache.valid = 1; +	} +	 +	switch (mode) { +	case CONNECT: +		do_connect(); +		break; + +	case LISTEN: +		do_listen(); +		break; +	} + +	return 0; +} diff --git a/pand/pand.1 b/pand/pand.1 new file mode 100644 index 00000000..a11a33c0 --- /dev/null +++ b/pand/pand.1 @@ -0,0 +1,59 @@ +.\" 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\-\-device\fR \fB\-i\fR <name> +Network interface name +.TP +\fB\-\-source\fR \fB\-S\fR <bdaddr> +Source bdaddr +.TP +\fB\-\-nosdp\fR \fB\-D\fR +Disable SDP +.TP +\fB\-\-encrypt\fR \fB\-E\fR +Enable encryption +.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 + diff --git a/pand/pand.h b/pand/pand.h new file mode 100644 index 00000000..268316de --- /dev/null +++ b/pand/pand.h @@ -0,0 +1,44 @@ +/* +  pand - Bluetooth PAN daemon for BlueZ +  Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> + +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License, version 2, as +  published by the Free Software Foundation. + +  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA +*/ + +/* + * $Id$ + */ + +/* PAN scripts & commands */ +#define PAND_CONFIG_DIR  "/etc/bluetooth/pan" +#define PAND_DEVUP_CMD   "dev-up" + +/* BNEP functions */ +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); + +/* SDP functions */ +int  bnep_sdp_register(uint16_t role); +void bnep_sdp_unregister(void); +int  bnep_sdp_search(bdaddr_t *src, bdaddr_t *dst, uint16_t service); diff --git a/pand/sdp.c b/pand/sdp.c new file mode 100644 index 00000000..16f092d9 --- /dev/null +++ b/pand/sdp.c @@ -0,0 +1,201 @@ +/* +  pand - Bluetooth PAN daemon for BlueZ +  Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> + +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License, version 2, as +  published by the Free Software Foundation. + +  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA +*/ + +/* + * $Id$ + */ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <syslog.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> +#include <bluetooth/bnep.h> + +#include "pand.h" + +static sdp_record_t  *record; +static sdp_session_t *session; + +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(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]; +	uint16_t psm = 15, version = 0x0100; +	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_uuid16_create(&l2cap, L2CAP_UUID); +	proto[0] = sdp_list_append(NULL, &l2cap); +	proto[0] = sdp_list_append(proto[0], sdp_data_alloc(SDP_UINT16, &psm)); +	apseq    = sdp_list_append(NULL, proto[0]); + +	sdp_uuid16_create(&bnep, BNEP_UUID); +	proto[1] = sdp_list_append(NULL, &bnep); +	proto[1] = sdp_list_append(proto[1], sdp_data_alloc(SDP_UINT16, &version)); + +	/* 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); + +	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", NULL, NULL); +		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", NULL, NULL); +		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_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_set_info_attr(record, "PAN User", NULL, NULL); +		break; +	} + +	status = sdp_record_register(session, 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; +} | 
