summaryrefslogtreecommitdiffstats
path: root/dund/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'dund/main.c')
-rw-r--r--dund/main.c558
1 files changed, 558 insertions, 0 deletions
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;
+}