diff options
| author | Marcel Holtmann <marcel@holtmann.org> | 2004-04-25 22:03:05 +0000 | 
|---|---|---|
| committer | Marcel Holtmann <marcel@holtmann.org> | 2004-04-25 22:03:05 +0000 | 
| commit | bc4cb70ddb8fe59d8b097a62029b0f24849163a3 (patch) | |
| tree | 0927b88a57f9c94dbbdb0a543dea1478fe0d36e8 | |
| parent | 965c347ab154fd36ca98cd1fd29b048068d70dab (diff) | |
Add the dund utility
| -rw-r--r-- | Makefile.am | 2 | ||||
| -rw-r--r-- | configure.in | 2 | ||||
| -rw-r--r-- | dund/Makefile.am | 15 | ||||
| -rw-r--r-- | dund/dun.c | 308 | ||||
| -rw-r--r-- | dund/dund.1 | 49 | ||||
| -rw-r--r-- | dund/dund.h | 44 | ||||
| -rw-r--r-- | dund/lib.h | 95 | ||||
| -rw-r--r-- | dund/main.c | 558 | ||||
| -rw-r--r-- | dund/msdun.c | 145 | ||||
| -rw-r--r-- | dund/sdp.c | 146 | 
10 files changed, 1362 insertions, 2 deletions
| diff --git a/Makefile.am b/Makefile.am index 6a87b06c..0d627329 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@  #  $Id$  # -SUBDIRS := hcid tools rfcomm sdpd pand test scripts pcmcia +SUBDIRS := hcid tools rfcomm sdpd dund pand test scripts pcmcia  DISTCLEANFILES = conftest.c conftest diff --git a/configure.in b/configure.in index c2868a9d..9a6a9630 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 pand/Makefile test/Makefile scripts/Makefile pcmcia/Makefile) +AC_OUTPUT(Makefile hcid/Makefile tools/Makefile rfcomm/Makefile sdpd/Makefile dund/Makefile pand/Makefile test/Makefile scripts/Makefile pcmcia/Makefile) diff --git a/dund/Makefile.am b/dund/Makefile.am new file mode 100644 index 00000000..9c967ce0 --- /dev/null +++ b/dund/Makefile.am @@ -0,0 +1,15 @@ +# +#  $Id$ +# + +bin_PROGRAMS = dund + +dund_SOURCES = main.c dun.c dund.h sdp.c lib.h msdun.c + +LDFLAGS = @BLUEZ_LIBS@ + +INCLUDES = @BLUEZ_INCLUDES@ + +man_MANS = dund.1 + +EXTRA_DIST = $(man_MANS) diff --git a/dund/dun.c b/dund/dun.c new file mode 100644 index 00000000..8ef72ce6 --- /dev/null +++ b/dund/dun.c @@ -0,0 +1,308 @@ +/* +  dund - Bluetooth LAN/DUN 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 <fcntl.h> +#include <syslog.h> +#include <dirent.h> +#include <ctype.h> + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <sys/wait.h> + +#include <netinet/in.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> + +#include "dund.h" +#include "lib.h" + +#define PROC_BASE  "/proc" + +static int for_each_port(int (*func)(struct rfcomm_dev_info *, unsigned long), unsigned long arg) +{ +	struct rfcomm_dev_list_req *dl; +	struct rfcomm_dev_info *di; +	long r = 0; +	int  sk, i; + +	sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM); +	if (sk < 0 ) { +		perror("Can't open RFCOMM control socket"); +		exit(1); +	} + +	dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di)); +	if (!dl) { +		perror("Can't allocate request memory"); +		close(sk); +		exit(1); +	} + +	dl->dev_num = RFCOMM_MAX_DEV; +	di = dl->dev_info; + +	if (ioctl(sk, RFCOMMGETDEVLIST, (void *) dl) < 0) { +		perror("Can't get device list"); +		exit(1); +	} + +	for (i = 0; i < dl->dev_num; i++) { +		r = func(di + i, arg); +		if (r) break; +	} + +	close(sk); +	return r; +} + +static int uses_rfcomm(char *path, char *dev) +{ +	struct dirent *de; +	DIR   *dir; + +	dir = opendir(path); +	if (!dir) +		return 0; +	chdir(path); + +	while ((de = readdir(dir)) != NULL) { +		char link[PATH_MAX + 1]; +		int  len = readlink(de->d_name, link, sizeof(link)); +		if (len > 0) { +			link[len] = 0; +			if (strstr(link, dev)) +				return 1; +		} +	} +	closedir(dir); +	return 0; +} + +static int find_pppd(int id, pid_t *pid) +{ +	struct dirent *de; +	char  path[PATH_MAX + 1]; +	char  dev[10]; +	int   empty = 1; +	DIR   *dir; + +	dir = opendir(PROC_BASE); +	if (!dir) { +		perror(PROC_BASE); +		return -1; +	} + +	sprintf(dev, "rfcomm%d", id); + +	*pid = 0; +	while ((de = readdir(dir)) != NULL) { +		empty = 0; +		if (isdigit(de->d_name[0])) { +			sprintf(path, "%s/%s/fd", PROC_BASE, de->d_name); +			if (uses_rfcomm(path, dev)) { +				*pid = atoi(de->d_name); +				break; +			} +		} +	} +	closedir(dir); + +	if (empty) +		fprintf(stderr, "%s is empty (not mounted ?)\n", PROC_BASE); + +	return *pid != 0; +} + +static int dun_exec(char *tty, char *prog, char **args) +{ +	int pid = fork(); +	int fd; +	 +	switch (pid) { +	case -1: +		return -1; + +	case 0: +		break; + +	default: +		return pid; +	} + +	setsid(); + +	/* Close all FDs */ +	for (fd=3; fd < 20; fd++) +		close(fd); + +	execvp(prog, args); +	exit(1); +} + +static int dun_create_tty(int sk, char *tty, int size) +{ +	struct sockaddr_rc sa; +	struct stat st; +	int id, alen; + +	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); +	if (stat(tty, &st) < 0) { +		snprintf(tty, size, "/dev/bluetooth/rfcomm/%d", id); +		if (stat(tty, &st) < 0) { +			snprintf(tty, size, "/dev/rfcomm%d", id); +			return -1; +		} +	} + +	return id; +} + +int dun_init(void) +{ +	return 0; +} + +int dun_cleanup(void) +{ +	return 0; +} + +static int show_conn(struct rfcomm_dev_info *di, unsigned long arg) +{ +	pid_t pid; +	 +	if (di->state == BT_CONNECTED && +		(di->flags & (1<<RFCOMM_REUSE_DLC)) && +		(di->flags & (1<<RFCOMM_TTY_ATTACHED)) && +		(di->flags & (1<<RFCOMM_RELEASE_ONHUP))) { + +		if (find_pppd(di->id, &pid)) { +			char dst[18]; +			ba2str(&di->dst, dst); + +			printf("rfcomm%d: %s channel %d pppd pid %d\n", +					di->id, dst, di->channel, pid); +		} +	} +	return 0; +} + +static int kill_conn(struct rfcomm_dev_info *di, unsigned long arg) +{ +	bdaddr_t *dst = (bdaddr_t *) arg; +	pid_t pid; +	 +	if (di->state == BT_CONNECTED && +		(di->flags & (1<<RFCOMM_REUSE_DLC)) && +		(di->flags & (1<<RFCOMM_TTY_ATTACHED)) && +		(di->flags & (1<<RFCOMM_RELEASE_ONHUP))) { + +		if (dst && bacmp(&di->dst, dst)) +			return 0; + +		if (find_pppd(di->id, &pid)) { +			if (kill(pid, SIGINT) < 0) +				perror("Kill"); + +			if (!dst) +				return 0; +			return 1; +		} +	} +	return 0; +} + +int dun_show_connections(void) +{ +	for_each_port(show_conn, 0); +	return 0; +} + +int dun_kill_connection(uint8_t *dst) +{ +	for_each_port(kill_conn, (unsigned long) dst); +	return 0; +} + +int dun_kill_all_connections(void) +{ +	for_each_port(kill_conn, 0); +	return 0; +} + +int dun_open_connection(int sk, char *pppd, char **args, int wait) +{ +	char tty[100]; +	int  pid; + +	if (dun_create_tty(sk, tty, sizeof(tty) - 1) < 0) { +		syslog(LOG_ERR, "RFCOMM TTY creation failed. %s(%d)", strerror(errno), errno); +		return -1; +	} + +	args[0] = "pppd"; +	args[1] = tty; +	args[2] = "nodetach"; + +	pid = dun_exec(tty, pppd, args); +	if (pid < 0) { +		syslog(LOG_ERR, "Exec failed. %s(%d)", strerror(errno), errno); +		return -1; +	} + +	if (wait) { +		int status; +		waitpid(pid, &status, 0); +		/* FIXME: Check for waitpid errors */ +	} + +	return 0; +} diff --git a/dund/dund.1 b/dund/dund.1 new file mode 100644 index 00000000..ad2b775c --- /dev/null +++ b/dund/dund.1 @@ -0,0 +1,49 @@ +.\" 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> +.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\-\-connect\fR \fB\-c\fR <bdaddr> +Create DUN 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\-\-source\fR \fB\-S\fR <bdaddr> +Source bdaddr +.TP +\fB\-\-sdp\fR \fB\-D\fR +Enable 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 + diff --git a/dund/dund.h b/dund/dund.h new file mode 100644 index 00000000..d7c6d408 --- /dev/null +++ b/dund/dund.h @@ -0,0 +1,44 @@ +/* +  dund - Bluetooth LAN/DUN 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$ + */ + +/* DUN scripts & commands */ +#define DUN_CONFIG_DIR      "/etc/bluetooth/dun" + +#define DUN_DEFAULT_CHANNEL 1 + +#define DUN_MAX_PPP_OPTS    40 + +/* DUN functions */ +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); + +/* SDP functions */ +int  dun_sdp_register(uint8_t channel); +void dun_sdp_unregister(void); +int  dun_sdp_search(bdaddr_t *src, bdaddr_t *dst, int *channel); + diff --git a/dund/lib.h b/dund/lib.h new file mode 100644 index 00000000..5de0ba0f --- /dev/null +++ b/dund/lib.h @@ -0,0 +1,95 @@ +/*  +	RFCOMMd - RFCOMM daemon. +	Copyright (C) 2001 Qualcomm Incorporated + +	Written 2001 by 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; + +	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. +	IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, +	OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER +	RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +	NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE +	USE OR PERFORMANCE OF THIS SOFTWARE. + +	ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, +	TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. +*/ + +/* + * $Id$ + */ +#ifndef _DUND_LIB_H +#define _DUND_LIB_H + +#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; +} + +int ms_dun(int fd, int server, int timeo); + +#endif	/* _DUND_LIB_H */ diff --git a/dund/main.c b/dund/main.c new file mode 100644 index 00000000..50bebf0f --- /dev/null +++ b/dund/main.c @@ -0,0 +1,558 @@ +/* +  dund - Bluetooth LAN/DUN 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/rfcomm.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.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  encrypt; +static int  master; +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 do_listen(void) +{ +	struct sockaddr_rc sa; +	int sk; + +	if (!channel) +		channel = DUN_DEFAULT_CHANNEL; + +	if (use_sdp) +		dun_sdp_register(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; +	} + +	listen(sk, 10); + +	while (!terminate) { +		int  alen = sizeof(sa), 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); +			continue; +		} + +		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) +{ +	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", "LAP", dst); + +		if (dun_sdp_search(&src_addr, bdaddr, &ch) <= 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)) ) { +		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); +			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); +			 +			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) +{ +	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' }, +	{ "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' }, +	{ "pppd",     1, 0, 'd' }, +	{ "msdun",    2, 0, 'X' }, +	{ 0, 0, 0, 0 } +}; + +static char main_sopts[] = "hsc:k:Kr:S:lnp::DQ::EMP:C::P:X"; + +static char main_help[] =  +	"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--connect -c <bdaddr>     Create LAP 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--source -S <bdaddr>      Source bdaddr\n" +	"\t--nosdp -D                Disable SDP\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--cache -C[valid]         Enable addess 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; +			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 'S': +			src = strdup(optarg); +			break; +	 +		case 'D': +			use_sdp = 0; +			break; + +		case 'E': +			encrypt = 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 '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) { +		int fd; + +		if (fork()) exit(0); +		 +		/* Direct stdin,stdout,stderr to '/dev/null' */ +		fd = open("/dev/null", O_RDWR); +		dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); +		close(fd); +		 +		setsid(); +		chdir("/"); +	} + +	openlog("dund", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON); +	syslog(LOG_INFO, "DUN 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) { +		strncpy(cache.dst, dst, sizeof(cache.dst) - 1); +		str2ba(dst, &cache.bdaddr); + +		/* Disable cache invalidation */ +		use_cache = cache.valid = ~0; +	} + +	switch (mode) { +	case CONNECT: +		do_connect(); +		break; + +	case LISTEN: +		do_listen(); +		break; +	} + +	return 0; +} diff --git a/dund/msdun.c b/dund/msdun.c new file mode 100644 index 00000000..89fa3ff0 --- /dev/null +++ b/dund/msdun.c @@ -0,0 +1,145 @@ +/* +  dund - Bluetooth LAN/DUN 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 <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> +#include <fcntl.h> +#include <syslog.h> +#include <setjmp.h> +#include <string.h> + +#include "lib.h" + +#define MS_PPP      2 +#define MS_SUCCESS  1 +#define MS_FAILED  -1 +#define MS_TIMEOUT -2 + +static sigjmp_buf jmp; +static int        retry; +static int        timeout; + +static void sig_alarm(int sig) +{ +	siglongjmp(jmp, MS_TIMEOUT); +} + +static int w4_str(int fd, char *str) +{ +	char buf[40]; +	int  r, len = 0; +	 +	while (1) { +		r = read(fd, buf + len, sizeof(buf) - len - 1); +		if (r < 0) { +			if (errno == EINTR || errno == EAGAIN) +				continue; +			break; +		} +		if (!r) +			break; + +		len += r; + +		if (len < strlen(str)) +			continue; +		buf[len] = 0; + +		if (strstr(buf, str)) +			return MS_SUCCESS; + +		/* Detect PPP */ +		if (strchr(buf, '~')) +			return MS_PPP; +	} +	return MS_FAILED; +} + +static int ms_server(int fd) +{ +	switch (w4_str(fd, "CLIENT")) { +	case MS_SUCCESS: +		write_n(fd, "CLIENTSERVER", 12); +	case MS_PPP: +		return MS_SUCCESS; +	default:	 +		return MS_FAILED; +	} +} + +static int ms_client(int fd) +{ +	write_n(fd, "CLIENT", 6); +	return w4_str(fd, "CLIENTSERVER"); +} + +int ms_dun(int fd, int server, int timeo) +{ +	sig_t osig; + +	retry    = 4; +	timeout  = timeo; + +	if (!server) +		timeout /= retry; + +	osig = signal(SIGALRM, sig_alarm); + +	while (1) { +		int r = sigsetjmp(jmp, 1); +		if (r) { +			if (r == MS_TIMEOUT && !server && --retry) +				continue; + +			alarm(0); +			signal(SIGALRM, osig); +		 +			switch (r) { +			case MS_SUCCESS: +			case MS_PPP: +				errno = 0; +				return 0; + +			case MS_FAILED: +				errno = EPROTO; +				break; + +			case MS_TIMEOUT: +				errno = ETIMEDOUT; +				break; +			} +			return -1; +		} + +		alarm(timeout); +		 +		if (server) +			r = ms_server(fd); +		else +			r = ms_client(fd); + +		siglongjmp(jmp, r); +	} +} diff --git a/dund/sdp.c b/dund/sdp.c new file mode 100644 index 00000000..9cac4efa --- /dev/null +++ b/dund/sdp.c @@ -0,0 +1,146 @@ +/* +  dund - Bluetooth LAN/DUN 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 "dund.h" + +static sdp_record_t  *record; +static sdp_session_t *session; + +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(uint8_t channel) +{ +	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); + +	sdp_uuid16_create(&dun, LAN_ACCESS_SVCLASS_ID); +	svclass = sdp_list_append(NULL, &dun); +	sdp_set_service_classes(record, svclass); + +	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); +		 +	sdp_set_info_attr(record, "LAN Access Point", NULL, NULL); + +	status = sdp_record_register(session, 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) +{ +	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; +	} + +	sdp_uuid16_create(&svclass, LAN_ACCESS_SVCLASS_ID); +	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; +} | 
