diff options
Diffstat (limited to 'compat/dund.c')
| -rw-r--r-- | compat/dund.c | 637 | 
1 files changed, 637 insertions, 0 deletions
| diff --git a/compat/dund.c b/compat/dund.c new file mode 100644 index 00000000..dea945d2 --- /dev/null +++ b/compat/dund.c @@ -0,0 +1,637 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com> + *  Copyright (C) 2002-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <signal.h> +#include <getopt.h> + +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/rfcomm.h> +#include <bluetooth/hidp.h> + +#include "sdp.h" +#include "dund.h" +#include "lib.h" + +volatile sig_atomic_t __io_canceled; + +/* MS dialup networking support (i.e. CLIENT / CLIENTSERVER thing) */ +static int msdun = 0; + +static char *pppd = "/usr/sbin/pppd"; +static char *pppd_opts[DUN_MAX_PPP_OPTS] = +	{ +		/* First 3 are reserved */ +		"", "", "", +		"noauth", +		"noipdefault", +		NULL +	}; + +static int  detach = 1; +static int  persist; +static int  use_sdp = 1; +static int  auth; +static int  encrypt; +static int  secure; +static int  master; +static int  type = LANACCESS; +static int  search_duration = 10; +static uint use_cache; + +static int  channel; + +static struct { +	uint     valid; +	char     dst[40]; +	bdaddr_t bdaddr; +	int      channel; +} cache; + +static bdaddr_t src_addr = *BDADDR_ANY; +static int src_dev = -1; + +volatile int terminate; + +enum { +	NONE, +	SHOW, +	LISTEN, +	CONNECT, +	KILL +} modes; + +static int create_connection(char *dst, bdaddr_t *bdaddr, int mrouter); + +static int do_listen(void) +{ +	struct sockaddr_rc sa; +	int sk, lm; + +	if (type == MROUTER) { +		if (!cache.valid) +			return -1; + +		if (create_connection(cache.dst, &cache.bdaddr, type) < 0) { +			syslog(LOG_ERR, "Cannot connect to mRouter device. %s(%d)", +								strerror(errno), errno); +			return -1; +		} +	} + +	if (!channel) +		channel = DUN_DEFAULT_CHANNEL; + +	if (use_sdp) +		dun_sdp_register(&src_addr, channel, type); + +	if (type == MROUTER) +		syslog(LOG_INFO, "Waiting for mRouter callback on channel %d", channel); + +	/* Create RFCOMM socket */ +	sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); +	if (sk < 0) { +		syslog(LOG_ERR, "Cannot create RFCOMM socket. %s(%d)", +				strerror(errno), errno); +		return -1; +	} + +	sa.rc_family  = AF_BLUETOOTH; +	sa.rc_channel = channel; +	sa.rc_bdaddr  = src_addr; + +	if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) { +		syslog(LOG_ERR, "Bind failed. %s(%d)", strerror(errno), errno); +		return -1; +	} + +	/* Set link mode */ +	lm = 0; +	if (master) +		lm |= RFCOMM_LM_MASTER; +	if (auth) +		lm |= RFCOMM_LM_AUTH; +	if (encrypt) +		lm |= RFCOMM_LM_ENCRYPT; +	if (secure) +		lm |= RFCOMM_LM_SECURE; + +	if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { +		syslog(LOG_ERR, "Failed to set link mode. %s(%d)", strerror(errno), errno); +		return -1; +	} + +	listen(sk, 10); + +	while (!terminate) { +		socklen_t alen = sizeof(sa); +		int nsk; +		char ba[40]; +		char ch[10]; + +		nsk = accept(sk, (struct sockaddr *) &sa, &alen); +		if (nsk < 0) { +			syslog(LOG_ERR, "Accept failed. %s(%d)", strerror(errno), errno); +			continue; +		} + +		switch (fork()) { +		case 0: +			break; +		case -1: +			syslog(LOG_ERR, "Fork failed. %s(%d)", strerror(errno), errno); +		default: +			close(nsk); +			if (type == MROUTER) { +				close(sk); +				terminate = 1; +			} +			continue; +		} + +		close(sk); + +		if (msdun && ms_dun(nsk, 1, msdun) < 0) { +			syslog(LOG_ERR, "MSDUN failed. %s(%d)", strerror(errno), errno); +			exit(0); +		} + +		ba2str(&sa.rc_bdaddr, ba); +		sprintf(ch, "%d", channel); + +		/* Setup environment */ +		setenv("DUN_BDADDR",  ba, 1); +		setenv("DUN_CHANNEL", ch, 1); + +		if (!dun_open_connection(nsk, pppd, pppd_opts, 0)) +			syslog(LOG_INFO, "New connection from %s", ba); + +		close(nsk); +		exit(0); +	} + +	if (use_sdp) +		dun_sdp_unregister(); +	return 0; +} + +/* Connect and initiate RFCOMM session + * Returns: + *   -1 - critical error (exit persist mode) + *   1  - non critical error + *   0  - success + */ +static int create_connection(char *dst, bdaddr_t *bdaddr, int mrouter) +{ +	struct sockaddr_rc sa; +	int sk, err = 0, ch; + +	if (use_cache && cache.valid && cache.channel) { +		/* Use cached channel */ +		ch = cache.channel; + +	} else if (!channel) { +		syslog(LOG_INFO, "Searching for %s on %s", mrouter ? "SP" : "LAP", dst); + +		if (dun_sdp_search(&src_addr, bdaddr, &ch, mrouter) <= 0) +			return 0; +	} else +		ch = channel; + +	syslog(LOG_INFO, "Connecting to %s channel %d", dst, ch); + +	sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); +	if (sk < 0) { +		syslog(LOG_ERR, "Cannot create RFCOMM socket. %s(%d)", +		     strerror(errno), errno); +		return -1; +	} + +	sa.rc_family  = AF_BLUETOOTH; +	sa.rc_channel = 0; +	sa.rc_bdaddr  = src_addr; + +	if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) +		syslog(LOG_ERR, "Bind failed. %s(%d)",  +			strerror(errno), errno); + +	sa.rc_channel = ch; +	sa.rc_bdaddr  = *bdaddr; + +	if (!connect(sk, (struct sockaddr *) &sa, sizeof(sa)) ) { +		if (mrouter) { +			sleep(1); +			close(sk); +			return 0; +		} + +		syslog(LOG_INFO, "Connection established"); + +		if (msdun && ms_dun(sk, 0, msdun) < 0) { +			syslog(LOG_ERR, "MSDUN failed. %s(%d)", strerror(errno), errno); +			err = 1; +			goto out; +		} + +		if (!dun_open_connection(sk, pppd, pppd_opts, (persist > 0))) +			err = 0; +		else +			err = 1; +	} else { +		syslog(LOG_ERR, "Connect to %s failed. %s(%d)", +				dst, strerror(errno), errno); +		err = 1; +	} + +out: +	if (use_cache) { +		if (!err) { +			/* Succesesful connection, validate cache */ +			strcpy(cache.dst, dst); +			bacpy(&cache.bdaddr, bdaddr); +			cache.channel = ch; +			cache.valid   = use_cache; +		} else { +			cache.channel = 0; +			cache.valid--; +		} +	} + +	close(sk); +	return err; +} + +/* Search and connect + * Returns: + *   -1 - critical error (exit persist mode) + *   1  - non critical error + *   0  - success + */ +static int do_connect(void) +{ +	inquiry_info *ii; +	int reconnect = 0; +	int i, n, r = 0; + +	do { +		if (reconnect) +			sleep(persist); +		reconnect = 1; + +		if (cache.valid) { +			/* Use cached bdaddr */ +			r = create_connection(cache.dst, &cache.bdaddr, 0); +			if (r < 0) { +				terminate = 1; +				break; +			} +			continue; +		} + +		syslog(LOG_INFO, "Inquiring"); + +		/* FIXME: Should we use non general LAP here ? */ + +		ii = NULL; +		n  = hci_inquiry(src_dev, search_duration, 0, NULL, &ii, 0); +		if (n < 0) { +			syslog(LOG_ERR, "Inquiry failed. %s(%d)", strerror(errno), errno); +			continue; +		} + +		for (i = 0; i < n; i++) { +			char dst[40]; +			ba2str(&ii[i].bdaddr, dst); +			 +			r = create_connection(dst, &ii[i].bdaddr, 0); +			if (r < 0) { +				terminate = 1; +				break; +			} +		} +		bt_free(ii); +	} while (!terminate && persist); + +	return r; +} + +static void do_show(void) +{ +	dun_show_connections(); +} + +static void do_kill(char *dst) +{ +	if (dst) { +		bdaddr_t ba; +		str2ba(dst, &ba); +		dun_kill_connection((void *) &ba); +	} else +		dun_kill_all_connections(); +} + +void sig_hup(int sig) +{ +	return; +} + +void sig_term(int sig) +{ +	io_cancel(); +	terminate = 1; +} + +static struct option main_lopts[] = { +	{ "help",	0, 0, 'h' }, +	{ "listen",	0, 0, 's' }, +	{ "connect",	1, 0, 'c' }, +	{ "search",	2, 0, 'Q' }, +	{ "kill",	1, 0, 'k' }, +	{ "killall",	0, 0, 'K' }, +	{ "channel",	1, 0, 'P' }, +	{ "device",	1, 0, 'i' }, +	{ "nosdp",	0, 0, 'D' }, +	{ "list",	0, 0, 'l' }, +	{ "show",	0, 0, 'l' }, +	{ "nodetach",	0, 0, 'n' }, +	{ "persist",	2, 0, 'p' }, +	{ "auth",	0, 0, 'A' }, +	{ "encrypt",	0, 0, 'E' }, +	{ "secure",	0, 0, 'S' }, +	{ "master",	0, 0, 'M' }, +	{ "cache",	0, 0, 'C' }, +	{ "pppd",	1, 0, 'd' }, +	{ "msdun",	2, 0, 'X' }, +	{ "activesync",	0, 0, 'a' }, +	{ "mrouter",	1, 0, 'm' }, +	{ "dialup",	0, 0, 'u' }, +	{ 0, 0, 0, 0 } +}; + +static char main_sopts[] = "hsc:k:Kr:i:lnp::DQ::AESMP:C::P:Xam:u"; + +static char main_help[] =  +	"Bluetooth LAP (LAN Access over PPP) daemon version " VERSION " \n" +	"Usage:\n" +	"\tdund <options> [pppd options]\n" +	"Options:\n" +	"\t--show --list -l          Show active LAP connections\n" +	"\t--listen -s               Listen for LAP connections\n" +	"\t--dialup -u               Pretend to be a dialup/telephone\n" +	"\t--connect -c <bdaddr>     Create LAP connection\n" +	"\t--mrouter -m <bdaddr>     Create mRouter connection\n" +	"\t--search -Q[duration]     Search and connect\n" +	"\t--kill -k <bdaddr>        Kill LAP connection\n" +	"\t--killall -K              Kill all LAP connections\n" +	"\t--channel -P <channel>    RFCOMM channel\n" +	"\t--device -i <bdaddr>      Source bdaddr\n" +	"\t--nosdp -D                Disable SDP\n" +	"\t--auth -A                 Enable authentication\n" +	"\t--encrypt -E              Enable encryption\n" +	"\t--secure -S               Secure connection\n" +	"\t--master -M               Become the master of a piconet\n" +	"\t--nodetach -n             Do not become a daemon\n" +	"\t--persist -p[interval]    Persist mode\n" +	"\t--pppd -d <pppd>          Location of the PPP daemon (pppd)\n" +	"\t--msdun -X[timeo]         Enable Microsoft dialup networking support\n" +	"\t--activesync -a           Enable Microsoft ActiveSync networking\n" +	"\t--cache -C[valid]         Enable address cache\n"; + +int main(int argc, char *argv[]) +{ +	char *dst = NULL, *src = NULL; +	struct sigaction sa; +	int mode = NONE; +	int opt; + +	while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) { +		switch(opt) { +		case 'l': +			mode = SHOW; +			detach = 0; +			break; + +		case 's': +			mode = LISTEN; +			type = LANACCESS; +			break; + +		case 'c': +			mode = CONNECT; +			dst  = strdup(optarg); +			break; + +		case 'Q': +			mode = CONNECT; +			dst  = NULL; +			if (optarg) +				search_duration = atoi(optarg); +			break; + +		case 'k': +			mode = KILL; +			detach = 0; +			dst  = strdup(optarg); +			break; + +		case 'K': +			mode = KILL; +			detach = 0; +			dst  = NULL; +			break; + +		case 'P': +			channel = atoi(optarg); +			break; + +		case 'i': +			src = strdup(optarg); +			break; + +		case 'D': +			use_sdp = 0; +			break; + +		case 'A': +			auth = 1; +			break; + +		case 'E': +			encrypt = 1; +			break; + +		case 'S': +			secure = 1; +			break; + +		case 'M': +			master = 1; +			break; + +		case 'n': +			detach = 0; +			break; + +		case 'p': +			if (optarg) +				persist = atoi(optarg); +			else +				persist = 5; +			break; + +		case 'C': +			if (optarg) +				use_cache = atoi(optarg); +			else +				use_cache = 2; +			break; + +		case 'd': +			pppd  = strdup(optarg); +			break; + +		case 'X': +			if (optarg) +				msdun = atoi(optarg); +			else +				msdun = 10; +			break; + +		case 'a': +			msdun = 10; +			type = ACTIVESYNC; +			break; + +		case 'm': +			mode = LISTEN; +			dst  = strdup(optarg); +			type = MROUTER; +			break; + +		case 'u': +			mode = LISTEN; +			type = DIALUP; +			break; + +		case 'h': +		default: +			printf(main_help); +			exit(0); +		} +	} + +	argc -= optind; +	argv += optind; + +	/* The rest is pppd options */ +	if (argc > 0) { +		for (opt = 3; argc && opt < DUN_MAX_PPP_OPTS; argc--, opt++) +			pppd_opts[opt] = *argv++; +		pppd_opts[opt] = NULL; +	} + +	io_init(); + +	if (dun_init()) +		return -1; + +	/* Check non daemon modes first */ +	switch (mode) { +	case SHOW: +		do_show(); +		return 0; + +	case KILL: +		do_kill(dst); +		return 0; + +	case NONE: +		printf(main_help); +		return 0; +	} + +	/* Initialize signals */ +	memset(&sa, 0, sizeof(sa)); +	sa.sa_flags   = SA_NOCLDSTOP; +	sa.sa_handler = SIG_IGN; +	sigaction(SIGCHLD, &sa, NULL); +	sigaction(SIGPIPE, &sa, NULL); + +	sa.sa_handler = sig_term; +	sigaction(SIGTERM, &sa, NULL); +	sigaction(SIGINT,  &sa, NULL); + +	sa.sa_handler = sig_hup; +	sigaction(SIGHUP, &sa, NULL); + +	if (detach && daemon(0, 0)) { +		perror("Can't start daemon"); +		exit(1); +	} + +	openlog("dund", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON); +	syslog(LOG_INFO, "Bluetooth DUN daemon version %s", VERSION); + +	if (src) { +		src_dev = hci_devid(src); +		if (src_dev < 0 || hci_devba(src_dev, &src_addr) < 0) { +			syslog(LOG_ERR, "Invalid source. %s(%d)", strerror(errno), errno); +			return -1; +		} +	} + +	if (dst) { +		strncpy(cache.dst, dst, sizeof(cache.dst) - 1); +		str2ba(dst, &cache.bdaddr); + +		/* Disable cache invalidation */ +		use_cache = cache.valid = ~0; +	} + +	switch (mode) { +	case CONNECT: +		do_connect(); +		break; + +	case LISTEN: +		do_listen(); +		break; +	} + +	return 0; +} | 
