summaryrefslogtreecommitdiffstats
path: root/dund
diff options
context:
space:
mode:
Diffstat (limited to 'dund')
-rw-r--r--dund/Makefile.am15
-rw-r--r--dund/dun.c308
-rw-r--r--dund/dund.149
-rw-r--r--dund/dund.h44
-rw-r--r--dund/lib.h95
-rw-r--r--dund/main.c558
-rw-r--r--dund/msdun.c145
-rw-r--r--dund/sdp.c146
8 files changed, 1360 insertions, 0 deletions
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;
+}