summaryrefslogtreecommitdiffstats
path: root/network
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2008-07-26 19:00:53 +0200
committerMarcel Holtmann <marcel@holtmann.org>2008-07-26 19:00:53 +0200
commitd6ae1c3f777832f8e32702f81fe64e33a1396928 (patch)
tree159a1e59f3929c9d795dbd1f3edd84d9dccba048 /network
parentb8e5fea8d31fbcd3d1c044385f8217dbf39892bb (diff)
parent3382af9114a9b2e657c7ddd0a5511edda6a37a90 (diff)
Import bluez-utils-3.36 revision history
Diffstat (limited to 'network')
-rw-r--r--network/Makefile.am24
-rw-r--r--network/bridge.c148
-rw-r--r--network/bridge.h30
-rw-r--r--network/common.c396
-rw-r--r--network/common.h42
-rw-r--r--network/connection.c719
-rw-r--r--network/connection.h34
-rw-r--r--network/main.c256
-rw-r--r--network/manager.c855
-rw-r--r--network/manager.h43
-rw-r--r--network/network-api.txt199
-rw-r--r--network/network.conf37
-rw-r--r--network/server.c1097
-rw-r--r--network/server.h33
-rwxr-xr-xnetwork/test-network37
15 files changed, 3950 insertions, 0 deletions
diff --git a/network/Makefile.am b/network/Makefile.am
new file mode 100644
index 00000000..15208f5a
--- /dev/null
+++ b/network/Makefile.am
@@ -0,0 +1,24 @@
+
+if NETWORKPLUGIN
+plugindir = $(libdir)/bluetooth/plugins
+
+plugin_LTLIBRARIES = network.la
+
+network_la_SOURCES = main.c manager.h manager.c \
+ server.h server.c bridge.h bridge.c \
+ connection.h connection.c common.h common.c
+
+LDADD = $(top_builddir)/common/libhelper.a \
+ @GDBUS_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@
+endif
+
+AM_LDFLAGS = -module -avoid-version -no-undefined \
+ -export-symbols-regex bluetooth_plugin_desc
+
+AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @GDBUS_CFLAGS@
+
+INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/hcid -I$(top_srcdir)/sdpd
+
+EXTRA_DIST = network.conf network-api.txt test-network
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/network/bridge.c b/network/bridge.c
new file mode 100644
index 00000000..ba59f380
--- /dev/null
+++ b/network/bridge.c
@@ -0,0 +1,148 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/bnep.h>
+
+#include "logging.h"
+#include "bridge.h"
+#include "common.h"
+
+static int bridge_socket = -1;
+static const char *gn_bridge = NULL;
+static const char *nap_bridge = NULL;
+
+int bridge_init(const char *gn_iface, const char *nap_iface)
+{
+#if 0
+ struct stat st;
+
+ if (stat("/sys/module/bridge", &st) < 0)
+ return -EOPNOTSUPP;
+#endif
+ bridge_socket = socket(AF_INET, SOCK_STREAM, 0);
+ if (bridge_socket < 0) {
+ error("Failed to open bridge socket: %s (%d)",
+ strerror(errno), errno);
+ return -errno;
+ }
+
+ gn_bridge = gn_iface;
+ nap_bridge = nap_iface;
+
+ return 0;
+}
+
+void bridge_cleanup(void)
+{
+ close(bridge_socket);
+
+ bridge_socket = -1;
+}
+
+int bridge_create(int id)
+{
+ int err;
+ const char *name = bridge_get_name(id);
+
+ err = ioctl(bridge_socket, SIOCBRADDBR, name);
+ if (err < 0)
+ return -errno;
+
+ info("bridge %s created", name);
+
+ return 0;
+}
+
+int bridge_remove(int id)
+{
+ int err;
+ const char *name = bridge_get_name(id);
+
+ err = bnep_if_down(name);
+ if (err < 0)
+ return err;
+
+ err = ioctl(bridge_socket, SIOCBRDELBR, name);
+ if (err < 0)
+ return -errno;
+
+ info("bridge %s removed", name);
+
+ return 0;
+}
+
+int bridge_add_interface(int id, const char *dev)
+{
+ struct ifreq ifr;
+ int err;
+ int ifindex = if_nametoindex(dev);
+ const char *name = bridge_get_name(id);
+
+ if (!name)
+ return -EINVAL;
+
+ if (ifindex == 0)
+ return -ENODEV;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, name, IFNAMSIZ);
+ ifr.ifr_ifindex = ifindex;
+
+ err = ioctl(bridge_socket, SIOCBRADDIF, &ifr);
+ if (err < 0)
+ return err;
+
+ info("bridge %s: interface %s added", name, dev);
+
+ err = bnep_if_up(name, id);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+const char *bridge_get_name(int id)
+{
+ if (id == BNEP_SVC_GN)
+ return gn_bridge;
+
+ if (id == BNEP_SVC_NAP)
+ return nap_bridge;
+
+ return NULL;
+}
diff --git a/network/bridge.h b/network/bridge.h
new file mode 100644
index 00000000..13166520
--- /dev/null
+++ b/network/bridge.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int bridge_init(const char *gn_iface, const char *nap_iface);
+void bridge_cleanup(void);
+
+int bridge_create(int id);
+int bridge_remove(int id);
+int bridge_add_interface(int id, const char *dev);
+const char *bridge_get_name(int id);
diff --git a/network/common.c b/network/common.c
new file mode 100644
index 00000000..f1dbe1c5
--- /dev/null
+++ b/network/common.c
@@ -0,0 +1,396 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <net/if.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/bnep.h>
+
+#include <glib.h>
+
+#include "logging.h"
+#include "common.h"
+#include "textfile.h"
+
+static int ctl;
+static GSList *pids;
+
+#define PANU_UUID "00001115-0000-1000-8000-00805f9b34fb"
+#define NAP_UUID "00001116-0000-1000-8000-00805f9b34fb"
+#define GN_UUID "00001117-0000-1000-8000-00805f9b34fb"
+
+static struct {
+ const char *name; /* Friendly name */
+ const char *uuid128; /* UUID 128 */
+ uint16_t id; /* Service class identifier */
+} __svc[] = {
+ { "panu", PANU_UUID, BNEP_SVC_PANU },
+ { "gn", GN_UUID, BNEP_SVC_GN },
+ { "nap", NAP_UUID, BNEP_SVC_NAP },
+ { NULL }
+};
+
+static const char *panu = NULL;
+static const char *gn = NULL;
+static const char *nap = NULL;
+
+struct bnep_data {
+ char *devname;
+ char *script;
+ int pid;
+};
+
+static gint find_devname(gconstpointer a, gconstpointer b)
+{
+ struct bnep_data *data = (struct bnep_data *) a;
+ const char *devname = b;
+
+ return strcmp(data->devname, devname);
+}
+
+static void script_exited(GPid pid, gint status, gpointer data)
+{
+ if (WIFEXITED(status))
+ debug("%d exited with status %d", pid, WEXITSTATUS(status));
+ else
+ debug("%d was killed by signal %d", pid, WTERMSIG(status));
+
+ g_spawn_close_pid(pid);
+}
+
+uint16_t bnep_service_id(const char *svc)
+{
+ int i;
+ uint16_t id;
+
+ /* Friendly service name */
+ for (i = 0; __svc[i].name; i++)
+ if (!strcasecmp(svc, __svc[i].name)) {
+ return __svc[i].id;
+ }
+
+ /* UUID 128 string */
+ for (i = 0; __svc[i].uuid128; i++)
+ if (!strcasecmp(svc, __svc[i].uuid128)) {
+ return __svc[i].id;
+ }
+
+ /* Try convert to HEX */
+ id = strtol(svc, NULL, 16);
+ if ((id < BNEP_SVC_PANU) || (id > BNEP_SVC_GN))
+ return 0;
+
+ return id;
+}
+
+const char *bnep_uuid(uint16_t id)
+{
+ int i;
+
+ for (i = 0; __svc[i].uuid128; i++)
+ if (__svc[i].id == id)
+ return __svc[i].uuid128;
+ return NULL;
+}
+
+const char *bnep_name(uint16_t id)
+{
+ int i;
+
+ for (i = 0; __svc[i].name; i++)
+ if (__svc[i].id == id)
+ return __svc[i].name;
+ return NULL;
+}
+
+int bnep_init(const char *panu_script, const char *gn_script,
+ const char *nap_script)
+{
+ ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
+
+ if (ctl < 0) {
+ int err = errno;
+ error("Failed to open control socket: %s (%d)",
+ strerror(err), err);
+ return -err;
+ }
+
+ panu = panu_script;
+ gn = gn_script;
+ nap = nap_script;
+ return 0;
+}
+
+int bnep_cleanup(void)
+{
+ close(ctl);
+ return 0;
+}
+
+int bnep_kill_connection(bdaddr_t *dst)
+{
+ struct bnep_conndel_req req;
+
+ memset(&req, 0, sizeof(req));
+ baswap((bdaddr_t *)&req.dst, dst);
+ req.flags = 0;
+ if (ioctl(ctl, BNEPCONNDEL, &req)) {
+ int err = errno;
+ error("Failed to kill connection: %s (%d)",
+ strerror(err), err);
+ return -err;
+ }
+ return 0;
+}
+
+int bnep_kill_all_connections(void)
+{
+ struct bnep_connlist_req req;
+ struct bnep_conninfo ci[7];
+ int i, err;
+
+ memset(&req, 0, sizeof(req));
+ req.cnum = 7;
+ req.ci = ci;
+ if (ioctl(ctl, BNEPGETCONNLIST, &req)) {
+ err = errno;
+ error("Failed to get connection list: %s (%d)",
+ strerror(err), err);
+ return -err;
+ }
+
+ for (i=0; i < req.cnum; i++) {
+ struct bnep_conndel_req del;
+
+ memset(&del, 0, sizeof(del));
+ memcpy(del.dst, ci[i].dst, ETH_ALEN);
+ del.flags = 0;
+ ioctl(ctl, BNEPCONNDEL, &del);
+ }
+ return 0;
+}
+
+int bnep_connadd(int sk, uint16_t role, char *dev)
+{
+ struct bnep_connadd_req req;
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.device, dev, 16);
+ req.device[15] = '\0';
+ req.sock = sk;
+ req.role = role;
+ if (ioctl(ctl, BNEPCONNADD, &req) < 0) {
+ int err = errno;
+ error("Failed to add device %s: %s(%d)",
+ dev, strerror(err), err);
+ return -err;
+ }
+
+ strncpy(dev, req.device, 16);
+ return 0;
+}
+
+static void bnep_setup(gpointer data)
+{
+}
+
+static int bnep_exec(const char **argv)
+{
+ int pid;
+ GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH;
+
+ if (!g_spawn_async(NULL, (char **) argv, NULL, flags, bnep_setup, NULL,
+ &pid, NULL)) {
+ error("Unable to execute %s %s", *argv[0], *argv[1]);
+ return -EINVAL;
+ }
+
+ return pid;
+}
+
+int bnep_if_up(const char *devname, uint16_t id)
+{
+ int sd, err;
+ struct ifreq ifr;
+ const char *argv[5];
+ struct bnep_data *bnep = NULL;
+ GSList *l;
+
+ /* Check if a script is running */
+ l = g_slist_find_custom(pids, devname, find_devname);
+ if (l) {
+ bnep = l->data;
+
+ if (bnep->script && !strcmp(bnep->script, "avahi-autoipd")) {
+ argv[0] = bnep->script;
+ argv[1] = devname;
+ argv[2] = "--refresh";
+ argv[3] = NULL;
+
+ bnep->pid = bnep_exec(argv);
+ }
+ }
+
+ sd = socket(AF_INET6, SOCK_DGRAM, 0);
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, devname);
+
+ ifr.ifr_flags |= IFF_UP;
+ ifr.ifr_flags |= IFF_MULTICAST;
+
+ if ((ioctl(sd, SIOCSIFFLAGS, (caddr_t) &ifr)) < 0) {
+ err = errno;
+ error("Could not bring up %s. %s(%d)", devname, strerror(err),
+ err);
+ return -err;
+ }
+
+ if (bnep)
+ return bnep->pid;
+
+ bnep = g_new0(struct bnep_data, 1);
+ bnep->devname = g_strdup(devname);
+
+ if (!id)
+ goto done;
+
+ if (id == BNEP_SVC_PANU)
+ bnep->script = g_strdup(panu);
+ else if (id == BNEP_SVC_GN)
+ bnep->script = g_strdup(gn);
+ else
+ bnep->script = g_strdup(nap);
+
+ if (!bnep->script)
+ goto done;
+
+ argv[0] = bnep->script;
+ argv[1] = devname;
+
+ if (!strcmp(bnep->script, "avahi-autoipd")) {
+ argv[2] = "--no-drop-root";
+ argv[3] = "--no-chroot";
+ argv[4] = NULL;
+ } else
+ argv[2] = NULL;
+
+ bnep->pid = bnep_exec(argv);
+ g_child_watch_add(bnep->pid, script_exited, bnep);
+
+done:
+ pids = g_slist_append(pids, bnep);
+
+ return bnep->pid;
+}
+
+int bnep_if_down(const char *devname)
+{
+ int sd, err, pid;
+ struct ifreq ifr;
+ struct bnep_data *bnep;
+ GSList *l;
+ GSpawnFlags flags;
+ const char *argv[4];
+
+ l = g_slist_find_custom(pids, devname, find_devname);
+ if (!l)
+ return 0;
+
+ bnep = l->data;
+
+ if (!bnep->pid)
+ goto done;
+
+ if (bnep->script && !strcmp(bnep->script, "avahi-autoipd")) {
+ argv[0] = bnep->script;
+ argv[1] = devname;
+ argv[2] = "--kill";
+ argv[3] = NULL;
+
+ flags = G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH;
+ g_spawn_async(NULL, (char **) argv, NULL, flags, bnep_setup,
+ (gpointer) devname, &pid, NULL);
+
+ goto done;
+ }
+
+ /* Kill script */
+ err = kill(bnep->pid, SIGTERM);
+ if (err < 0)
+ error("kill(%d, SIGTERM): %s (%d)", bnep->pid,
+ strerror(errno), errno);
+
+done:
+ sd = socket(AF_INET6, SOCK_DGRAM, 0);
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, devname);
+
+ ifr.ifr_flags &= ~IFF_UP;
+
+ /* Bring down the interface */
+ ioctl(sd, SIOCSIFFLAGS, (caddr_t) &ifr);
+
+ pids = g_slist_remove(pids, bnep);
+
+ if (bnep->devname)
+ g_free(bnep->devname);
+
+ if (bnep->script)
+ g_free(bnep->script);
+
+ g_free(bnep);
+
+ return 0;
+}
+
+int read_remote_name(bdaddr_t *src, bdaddr_t *dst, char *buf, size_t size)
+{
+ char filename[PATH_MAX + 1], addr[18], *str;
+
+ ba2str(src, addr);
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "names");
+
+ ba2str(dst, addr);
+ str = textfile_get(filename, addr);
+ if (!str)
+ return -ENOENT;
+
+ snprintf(buf, size, "%s", str);
+ free(str);
+
+ return 0;
+}
diff --git a/network/common.h b/network/common.h
new file mode 100644
index 00000000..e6aa90f6
--- /dev/null
+++ b/network/common.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#define MAX_PATH_LENGTH 64 /* D-Bus path */
+#define NETWORK_PATH "/org/bluez/network"
+
+int bnep_init(const char *panu_script, const char *gn_script,
+ const char *nap_script);
+int bnep_cleanup(void);
+
+uint16_t bnep_service_id(const char *svc);
+const char *bnep_uuid(uint16_t id);
+const char *bnep_name(uint16_t id);
+
+int bnep_kill_connection(bdaddr_t *dst);
+int bnep_kill_all_connections(void);
+
+int bnep_connadd(int sk, uint16_t role, char *dev);
+int bnep_if_up(const char *devname, uint16_t id);
+int bnep_if_down(const char *devname);
+
+int read_remote_name(bdaddr_t *src, bdaddr_t *dst, char *buf, size_t size);
diff --git a/network/connection.c b/network/connection.c
new file mode 100644
index 00000000..d2fd85c2
--- /dev/null
+++ b/network/connection.c
@@ -0,0 +1,719 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../hcid/dbus-common.h"
+
+#include "logging.h"
+#include "textfile.h"
+#include "glib-helper.h"
+
+#include "error.h"
+#include "common.h"
+#include "connection.h"
+
+typedef enum {
+ CONNECTED,
+ CONNECTING,
+ DISCONNECTED
+} conn_state;
+
+struct network_conn {
+ DBusMessage *msg;
+ bdaddr_t store;
+ bdaddr_t src;
+ bdaddr_t dst;
+ char *path; /* D-Bus path */
+ char dev[16]; /* Interface name */
+ char *name; /* Service Name */
+ char *desc; /* Service Description*/
+ uint16_t id; /* Role: Service Class Identifier */
+ conn_state state;
+ int sk;
+};
+
+struct __service_16 {
+ uint16_t dst;
+ uint16_t src;
+} __attribute__ ((packed));
+
+static DBusConnection *connection = NULL;
+static const char *prefix = NULL;
+static GSList *connections = NULL;
+
+gint find_connection(gconstpointer a, gconstpointer b)
+{
+ const struct network_conn *nc = a;
+ const char *path = b;
+
+ return strcmp(nc->path, path);
+}
+
+static inline DBusMessage *not_supported(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ "Not suported");
+}
+
+static inline DBusMessage *already_connected(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ "Device already connected");
+}
+
+static inline DBusMessage *not_connected(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ "Device not connected");
+}
+
+static inline DBusMessage *no_pending_connect(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ "Device has no pending connect");
+}
+
+static inline DBusMessage *connection_attempt_failed(DBusMessage *msg, int err)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".ConnectionAttemptFailed",
+ err ? strerror(err) : "Connection attempt failed");
+}
+
+static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ struct network_conn *nc = data;
+
+ if (connection != NULL) {
+ g_dbus_emit_signal(connection, nc->path,
+ NETWORK_CONNECTION_INTERFACE,
+ "Disconnected",
+ DBUS_TYPE_INVALID);
+ }
+
+ info("%s disconnected", nc->dev);
+
+ bnep_if_down(nc->dev);
+ nc->state = DISCONNECTED;
+ memset(nc->dev, 0, 16);
+ strncpy(nc->dev, prefix, strlen(prefix));
+ g_io_channel_close(chan);
+
+ return FALSE;
+}
+
+static gboolean bnep_connect_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer data)
+{
+ struct network_conn *nc = data;
+ struct bnep_control_rsp *rsp;
+ char pkt[BNEP_MTU];
+ gsize r;
+ int sk;
+ DBusMessage *reply;
+ const char *pdev;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ if (cond & (G_IO_HUP | G_IO_ERR)) {
+ error("Hangup or error on l2cap server socket");
+ goto failed;
+ }
+
+ memset(pkt, 0, BNEP_MTU);
+ if (g_io_channel_read(chan, pkt, sizeof(pkt) - 1,
+ &r) != G_IO_ERROR_NONE) {
+ error("IO Channel read error");
+ goto failed;
+ }
+
+ if (r <= 0) {
+ error("No packet received on l2cap socket");
+ goto failed;
+ }
+
+ errno = EPROTO;
+
+ if (r < sizeof(*rsp)) {
+ error("Packet received is not bnep type");
+ goto failed;
+ }
+
+ rsp = (void *) pkt;
+ if (rsp->type != BNEP_CONTROL) {
+ error("Packet received is not bnep type");
+ goto failed;
+ }
+
+ if (rsp->ctrl != BNEP_SETUP_CONN_RSP)
+ return TRUE;
+
+ r = ntohs(rsp->resp);
+
+ if (r != BNEP_SUCCESS) {
+ error("bnep failed");
+ goto failed;
+ }
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ if (bnep_connadd(sk, BNEP_SVC_PANU, nc->dev)) {
+ error("%s could not be added", nc->dev);
+ goto failed;
+ }
+
+ bnep_if_up(nc->dev, nc->id);
+ g_dbus_emit_signal(connection, nc->path,
+ NETWORK_CONNECTION_INTERFACE,
+ "Connected",
+ DBUS_TYPE_INVALID);
+
+ pdev = nc->dev;
+
+ reply = g_dbus_create_reply(nc->msg, DBUS_TYPE_STRING, &pdev,
+ DBUS_TYPE_INVALID);
+ g_dbus_send_message(connection, reply);
+
+ nc->state = CONNECTED;
+
+ info("%s connected", nc->dev);
+ /* Start watchdog */
+ g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) bnep_watchdog_cb, nc);
+ return FALSE;
+
+failed:
+ if (nc->state != DISCONNECTED) {
+ nc->state = DISCONNECTED;
+ reply = connection_attempt_failed(nc->msg, EIO);
+ g_dbus_send_message(connection, reply);
+ g_io_channel_close(chan);
+ }
+
+ return FALSE;
+}
+
+static int bnep_connect(struct network_conn *nc)
+{
+ struct bnep_setup_conn_req *req;
+ struct __service_16 *s;
+ unsigned char pkt[BNEP_MTU];
+ GIOChannel *io;
+ int err = 0;
+
+ /* Send request */
+ req = (void *) pkt;
+ req->type = BNEP_CONTROL;
+ req->ctrl = BNEP_SETUP_CONN_REQ;
+ req->uuid_size = 2; /* 16bit UUID */
+ s = (void *) req->service;
+ s->dst = htons(nc->id);
+ s->src = htons(BNEP_SVC_PANU);
+
+ io = g_io_channel_unix_new(nc->sk);
+ g_io_channel_set_close_on_unref(io, FALSE);
+
+ if (send(nc->sk, pkt, sizeof(*req) + sizeof(*s), 0) < 0) {
+ err = -errno;
+ goto out;
+ }
+
+ g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) bnep_connect_cb, nc);
+
+out:
+ g_io_channel_unref(io);
+ return err;
+}
+
+static void connect_cb(GIOChannel *chan, int err, const bdaddr_t *src,
+ const bdaddr_t *dst, gpointer data)
+{
+ struct network_conn *nc = data;
+ DBusMessage *reply;
+
+ if (err < 0) {
+ error("l2cap connect(): %s (%d)", strerror(-err), -err);
+ goto failed;
+ }
+
+ nc->sk = g_io_channel_unix_get_fd(chan);
+
+ err = bnep_connect(nc);
+ if (err < 0) {
+ error("bnep connect(): %s (%d)", strerror(-err), -err);
+ g_io_channel_close(chan);
+ g_io_channel_unref(chan);
+ goto failed;
+ }
+
+ return;
+
+failed:
+ nc->state = DISCONNECTED;
+
+ reply = connection_attempt_failed(nc->msg, -err);
+ g_dbus_send_message(connection, reply);
+}
+
+static DBusMessage *get_adapter(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct network_conn *nc = data;
+ char addr[18];
+ const char *paddr = addr;
+
+ ba2str(&nc->src, addr);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &paddr,
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *get_address(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct network_conn *nc = data;
+ char addr[18];
+ const char *paddr = addr;
+
+ ba2str(&nc->dst, addr);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &paddr,
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *get_uuid(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_conn *nc = data;
+ const char *uuid;
+
+ uuid = bnep_uuid(nc->id);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &uuid,
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *get_name(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_conn *nc = data;
+
+ if (!nc->name)
+ return not_supported(msg);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &nc->name,
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *get_description(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_conn *nc = data;
+
+ if (!nc->desc)
+ return not_supported(msg);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &nc->desc,
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *get_interface(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_conn *nc = data;
+ const char *pdev = nc->dev;
+
+ if (nc->state != CONNECTED)
+ return not_connected(msg);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &pdev,
+ DBUS_TYPE_INVALID);
+}
+
+/* Connect and initiate BNEP session */
+static DBusMessage *connection_connect(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_conn *nc = data;
+ int err;
+
+ if (nc->state != DISCONNECTED)
+ return already_connected(msg);
+
+ nc->state = CONNECTING;
+ nc->msg = dbus_message_ref(msg);
+
+ err = bt_l2cap_connect(&nc->src, &nc->dst, BNEP_PSM, BNEP_MTU,
+ connect_cb, nc);
+ if (err < 0) {
+ error("Connect failed. %s(%d)", strerror(errno), errno);
+ dbus_message_unref(nc->msg);
+ nc->msg = NULL;
+ nc->state = DISCONNECTED;
+ return connection_attempt_failed(msg, -err);
+ }
+
+ return NULL;
+}
+
+static DBusMessage *connection_cancel(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_conn *nc = data;
+
+ if (nc->state != CONNECTING)
+ return no_pending_connect(msg);
+
+ close(nc->sk);
+ nc->state = DISCONNECTED;
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *connection_disconnect(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_conn *nc = data;
+
+ if (nc->state != CONNECTED)
+ return not_connected(msg);
+
+ bnep_if_down(nc->dev);
+ bnep_kill_connection(&nc->dst);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *is_connected(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_conn *nc = data;
+ gboolean up = (nc->state == CONNECTED);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_BOOLEAN, &up,
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *get_info(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_conn *nc = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ const char *uuid;
+ char raddr[18];
+ const char *paddr = raddr;
+
+ reply = dbus_message_new_method_return(msg);
+ if (reply == NULL)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ dbus_message_iter_append_dict_entry(&dict, "name",
+ DBUS_TYPE_STRING, &nc->name);
+
+ uuid = bnep_uuid(nc->id);
+ dbus_message_iter_append_dict_entry(&dict, "uuid",
+ DBUS_TYPE_STRING, &uuid);
+
+ ba2str(&nc->dst, raddr);
+ dbus_message_iter_append_dict_entry(&dict, "address",
+ DBUS_TYPE_STRING, &paddr);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static void connection_free(struct network_conn *nc)
+{
+ if (!nc)
+ return;
+
+ if (nc->path)
+ g_free(nc->path);
+
+ if (nc->state == CONNECTED) {
+ bnep_if_down(nc->dev);
+ bnep_kill_connection(&nc->dst);
+ }
+
+ if (nc->name)
+ g_free(nc->name);
+
+ if (nc->desc)
+ g_free(nc->desc);
+
+ g_free(nc);
+ nc = NULL;
+}
+
+static void connection_unregister(void *data)
+{
+ struct network_conn *nc = data;
+
+ info("Unregistered connection path:%s", nc->path);
+
+ connections = g_slist_remove(connections, nc);
+ connection_free(nc);
+}
+
+static GDBusMethodTable connection_methods[] = {
+ { "GetAdapter", "", "s", get_adapter },
+ { "GetAddress", "", "s", get_address },
+ { "GetUUID", "", "s", get_uuid },
+ { "GetName", "", "s", get_name },
+ { "GetDescription", "", "s", get_description },
+ { "GetInterface", "", "s", get_interface },
+ { "Connect", "", "s", connection_connect,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "CancelConnect", "", "", connection_cancel },
+ { "Disconnect", "", "", connection_disconnect },
+ { "IsConnected", "", "b", is_connected },
+ { "GetInfo", "", "a{sv}",get_info },
+ { }
+};
+
+static GDBusSignalTable connection_signals[] = {
+ { "Connected", "" },
+ { "Disconnected", "" },
+ { }
+};
+
+int connection_register(const char *path, bdaddr_t *src, bdaddr_t *dst,
+ uint16_t id, const char *name, const char *desc)
+{
+ struct network_conn *nc;
+ bdaddr_t default_src;
+ int dev_id;
+
+ if (!path)
+ return -EINVAL;
+
+ bacpy(&default_src, BDADDR_ANY);
+ dev_id = hci_get_route(&default_src);
+ if (dev_id < 0 || hci_devba(dev_id, &default_src) < 0)
+ return -1;
+
+ nc = g_new0(struct network_conn, 1);
+
+ if (g_dbus_register_interface(connection, path,
+ NETWORK_CONNECTION_INTERFACE,
+ connection_methods,
+ connection_signals, NULL,
+ nc, connection_unregister) == FALSE) {
+ error("D-Bus failed to register %s interface",
+ NETWORK_CONNECTION_INTERFACE);
+ return -1;
+ }
+
+ nc->path = g_strdup(path);
+ bacpy(&nc->store, src);
+ bacpy(&nc->src, &default_src);
+ bacpy(&nc->dst, dst);
+ nc->id = id;
+ nc->name = g_strdup(name);
+ nc->desc = g_strdup(desc);
+ memset(nc->dev, 0, 16);
+ strncpy(nc->dev, prefix, strlen(prefix));
+ nc->state = DISCONNECTED;
+
+ connections = g_slist_append(connections, nc);
+
+ info("Registered connection path:%s", path);
+
+ return 0;
+}
+
+int connection_store(const char *path, gboolean default_path)
+{
+ struct network_conn *nc;
+ const char *role;
+ char key[32], *value;
+ char filename[PATH_MAX + 1];
+ char src_addr[18], dst_addr[18];
+ int len, err;
+ GSList *l;
+
+ l = g_slist_find_custom(connections, path, find_connection);
+ if (!l)
+ return -ENOENT;
+
+ nc = l->data;
+ if (!nc->name || !nc->desc)
+ return -EINVAL;
+
+ /* FIXME: name and desc validation - remove ':' */
+
+ ba2str(&nc->dst, dst_addr);
+ role = bnep_name(nc->id);
+ snprintf(key, 32, "%s#%s", dst_addr, role);
+
+ ba2str(&nc->store, src_addr);
+ create_name(filename, PATH_MAX, STORAGEDIR, src_addr, "network");
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ if (default_path)
+ err = textfile_put(filename, "default", key);
+ else {
+ len = strlen(nc->name) + strlen(nc->desc) + 2;
+ value = g_malloc0(len);
+ snprintf(value, len, "%s:%s", nc->name, nc->desc);
+ err = textfile_put(filename, key, value);
+ g_free(value);
+ }
+
+ return err;
+}
+
+int connection_find_data(const char *path, const char *pattern)
+{
+ struct network_conn *nc;
+ char addr[18], key[32];
+ const char *role;
+ GSList *l;
+
+ l = g_slist_find_custom(connections, path, find_connection);
+ if (!l)
+ return -1;
+
+ nc = l->data;
+ if (strcasecmp(pattern, nc->dev) == 0)
+ return 0;
+
+ if (strcasecmp(pattern, nc->name) == 0)
+ return 0;
+
+ ba2str(&nc->dst, addr);
+
+ if (strcasecmp(pattern, addr) == 0)
+ return 0;
+
+ role = bnep_name(nc->id);
+ snprintf(key, 32, "%s#%s", addr, role);
+
+ if (strcasecmp(pattern, key) == 0)
+ return 0;
+
+ return -1;
+}
+
+gboolean connection_has_pending(const char *path)
+{
+ struct network_conn *nc;
+ GSList *l;
+
+ l = g_slist_find_custom(connections, path, find_connection);
+ if (!l)
+ return FALSE;
+
+ nc = l->data;
+
+ return (nc->state == CONNECTING);
+}
+
+int connection_remove_stored(const char *path)
+{
+ struct network_conn *nc;
+ const char *role;
+ char key[32];
+ char filename[PATH_MAX + 1];
+ char src_addr[18], dst_addr[18];
+ int err;
+ GSList *l;
+
+ l = g_slist_find_custom(connections, path, find_connection);
+ if (!l)
+ return -ENOENT;
+
+ nc = l->data;
+
+ ba2str(&nc->dst, dst_addr);
+ role = bnep_name(nc->id);
+ snprintf(key, 32, "%s#%s", dst_addr, role);
+
+ ba2str(&nc->store, src_addr);
+ create_name(filename, PATH_MAX, STORAGEDIR, src_addr, "network");
+
+ err = textfile_del(filename, key);
+
+ return err;
+}
+
+gboolean connection_is_connected(const char *path)
+{
+ struct network_conn *nc;
+ GSList *l;
+
+ l = g_slist_find_custom(connections, path, find_connection);
+ if (!l)
+ return FALSE;
+
+ nc = l->data;
+
+ return (nc->state == CONNECTED);
+}
+
+int connection_init(DBusConnection *conn, const char *iface_prefix)
+{
+ connection = dbus_connection_ref(conn);
+ prefix = iface_prefix;
+
+ return 0;
+}
+
+void connection_exit()
+{
+ dbus_connection_unref(connection);
+ connection = NULL;
+ prefix = NULL;
+}
diff --git a/network/connection.h b/network/connection.h
new file mode 100644
index 00000000..fd15e816
--- /dev/null
+++ b/network/connection.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#define NETWORK_CONNECTION_INTERFACE "org.bluez.network.Connection"
+
+int connection_init(DBusConnection *conn, const char *iface_prefix);
+void connection_exit();
+int connection_register(const char *path, bdaddr_t *src, bdaddr_t *dst,
+ uint16_t id, const char *name, const char *desc);
+int connection_store(const char *path, gboolean default_path);
+int connection_remove_stored(const char *path);
+int connection_find_data(const char *path, const char *pattern);
+gboolean connection_has_pending(const char *path);
+gboolean connection_is_connected(const char *path);
diff --git a/network/main.c b/network/main.c
new file mode 100644
index 00000000..f18223cc
--- /dev/null
+++ b/network/main.c
@@ -0,0 +1,256 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "device.h"
+#include "logging.h"
+#include "manager.h"
+
+#define IFACE_PREFIX "bnep%d"
+#define GN_IFACE "pan0"
+#define NAP_IFACE "pan1"
+
+#define PANU_UUID "00001115-0000-1000-8000-00805f9b34fb"
+#define NAP_UUID "00001116-0000-1000-8000-00805f9b34fb"
+#define GN_UUID "00001117-0000-1000-8000-00805f9b34fb"
+
+#define NETWORK_INTERFACE "org.bluez.Network"
+
+static DBusMessage *network_connect(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ const char *target, *device = "bnep0";
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &target,
+ DBUS_TYPE_INVALID) == FALSE)
+ return NULL;
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &device,
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *network_disconnect(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID) == FALSE)
+ return NULL;
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static GDBusMethodTable network_methods[] = {
+ { "Connect", "s", "s", network_connect },
+ { "Disconnect", "", "", network_disconnect },
+ { }
+};
+
+static GDBusSignalTable network_signals[] = {
+ { "Connected", "ss" },
+ { "Disconnected", "s" },
+ { }
+};
+
+static DBusConnection *conn;
+
+static int network_probe(struct btd_device *device)
+{
+ DBG("path %s", device->path);
+
+ if (g_dbus_register_interface(conn, device->path, NETWORK_INTERFACE,
+ network_methods, network_signals, NULL,
+ device, NULL) == FALSE)
+ return -1;
+
+ return 0;
+}
+
+static void network_remove(struct btd_device *device)
+{
+ DBG("path %s", device->path);
+
+ g_dbus_unregister_interface(conn, device->path, NETWORK_INTERFACE);
+}
+
+static struct btd_device_driver network_driver = {
+ .name = "network",
+ .uuids = BTD_UUIDS(PANU_UUID, NAP_UUID, GN_UUID),
+ .probe = network_probe,
+ .remove = network_remove,
+};
+
+static struct network_conf conf = {
+ .connection_enabled = TRUE,
+ .server_enabled = TRUE,
+ .iface_prefix = NULL,
+ .panu_script = NULL,
+ .gn_script = NULL,
+ .nap_script = NULL,
+ .gn_iface = NULL,
+ .nap_iface = NULL,
+ .security = TRUE
+};
+
+static void read_config(const char *file)
+{
+ GKeyFile *keyfile;
+ GError *err = NULL;
+ char **disabled;
+
+ keyfile = g_key_file_new();
+
+ if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
+ error("Parsing %s failed: %s", file, err->message);
+ g_error_free(err);
+ goto done;
+ }
+
+ disabled = g_key_file_get_string_list(keyfile, "General",
+ "Disable", NULL, &err);
+ if (err) {
+ debug("%s: %s", file, err->message);
+ g_error_free(err);
+ err = NULL;
+ } else {
+ int i;
+ for (i = 0; disabled[i] != NULL; i++) {
+ if (g_str_equal(disabled[i], "Connection"))
+ conf.connection_enabled = FALSE;
+ else if (g_str_equal(disabled[i], "Server"))
+ conf.server_enabled = FALSE;
+ }
+ g_strfreev(disabled);
+ }
+
+ conf.security = !g_key_file_get_boolean(keyfile, "General",
+ "DisableSecurity", &err);
+ if (err) {
+ debug("%s: %s", file, err->message);
+ g_error_free(err);
+ err = NULL;
+ }
+
+ conf.panu_script = g_key_file_get_string(keyfile, "PANU Role",
+ "Script", &err);
+ if (err) {
+ debug("%s: %s", file, err->message);
+ g_error_free(err);
+ err = NULL;
+ }
+
+ conf.gn_script = g_key_file_get_string(keyfile, "GN Role",
+ "Script", &err);
+ if (err) {
+ debug("%s: %s", file, err->message);
+ g_error_free(err);
+ err = NULL;
+ }
+
+ conf.nap_script = g_key_file_get_string(keyfile, "NAP Role",
+ "Script", &err);
+ if (err) {
+ debug("%s: %s", file, err->message);
+ g_error_free(err);
+ err = NULL;
+ }
+
+ conf.iface_prefix = g_key_file_get_string(keyfile, "PANU Role",
+ "Interface", &err);
+ if (err) {
+ debug("%s: %s", file, err->message);
+ g_error_free(err);
+ err = NULL;
+ }
+
+ conf.gn_iface = g_key_file_get_string(keyfile, "GN Role",
+ "Interface", &err);
+ if (err) {
+ debug("%s: %s", file, err->message);
+ g_error_free(err);
+ err = NULL;
+ }
+
+ conf.nap_iface = g_key_file_get_string(keyfile, "NAP Role",
+ "Interface", &err);
+ if (err) {
+ debug("%s: %s", file, err->message);
+ g_error_free(err);
+ err = NULL;
+ }
+
+done:
+ g_key_file_free(keyfile);
+
+ if (!conf.iface_prefix)
+ conf.iface_prefix = g_strdup(IFACE_PREFIX);
+ if (!conf.gn_iface)
+ conf.gn_iface = g_strdup(GN_IFACE);
+ if (!conf.nap_iface)
+ conf.nap_iface = g_strdup(NAP_IFACE);
+
+ debug("Config options: InterfacePrefix=%s, PANU_Script=%s, "
+ "GN_Script=%s, NAP_Script=%s, GN_Interface=%s, "
+ "NAP_Interface=%s, Security=%s",
+ conf.iface_prefix, conf.panu_script, conf.gn_script,
+ conf.nap_script, conf.gn_iface, conf.nap_iface,
+ conf.security ? "true" : "false");
+}
+
+static int network_init(void)
+{
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (conn == NULL)
+ return -EIO;
+
+ read_config(CONFIGDIR "/network.conf");
+
+ if (network_manager_init(conn, &conf) < 0) {
+ dbus_connection_unref(conn);
+ return -EIO;
+ }
+
+ btd_register_device_driver(&network_driver);
+
+ return 0;
+}
+
+static void network_exit(void)
+{
+ btd_unregister_device_driver(&network_driver);
+
+ network_manager_exit();
+
+ dbus_connection_unref(conn);
+}
+
+BLUETOOTH_PLUGIN_DEFINE("network", network_init, network_exit)
diff --git a/network/manager.c b/network/manager.c
new file mode 100644
index 00000000..dd23b58d
--- /dev/null
+++ b/network/manager.c
@@ -0,0 +1,855 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <ctype.h>
+#include <dirent.h>
+
+#include <sys/stat.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "logging.h"
+#include "textfile.h"
+#include "glib-helper.h"
+
+#define NETWORK_MANAGER_INTERFACE "org.bluez.network.Manager"
+
+#include "error.h"
+#include "bridge.h"
+#include "manager.h"
+#include "common.h"
+
+#define MAX_NAME_SIZE 256
+
+struct pending_reply {
+ DBusConnection *conn;
+ DBusMessage *msg;
+ bdaddr_t src; /* Source address */
+ bdaddr_t dst; /* Destination address */
+ char *addr; /* Destination address */
+ char *path; /* D-Bus object path */
+ char *adapter_path; /* Default adapter path */
+ uint16_t id; /* Role */
+};
+
+static struct network_conf *conf = NULL;/* Network service configuration */
+static GSList *server_paths = NULL; /* Network registered servers paths */
+static GSList *connection_paths = NULL; /* Network registered connections paths */
+static int default_index = -1; /* Network default connection path index */
+static int net_uid = 0; /* Network objects identifier */
+
+static DBusConnection *connection = NULL;
+
+static void pending_reply_free(struct pending_reply *pr)
+{
+ if (pr->addr)
+ g_free(pr->addr);
+ if (pr->path)
+ g_free(pr->path);
+ if (pr->adapter_path)
+ g_free(pr->adapter_path);
+ if (pr->msg)
+ dbus_message_unref(pr->msg);
+ if (pr->conn)
+ dbus_connection_unref(pr->conn);
+}
+
+static inline DBusMessage *does_not_exist(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyExists",
+ "No such connection");
+}
+
+static inline DBusMessage *already_exists(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyExists",
+ "Connection already exists");
+}
+
+static inline DBusMessage *not_supported(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".NotSupported",
+ "Not supported");
+}
+
+static inline DBusMessage *connection_is_busy(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ "Connection is Busy");
+}
+
+static inline DBusMessage *adapter_not_available(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ "Adapter not available");
+}
+
+static void create_path(DBusConnection *conn, DBusMessage *msg,
+ const char *path, const char *sname)
+{
+ /* emit signal when it is a new path */
+ if (sname) {
+ g_dbus_emit_signal(conn, NETWORK_PATH,
+ NETWORK_MANAGER_INTERFACE,
+ sname, DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_INVALID);
+ }
+
+ g_dbus_send_reply(conn, msg, DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *list_paths(DBusConnection *conn, DBusMessage *msg,
+ GSList *list)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter array_iter;
+
+ reply = dbus_message_new_method_return(msg);
+ if (reply == NULL)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &array_iter);
+
+ for (; list; list = list->next) {
+ dbus_message_iter_append_basic(&array_iter,
+ DBUS_TYPE_STRING,
+ &list->data);
+ }
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ return reply;
+}
+
+static const char *last_connection_used(DBusConnection *conn)
+{
+ const char *path = NULL;
+ GSList *l;
+ int i;
+
+ for (i = g_slist_length (connection_paths) -1; i > -1; i--) {
+ path = g_slist_nth_data (connection_paths, i);
+ if (connection_is_connected(path))
+ break;
+ }
+
+ /* No connection connected fallback to last connection */
+ if (i == -1) {
+ l = g_slist_last(connection_paths);
+ path = l->data;
+ }
+
+ return path;
+}
+
+static DBusMessage *remove_path(DBusConnection *conn,
+ DBusMessage *msg, GSList **list,
+ const char *sname)
+{
+ const char *path;
+ GSList *l;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_INVALID) == FALSE)
+ return NULL;
+
+ l = g_slist_find_custom(*list, path, (GCompareFunc) strcmp);
+ if (!l)
+ return does_not_exist(msg);
+
+ /* Remove references from the storage */
+ if (*list == connection_paths) {
+ if (connection_has_pending(path))
+ return connection_is_busy(msg);
+
+ connection_remove_stored(path);
+ /* Reset default connection */
+ if (l == g_slist_nth(*list, default_index)) {
+ const char *dpath;
+
+ dpath = last_connection_used(conn);
+ connection_store(dpath, TRUE);
+ }
+ }
+
+ g_free(l->data);
+ *list = g_slist_remove(*list, l->data);
+
+ g_dbus_emit_signal(conn, NETWORK_PATH,
+ NETWORK_MANAGER_INTERFACE,
+ sname, DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_INVALID);
+
+ g_dbus_unregister_interface(conn, path, NETWORK_CONNECTION_INTERFACE);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void records_cb(sdp_list_t *recs, int err, gpointer data)
+{
+ struct pending_reply *pr = data;
+ int len;
+ sdp_data_t *d;
+ sdp_record_t *rec = NULL;
+ char name[MAX_NAME_SIZE], *desc = NULL;
+
+ if (err < 0) {
+ error_connection_attempt_failed(pr->conn, pr->msg, -err);
+ goto fail;
+ }
+
+ if (!recs || !recs->data) {
+ error_not_supported(pr->conn, pr->msg);
+ error("Invalid PAN service record");
+ goto fail;
+ }
+
+ rec = recs->data;
+
+ /* Concat remote name and service name */
+ memset(name, 0, MAX_NAME_SIZE);
+ if (read_remote_name(&pr->src, &pr->dst, name, MAX_NAME_SIZE) < 0)
+ len = 0;
+ else
+ len = strlen(name);
+
+ d = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY);
+ if (d) {
+ snprintf(name + len, MAX_NAME_SIZE - len,
+ len ? " (%.*s)" : "%.*s", d->unitSize, d->val.str);
+ }
+
+ /* Extract service description from record */
+ d = sdp_data_get(rec, SDP_ATTR_SVCDESC_PRIMARY);
+ if (d) {
+ desc = g_new0(char, d->unitSize);
+ snprintf(desc, d->unitSize, "%.*s",
+ d->unitSize, d->val.str);
+ }
+
+ sdp_list_free(recs, (sdp_free_func_t) sdp_record_free);
+
+ if (connection_register(pr->path, &pr->src, &pr->dst, pr->id, name,
+ desc) < 0) {
+ error_failed(pr->conn, pr->msg, "D-Bus path registration failed");
+ goto fail;
+ }
+
+ connection_store(pr->path, FALSE);
+ connection_paths = g_slist_append(connection_paths, g_strdup(pr->path));
+
+ create_path(pr->conn, pr->msg, pr->path, "ConnectionCreated");
+
+fail:
+ g_free(desc);
+ pending_reply_free(pr);
+}
+
+static DBusMessage *list_servers(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return list_paths(conn, msg, server_paths);
+}
+
+static DBusMessage *find_server(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *pattern;
+ const char *path;
+ GSList *list;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+ DBUS_TYPE_INVALID) == FALSE)
+ return NULL;
+
+ for (list = server_paths; list; list = list->next) {
+ path = (const char *) list->data;
+ if (server_find_data(path, pattern) == 0)
+ break;
+ }
+
+ if (list == NULL)
+ return does_not_exist(msg);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *list_connections(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return list_paths(conn, msg, connection_paths);
+}
+
+static GSList *find_connection_pattern(DBusConnection *conn,
+ const char *pattern)
+{
+ const char *path;
+ GSList *list;
+
+ if (pattern == NULL)
+ return NULL;
+
+ for (list = connection_paths; list; list = list->next) {
+ path = (const char *) list->data;
+ if (connection_find_data(path, pattern) == 0)
+ break;
+ }
+
+ return list;
+}
+
+static DBusMessage *find_connection(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *pattern;
+ const char *path;
+ GSList *list;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+ DBUS_TYPE_INVALID) == FALSE)
+ return NULL;
+
+ list = find_connection_pattern(conn, pattern);
+ if (list == NULL)
+ return does_not_exist(msg);
+
+ path = list->data;
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *create_connection(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct pending_reply *pr;
+ const char *addr;
+ const char *str;
+ bdaddr_t src;
+ uint16_t id;
+ int dev_id, err;
+ char key[32];
+ GSList *l;
+ uuid_t uuid;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID) == FALSE)
+ return NULL;
+
+ id = bnep_service_id(str);
+ if (id != BNEP_SVC_GN && id != BNEP_SVC_NAP && id != BNEP_SVC_PANU)
+ return not_supported(msg);
+
+ snprintf(key, 32, "%s#%s", addr, bnep_name(id));
+
+ /* Checks if the connection was already been made */
+ for (l = connection_paths; l; l = l->next) {
+ if (connection_find_data(l->data, key) == 0)
+ return already_exists(msg);
+ }
+
+ bacpy(&src, BDADDR_ANY);
+ dev_id = hci_get_route(&src);
+ if (dev_id < 0 || hci_devba(dev_id, &src) < 0)
+ return adapter_not_available(msg);
+
+ pr = g_new0(struct pending_reply, 1);
+
+ /* FIXME just to maintain compatibility */
+ pr->adapter_path = g_strdup_printf("/org/bluez/hci%d", dev_id);
+ if (!pr->adapter_path) {
+ pending_reply_free (pr);
+ return adapter_not_available(msg);
+ }
+
+ pr->conn = dbus_connection_ref(conn);
+ pr->msg = dbus_message_ref(msg);
+ bacpy(&pr->src, &src);
+ str2ba(addr, &pr->dst);
+ pr->addr = g_strdup(addr);
+ pr->id = id;
+ pr->path = g_new0(char, MAX_PATH_LENGTH);
+ snprintf(pr->path, MAX_PATH_LENGTH,
+ NETWORK_PATH "/connection%d", net_uid++);
+
+ sdp_uuid16_create(&uuid, pr->id);
+ err = bt_search_service(&pr->src, &pr->dst, &uuid, records_cb, pr,
+ NULL);
+ if (err < 0) {
+ pending_reply_free(pr);
+ return not_supported(msg);
+ }
+
+ return NULL;
+}
+
+static DBusMessage *remove_connection(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return remove_path(conn, msg, &connection_paths, "ConnectionRemoved");
+}
+
+static DBusMessage *last_connection(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *path;
+
+ if (connection_paths == NULL ||
+ g_slist_length(connection_paths) == 0)
+ return does_not_exist(msg);
+
+ path = last_connection_used(conn);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *default_connection(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *path;
+
+ if (connection_paths == NULL ||
+ g_slist_length (connection_paths) == 0)
+ return does_not_exist(msg);
+
+ path = g_slist_nth_data (connection_paths, default_index);
+
+ if (path == NULL) {
+ path = last_connection_used(conn);
+ connection_store(path, TRUE);
+ }
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *change_default_connection(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *path;
+ const char *pattern;
+ GSList *list;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+ DBUS_TYPE_INVALID) == FALSE)
+ return NULL;
+
+ if (connection_paths == NULL ||
+ g_slist_length(connection_paths) == 0)
+ return does_not_exist(msg);
+
+ list = g_slist_find_custom(connection_paths, pattern,
+ (GCompareFunc) strcmp);
+
+ /* Find object path via pattern */
+ if (list == NULL) {
+ list = find_connection_pattern(conn, pattern);
+ if (list == NULL)
+ return does_not_exist(msg);
+
+ path = list->data;
+ } else
+ path = list->data;
+
+ default_index = g_slist_position(connection_paths, list);
+ connection_store(path, TRUE);
+
+ g_dbus_emit_signal(connection, NETWORK_PATH,
+ NETWORK_MANAGER_INTERFACE,
+ "DefaultConnectionChanged",
+ DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_INVALID);
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_INVALID);
+}
+
+static void manager_unregister(void *data)
+{
+ info("Unregistered manager path");
+
+ if (server_paths) {
+ g_slist_foreach(server_paths, (GFunc) g_free, NULL);
+ g_slist_free(server_paths);
+ server_paths = NULL;
+ }
+
+ if (connection_paths) {
+ g_slist_foreach(connection_paths, (GFunc) g_free, NULL);
+ g_slist_free(connection_paths);
+ connection_paths = NULL;
+ }
+
+ bnep_kill_all_connections();
+}
+
+static void parse_stored_connection(char *key, char *value, void *data)
+{
+ bdaddr_t dst, *src = data;
+ char path[MAX_PATH_LENGTH];
+ char addr[18];
+ const char *ptr;
+ char *name;
+ int len, id;
+
+ /* Format: XX:XX:XX:XX:XX:XX#{NAP, GN} name:description */
+
+ /* Parsing the key: address#role */
+ ptr = strchr(key, '#');
+
+ /* Empty address or invalid len */
+ if (!ptr || ((ptr - key) != 17))
+ return;
+
+ memset(addr, 0, 18);
+ strncpy(addr, key, 17);
+ str2ba(addr, &dst);
+
+ /* Empty role */
+ if (++ptr == NULL)
+ return;
+
+ if (strcasecmp("nap", ptr) == 0)
+ id = BNEP_SVC_NAP;
+ else if (strcasecmp("gn", ptr) == 0)
+ id = BNEP_SVC_GN;
+ else if (strcasecmp("panu", ptr) == 0)
+ id = BNEP_SVC_PANU;
+ else
+ return;
+
+ snprintf(path, MAX_PATH_LENGTH,
+ NETWORK_PATH "/connection%d", net_uid++);
+
+ /* Parsing the value: name and description */
+ ptr = strchr(value, ':');
+
+ /* Empty name */
+ if (!ptr)
+ return;
+
+ len = ptr-value;
+ name = g_malloc0(len + 1);
+ strncpy(name, value, len);
+
+ /* Empty description */
+ if (++ptr == NULL) {
+ g_free(name);
+ return;
+ }
+
+ if (connection_register(path, src, &dst, id, name, ptr) == 0) {
+ char *rpath = g_strdup(path);
+ connection_paths = g_slist_append(connection_paths, rpath);
+ }
+
+ g_free(name);
+}
+
+static void register_connections_stored(const char *adapter)
+{
+ char filename[PATH_MAX + 1];
+ char *pattern;
+ struct stat st;
+ GSList *list;
+ bdaddr_t src;
+ bdaddr_t default_src;
+ int dev_id;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter, "network");
+
+ str2ba(adapter, &src);
+
+ if (stat(filename, &st) < 0)
+ return;
+
+ if (!(st.st_mode & __S_IFREG))
+ return;
+
+ textfile_foreach(filename, parse_stored_connection, &src);
+
+ /* Check default connection for current default adapter */
+ bacpy(&default_src, BDADDR_ANY);
+ dev_id = hci_get_route(&default_src);
+ if (dev_id < 0 || hci_devba(dev_id, &default_src) < 0)
+ return;
+
+ if (bacmp(&default_src, &src) != 0)
+ return;
+
+ pattern = textfile_get(filename, "default");
+ if (!pattern)
+ return;
+
+ list = find_connection_pattern(connection, pattern);
+ if (!list)
+ return;
+ default_index = g_slist_position(connection_paths, list);
+}
+
+static void register_server(uint16_t id)
+{
+ char path[MAX_PATH_LENGTH];
+ bdaddr_t src;
+ int dev_id;
+
+ if (!conf->server_enabled)
+ return;
+
+ snprintf(path, MAX_PATH_LENGTH, NETWORK_PATH "/%s", bnep_name(id));
+
+ if (g_slist_find_custom(server_paths, path,
+ (GCompareFunc) strcmp))
+ return;
+
+ bacpy(&src, BDADDR_ANY);
+ dev_id = hci_get_route(&src);
+ if (dev_id < 0 || hci_devba(dev_id, &src))
+ return;
+
+ if (server_register(path, &src, id) < 0)
+ return;
+
+ server_store(path);
+
+ server_paths = g_slist_append(server_paths, g_strdup(path));
+}
+
+static void register_servers_stored(const char *adapter, const char *profile)
+{
+ char filename[PATH_MAX + 1];
+ char path[MAX_PATH_LENGTH];
+ uint16_t id;
+ struct stat s;
+ bdaddr_t src;
+
+ if (strcmp(profile, "nap") == 0)
+ id = BNEP_SVC_NAP;
+ else if (strcmp(profile, "gn") == 0)
+ id = BNEP_SVC_GN;
+ else
+ id = BNEP_SVC_PANU;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, adapter, profile);
+
+ str2ba(adapter, &src);
+
+ if (stat (filename, &s) == 0 && (s.st_mode & __S_IFREG)) {
+ snprintf(path, MAX_PATH_LENGTH, NETWORK_PATH "/%s", profile);
+ if (server_register_from_file(path, &src, id, filename) == 0) {
+ server_paths = g_slist_append(server_paths,
+ g_strdup(path));
+ }
+ }
+}
+
+static void register_stored(void)
+{
+ char dirname[PATH_MAX + 1];
+ struct dirent *de;
+ DIR *dir;
+
+ snprintf(dirname, PATH_MAX, "%s", STORAGEDIR);
+
+ dir = opendir(dirname);
+ if (!dir)
+ return;
+
+ while ((de = readdir(dir)) != NULL) {
+ if (!isdigit(de->d_name[0]))
+ continue;
+
+ /* Connection objects */
+ if (conf->connection_enabled)
+ register_connections_stored(de->d_name);
+
+ /* Server objects */
+ if (conf->server_enabled) {
+ /* NAP objects */
+ register_servers_stored(de->d_name, "nap");
+
+ /* GN objects */
+ register_servers_stored(de->d_name, "gn");
+
+ /* PANU objects */
+ register_servers_stored(de->d_name, "panu");
+ }
+ }
+
+ closedir(dir);
+}
+
+static GDBusMethodTable connection_methods[] = {
+ { "ListConnections", "", "as", list_connections },
+ { "FindConnection", "s", "s", find_connection },
+ { "CreateConnection", "ss", "s", create_connection,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "RemoveConnection", "s", "", remove_connection },
+ { "LastConnection", "", "s", last_connection },
+ { "DefaultConnection", "", "s", default_connection },
+ { "ChangeDefaultConnection", "s", "s", change_default_connection },
+ { }
+};
+
+static GDBusSignalTable connection_signals[] = {
+ { "ConnectionCreated", "s" },
+ { "ConnectionRemoved", "s" },
+ { "DefaultConnectionChanged", "s" },
+ { }
+};
+
+static GDBusMethodTable server_methods[] = {
+ { "ListServers", "", "as", list_servers },
+ { "FindServer", "s", "s", find_server },
+ { }
+};
+
+static GDBusMethodTable manager_methods[] = {
+ { "ListServers", "", "as", list_servers },
+ { "FindServer", "s", "s", find_server },
+ { "ListConnections", "", "as", list_connections },
+ { "FindConnection", "s", "s", find_connection },
+ { "CreateConnection", "ss", "s", create_connection,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "RemoveConnection", "s", "", remove_connection },
+ { "LastConnection", "", "s", last_connection },
+ { "DefaultConnection", "", "s", default_connection },
+ { "ChangeDefaultConnection", "s", "s", change_default_connection },
+ { }
+};
+
+int network_manager_init(DBusConnection *conn, struct network_conf *service_conf)
+{
+ GDBusMethodTable *methods = NULL;
+ GDBusSignalTable *signals = NULL;
+
+ conf = service_conf;
+
+ if (conf->server_enabled && conf->connection_enabled) {
+ methods = manager_methods;
+ signals = connection_signals;
+ } else if (conf->connection_enabled) {
+ methods = connection_methods;
+ signals = connection_signals;
+ } else if (conf->server_enabled)
+ methods = server_methods;
+ else {
+ error ("All interfaces were disabled");
+ return -1;
+ }
+
+ if (bnep_init(conf->panu_script, conf->gn_script, conf->nap_script)) {
+ error("Can't init bnep module");
+ return -1;
+ }
+
+ /*
+ * There is one socket to handle the incomming connections. NAP,
+ * GN and PANU servers share the same PSM. The initial BNEP message
+ * (setup connection request) contains the destination service
+ * field that defines which service the source is connecting to.
+ */
+ if (conf->server_enabled) {
+ if (bridge_init(conf->gn_iface, conf->nap_iface) < 0) {
+ error("Can't init bridge module");
+ return -1;
+ }
+
+ if (server_init(conn, conf->iface_prefix, conf->security) < 0)
+ return -1;
+ }
+
+ if (conf->connection_enabled) {
+ if (connection_init(conn, conf->iface_prefix) < 0)
+ return -1;
+ }
+
+ if (g_dbus_register_interface(conn, NETWORK_PATH,
+ NETWORK_MANAGER_INTERFACE,
+ methods, signals, NULL,
+ NULL, manager_unregister) == FALSE) {
+ error("Failed to register %s interface to %s",
+ NETWORK_MANAGER_INTERFACE, NETWORK_PATH);
+ return -1;
+ }
+
+ connection = dbus_connection_ref(conn);
+
+ info("Registered manager path:%s", NETWORK_PATH);
+
+ register_stored();
+
+ /* Register PANU, GN and NAP servers if they don't exist */
+ register_server(BNEP_SVC_PANU);
+ register_server(BNEP_SVC_GN);
+ register_server(BNEP_SVC_NAP);
+
+ return 0;
+}
+
+void network_manager_exit(void)
+{
+ if (conf->server_enabled)
+ server_exit();
+
+ if (conf->connection_enabled)
+ connection_exit();
+
+ g_dbus_unregister_interface(connection, NETWORK_PATH,
+ NETWORK_MANAGER_INTERFACE);
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+
+ bnep_cleanup();
+ bridge_cleanup();
+}
+
+static inline int create_filename(char *buf, size_t size,
+ bdaddr_t *bdaddr, const char *name)
+{
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+
+ return create_name(buf, size, STORAGEDIR, addr, name);
+}
diff --git a/network/manager.h b/network/manager.h
new file mode 100644
index 00000000..9b16c2a3
--- /dev/null
+++ b/network/manager.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "connection.h"
+#include "server.h"
+
+#define MAX_PATH_LENGTH 64 /* D-Bus path */
+#define NETWORK_PATH "/org/bluez/network"
+
+struct network_conf {
+ gboolean connection_enabled;
+ gboolean server_enabled;
+ gboolean security;
+ char *iface_prefix;
+ char *panu_script;
+ char *gn_script;
+ char *nap_script;
+ char *gn_iface;
+ char *nap_iface;
+};
+
+int network_manager_init(DBusConnection *conn, struct network_conf *service_conf);
+void network_manager_exit(void);
diff --git a/network/network-api.txt b/network/network-api.txt
new file mode 100644
index 00000000..2d46abfd
--- /dev/null
+++ b/network/network-api.txt
@@ -0,0 +1,199 @@
+Bluetooth network service API description
+*****************************************
+
+Copyright (C) 2006-2007 Marcel Holtmann <marcel@holtmann.org>
+
+
+Network Manager hierarchy
+=========================
+
+Interface org.bluez.network.Manager
+Object path /org/bluez/network
+
+Methods array{string} ListServers()
+
+ Returns an array of available network devices paths.
+ Currently only NAP and GN are supported.
+
+ string FindServer(string pattern)
+
+ Returns server path.
+
+ Possible errors: org.bluez.Error.DoesNotExist
+ org.bluez.Error.Failed
+
+ string CreateConnection(string address, string uuid)
+
+ Creates a network connection object(NAP or GN).
+
+ Possible errors: org.bluez.Error.AlreadyExists
+ org.bluez.Error.NotSupported
+ org.bluez.Error.ConnectionAttemptFailed
+ org.bluez.Error.Failed
+
+ void RemoveConnection(string path)
+
+ Removes a network connection object for a given path.
+
+ Possible errors: org.bluez.Error.DoesNotExist
+ org.bluez.Error.Failed
+
+ array{string} ListConnections()
+
+ Returns an array of available network connections paths.
+
+ string FindConnection(string pattern)
+
+ Returns connection path.
+
+ Possible errors: org.bluez.Error.DoesNotExist
+ org.bluez.Error.Failed
+
+ string LastConnection()
+
+ Returns last connected connection path, if none is connected
+ fallback to last created connection.
+
+ Possible errors: org.bluez.Error.DoesNotExist
+
+ string DefaultConnection()
+
+ Returns default connection path.
+
+ Possible errors: org.bluez.Error.DoesNotExist
+
+ string ChangeDefaultConnection(string pattern)
+
+ Changes default connection path.
+
+ Possible errors: org.bluez.Error.DoesNotExist
+
+Signals void ConnectionCreated(string path)
+
+ void ConnectionRemoved(string path)
+
+ void DefaultConnectionChanged(string path)
+
+
+Network Server hierarchy (experimental)
+=======================================
+
+Interface org.bluez.network.Server
+Object path /org/bluez/network/{gn, nap, panu}
+
+Methods string GetUUID()
+
+ Returns the UUID-128 string representation of
+ the server.
+
+ void Enable()
+
+ Enable server and updates service record.
+
+ Possible errors: org.bluez.Error.AlreadyExists
+ org.bluez.Error.Failed
+
+ void Disable()
+
+ Disable server and remove service record.
+
+ Possible errors: org.bluez.Error.Failed
+
+ bool IsEnabled()
+
+ Returns the server status.
+
+ void SetName(string name)
+
+ Sets the name attribute.
+
+ string GetName()
+
+ Returns the service name.
+
+ void SetAddressRange(string start, string end)
+
+ TBD
+
+ void SetRouting(string interface)
+
+ TBD
+
+ dict GetInfo()
+
+ Returns the server properties.
+
+Signals void Enabled()
+
+ void Disabled()
+
+
+Network Connection hierarchy (experimental)
+===========================================
+
+Interface org.bluez.network.Connection
+Object path /org/bluez/network/connection*
+
+Methods string GetAdapter()
+
+ Returns the Bluetooth address of the adapter.
+
+ string GetAddress()
+
+ Returns the Bluetooth address of the ending point.
+
+ string GetUUID()
+
+ Returns the uuid 128 string representation of
+ the connected service.
+
+ string GetName()
+
+ Returns the string representation of connected host.
+
+ Possible errors: org.bluez.Error.Failed
+
+ string GetDescription()
+
+ Returns the string description of connected host.
+
+ Possible errors: org.bluez.Error.Failed
+
+ string GetInterface()
+
+ Returns the string network interface.
+
+ Possible errors: org.bluez.Error.Failed
+
+ string Connect()
+
+ Connects to host and return the network interface
+ created.
+
+ Possible errors: org.bluez.Error.ConnectionAttemptFailed
+ org.bluez.Error.Failed
+
+ void CancelConnect()
+
+ Abort connection attempt in case of errors or
+ timeouts in the client.
+
+ Possible errors: org.bluez.Error.Failed
+
+ void Disconnect()
+
+ Disconnects to host.
+
+ Possible errors: org.bluez.Error.Failed
+
+ bool IsConnected()
+
+ Returns the connection status.
+
+ dict GetInfo()
+
+ Returns the connection properties.
+
+Signals void Connected()
+
+ void Disconnected()
diff --git a/network/network.conf b/network/network.conf
new file mode 100644
index 00000000..8677bd7b
--- /dev/null
+++ b/network/network.conf
@@ -0,0 +1,37 @@
+# Configuration file for the network service
+
+# This section contains options which are not specific to any
+# particular interface
+[General]
+
+# If we want to disable support for specific services
+# Defaults to supporting all implemented services
+#Disable=Connection,Server
+
+# Disable link encryption: default=false
+#DisableSecurity=true
+
+[PANU Role]
+
+# Network interface name for PANU for connections. default:bnep%d
+# (up to 16 characters)
+#Interface=
+
+# PAN user connection interface up script. default:none
+Script=avahi-autoipd
+
+[GN Role]
+
+# Network Interface name for Group Network server. default:pan0
+#Interface=
+
+# Group Network connection interface up script. default:none
+Script=avahi-autoipd
+
+[NAP Role]
+
+# Network Interface name for Network Access Point server. default:pan1
+#Interface=
+
+# Network Access Point connection interface up script. default:none
+Script=dhclient
diff --git a/network/server.c b/network/server.c
new file mode 100644
index 00000000..e94964ae
--- /dev/null
+++ b/network/server.c
@@ -0,0 +1,1097 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <net/if.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <netinet/in.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../hcid/dbus-common.h"
+
+#include "logging.h"
+#include "error.h"
+#include "textfile.h"
+#include "dbus-service.h"
+#include "sdpd.h"
+#include "glib-helper.h"
+
+#define NETWORK_SERVER_INTERFACE "org.bluez.network.Server"
+#define SETUP_TIMEOUT 1000
+#define MAX_SETUP_ATTEMPTS 3
+
+#include "bridge.h"
+#include "common.h"
+#include "manager.h"
+
+/* Pending Authorization */
+struct setup_session {
+ char *address; /* Remote Bluetooth Address */
+ uint16_t dst_role; /* Destination role */
+ uint16_t src_role; /* Source role */
+ int nsk; /* L2CAP socket */
+ guint watch; /* BNEP socket watch */
+};
+
+struct timeout {
+ guint id; /* Timeout id */
+ guint watch; /* BNEP socket watch */
+};
+
+/* Main server structure */
+struct network_server {
+ bdaddr_t src; /* Bluetooth Local Address */
+ char *iface; /* Routing interface */
+ char *name; /* Server service name */
+ char *range; /* IP Address range */
+ char *path; /* D-Bus path */
+ gboolean enable; /* Enable flag */
+ uint32_t record_id; /* Service record id */
+ uint16_t id; /* Service class identifier */
+ GSList *clients; /* Active connections */
+};
+
+static GIOChannel *bnep_io = NULL;
+static DBusConnection *connection = NULL;
+static struct setup_session *setup = NULL;
+static GSList *servers = NULL;
+static const char *prefix = NULL;
+static gboolean security = TRUE;
+
+gint find_server(gconstpointer a, gconstpointer b)
+{
+ const struct network_server *ns = a;
+ const char *path = b;
+
+ return strcmp(ns->path, path);
+}
+
+static struct setup_session *setup_session_new(gchar *address,
+ uint16_t dst_role, uint16_t src_role, int nsk, guint watch)
+{
+ struct setup_session *setup;
+
+ setup = g_new0(struct setup_session, 1);
+ setup->address = g_strdup(address);
+ setup->dst_role = dst_role;
+ setup->src_role = src_role;
+ setup->nsk = nsk;
+ setup->watch = watch;
+
+ return setup;
+}
+
+static void setup_session_free(struct setup_session *setup)
+{
+ g_source_remove(setup->watch);
+ g_free(setup->address);
+ g_free(setup);
+}
+
+static struct network_server *server_find(bdaddr_t *src, uint16_t role)
+{
+ struct network_server *ns;
+ GSList *l;
+
+ for (l = servers; l; l = l->next) {
+ ns = l->data;
+ if (bacmp(&ns->src, src) != 0)
+ continue;
+ if (ns->id == role)
+ return ns;
+ }
+
+ return NULL;
+}
+
+static int store_property(bdaddr_t *src, uint16_t id,
+ const char *key, const char *value)
+{
+ char filename[PATH_MAX + 1];
+ char addr[18];
+
+ ba2str(src, addr);
+ if (id == BNEP_SVC_NAP)
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "nap");
+ else if (id == BNEP_SVC_GN)
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "gn");
+ else if (id == BNEP_SVC_PANU)
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "panu");
+
+ return textfile_put(filename, key, value);
+}
+
+static void add_lang_attr(sdp_record_t *r)
+{
+ sdp_lang_attr_t base_lang;
+ sdp_list_t *langs = 0;
+
+ /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
+ base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
+ base_lang.encoding = 106;
+ base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
+ langs = sdp_list_append(0, &base_lang);
+ sdp_set_lang_attr(r, langs);
+ sdp_list_free(langs, 0);
+}
+
+static sdp_record_t *server_record_new(const char *name, uint16_t id)
+{
+ sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+ uuid_t root_uuid, pan, l2cap, bnep;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *proto[2];
+ sdp_data_t *v, *p;
+ uint16_t psm = BNEP_PSM, version = 0x0100;
+ uint16_t security_desc = (security ? 0x0001 : 0x0000);
+ uint16_t net_access_type = 0xfffe;
+ uint32_t max_net_access_rate = 0;
+ const char *desc = "BlueZ PAN service";
+ sdp_record_t *record;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ record->attrlist = NULL;
+ record->pattern = NULL;
+
+ switch (id) {
+ case BNEP_SVC_NAP:
+ sdp_uuid16_create(&pan, NAP_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &pan);
+ sdp_set_service_classes(record, svclass);
+
+ sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_set_info_attr(record, name, NULL, desc);
+
+ sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE,
+ SDP_UINT16, &net_access_type);
+ sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
+ SDP_UINT32, &max_net_access_rate);
+ break;
+ case BNEP_SVC_GN:
+ sdp_uuid16_create(&pan, GN_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &pan);
+ sdp_set_service_classes(record, svclass);
+
+ sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_set_info_attr(record, name, NULL, desc);
+ break;
+ case BNEP_SVC_PANU:
+ sdp_uuid16_create(&pan, PANU_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &pan);
+ sdp_set_service_classes(record, svclass);
+
+ sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_set_info_attr(record, name, NULL, desc);
+ break;
+ default:
+ return NULL;
+ }
+
+ 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);
+ p = sdp_data_alloc(SDP_UINT16, &psm);
+ proto[0] = sdp_list_append(proto[0], p);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&bnep, BNEP_UUID);
+ proto[1] = sdp_list_append(NULL, &bnep);
+ v = sdp_data_alloc(SDP_UINT16, &version);
+ proto[1] = sdp_list_append(proto[1], v);
+
+ /* Supported protocols */
+ {
+ uint16_t ptype[] = {
+ 0x0800, /* IPv4 */
+ 0x0806, /* ARP */
+ };
+ sdp_data_t *head, *pseq;
+ int p;
+
+ for (p = 0, head = NULL; p < 2; p++) {
+ sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
+ if (head)
+ sdp_seq_append(head, data);
+ else
+ head = data;
+ }
+ pseq = sdp_data_alloc(SDP_SEQ16, head);
+ proto[1] = sdp_list_append(proto[1], pseq);
+ }
+
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ add_lang_attr(record);
+
+ sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC,
+ SDP_UINT16, &security_desc);
+
+ sdp_data_free(p);
+ sdp_data_free(v);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(svclass, NULL);
+ sdp_list_free(pfseq, NULL);
+
+ return record;
+}
+
+static ssize_t send_bnep_ctrl_rsp(int sk, uint16_t val)
+{
+ struct bnep_control_rsp rsp;
+
+ rsp.type = BNEP_CONTROL;
+ rsp.ctrl = BNEP_SETUP_CONN_RSP;
+ rsp.resp = htons(val);
+
+ return send(sk, &rsp, sizeof(rsp), 0);
+}
+
+static int server_connadd(struct network_server *ns, int nsk,
+ const gchar *address, uint16_t dst_role)
+{
+ char devname[16];
+ const char *bridge;
+ int err;
+
+ /* Server can be disabled in the meantime */
+ if (ns->enable == FALSE)
+ return -EPERM;
+
+ memset(devname, 0, 16);
+ strncpy(devname, prefix, strlen(prefix));
+
+ err = bnep_connadd(nsk, dst_role, devname);
+ if (err < 0)
+ return err;
+
+ info("Added new connection: %s", devname);
+
+ bridge = bridge_get_name(ns->id);
+ if (bridge) {
+ if (bridge_add_interface(ns->id, devname) < 0) {
+ error("Can't add %s to the bridge %s: %s(%d)",
+ devname, bridge, strerror(errno),
+ errno);
+ return -EPERM;
+ }
+
+ bnep_if_up(devname, 0);
+ } else
+ bnep_if_up(devname, ns->id);
+
+ ns->clients = g_slist_append(ns->clients, g_strdup(address));
+
+ return 0;
+}
+
+static void req_auth_cb(DBusError *derr, void *user_data)
+{
+ struct network_server *ns = user_data;
+ uint16_t val;
+
+ if (!setup) {
+ info("Authorization cancelled: Client exited");
+ return;
+ }
+
+ if (derr) {
+ error("Access denied: %s", derr->message);
+ if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY)) {
+ bdaddr_t dst;
+ str2ba(setup->address, &dst);
+ service_cancel_auth(&ns->src, &dst);
+ }
+ val = BNEP_CONN_NOT_ALLOWED;
+ goto done;
+ }
+
+ if (server_connadd(ns, setup->nsk,
+ setup->address, setup->dst_role) < 0)
+ val = BNEP_CONN_NOT_ALLOWED;
+ else
+ val = BNEP_SUCCESS;
+
+done:
+ send_bnep_ctrl_rsp(setup->nsk, val);
+ setup_session_free(setup);
+ setup = NULL;
+}
+
+static int authorize_connection(struct network_server *ns, const char *address)
+{
+ const char *uuid;
+ bdaddr_t dst;
+ int ret_val;
+
+ uuid = bnep_uuid(ns->id);
+ str2ba(address, &dst);
+
+ ret_val = service_req_auth(&ns->src, &dst, uuid, req_auth_cb, ns);
+
+ return ret_val;
+}
+
+static uint16_t inline bnep_setup_chk(uint16_t dst_role, uint16_t src_role)
+{
+ /* Allowed PAN Profile scenarios */
+ switch (dst_role) {
+ case BNEP_SVC_NAP:
+ case BNEP_SVC_GN:
+ if (src_role == BNEP_SVC_PANU)
+ return 0;
+ return BNEP_CONN_INVALID_SRC;
+ case BNEP_SVC_PANU:
+ if (src_role == BNEP_SVC_PANU ||
+ src_role == BNEP_SVC_GN ||
+ src_role == BNEP_SVC_NAP)
+ return 0;
+
+ return BNEP_CONN_INVALID_SRC;
+ }
+
+ return BNEP_CONN_INVALID_DST;
+}
+
+static uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req,
+ uint16_t *dst_role, uint16_t *src_role)
+{
+ uint8_t *dest, *source;
+
+ dest = req->service;
+ source = req->service + req->uuid_size;
+
+ switch (req->uuid_size) {
+ case 2: /* UUID16 */
+ *dst_role = ntohs(bt_get_unaligned((uint16_t *) dest));
+ *src_role = ntohs(bt_get_unaligned((uint16_t *) source));
+ break;
+ case 4: /* UUID32 */
+ case 16: /* UUID128 */
+ *dst_role = ntohl(bt_get_unaligned((uint32_t *) dest));
+ *src_role = ntohl(bt_get_unaligned((uint32_t *) source));
+ break;
+ default:
+ return BNEP_CONN_INVALID_SVC;
+ }
+
+ return 0;
+}
+
+static gboolean bnep_setup(GIOChannel *chan,
+ GIOCondition cond, gpointer user_data)
+{
+ struct timeout *to = user_data;
+ struct network_server *ns;
+ uint8_t packet[BNEP_MTU];
+ struct bnep_setup_conn_req *req = (void *) packet;
+ struct sockaddr_l2 sa;
+ socklen_t size;
+ char address[18];
+ uint16_t rsp, src_role, dst_role;
+ int n, sk;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ if (cond & (G_IO_ERR | G_IO_HUP)) {
+ error("Hangup or error on BNEP socket");
+ return FALSE;
+ }
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ /* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
+ n = read(sk, packet, sizeof(packet));
+ if (n < 0) {
+ error("read(): %s(%d)", strerror(errno), errno);
+ return FALSE;
+ }
+
+ if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ)
+ return FALSE;
+
+ rsp = bnep_setup_decode(req, &dst_role, &src_role);
+ if (rsp)
+ goto reply;
+
+ rsp = bnep_setup_chk(dst_role, src_role);
+ if (rsp)
+ goto reply;
+
+ size = sizeof(sa);
+ if (getsockname(sk, (struct sockaddr *) &sa, &size) < 0) {
+ rsp = BNEP_CONN_NOT_ALLOWED;
+ goto reply;
+ }
+
+ ba2str(&sa.l2_bdaddr, address);
+ ns = server_find(&sa.l2_bdaddr, dst_role);
+ if (!ns || ns->enable == FALSE) {
+ error("Server unavailable: %s (0x%x)", address, dst_role);
+ rsp = BNEP_CONN_NOT_ALLOWED;
+ goto reply;
+ }
+
+ if (getpeername(sk, (struct sockaddr *) &sa, &size) < 0) {
+ rsp = BNEP_CONN_NOT_ALLOWED;
+ goto reply;
+ }
+
+ ba2str(&sa.l2_bdaddr, address);
+
+ if (setup) {
+ error("Connection rejected: Pending authorization");
+ rsp = BNEP_CONN_NOT_ALLOWED;
+ goto reply;
+ }
+
+ setup = setup_session_new(address, dst_role, src_role, sk, to->watch);
+
+ /* Wait authorization before reply success */
+ if (authorize_connection(ns, address) < 0) {
+ setup_session_free(setup);
+ setup = NULL;
+ rsp = BNEP_CONN_NOT_ALLOWED;
+ goto reply;
+ }
+
+ g_source_remove(to->id);
+ to->id = 0;
+
+ return TRUE;
+
+reply:
+ send_bnep_ctrl_rsp(sk, rsp);
+
+ return FALSE;
+}
+
+static void setup_destroy(void *user_data)
+{
+ struct timeout *to = user_data;
+
+ if (to->id)
+ g_source_remove(to->id);
+
+ g_free(to);
+}
+
+static gboolean timeout_cb(void *user_data)
+{
+ struct timeout *to = user_data;
+
+ to->id = 0;
+ g_source_remove(to->watch);
+
+ return FALSE;
+}
+
+static void connect_event(GIOChannel *chan, int err, const bdaddr_t *src,
+ const bdaddr_t *dst, gpointer user_data)
+{
+ struct timeout *to;
+
+ if (err < 0) {
+ error("accept(): %s(%d)", strerror(errno), errno);
+ return;
+ }
+
+ g_io_channel_set_close_on_unref(chan, TRUE);
+
+ /*
+ * BNEP_SETUP_CONNECTION_REQUEST_MSG shall be received and
+ * user shall authorize the incomming connection before
+ * the time expires.
+ */
+ to = g_malloc0(sizeof(struct timeout));
+ to->id = g_timeout_add(SETUP_TIMEOUT, timeout_cb, to);
+ to->watch = g_io_add_watch_full(chan, G_PRIORITY_DEFAULT,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ bnep_setup, to, setup_destroy);
+ g_io_channel_unref(chan);
+
+ return;
+}
+
+int server_init(DBusConnection *conn, const char *iface_prefix,
+ gboolean secure)
+{
+ int lm;
+
+ lm = secure ? L2CAP_LM_SECURE : 0;
+
+ security = secure;
+ connection = dbus_connection_ref(conn);
+ prefix = iface_prefix;
+
+ bnep_io = bt_l2cap_listen(BDADDR_ANY, BNEP_PSM, BNEP_MTU, lm,
+ connect_event, NULL);
+ if (!bnep_io)
+ return -1;
+ g_io_channel_set_close_on_unref(bnep_io, FALSE);
+
+ if (bridge_create(BNEP_SVC_GN) < 0)
+ error("Can't create GN bridge");
+
+ return 0;
+}
+
+void server_exit()
+{
+ if (bnep_io != NULL) {
+ g_io_channel_close(bnep_io);
+ g_io_channel_unref(bnep_io);
+ bnep_io = NULL;
+ }
+
+ if (bridge_remove(BNEP_SVC_GN) < 0)
+ error("Can't remove GN bridge");
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
+
+static uint32_t register_server_record(struct network_server *ns)
+{
+ sdp_record_t *record;
+
+ record = server_record_new(ns->name, ns->id);
+ if (!record) {
+ error("Unable to allocate new service record");
+ return 0;
+ }
+
+ if (add_record_to_server(&ns->src, record) < 0) {
+ error("Failed to register service record");
+ sdp_record_free(record);
+ return 0;
+ }
+
+ debug("register_server_record: got record id 0x%x", record->handle);
+
+ return record->handle;
+}
+
+static DBusMessage *get_uuid(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_server *ns = data;
+ DBusMessage *reply;
+ const char *uuid;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ uuid = bnep_uuid(ns->id);
+ dbus_message_append_args(reply,
+ DBUS_TYPE_STRING, &uuid,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static inline DBusMessage *failed(DBusMessage *msg, const char *description)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ description);
+}
+
+static inline DBusMessage *invalid_arguments(DBusMessage *msg,
+ const char *description)
+{
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
+ description);
+}
+
+static DBusMessage *enable(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_server *ns = data;
+ DBusMessage *reply;
+
+ if (ns->enable)
+ return g_dbus_create_error(msg, ERROR_INTERFACE
+ ".AlreadyExist",
+ "Server already enabled");
+
+ if (bacmp(&ns->src, BDADDR_ANY) == 0) {
+ int dev_id;
+
+ dev_id = hci_get_route(&ns->src);
+ if ((dev_id < 0) || (hci_devba(dev_id, &ns->src) < 0))
+ return failed(msg, "Adapter not available");
+
+ /* Store the server info */
+ server_store(ns->path);
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ /* Add the service record */
+ ns->record_id = register_server_record(ns);
+ if (!ns->record_id) {
+ dbus_message_unref(reply);
+ return failed(msg, "Service record registration failed");
+ }
+
+ ns->enable = TRUE;
+
+ store_property(&ns->src, ns->id, "enabled", "1");
+
+ g_dbus_emit_signal(conn, ns->path, NETWORK_SERVER_INTERFACE,
+ "Enabled", DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static void kill_connection(void *data, void *udata)
+{
+ const char *address = data;
+ bdaddr_t dst;
+
+ str2ba(address, &dst);
+ bnep_kill_connection(&dst);
+}
+
+static DBusMessage *disable(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_server *ns = data;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ if (!ns->enable)
+ return failed(msg, "Not enabled");
+
+ /* Remove the service record */
+ if (ns->record_id) {
+ remove_record_from_server(ns->record_id);
+ ns->record_id = 0;
+ }
+
+ ns->enable = FALSE;
+
+ g_slist_foreach(ns->clients, (GFunc) kill_connection, NULL);
+
+ store_property(&ns->src, ns->id, "enabled", "0");
+
+ g_dbus_emit_signal(conn, ns->path, NETWORK_SERVER_INTERFACE,
+ "Disabled", DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusMessage *is_enabled(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct network_server *ns = data;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &ns->enable,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusMessage *set_name(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_server *ns = data;
+ DBusMessage *reply;
+ const char *name;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ if (!name || (strlen(name) == 0))
+ return invalid_arguments(msg, "Invalid name");
+
+ if (ns->name)
+ g_free(ns->name);
+ ns->name = g_strdup(name);
+
+ if (ns->enable && ns->record_id) {
+ uint32_t handle = register_server_record(ns);
+ if (!handle) {
+ dbus_message_unref(reply);
+ return failed(msg,
+ "Service record attribute update failed");
+ }
+
+ remove_record_from_server(ns->record_id);
+ ns->record_id = handle;
+ }
+
+ store_property(&ns->src, ns->id, "name", ns->name);
+
+ return reply;
+}
+
+static DBusMessage *get_name(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_server *ns = data;
+ char name[] = "";
+ const char *pname = (ns->name ? ns->name : name);
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_append_args(reply,
+ DBUS_TYPE_STRING, &pname,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusMessage *set_address_range(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return NULL;
+}
+
+static DBusMessage *set_routing(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct network_server *ns = data;
+ const char *iface;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &iface,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ /* FIXME: Check if the interface is valid/UP */
+ if (!iface || (strlen(iface) == 0))
+ return invalid_arguments(msg, "Invalid interface");
+
+ if (ns->iface)
+ g_free(ns->iface);
+ ns->iface = g_strdup(iface);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *get_info(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_server *ns = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ const char *uuid;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ dbus_message_iter_append_dict_entry(&dict, "name",
+ DBUS_TYPE_STRING, &ns->name);
+
+ uuid = bnep_uuid(ns->id);
+ dbus_message_iter_append_dict_entry(&dict, "uuid",
+ DBUS_TYPE_STRING, &uuid);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static void server_free(struct network_server *ns)
+{
+ if (!ns)
+ return;
+
+ /* FIXME: Missing release/free all bnepX interfaces */
+ if (ns->record_id)
+ remove_record_from_server(ns->record_id);
+
+ if (ns->iface)
+ g_free(ns->iface);
+
+ if (ns->name)
+ g_free(ns->name);
+
+ if (ns->range)
+ g_free(ns->range);
+
+ if (ns->path)
+ g_free(ns->path);
+
+ if (ns->clients) {
+ g_slist_foreach(ns->clients, (GFunc) g_free, NULL);
+ g_slist_free(ns->clients);
+ }
+
+ g_free(ns);
+}
+
+static void server_unregister(void *data)
+{
+ struct network_server *ns = data;
+
+ info("Unregistered server path:%s", ns->path);
+
+ servers = g_slist_remove(servers, ns);
+ server_free(ns);
+}
+
+static GDBusMethodTable server_methods[] = {
+ { "GetUUID", "", "s", get_uuid },
+ { "Enable", "", "", enable },
+ { "Disable", "", "", disable },
+ { "IsEnabled", "", "b", is_enabled },
+ { "SetName", "s", "", set_name },
+ { "GetName", "", "s", get_name },
+ { "SetAddressRange", "ss", "", set_address_range },
+ { "SetRouting", "s", "", set_routing },
+ { "GetInfo", "", "a{sv}",get_info },
+ { }
+};
+
+static GDBusSignalTable server_signals[] = {
+ { "Enabled", "" },
+ { "Disabled", "" },
+ { }
+};
+
+int server_register(const char *path, bdaddr_t *src, uint16_t id)
+{
+ struct network_server *ns;
+
+ if (!path)
+ return -EINVAL;
+
+ ns = g_new0(struct network_server, 1);
+
+ if (!g_dbus_register_interface(connection, path,
+ NETWORK_SERVER_INTERFACE,
+ server_methods, server_signals, NULL,
+ ns, server_unregister)) {
+ error("D-Bus failed to register %s interface",
+ NETWORK_SERVER_INTERFACE);
+ server_free(ns);
+ return -1;
+ }
+
+ /* Setting a default name */
+ if (id == BNEP_SVC_NAP)
+ ns->name = g_strdup("BlueZ NAP service");
+ else if (id == BNEP_SVC_GN)
+ ns->name = g_strdup("BlueZ GN service");
+ else
+ ns->name = g_strdup("BlueZ PANU service");
+
+ ns->path = g_strdup(path);
+ ns->id = id;
+ bacpy(&ns->src, src);
+
+ servers = g_slist_append(servers, ns);
+
+ info("Registered server path:%s", path);
+
+ return 0;
+}
+
+int server_register_from_file(const char *path, const bdaddr_t *src,
+ uint16_t id, const char *filename)
+{
+ struct network_server *ns;
+ char *str;
+
+ if (!path)
+ return -EINVAL;
+
+ ns = g_new0(struct network_server, 1);
+
+ bacpy(&ns->src, src);
+ ns->path = g_strdup(path);
+ ns->id = id;
+ ns->name = textfile_get(filename, "name");
+ if (!ns->name) {
+ /* Name is mandatory */
+ server_free(ns);
+ return -1;
+ }
+
+ ns->range = textfile_get(filename, "address_range");
+ ns->iface = textfile_get(filename, "routing");
+
+ str = textfile_get(filename, "enabled");
+ if (str) {
+ if (strcmp("1", str) == 0) {
+ ns->record_id = register_server_record(ns);
+ ns->enable = TRUE;
+ }
+ g_free(str);
+ }
+
+ if (!g_dbus_register_interface(connection, path,
+ NETWORK_SERVER_INTERFACE,
+ server_methods, server_signals, NULL,
+ ns, server_unregister)) {
+ error("D-Bus failed to register %s interface",
+ NETWORK_SERVER_INTERFACE);
+ server_free(ns);
+ return -1;
+ }
+
+ servers = g_slist_append(servers, ns);
+
+ info("Registered server path:%s", path);
+
+ return 0;
+}
+
+int server_store(const char *path)
+{
+ struct network_server *ns;
+ char filename[PATH_MAX + 1];
+ char addr[18];
+ GSList *l;
+
+ l = g_slist_find_custom(servers, path, find_server);
+ if (!l) {
+ error("Unable to salve %s on storage", path);
+ return -ENOENT;
+ }
+
+ ns = l->data;
+ ba2str(&ns->src, addr);
+ if (ns->id == BNEP_SVC_NAP)
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "nap");
+ else if (ns->id == BNEP_SVC_GN)
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "gn");
+ else
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "panu");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ textfile_put(filename, "name", ns->name);
+
+ if (ns->iface)
+ textfile_put(filename, "routing", ns->iface);
+
+ if (ns->range)
+ textfile_put(filename, "range", ns->range);
+
+ textfile_put(filename, "enabled", ns->enable ? "1": "0");
+
+ return 0;
+}
+
+int server_find_data(const char *path, const char *pattern)
+{
+ struct network_server *ns;
+ const char *uuid;
+ GSList *l;
+
+ l = g_slist_find_custom(servers, path, find_server);
+ if (!l)
+ return -1;
+
+ ns = l->data;
+ if (ns->name && strcasecmp(pattern, ns->name) == 0)
+ return 0;
+
+ if (ns->iface && strcasecmp(pattern, ns->iface) == 0)
+ return 0;
+
+ uuid = bnep_name(ns->id);
+ if (uuid && strcasecmp(pattern, uuid) == 0)
+ return 0;
+
+ if (bnep_service_id(pattern) == ns->id)
+ return 0;
+
+ return -1;
+}
diff --git a/network/server.h b/network/server.h
new file mode 100644
index 00000000..6fb06b58
--- /dev/null
+++ b/network/server.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int server_init(DBusConnection *conn, const char *iface_prefix,
+ gboolean secure);
+void server_exit();
+int server_register(const char *path, bdaddr_t *src, uint16_t id);
+int server_register_from_file(const char *path, const bdaddr_t *src,
+ uint16_t id, const char *filename);
+
+int server_store(const char *path);
+
+int server_find_data(const char *path, const char *pattern);
diff --git a/network/test-network b/network/test-network
new file mode 100755
index 00000000..f428c25f
--- /dev/null
+++ b/network/test-network
@@ -0,0 +1,37 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.bluez', '/org/bluez'),
+ 'org.bluez.Manager')
+
+conn = manager.ActivateService('network')
+
+network = dbus.Interface(bus.get_object(conn, '/org/bluez/network'),
+ 'org.bluez.network.Manager')
+
+try:
+ nap = dbus.Interface(bus.get_object(conn, network.FindServer('nap')),
+ 'org.bluez.network.Server')
+except:
+ pass
+
+try:
+ gn = dbus.Interface(bus.get_object(conn, network.FindServer('gn')),
+ 'org.bluez.network.Server')
+except:
+ pass
+
+try:
+ panu = dbus.Interface(bus.get_object(conn, network.FindServer('panu')),
+ 'org.bluez.network.Server')
+except:
+ pass
+
+try:
+ client = dbus.Interface(bus.get_object(conn, network.LastConnection()),
+ 'org.bluez.network.Connection')
+except:
+ pass