diff options
Diffstat (limited to 'hcid/dbus-sdp.c')
-rw-r--r-- | hcid/dbus-sdp.c | 1125 |
1 files changed, 0 insertions, 1125 deletions
diff --git a/hcid/dbus-sdp.c b/hcid/dbus-sdp.c deleted file mode 100644 index 02f9e31c..00000000 --- a/hcid/dbus-sdp.c +++ /dev/null @@ -1,1125 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2007 Nokia Corporation - * 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 <fcntl.h> -#include <unistd.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/param.h> -#include <sys/socket.h> - -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/l2cap.h> -#include <bluetooth/sdp.h> -#include <bluetooth/sdp_lib.h> - -#include <netinet/in.h> - -#include <glib.h> -#include <dbus/dbus.h> -#include <gdbus.h> - -#include "hcid.h" -#include "textfile.h" -#include "adapter.h" -#include "dbus-hci.h" -#include "dbus-common.h" -#include "dbus-error.h" -#include "error.h" -#include "dbus-sdp.h" -#include "sdp-xml.h" -#include "glib-helper.h" - -#define SESSION_TIMEOUT 2000 -#define DEFAULT_XML_BUF_SIZE 1024 - -struct transaction_context { - char *src; - char *dst; - DBusConnection *conn; - DBusMessage *rq; - sdp_session_t *session; - GIOChannel *io; - guint io_id; - uuid_t uuid; - GSList *identifiers; -}; - -typedef int connect_cb_t(struct transaction_context *t); - -struct pending_connect { - DBusConnection *conn; - DBusMessage *rq; - char *src; - char *dst; - sdp_session_t *session; - connect_cb_t *conn_cb; -}; - -struct cached_session { - sdp_session_t *session; - guint timeout_id; - guint io_id; -}; - -static GSList *cached_sessions = NULL; - -static inline DBusMessage *invalid_args(DBusMessage *msg) -{ - return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments", - "Invalid arguments in method call"); -} - -static inline DBusMessage *in_progress(DBusMessage *msg, const char *str) -{ - return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress", str); -} - -static inline DBusMessage *adapter_not_ready(DBusMessage *msg) -{ - return g_dbus_create_error(msg, ERROR_INTERFACE ".NotReady", - "Adapter is not ready"); -} - -static inline DBusMessage *failed_strerror(DBusMessage *msg, int err) -{ - return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", - strerror(err)); -} - -static gboolean session_timeout(gpointer user_data) -{ - struct cached_session *s = user_data; - - debug("sdp session timed out. closing"); - - cached_sessions = g_slist_remove(cached_sessions, s); - - g_source_remove(s->io_id); - sdp_close(s->session); - g_free(s); - - return FALSE; -} - -gboolean idle_callback(GIOChannel *io, GIOCondition cond, gpointer user_data) -{ - struct cached_session *s = user_data; - - if (cond & G_IO_NVAL) - return FALSE; - - if (cond & (G_IO_ERR | G_IO_HUP)) - debug("idle_callback: session got disconnected"); - - if (cond & G_IO_IN) - debug("got unexpected input on idle SDP socket"); - - cached_sessions = g_slist_remove(cached_sessions, s); - - g_source_remove(s->timeout_id); - sdp_close(s->session); - g_free(s); - - return FALSE; -} - -static void cache_sdp_session(sdp_session_t *sess, GIOChannel *io) -{ - struct cached_session *s; - - s = g_new0(struct cached_session, 1); - - s->session = sess; - s->timeout_id = g_timeout_add(SESSION_TIMEOUT, session_timeout, s); - s->io_id = g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - idle_callback, s); - - cached_sessions = g_slist_append(cached_sessions, s); - - debug("sdp session added to cache"); -} - -static int get_bdaddrs(int sock, bdaddr_t *sba, bdaddr_t *dba) -{ - struct sockaddr_l2 a; - socklen_t len; - - len = sizeof(a); - if (getsockname(sock, (struct sockaddr *) &a, &len) < 0) { - error("getsockname: %s (%d)", strerror(errno), errno); - return -1; - } - - bacpy(sba, &a.l2_bdaddr); - - len = sizeof(a); - if (getpeername(sock, (struct sockaddr *) &a, &len) < 0) { - error("getpeername: %s (%d)", strerror(errno), errno); - return -1; - } - - bacpy(dba, &a.l2_bdaddr); - - return 0; -} - -static struct cached_session *get_cached_session(bdaddr_t *src, bdaddr_t *dst) -{ - GSList *l; - - for (l = cached_sessions; l != NULL; l = l->next) { - struct cached_session *s = l->data; - int sock = sdp_get_socket(s->session); - bdaddr_t sba, dba; - - if (get_bdaddrs(sock, &sba, &dba) < 0) - continue; - - if (bacmp(&sba, src) || bacmp(&dba, dst)) - continue; - - debug("found matching session, removing from list"); - - cached_sessions = g_slist_remove(cached_sessions, s); - - return s; - } - - return NULL; -} - -static sdp_session_t *get_sdp_session(bdaddr_t *src, bdaddr_t *dst) -{ - struct cached_session *s; - sdp_session_t *session; - - s = get_cached_session(src, dst); - if (!s) { - debug("no matching session found. creating a new one"); - return sdp_connect(src, dst, SDP_NON_BLOCKING); - } - - session = s->session; - - g_source_remove(s->timeout_id); - g_source_remove(s->io_id); - g_free(s); - - return session; -} - -void append_and_grow_string(void *data, const char *str) -{ - sdp_buf_t *buff = data; - int len; - - len = strlen(str); - - if (!buff->data) { - buff->data = malloc(DEFAULT_XML_BUF_SIZE); - if (!buff->data) - return; - buff->buf_size = DEFAULT_XML_BUF_SIZE; - } - - /* Grow string */ - while (buff->buf_size < (buff->data_size + len + 1)) { - void *tmp; - uint32_t new_size; - - /* Grow buffer by a factor of 2 */ - new_size = (buff->buf_size << 1); - - tmp = realloc(buff->data, new_size); - if (!tmp) - return; - - buff->data = tmp; - buff->buf_size = new_size; - } - - /* Include the NULL character */ - memcpy(buff->data + buff->data_size, str, len + 1); - buff->data_size += len; -} - -/* list of remote and local service records */ -static GSList *pending_connects = NULL; - -static struct pending_connect *pending_connect_new(DBusConnection *conn, - DBusMessage *msg, const char *src, - const char *dst, connect_cb_t *cb) -{ - struct pending_connect *c; - - if (!dst) - return NULL; - - c = g_new0(struct pending_connect, 1); - c->src = g_strdup(src); - c->dst = g_strdup(dst); - c->conn = dbus_connection_ref(conn); - c->rq = dbus_message_ref(msg); - c->conn_cb = cb; - - return c; -} - -static void pending_connect_free(struct pending_connect *c) -{ - if (!c) - return; - - g_free(c->src); - g_free(c->dst); - - if (c->rq) - dbus_message_unref(c->rq); - - if (c->conn) - dbus_connection_unref(c->conn); - - g_free(c); -} - -static struct pending_connect *find_pending_connect(const char *dst) -{ - GSList *l; - - for (l = pending_connects; l != NULL; l = l->next) { - struct pending_connect *pending = l->data; - if (!strcmp(dst, pending->dst)) - return pending; - } - - return NULL; -} - -static int sdp_store_record(const char *src, const char *dst, uint32_t handle, uint8_t *buf, size_t size) -{ - char filename[PATH_MAX + 1], key[28], *value; - int i, err; - - create_name(filename, PATH_MAX, STORAGEDIR, src, "sdp"); - - create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - - snprintf(key, sizeof(key), "%17s#%08X", dst, handle); - - value = g_malloc0(size * 2 + 1); - - for (i = 0; i < size; i++) - sprintf(value + (i * 2), "%02X", buf[i]); - - err = textfile_put(filename, key, value); - - g_free(value); - - return err; -} - -static void transaction_context_free(void *udata, gboolean cache) -{ - struct transaction_context *ctxt = udata; - - if (!ctxt) - return; - - g_free(ctxt->src); - g_free(ctxt->dst); - - if (ctxt->conn) - dbus_connection_unref(ctxt->conn); - - if (ctxt->rq) - dbus_message_unref(ctxt->rq); - - if (ctxt->session && !ctxt->io) - sdp_close(ctxt->session); - - if (ctxt->session && ctxt->io) { - g_source_remove(ctxt->io_id); - - if (cache) - cache_sdp_session(ctxt->session, ctxt->io); - else - sdp_close(ctxt->session); - - g_io_channel_unref(ctxt->io); - } - - if (ctxt->identifiers) { - g_slist_foreach(ctxt->identifiers, (GFunc) g_free, NULL); - g_slist_free(ctxt->identifiers); - } - - g_free(ctxt); -} - -static gboolean search_process_cb(GIOChannel *chan, - GIOCondition cond, void *udata) -{ - struct transaction_context *ctxt = udata; - int err = 0; - - if (cond & G_IO_NVAL) { - g_io_channel_unref(chan); - return FALSE; - } - - if (cond & (G_IO_ERR | G_IO_HUP)) { - err = EIO; - goto failed; - } - - if (sdp_process(ctxt->session) < 0) - goto failed; - - return TRUE; - -failed: - if (err) { - error_failed_errno(ctxt->conn, ctxt->rq, err); - transaction_context_free(ctxt, FALSE); - } - - return TRUE; -} - -static void remote_svc_rec_completed_cb(uint8_t type, uint16_t err, - uint8_t *rsp, size_t size, void *udata) -{ - struct transaction_context *ctxt = udata; - sdp_record_t *rec; - DBusMessage *reply; - DBusMessageIter iter, array_iter; - int scanned; - - if (!ctxt) - return; - - if (err == 0xffff) { - /* Check for protocol error or I/O error */ - int sdp_err = sdp_get_error(ctxt->session); - if (sdp_err < 0) { - error("search failed: Invalid session!"); - error_failed_errno(ctxt->conn, ctxt->rq, EINVAL); - goto failed; - } - - error("search failed: %s (%d)", strerror(sdp_err), sdp_err); - error_failed_errno(ctxt->conn, ctxt->rq, sdp_err); - goto failed; - } - - if (type == SDP_ERROR_RSP) { - error_sdp_failed(ctxt->conn, ctxt->rq, err); - goto failed; - } - - /* check response PDU ID */ - if (type != SDP_SVC_ATTR_RSP) { - error("SDP error: %s (%d)", strerror(EPROTO), EPROTO); - error_failed_errno(ctxt->conn, ctxt->rq, EPROTO); - goto failed; - } - - reply = dbus_message_new_method_return(ctxt->rq); - dbus_message_iter_init_append(reply, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_BYTE_AS_STRING, &array_iter); - - rec = sdp_extract_pdu_safe(rsp, size, &scanned); - if (rec == NULL || size != scanned) { - error("Invalid service record!"); - goto done; - } - - sdp_store_record(ctxt->src, ctxt->dst, rec->handle, rsp, size); - - sdp_record_free(rec); - - dbus_message_iter_append_fixed_array(&array_iter, - DBUS_TYPE_BYTE, &rsp, size); - -done: - dbus_message_iter_close_container(&iter, &array_iter); - dbus_connection_send(ctxt->conn, reply, NULL); - dbus_message_unref(reply); - -failed: - transaction_context_free(ctxt, TRUE); -} - -static void remote_svc_rec_completed_xml_cb(uint8_t type, uint16_t err, - uint8_t *rsp, size_t size, - void *udata) -{ - struct transaction_context *ctxt = udata; - sdp_record_t *rec; - DBusMessage *reply; - int scanned; - sdp_buf_t result; - - if (!ctxt) - return; - - if (err == 0xffff) { - /* Check for protocol error or I/O error */ - int sdp_err = sdp_get_error(ctxt->session); - if (sdp_err < 0) { - error("search failed: Invalid session!"); - error_failed_errno(ctxt->conn, ctxt->rq, EINVAL); - goto failed; - } - - error("search failed: %s (%d)", strerror(sdp_err), sdp_err); - error_failed_errno(ctxt->conn, ctxt->rq, sdp_err); - goto failed; - } - - if (type == SDP_ERROR_RSP) { - error_sdp_failed(ctxt->conn, ctxt->rq, err); - goto failed; - } - - /* check response PDU ID */ - if (type != SDP_SVC_ATTR_RSP) { - error("SDP error: %s (%d)", strerror(EPROTO), EPROTO); - error_failed_errno(ctxt->conn, ctxt->rq, EPROTO); - goto failed; - } - - reply = dbus_message_new_method_return(ctxt->rq); - - rec = sdp_extract_pdu_safe(rsp, size, &scanned); - if (rec == NULL || size != scanned) { - error("Invalid service record!"); - goto done; - } - - sdp_store_record(ctxt->src, ctxt->dst, rec->handle, rsp, size); - - memset(&result, 0, sizeof(sdp_buf_t)); - - convert_sdp_record_to_xml(rec, &result, append_and_grow_string); - - sdp_record_free(rec); - - if (result.data) { - dbus_message_append_args(reply, - DBUS_TYPE_STRING, &result.data, - DBUS_TYPE_INVALID); - - free(result.data); - } -done: - dbus_connection_send(ctxt->conn, reply, NULL); - dbus_message_unref(reply); - -failed: - transaction_context_free(ctxt, TRUE); -} - -static void remote_svc_handles_completed_cb(uint8_t type, uint16_t err, - uint8_t *rsp, size_t size, void *udata) -{ - struct transaction_context *ctxt = udata; - DBusMessage *reply; - DBusMessageIter iter, array_iter; - uint8_t *pdata; - int csrc, tsrc; - - if (!ctxt) - return; - - if (err == 0xffff) { - /* Check for protocol error or I/O error */ - int sdp_err = sdp_get_error(ctxt->session); - if (sdp_err < 0) { - error("search failed: Invalid session!"); - error_failed_errno(ctxt->conn, ctxt->rq, EINVAL); - goto failed; - } - - error("search failed: %s (%d)", strerror(sdp_err), sdp_err); - error_failed_errno(ctxt->conn, ctxt->rq, sdp_err); - goto failed; - } - - if (type == SDP_ERROR_RSP) { - error_sdp_failed(ctxt->conn, ctxt->rq, err); - goto failed; - } - - /* check response PDU ID */ - if (type != SDP_SVC_SEARCH_RSP) { - error("SDP error: %s (%d)", strerror(EPROTO), EPROTO); - error_failed_errno(ctxt->conn, ctxt->rq, EPROTO); - goto failed; - } - - reply = dbus_message_new_method_return(ctxt->rq); - dbus_message_iter_init_append(reply, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_UINT32_AS_STRING, &array_iter); - - pdata = rsp; - - tsrc = ntohs(bt_get_unaligned((uint16_t *) pdata)); - if (tsrc <= 0) - goto done; - - pdata += sizeof(uint16_t); - - csrc = ntohs(bt_get_unaligned((uint16_t *) pdata)); - if (csrc <= 0) - goto done; - - pdata += sizeof(uint16_t); - - do { - uint32_t handle = ntohl(bt_get_unaligned((uint32_t*)pdata)); - pdata += sizeof(uint32_t); - - dbus_message_iter_append_basic(&array_iter, - DBUS_TYPE_UINT32, &handle); - } while (--tsrc); - - -done: - dbus_message_iter_close_container(&iter, &array_iter); - dbus_connection_send(ctxt->conn, reply, NULL); - dbus_message_unref(reply); - -failed: - transaction_context_free(ctxt, TRUE); -} - -static const char *extract_service_class(sdp_data_t *d) -{ - sdp_data_t *seq; - uuid_t *uuid; - static char uuid_str[37]; - - /* Expected sequence of UUID16 */ - if (d->attrId != SDP_ATTR_SVCLASS_ID_LIST) - return NULL; - - if (d->dtd != SDP_SEQ8 && d->dtd != SDP_SEQ16 && d->dtd != SDP_SEQ32) - return NULL; - - if (!d->val.dataseq) - return NULL; - - seq = d->val.dataseq; - if (!SDP_IS_UUID(seq->dtd)) - return NULL; - - uuid = &seq->val.uuid; - if (uuid->type != SDP_UUID16) - return NULL; - - sprintf(uuid_str, "0000%04x-0000-1000-8000-00805f9b34fb", - uuid->value.uuid16); - - return uuid_str; -} - -static int service_search_attr(struct transaction_context *ctxt, uint16_t uuid) -{ - sdp_list_t *attrids, *search; - uint32_t range = 0x0000ffff; - int ret = 0; - - sdp_uuid16_create(&ctxt->uuid, uuid); - - search = sdp_list_append(0, &ctxt->uuid); - attrids = sdp_list_append(NULL, &range); - - /* - * Create/send the search request and set the - * callback to indicate the request completion - */ - if (sdp_service_search_attr_async(ctxt->session, search, - SDP_ATTR_REQ_RANGE, attrids) < 0) - ret = -sdp_get_error(ctxt->session); - - sdp_list_free(search, NULL); - sdp_list_free(attrids, NULL); - - return ret; -} - -static void remote_svc_identifiers_completed_cb(uint8_t type, uint16_t err, - uint8_t *rsp, size_t size, void *udata) -{ - struct transaction_context *ctxt = udata; - const char *puuid; - const char *devid_uuid = "00001200-0000-1000-8000-00805f9b34fb"; - char **identifiers; - DBusMessage *reply; - GSList *l = NULL; - int scanned, extracted = 0, len = 0, recsize = 0, bytesleft = size; - uint8_t dtd = 0; - - if (!ctxt) - return; - - if (err == 0xffff) { - /* Check for protocol error or I/O error */ - int sdp_err = sdp_get_error(ctxt->session); - if (sdp_err < 0) { - error("search failed: Invalid session!"); - error_failed_errno(ctxt->conn, ctxt->rq, EINVAL); - goto failed; - } - - error("search failed: %s (%d)", strerror(sdp_err), sdp_err); - error_failed_errno(ctxt->conn, ctxt->rq, sdp_err); - goto failed; - } - - if (type == SDP_ERROR_RSP) { - error_sdp_failed(ctxt->conn, ctxt->rq, err); - goto failed; - } - - /* Check response PDU ID */ - if (type != SDP_SVC_SEARCH_ATTR_RSP) { - error("SDP error: %s (%d)", strerror(EPROTO), EPROTO); - error_failed_errno(ctxt->conn, ctxt->rq, EPROTO); - goto failed; - } - - scanned = sdp_extract_seqtype_safe(rsp, bytesleft, &dtd, &len); - rsp += scanned; - bytesleft -= scanned; - for (; extracted < len; rsp += recsize, extracted += recsize, bytesleft -= recsize) { - sdp_record_t *rec; - sdp_data_t *d; - - recsize = 0; - rec = sdp_extract_pdu_safe(rsp, bytesleft, &recsize); - if (!rec) - break; - - sdp_store_record(ctxt->src, ctxt->dst, rec->handle, rsp, recsize); - - d = sdp_data_get(rec, SDP_ATTR_SVCLASS_ID_LIST); - if (!d) { - sdp_record_free(rec); - continue; - } - - puuid = extract_service_class(d); - sdp_record_free(rec); - if (!puuid) - continue; - - /* Ignore repeated identifiers */ - l = g_slist_find_custom(ctxt->identifiers, - puuid, (GCompareFunc) strcmp); - if (l) - continue; - - ctxt->identifiers = g_slist_append(ctxt->identifiers, - g_strdup(puuid)); - } - - /* If public browse response is empty: search for L2CAP */ - if (!ctxt->identifiers && ctxt->uuid.value.uuid16 == PUBLIC_BROWSE_GROUP) - if (service_search_attr(ctxt, L2CAP_UUID) == 0) - return; /* Wait the response */ - - /* Request DeviceID if it was not returned previously */ - l = g_slist_find_custom(ctxt->identifiers, - devid_uuid, (GCompareFunc) strcmp); - if (!l && ctxt->uuid.value.uuid16 != PNP_INFO_SVCLASS_ID) - if (service_search_attr(ctxt, PNP_INFO_SVCLASS_ID) == 0) - return; /* Wait the response */ - - reply = dbus_message_new_method_return(ctxt->rq); - - identifiers = g_new(char *, g_slist_length(ctxt->identifiers)); - - for (l = ctxt->identifiers, len = 0; l; l = l->next, len++) - identifiers[len] = l->data; - - dbus_message_append_args(reply, - DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, - &identifiers, len, - DBUS_TYPE_INVALID); - - dbus_connection_send(ctxt->conn, reply, NULL); - dbus_message_unref(reply); - - if (len) - g_dbus_emit_signal(ctxt->conn, - dbus_message_get_path(ctxt->rq), - ADAPTER_INTERFACE, - "RemoteIdentifiersUpdated", - DBUS_TYPE_STRING, &ctxt->dst, - DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, - &identifiers, len, - DBUS_TYPE_INVALID); - - if (identifiers) - g_free(identifiers); - -failed: - transaction_context_free(ctxt, TRUE); -} - -static gboolean sdp_client_connect_cb(GIOChannel *chan, - GIOCondition cond, void *udata) -{ - struct pending_connect *c = udata; - struct transaction_context *ctxt = NULL; - int sdp_err, err = 0, sk; - socklen_t len; - - sk = g_io_channel_unix_get_fd(chan); - - len = sizeof(err); - if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { - error("getsockopt(): %s (%d)", strerror(errno), errno); - err = errno; - goto failed; - } - if (err != 0) { - error("connect(): %s (%d)", strerror(err), err); - goto failed; - } - - ctxt = g_new0(struct transaction_context, 1); - ctxt->src = g_strdup(c->src); - ctxt->dst = g_strdup(c->dst); - ctxt->conn = dbus_connection_ref(c->conn); - ctxt->rq = dbus_message_ref(c->rq); - ctxt->session = c->session; - - /* set the complete transaction callback and send the search request */ - sdp_err = c->conn_cb(ctxt); - if (sdp_err < 0) { - err = -sdp_err; - error("search failed: %s (%d)", strerror(err), err); - goto failed; - } - - /* set the callback responsible for update the transaction data */ - ctxt->io_id = g_io_add_watch(chan, - G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - search_process_cb, ctxt); - ctxt->io = g_io_channel_ref(chan); - - goto done; - -failed: - error_connection_attempt_failed(c->conn, c->rq, err); - - if (ctxt) - transaction_context_free(ctxt, FALSE); - else - sdp_close(c->session); - -done: - pending_connects = g_slist_remove(pending_connects, c); - pending_connect_free(c); - - return FALSE; -} - -static struct pending_connect *connect_request(DBusConnection *conn, - DBusMessage *msg, - const char *src, - const char *dst, - connect_cb_t *cb, int *err) -{ - struct pending_connect *c; - bdaddr_t srcba, dstba; - GIOChannel *chan; - - c = pending_connect_new(conn, msg, src, dst, cb); - if (!c) { - if (err) - *err = ENOMEM; - return NULL; - } - - str2ba(src, &srcba); - str2ba(dst, &dstba); - c->session = get_sdp_session(&srcba, &dstba); - if (!c->session) { - if (err) - *err = errno; - error("sdp_connect() failed: %s (%d)", strerror(errno), errno); - pending_connect_free(c); - return NULL; - } - - chan = g_io_channel_unix_new(sdp_get_socket(c->session)); - g_io_add_watch(chan, G_IO_OUT, sdp_client_connect_cb, c); - g_io_channel_unref(chan); - pending_connects = g_slist_append(pending_connects, c); - - return c; -} - -static int remote_svc_rec_conn_cb(struct transaction_context *ctxt) -{ - sdp_list_t *attrids; - uint32_t range = 0x0000ffff; - const char *dst; - uint32_t handle; - - if (sdp_set_notify(ctxt->session, remote_svc_rec_completed_cb, ctxt) < 0) - return -EINVAL; - - dbus_message_get_args(ctxt->rq, NULL, - DBUS_TYPE_STRING, &dst, - DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_INVALID); - - attrids = sdp_list_append(NULL, &range); - /* - * Create/send the search request and set the - * callback to indicate the request completion - */ - if (sdp_service_attr_async(ctxt->session, handle, - SDP_ATTR_REQ_RANGE, attrids) < 0) { - sdp_list_free(attrids, NULL); - return -sdp_get_error(ctxt->session); - } - - sdp_list_free(attrids, NULL); - - return 0; -} - -static int remote_svc_rec_conn_xml_cb(struct transaction_context *ctxt) -{ - sdp_list_t *attrids; - uint32_t range = 0x0000ffff; - const char *dst; - uint32_t handle; - - if (sdp_set_notify(ctxt->session, remote_svc_rec_completed_xml_cb, ctxt) < 0) - return -EINVAL; - - dbus_message_get_args(ctxt->rq, NULL, - DBUS_TYPE_STRING, &dst, - DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_INVALID); - - attrids = sdp_list_append(NULL, &range); - /* - * Create/send the search request and set the - * callback to indicate the request completion - */ - if (sdp_service_attr_async(ctxt->session, handle, - SDP_ATTR_REQ_RANGE, attrids) < 0) { - sdp_list_free(attrids, NULL); - return -sdp_get_error(ctxt->session); - } - - sdp_list_free(attrids, NULL); - - return 0; -} - -DBusMessage *get_remote_svc_rec(DBusConnection *conn, DBusMessage *msg, - void *data, sdp_format_t format) -{ - struct adapter *adapter = data; - const char *dst; - uint32_t handle; - int err; - connect_cb_t *cb; - - if (!adapter->up) - return adapter_not_ready(msg); - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &dst, - DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_INVALID)) - return invalid_args(msg); - - if (find_pending_connect(dst)) - return in_progress(msg, "Service search in progress"); - - cb = remote_svc_rec_conn_cb; - if (format == SDP_FORMAT_XML) - cb = remote_svc_rec_conn_xml_cb; - - if (!connect_request(conn, msg, adapter->address, - dst, cb, &err)) { - error("Search request failed: %s (%d)", strerror(err), err); - return failed_strerror(msg, err); - } - - return NULL; -} - -static int remote_svc_handles_conn_cb(struct transaction_context *ctxt) -{ - sdp_list_t *search = NULL; - const char *dst, *svc; - - if (sdp_set_notify(ctxt->session, remote_svc_handles_completed_cb, ctxt) < 0) - return -EINVAL; - - dbus_message_get_args(ctxt->rq, NULL, - DBUS_TYPE_STRING, &dst, - DBUS_TYPE_STRING, &svc, - DBUS_TYPE_INVALID); - - if (strlen(svc) > 0) - bt_string2uuid(&ctxt->uuid, svc); - else - sdp_uuid16_create(&ctxt->uuid, PUBLIC_BROWSE_GROUP); - - search = sdp_list_append(0, &ctxt->uuid); - - /* Create/send the search request and set the callback to indicate the request completion */ - if (sdp_service_search_async(ctxt->session, search, 64) < 0) { - error("send request failed: %s (%d)", strerror(errno), errno); - sdp_list_free(search, NULL); - return -sdp_get_error(ctxt->session); - } - - sdp_list_free(search, NULL); - - return 0; -} - -static int remote_svc_identifiers_conn_cb(struct transaction_context *ctxt) -{ - if (sdp_set_notify(ctxt->session, - remote_svc_identifiers_completed_cb, ctxt) < 0) - return -EINVAL; - - return service_search_attr(ctxt, PUBLIC_BROWSE_GROUP); -} - -DBusMessage *get_remote_svc_handles(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct adapter *adapter = data; - const char *dst, *svc; - int err; - uuid_t uuid; - - if (!adapter->up) - return adapter_not_ready(msg); - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &dst, - DBUS_TYPE_STRING, &svc, - DBUS_TYPE_INVALID)) - return invalid_args(msg); - - if (strlen(svc) > 0) { - /* Check if it is a service name string */ - if (bt_string2uuid(&uuid, svc) < 0) { - error("Invalid service class name"); - return invalid_args(msg); - } - } - - if (find_pending_connect(dst)) - return in_progress(msg, "Service search in progress"); - - if (!connect_request(conn, msg, adapter->address, - dst, remote_svc_handles_conn_cb, &err)) { - error("Search request failed: %s (%d)", strerror(err), err); - return failed_strerror(msg, err); - } - - return NULL; -} - -DBusMessage *get_remote_svc_identifiers(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct adapter *adapter = data; - const char *dst; - int err; - - if (!adapter->up) - return adapter_not_ready(msg); - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &dst, - DBUS_TYPE_INVALID)) - return invalid_args(msg); - - if (find_pending_connect(dst)) - return in_progress(msg, "Service search in progress"); - - if (!connect_request(conn, msg, adapter->address, - dst, remote_svc_identifiers_conn_cb, &err)) { - error("Search request failed: %s (%d)", strerror(err), err); - return failed_strerror(msg, err); - } - - return NULL; -} - -DBusMessage *finish_remote_svc_transact(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct cached_session *s; - const char *address; - struct adapter *adapter = data; - bdaddr_t sba, dba; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID)) - return invalid_args(msg); - - str2ba(adapter->address, &sba); - str2ba(address, &dba); - - while ((s = get_cached_session(&sba, &dba))) { - sdp_close(s->session); - g_source_remove(s->timeout_id); - g_source_remove(s->io_id); - g_free(s); - } - - return dbus_message_new_method_return(msg); -} |