summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2014-06-30 11:26:52 -0500
committerDan Williams <dcbw@redhat.com>2014-06-30 11:26:52 -0500
commit3fdf47697d57ea18e55444bd6098362c2445ac1b (patch)
treef734ca551f34220f5188c9de46181b0d905a1ae5
parent435d2e927c63e1587890d4ba78080def8c8f46b2 (diff)
downloadNetworkManager-dcbw/dun.tar.gz
asdfasdfasdfdcbw/dun
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.c379
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.h34
2 files changed, 260 insertions, 153 deletions
diff --git a/src/devices/bluetooth/nm-bluez5-dun.c b/src/devices/bluetooth/nm-bluez5-dun.c
index 872f20c931..fbb877e662 100644
--- a/src/devices/bluetooth/nm-bluez5-dun.c
+++ b/src/devices/bluetooth/nm-bluez5-dun.c
@@ -34,20 +34,6 @@
#include "nm-logging.h"
#include "NetworkManagerUtils.h"
-void
-nm_bluez5_dun_cleanup (int sk, int rfcomm_id)
-{
- struct rfcomm_dev_req req = { 0 };
-
- g_return_val_if_fail (sk >= 0, FALSE);
-
- if (rfcomm_id >= 0) {
- req.dev_id = rfcomm_id;
- ioctl (sk, RFCOMMRELEASEDEV, &req);
- }
- close (sk);
-}
-
static void
setba (bdaddr_t *dst, const guint8 src[ETH_ALEN])
{
@@ -55,38 +41,141 @@ setba (bdaddr_t *dst, const guint8 src[ETH_ALEN])
register const guint8 *s = (const guint8 *) src;
register int i;
+ /* BT MAC addresses get swapped when sending to the kernel */
memcpy (dst, src, ETH_ALEN);
for (i = 0; i < 6; i++)
d[i] = s[5-i];
}
typedef struct {
- sdp_session_t *session;
- guint watch_id;
- NMBluez5DunFindChannelFunc callback;
- gpointer user_data;
-} SdpInfo;
+ bdaddr_t src[ETH_ALEN];
+ bdaddr_t dst[ETH_ALEN];
+ int rfcomm_channel;
+ int rfcomm_fd;
+ char *rfcomm_dev;
+ int rfcomm_id;
+
+ NMBluez5DunFunc callback;
+ gpointer user_data;
+
+ sdp_session_t *sdp_session;
+ guint sdp_watch_id;
+} NMBluez5DunContext;
static void
-sdp_search_cleanup (SdpInfo *info)
+sdp_search_cleanup (NMBluez5DunContext *context)
{
- sdp_close (info->session);
- if (info->watch_id)
- g_source_remove (info->watch_id);
- g_free (info);
+ if (context->sdp_session) {
+ sdp_close (context->sdp_session);
+ context->sdp_session = NULL;
+ }
+
+ if (context->sdp_watch_id) {
+ g_source_remove (context->sdp_watch_id);
+ context->sdp_watch_id = 0;
+ }
+}
+
+static void
+sdp_search_completed_cb (uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *user_data)
+{
+ NMBluez5DunContext *context = user_data;
+ sdp_list_t *recs = NULL;
+ int scanned, seqlen = 0, bytesleft = size;
+ uint8_t dataType;
+ int err = 0;
+ int channel = -1;
+
+ if (status || type != SDP_SVC_SEARCH_ATTR_RSP) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ scanned = sdp_extract_seqtype (rsp, bytesleft, &dataType, &seqlen);
+ if (!scanned || !seqlen)
+ goto done;
+
+ rsp += scanned;
+ bytesleft -= scanned;
+ do {
+ sdp_record_t *rec;
+ int recsize = 0;
+ sdp_list_t *protos;
+
+ rec = sdp_extract_pdu (rsp, bytesleft, &recsize);
+ if (!rec)
+ break;
+
+ if (!recsize) {
+ sdp_record_free (rec);
+ break;
+ }
+
+ if (sdp_get_access_protos (rec, &protos) == 0) {
+ /* Extract the DUN channel number */
+ channel = sdp_get_proto_port (protos, RFCOMM_UUID);
+ sdp_list_free (protos, NULL);
+ }
+ sdp_record_free (rec);
+
+ scanned += recsize;
+ rsp += recsize;
+ bytesleft -= recsize;
+ } while ((scanned < (ssize_t) size) && (bytesleft > 0) && (channel < 0));
+
+done:
+ cache_sdp_session(&ctxt->src, &ctxt->dst, ctxt->session);
+
+ if (ctxt->cb)
+ ctxt->cb(recs, err, ctxt->user_data);
+
+ if (recs)
+ sdp_list_free(recs, (sdp_free_func_t) sdp_record_free);
+
+ search_context_cleanup(ctxt);
+}
+
+static gboolean
+sdp_search_process_cb (GIOChannel *channel, GIOCondition condition, gpointer user_data)
+{
+ NMBluez5DunContext *context = user_data;
+ int err = 0;
+
+ if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ err = EIO;
+ goto failed;
+ }
+
+ if (sdp_process (context->sdp_session) < 0)
+ goto failed;
+
+ return TRUE;
+
+failed:
+ if (err) {
+ sdp_close(ctxt->session);
+ ctxt->session = NULL;
+
+ if (ctxt->cb)
+ ctxt->cb(NULL, err, ctxt->user_data);
+
+ search_context_cleanup(ctxt);
+ }
+
+ return FALSE;
}
static gboolean
sdp_connect_watch (GIOChannel *channel, GIOCondition condition, gpointer user_data)
{
- SdpInfo *info = user_data;
+ NMBluez5DunContext *context = user_data;
sdp_list_t *search, *attrs;
uuid_t svclass;
uint16_t attr;
int fd, err, fd_err = 0;
socklen_t len = sizeof (fd_err);
- info->watch_id = 0;
+ context->watch_id = 0;
fd = g_io_channel_unix_get_fd (channel);
if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &fd_err, &len) < 0)
@@ -97,7 +186,7 @@ sdp_connect_watch (GIOChannel *channel, GIOCondition condition, gpointer user_da
if (err != 0)
goto failed;
- if (sdp_set_notify (info->session, search_completed_cb, ctxt) < 0) {
+ if (sdp_set_notify (context->sdp_session, sdp_search_completed_cb, ctxt) < 0) {
err = -EIO;
goto failed;
}
@@ -107,12 +196,12 @@ sdp_connect_watch (GIOChannel *channel, GIOCondition condition, gpointer user_da
attr = SDP_ATTR_PROTO_DESC_LIST;
attrs = sdp_list_append (NULL, &attr);
- if (!sdp_service_search_attr_async (info->session, search, SDP_ATTR_REQ_INDIVIDUAL, attrs)) {
+ if (!sdp_service_search_attr_async (context->sdp_session, search, SDP_ATTR_REQ_INDIVIDUAL, attrs)) {
/* Set callback responsible for update the internal SDP transaction */
- info->watch_id = g_io_add_watch (channel,
- G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- sdp_search_process_cb,
- info);
+ context->sdp_watch_id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ sdp_search_process_cb,
+ context);
}
done:
@@ -121,93 +210,25 @@ done:
return G_SOURCE_REMOVE;
}
-gconstpointer
-nm_bluez5_dun_find_channel (const guint8 adapter[ETH_ALEN],
- const guint8 remote[ETH_ALEN],
- NMBluez5DunFindChannelFunc callback,
- gpointer user_data,
- GError **error)
+static gboolean
+dun_find_channel (NMBluez5DunContext *context, GError **error)
{
- SdpInfo *info;
- sdp_session_t *s;
- GError *error = NULL;
- bdaddr_t src, dst;
- GSimpleAsyncResult *simple;
GIOChannel *channel;
- setba (&src, adapter);
- setba (&dst, remote);
- s = sdp_connect (&src, &dst, SDP_NON_BLOCKING);
- if (!s) {
+ context->sdp_session = sdp_connect (&context->src, &context->dst, SDP_NON_BLOCKING);
+ if (!context->sdp_session) {
g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
"(" MAC_FMT "): failed to connect to the SDP server: (%d) %s",
- MAC_ARG (adapter), errno, strerror (errno));
- return NULL;
- }
-
- info = g_slice_new0 (SdpInfo);
- info->session = s;
- info->callback = callback;
- info->user_data = user_data;
- channel = g_io_channel_unix_new (sdp_get_socket (s));
- info->watch_id = g_io_add_watch (channel,
- G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- sdp_connect_watch,
- info);
- g_io_channel_unref (channel);
- return info;
-}
-
-static gboolean
-dun_create_tty (int sk,
- const guint8 adapter[ETH_ALEN],
- const guint8 remote[ETH_ALEN],
- int channel,
- char **out_rfcomm_tty,
- int *out_rfcomm_id,
- GError **error)
-{
- struct stat st;
- int devid, try = 30;
- char tty[100];
- const int ttylen = sizeof (tty) - 1;
-
- struct rfcomm_dev_req req = {
- .flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP),
- .dev_id = -1,
- .channel = channel
- };
-
- g_return_val_if_fail (channel >= 0, FALSE);
- g_return_val_if_fail (out_rfcomm_tty, FALSE);
- g_return_val_if_fail (*out_rfcomm_tty == NULL, FALSE);
- g_return_val_if_fail (out_rfcomm_id, FALSE);
-
- setba (&req.src, adapter);
- setba (&req.dst, remote);
- devid = ioctl (sk, RFCOMMCREATEDEV, &req);
- if (devid < 0) {
- g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "(" MAC_FMT "): failed to create rfcomm device: (%d) %s",
- MAC_ARG (adapter), errno, strerror (errno));
- return FALSE;
- }
-
- snprintf (tty, ttylen, "/dev/rfcomm%d", devid);
- while (stat (tty, &st) < 0 && try--) {
- snprintf (tty, ttylen, "/dev/rfcomm%d", devid);
- if (try--) {
- g_usleep (100 * 1000);
- continue;
- }
-
- g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "(" MAC_FMT "): failed to find rfcomm device",
- MAC_ARG (adapter));
+ MAC_ARG (context->src), errno, strerror (errno));
return FALSE;
}
- *out_rfcomm_tty = g_strdup (tty);
+ channel = g_io_channel_unix_new (sdp_get_socket (context->sdp_session));
+ context->sdp_watch_id = g_io_add_watch (channel,
+ G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ sdp_connect_watch,
+ context);
+ g_io_channel_unref (channel);
return TRUE;
}
@@ -238,55 +259,141 @@ dun_create_tty (int sk,
return MAX (0, ch);
}
-gboolean
-nm_bluez5_dun_connect (const guint8 adapter[ETH_ALEN],
- const guint8 remote[ETH_ALEN],
- int rfcomm_channel,
- int *out_rfcomm_fd,
- char **out_rfcomm_dev,
- int *out_rfcomm_id,
- GError **error)
+static gboolean
+dun_connect (NMBluez5DunContext *context, GError **error)
{
struct sockaddr_rc sa;
- int sk;
+ struct stat st;
+ int devid, try = 30;
+ char tty[100];
+ const int ttylen = sizeof (tty) - 1;
- g_return_val_if_fail (rfcomm_channel >= 0), FALSE);
+ struct rfcomm_dev_req req = {
+ .flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP),
+ .dev_id = -1,
+ .channel = context->rfcomm_channel
+ };
- sk = socket (AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
- if (sk < 0) {
+ context->rfcomm_fd = socket (AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (context->rfcomm_fd < 0) {
g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
"Failed to create RFCOMM socket: (%d) %s",
errno, strerror (errno));
return FALSE;
}
+ /* Connect to the remote device */
sa.rc_family = AF_BLUETOOTH;
sa.rc_channel = 0;
- setba (&sa.rc_bdaddr, adapter);
- if (bind (sk, (struct sockaddr *) &sa, sizeof(sa))) {
+ memcpy (&sa.rc_bdaddr, context->src, ETH_ALEN);
+ if (bind (context->rfcomm_fd, (struct sockaddr *) &sa, sizeof(sa))) {
nm_log_dbg (LOGD_BT, "(" MAC_FMT "): failed to bind socket: (%d) %s",
- MAC_ARG (adapter), errno, strerror (errno));
+ MAC_ARG (context->src), errno, strerror (errno));
}
- sa.rc_channel = rfcomm_channel;
- setba (&sa.rc_bdaddr, remote);
+ sa.rc_channel = context->rfcomm_channel;
+ memcpy (&sa.rc_bdaddr, context->dst, ETH_ALEN);
if (connect (sk, (struct sockaddr *) &sa, sizeof (sa)) ) {
g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
"Failed to connect to remote device: (%d) %s",
errno, strerror (errno));
- goto error;
+ return FALSE;
}
nm_log_dbg (LOGD_BT, "(" MAC_FMT "): connected to " MAC_FMT "on channel %d",
- MAC_ARG (adapter), MAC_ARG (remote), ch);
+ MAC_ARG (context->src), MAC_ARG (remote), ch);
- if (!dun_create_tty (sk, adapter, remote, rfcomm_channel, out_rfcomm_dev, out_rfcomm_id, error))
- goto error;
+ /* Create an RFCOMM kernel device for the DUN channel */
+ memcpy (&req.src, context->src, ETH_ALEN);
+ memcpy (&req.dst, context->dst, ETH_ALEN);
+ devid = ioctl (sk, RFCOMMCREATEDEV, &req);
+ if (devid < 0) {
+ g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "(" MAC_FMT "): failed to create rfcomm device: (%d) %s",
+ MAC_ARG (context->src), errno, strerror (errno));
+ return FALSE;
+ }
+ context->rfcomm_id = devid;
- *out_rfcomm_fd = sk;
+ snprintf (tty, ttylen, "/dev/rfcomm%d", devid);
+ while (stat (tty, &st) < 0 && try--) {
+ snprintf (tty, ttylen, "/dev/rfcomm%d", devid);
+ if (try--) {
+ g_usleep (100 * 1000);
+ continue;
+ }
+
+ g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "(" MAC_FMT "): failed to find rfcomm device",
+ MAC_ARG (context->src));
+ return FALSE;
+ }
+
+ context->rfcomm_dev = g_strdup (tty);
return TRUE;
+}
-error:
- close (sk);
- return FALSE;
+NMBluez5DunContext *
+nm_bluez5_dun_new (const guint8 adapter[ETH_ALEN],
+ const guint8 remote[ETH_ALEN],
+ int rfcomm_channel,
+ NMBluez5DunFunc callback,
+ gpointer user_data,
+ GError **error)
+{
+ NMBluez5DunContext *context;
+
+ context = g_slice_new0 (NMBluez5DunContext);
+ setba (context->src, adapter);
+ setba (context->dest, remote);
+ context->channel = (rfcomm_channel >= 0) ? rfcomm_channel : -1;
+ context->rfcomm_id = -1;
+ context->rfcomm_fd = -1;
+ context->callback = callback;
+ context->user_data = user_data;
+ return context;
+}
+
+gboolean
+nm_bluez5_dun_connect (NMBluez5DunContext *context, GError **error)
+{
+ /* Find channel */
+ if (context->rfcomm_channel < 0)
+ return dun_find_channel (context, error);
+
+ /* Connect */
+ return dun_connect (context, error);
}
+
+/* Only clean up connection-related stuff to allow reconnect */
+void
+nm_bluez5_dun_cleanup (NMBluez5DunContext *context)
+{
+ g_return_if_fail (context != NULL);
+
+ sdp_search_cleanuP (context);
+
+ g_clear_pointer (&context->rfcomm_dev, g_free);
+
+ if (context->rfcomm_fd >= 0) {
+ if (context->rfcomm_id >= 0) {
+ struct rfcomm_dev_req req = { 0 };
+
+ req.dev_id = rfcomm_id;
+ ioctl (context->rfcomm_fd, RFCOMMRELEASEDEV, &req);
+ context->rfcomm_id = -1;
+ }
+ close (context->rfcomm_fd);
+ context->rfcomm_fd = -1;
+ }
+}
+
+void
+nm_bluez5_dun_free (NMBluez5DunContext *context)
+{
+ g_return_if_fail (context != NULL);
+
+ nm_bluez5_dun_cleanup (context);
+ g_slice_free (NMBluez5DunContext, context);
+}
+
diff --git a/src/devices/bluetooth/nm-bluez5-dun.h b/src/devices/bluetooth/nm-bluez5-dun.h
index f8c8ed7e2c..6bee6caf0b 100644
--- a/src/devices/bluetooth/nm-bluez5-dun.h
+++ b/src/devices/bluetooth/nm-bluez5-dun.h
@@ -25,26 +25,26 @@
typedef struct NMBluez5DunContext;
-typedef void (*NMBluez5DunFindChannelFunc) (NMBluez5DunContext ctx,
- int rfcomm_channel,
- GError *error,
- gpointer user_data);
+typedef void (*NMBluez5DunFunc) (NMBluez5DunContext *context,
+ int rfcomm_channel,
+ const char *rfcomm_dev,
+ GError *error,
+ gpointer user_data);
-NMBluez5DunContext *nm_bluez5_dun_find_channel (const guint8 adapter[ETH_ALEN],
- const guint8 remote[ETH_ALEN],
- NMBluez5DunFindChannelFunc callback,
- gpointer user_data);
+NMBluez5DunContext *nm_bluez5_dun_new (const guint8 adapter[ETH_ALEN],
+ const guint8 remote[ETH_ALEN],
+ int rfcomm_channel,
+ NMBluez5DunFunc callback,
+ gpointer user_data,
+ GError **error);
-void nm_bluez5_dun_find_channel_cancel (gconstpointer id);
+gboolean nm_bluez5_dun_connect (NMBluez5DunContext *context, GError **error);
-gboolean nm_bluez5_dun_connect (const guint8 adapter[ETH_ALEN],
- const guint8 remote[ETH_ALEN],
- int rfcomm_channel,
- int *out_rfcomm_fd,
- char **out_rfcomm_dev,
- int *out_rfcomm_id,
- GError **error);
+/* Clean up connection resources */
+void nm_bluez5_dun_cleanup (NMBluez5DunContext *context);
-void nm_bluez5_dun_cleanup (int sk, int rfcomm_id);
+/* Clean up and dispose all resources */
+void nm_bluez5_dun_free (NMBluez5DunContext *context);
#endif /* _NM_BLUEZ5_UTILS_H_ */
+