Add the dund utility

This commit is contained in:
Marcel Holtmann 2004-04-25 22:03:05 +00:00
parent 965c347ab1
commit bc4cb70ddb
10 changed files with 1362 additions and 2 deletions

View File

@ -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

View File

@ -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)

15
dund/Makefile.am Normal file
View File

@ -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)

308
dund/dun.c Normal file
View File

@ -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;
}

49
dund/dund.1 Normal file
View File

@ -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

44
dund/dund.h Normal file
View File

@ -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);

95
dund/lib.h Normal file
View File

@ -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 */

558
dund/main.c Normal file
View File

@ -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;
}

145
dund/msdun.c Normal file
View File

@ -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);
}
}

146
dund/sdp.c Normal file
View File

@ -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;
}