diff options
-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; +} |