diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/Makefile.am | 2 | ||||
| -rw-r--r-- | tools/ppporc.c | 282 | 
2 files changed, 283 insertions, 1 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am index 281be99e..b4049c32 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -9,6 +9,6 @@ bin_PROGRAMS  = hcitool l2ping  man_MANS      = hciattach.8 l2ping.8 -noinst_PROGRAMS	= l2test scotest rctest +noinst_PROGRAMS	= ppporc l2test scotest rctest  EXTRA_DIST    = $(man_MANS) diff --git a/tools/ppporc.c b/tools/ppporc.c new file mode 100644 index 00000000..514ceb65 --- /dev/null +++ b/tools/ppporc.c @@ -0,0 +1,282 @@ +/* + * + *  PPP over Bluetooth RFCOMM + * + *  Copyright (C) 2002  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <syslog.h> +#include <sys/poll.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> + + +extern int optind, opterr, optopt; +extern char *optarg; + + +/* IO cancelation */ +static volatile sig_atomic_t __io_canceled; + +static inline void io_init(void) +{ +	__io_canceled = 0; +} + +static inline void io_cancel(void) +{ +	__io_canceled = 1; +} + + +/* Signal functions */ +static void sig_hup(int sig) +{ +	return; +} + +static void sig_term(int sig) +{ +	syslog(LOG_INFO, "Closing RFCOMM channel"); +	io_cancel(); +} + + +/* 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; +} + + +/* Create the RFCOMM connection */ +static int create_connection(bdaddr_t *bdaddr, uint8_t channel) +{ +	struct sockaddr_rc remote_addr, local_addr; +	int fd, err; + +	if ((fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) +		return fd; + +	memset(&local_addr, 0, sizeof(local_addr)); +	local_addr.rc_family = AF_BLUETOOTH; +	bacpy(&local_addr.rc_bdaddr, BDADDR_ANY); +	if ((err = bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr))) < 0) { +		close(fd); +		return err; +	} + +	memset(&remote_addr, 0, sizeof(remote_addr)); +	remote_addr.rc_family = AF_BLUETOOTH; +	bacpy(&remote_addr.rc_bdaddr, bdaddr); +	remote_addr.rc_channel = channel; +	if ((err = connect(fd, (struct sockaddr *)&remote_addr, sizeof(remote_addr))) < 0) { +		close(fd); +		return err; +	} + +	syslog(LOG_INFO, "RFCOMM channel %d connected", channel); + +	return fd; +} + + +/* Process the data from socket and pseudo tty */ +static int process_data(int fd) +{ +	struct pollfd p[2]; +	char buf[1024]; +	int err, r; + +	p[0].fd = 0; +	p[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL; +	 +	p[1].fd = fd; +	p[1].events = POLLIN | POLLERR | POLLHUP | POLLNVAL; + +	err = 0; +	 +	while (!__io_canceled) { +		p[0].revents = 0; +		p[1].revents = 0; +		 +		err = poll(p, 2, -1); +		if (err < 0) +			break; + +		err = 0; + +		if (p[0].revents) { +			if (p[0].revents & (POLLERR | POLLHUP | POLLNVAL)) +			  break; +			r = read(0, buf, sizeof(buf)); +			if (r < 0) { +				if (errno != EINTR && errno != EAGAIN) { +					err = r; +					break; +				} +			} + +			err = write_n(fd, buf, r); +			if (err < 0) +				break; +		} + +		if (p[1].revents) { +			if (p[1].revents & (POLLERR | POLLHUP | POLLNVAL)) +			  break; +			r = read(fd, buf, sizeof(buf)); +			if (r < 0) { +				if (errno != EINTR && errno != EAGAIN) { +					err = r; +					break; +				} +			} + +			err = write_n(1, buf, r); +			if (err < 0) +				break; +		} +	} + +	return err; +} + + +static void usage(void) +{ +	printf("Usage:\tppporc <bdaddr> [channel]\n"); +} + + +int main(int argc, char** argv) +{ +	struct sigaction sa; +	int fd, err, opt; + +	bdaddr_t bdaddr; +	uint8_t channel; + + +	/* Parse command line options */ +	while ((opt = getopt(argc, argv, "h")) != EOF) { +		switch(opt) { +		case 'h': +			usage(); +			exit(0); +		} +	} + +	argc -= optind; +	argv += optind; + +	switch (argc) { +	case 1: +		str2ba(argv[0], &bdaddr); +		channel = 1; +		break; +	case 2: +		str2ba(argv[0], &bdaddr); +		channel = atoi(argv[1]); +		break; +	default: +		usage(); +		exit(0); +	} + + +	/* Initialize syslog */ +	openlog("ppporc", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON); +	syslog(LOG_INFO, "PPP over RFCOMM"); + + +	/* 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); + + +	syslog(LOG_INFO, "Connecting to %s", argv[0]); + +	if ((fd = create_connection(&bdaddr, channel)) < 0) { +		syslog(LOG_ERR, "Can't connect to remote device (%s)", strerror(errno)); +		return fd; +	} + +	err = process_data(fd); + +	close(fd); + +	return err; +}  | 
