diff options
Diffstat (limited to 'plugins/service.c')
-rw-r--r-- | plugins/service.c | 832 |
1 files changed, 0 insertions, 832 deletions
diff --git a/plugins/service.c b/plugins/service.c deleted file mode 100644 index 288f8496..00000000 --- a/plugins/service.c +++ /dev/null @@ -1,832 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 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 <stdlib.h> -#include <string.h> - -#include <bluetooth/bluetooth.h> -#include <bluetooth/sdp.h> -#include <bluetooth/sdp_lib.h> - -#include <gdbus.h> - -#include "sdpd.h" -#include "sdp-xml.h" -#include "plugin.h" -#include "adapter.h" -#include "error.h" -#include "log.h" - -#define SERVICE_INTERFACE "org.bluez.Service" - -static DBusConnection *connection; - -struct record_data { - uint32_t handle; - char *sender; - guint listener_id; - struct service_adapter *serv_adapter; -}; - -struct context_data { - sdp_record_t *record; - sdp_data_t attr_data; - struct sdp_xml_data *stack_head; - uint16_t attr_id; -}; - -struct pending_auth { - DBusConnection *conn; - DBusMessage *msg; - char *sender; - bdaddr_t dst; - char uuid[MAX_LEN_UUID_STR]; -}; - -struct service_adapter { - struct btd_adapter *adapter; - GSList *pending_list; - GSList *records; -}; - -static struct service_adapter *serv_adapter_any = NULL; - -static int compute_seq_size(sdp_data_t *data) -{ - int unit_size = data->unitSize; - sdp_data_t *seq = data->val.dataseq; - - for (; seq; seq = seq->next) - unit_size += seq->unitSize; - - return unit_size; -} - -static void element_start(GMarkupParseContext *context, - const gchar *element_name, const gchar **attribute_names, - const gchar **attribute_values, gpointer user_data, GError **err) -{ - struct context_data *ctx_data = user_data; - - if (!strcmp(element_name, "record")) - return; - - if (!strcmp(element_name, "attribute")) { - int i; - for (i = 0; attribute_names[i]; i++) { - if (!strcmp(attribute_names[i], "id")) { - ctx_data->attr_id = strtol(attribute_values[i], 0, 0); - break; - } - } - DBG("New attribute 0x%04x", ctx_data->attr_id); - return; - } - - if (ctx_data->stack_head) { - struct sdp_xml_data *newelem = sdp_xml_data_alloc(); - newelem->next = ctx_data->stack_head; - ctx_data->stack_head = newelem; - } else { - ctx_data->stack_head = sdp_xml_data_alloc(); - ctx_data->stack_head->next = NULL; - } - - if (!strcmp(element_name, "sequence")) - ctx_data->stack_head->data = sdp_data_alloc(SDP_SEQ8, NULL); - else if (!strcmp(element_name, "alternate")) - ctx_data->stack_head->data = sdp_data_alloc(SDP_ALT8, NULL); - else { - int i; - /* Parse value, name, encoding */ - for (i = 0; attribute_names[i]; i++) { - if (!strcmp(attribute_names[i], "value")) { - int curlen = strlen(ctx_data->stack_head->text); - int attrlen = strlen(attribute_values[i]); - - /* Ensure we're big enough */ - while ((curlen + 1 + attrlen) > ctx_data->stack_head->size) { - sdp_xml_data_expand(ctx_data->stack_head); - } - - memcpy(ctx_data->stack_head->text + curlen, - attribute_values[i], attrlen); - ctx_data->stack_head->text[curlen + attrlen] = '\0'; - } - - if (!strcmp(attribute_names[i], "encoding")) { - if (!strcmp(attribute_values[i], "hex")) - ctx_data->stack_head->type = 1; - } - - if (!strcmp(attribute_names[i], "name")) { - ctx_data->stack_head->name = strdup(attribute_values[i]); - } - } - - ctx_data->stack_head->data = sdp_xml_parse_datatype(element_name, - ctx_data->stack_head, ctx_data->record); - - if (ctx_data->stack_head->data == NULL) - error("Can't parse element %s", element_name); - } -} - -static void element_end(GMarkupParseContext *context, - const gchar *element_name, gpointer user_data, GError **err) -{ - struct context_data *ctx_data = user_data; - struct sdp_xml_data *elem; - - if (!strcmp(element_name, "record")) - return; - - if (!strcmp(element_name, "attribute")) { - if (ctx_data->stack_head && ctx_data->stack_head->data) { - int ret = sdp_attr_add(ctx_data->record, ctx_data->attr_id, - ctx_data->stack_head->data); - if (ret == -1) - DBG("Could not add attribute 0x%04x", - ctx_data->attr_id); - - ctx_data->stack_head->data = NULL; - sdp_xml_data_free(ctx_data->stack_head); - ctx_data->stack_head = NULL; - } else { - DBG("No data for attribute 0x%04x", ctx_data->attr_id); - } - return; - } - - if (!strcmp(element_name, "sequence")) { - ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data); - - if (ctx_data->stack_head->data->unitSize > USHRT_MAX) { - ctx_data->stack_head->data->unitSize += sizeof(uint32_t); - ctx_data->stack_head->data->dtd = SDP_SEQ32; - } else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) { - ctx_data->stack_head->data->unitSize += sizeof(uint16_t); - ctx_data->stack_head->data->dtd = SDP_SEQ16; - } else { - ctx_data->stack_head->data->unitSize += sizeof(uint8_t); - } - } else if (!strcmp(element_name, "alternate")) { - ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data); - - if (ctx_data->stack_head->data->unitSize > USHRT_MAX) { - ctx_data->stack_head->data->unitSize += sizeof(uint32_t); - ctx_data->stack_head->data->dtd = SDP_ALT32; - } else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) { - ctx_data->stack_head->data->unitSize += sizeof(uint16_t); - ctx_data->stack_head->data->dtd = SDP_ALT16; - } else { - ctx_data->stack_head->data->unitSize += sizeof(uint8_t); - } - } - - if (ctx_data->stack_head->next && ctx_data->stack_head->data && - ctx_data->stack_head->next->data) { - switch (ctx_data->stack_head->next->data->dtd) { - case SDP_SEQ8: - case SDP_SEQ16: - case SDP_SEQ32: - case SDP_ALT8: - case SDP_ALT16: - case SDP_ALT32: - ctx_data->stack_head->next->data->val.dataseq = - sdp_seq_append(ctx_data->stack_head->next->data->val.dataseq, - ctx_data->stack_head->data); - ctx_data->stack_head->data = NULL; - break; - } - - elem = ctx_data->stack_head; - ctx_data->stack_head = ctx_data->stack_head->next; - - sdp_xml_data_free(elem); - } -} - -static GMarkupParser parser = { - element_start, element_end, NULL, NULL, NULL -}; - -static sdp_record_t *sdp_xml_parse_record(const char *data, int size) -{ - GMarkupParseContext *ctx; - struct context_data *ctx_data; - sdp_record_t *record; - - ctx_data = malloc(sizeof(*ctx_data)); - if (!ctx_data) - return NULL; - - record = sdp_record_alloc(); - if (!record) { - free(ctx_data); - return NULL; - } - - memset(ctx_data, 0, sizeof(*ctx_data)); - ctx_data->record = record; - - ctx = g_markup_parse_context_new(&parser, 0, ctx_data, NULL); - - if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) { - error("XML parsing error"); - g_markup_parse_context_free(ctx); - sdp_record_free(record); - free(ctx_data); - return NULL; - } - - g_markup_parse_context_free(ctx); - - free(ctx_data); - - return record; -} - -static struct record_data *find_record(struct service_adapter *serv_adapter, - uint32_t handle, const char *sender) -{ - GSList *list; - - for (list = serv_adapter->records; list; list = list->next) { - struct record_data *data = list->data; - if (handle == data->handle && !strcmp(sender, data->sender)) - return data; - } - - return NULL; -} - -static struct pending_auth *next_pending(struct service_adapter *serv_adapter) -{ - GSList *l = serv_adapter->pending_list; - - if (l) { - struct pending_auth *auth = l->data; - return auth; - } - - return NULL; -} - -static struct pending_auth *find_pending_by_sender( - struct service_adapter *serv_adapter, - const char *sender) -{ - GSList *l = serv_adapter->pending_list; - - for (; l; l = l->next) { - struct pending_auth *auth = l->data; - if (g_str_equal(auth->sender, sender)) - return auth; - } - - return NULL; -} - -static void exit_callback(DBusConnection *conn, void *user_data) -{ - struct record_data *user_record = user_data; - struct service_adapter *serv_adapter = user_record->serv_adapter; - struct pending_auth *auth; - - DBG("remove record"); - - serv_adapter->records = g_slist_remove(serv_adapter->records, - user_record); - - auth = find_pending_by_sender(serv_adapter, user_record->sender); - if (auth) { - serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list, - auth); - g_free(auth); - } - - remove_record_from_server(user_record->handle); - - g_free(user_record->sender); - g_free(user_record); -} - -static int add_xml_record(DBusConnection *conn, const char *sender, - struct service_adapter *serv_adapter, - const char *record, dbus_uint32_t *handle) -{ - struct record_data *user_record; - sdp_record_t *sdp_record; - bdaddr_t src; - - sdp_record = sdp_xml_parse_record(record, strlen(record)); - if (!sdp_record) { - error("Parsing of XML service record failed"); - return -EIO; - } - - if (serv_adapter->adapter) - adapter_get_address(serv_adapter->adapter, &src); - else - bacpy(&src, BDADDR_ANY); - - if (add_record_to_server(&src, sdp_record) < 0) { - error("Failed to register service record"); - sdp_record_free(sdp_record); - return -EIO; - } - - user_record = g_new0(struct record_data, 1); - user_record->handle = sdp_record->handle; - user_record->sender = g_strdup(sender); - user_record->serv_adapter = serv_adapter; - user_record->listener_id = g_dbus_add_disconnect_watch(conn, sender, - exit_callback, user_record, NULL); - - serv_adapter->records = g_slist_append(serv_adapter->records, - user_record); - - DBG("listener_id %d", user_record->listener_id); - - *handle = user_record->handle; - - return 0; -} - -static DBusMessage *update_record(DBusConnection *conn, DBusMessage *msg, - struct service_adapter *serv_adapter, - dbus_uint32_t handle, sdp_record_t *sdp_record) -{ - bdaddr_t src; - int err; - - if (remove_record_from_server(handle) < 0) { - sdp_record_free(sdp_record); - return btd_error_not_available(msg); - } - - if (serv_adapter->adapter) - adapter_get_address(serv_adapter->adapter, &src); - else - bacpy(&src, BDADDR_ANY); - - sdp_record->handle = handle; - err = add_record_to_server(&src, sdp_record); - if (err < 0) { - sdp_record_free(sdp_record); - error("Failed to update the service record"); - return btd_error_failed(msg, strerror(-err)); - } - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *update_xml_record(DBusConnection *conn, - DBusMessage *msg, - struct service_adapter *serv_adapter) -{ - struct record_data *user_record; - sdp_record_t *sdp_record; - const char *record; - dbus_uint32_t handle; - int len; - - if (dbus_message_get_args(msg, NULL, - DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_STRING, &record, - DBUS_TYPE_INVALID) == FALSE) - return NULL; - - len = (record ? strlen(record) : 0); - if (len == 0) - return btd_error_invalid_args(msg); - - user_record = find_record(serv_adapter, handle, - dbus_message_get_sender(msg)); - if (!user_record) - return btd_error_not_available(msg); - - sdp_record = sdp_xml_parse_record(record, len); - if (!sdp_record) { - error("Parsing of XML service record failed"); - return btd_error_failed(msg, - "Parsing of XML service record failed"); - } - - return update_record(conn, msg, serv_adapter, handle, sdp_record); -} - -static int remove_record(DBusConnection *conn, const char *sender, - struct service_adapter *serv_adapter, - dbus_uint32_t handle) -{ - struct record_data *user_record; - - DBG("remove record 0x%x", handle); - - user_record = find_record(serv_adapter, handle, sender); - if (!user_record) - return -1; - - DBG("listner_id %d", user_record->listener_id); - - g_dbus_remove_watch(conn, user_record->listener_id); - - exit_callback(conn, user_record); - - return 0; -} - -static DBusMessage *add_service_record(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct service_adapter *serv_adapter = data; - DBusMessage *reply; - const char *sender, *record; - dbus_uint32_t handle; - int err; - - if (dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &record, DBUS_TYPE_INVALID) == FALSE) - return NULL; - - sender = dbus_message_get_sender(msg); - err = add_xml_record(conn, sender, serv_adapter, record, &handle); - if (err < 0) - return btd_error_failed(msg, strerror(-err)); - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - dbus_message_append_args(reply, DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_INVALID); - - return reply; -} - -static DBusMessage *update_service_record(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct service_adapter *serv_adapter = data; - - return update_xml_record(conn, msg, serv_adapter); -} - -static DBusMessage *remove_service_record(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct service_adapter *serv_adapter = data; - dbus_uint32_t handle; - const char *sender; - - if (dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_INVALID) == FALSE) - return NULL; - - sender = dbus_message_get_sender(msg); - - if (remove_record(conn, sender, serv_adapter, handle) < 0) - return btd_error_not_available(msg); - - return dbus_message_new_method_return(msg); -} - -static void auth_cb(DBusError *derr, void *user_data) -{ - struct service_adapter *serv_adapter = user_data; - DBusMessage *reply; - struct pending_auth *auth; - bdaddr_t src; - - auth = next_pending(serv_adapter); - if (auth == NULL) { - info("Authorization cancelled: Client exited"); - return; - } - - if (derr) { - error("Access denied: %s", derr->message); - - reply = btd_error_not_authorized(auth->msg); - dbus_message_unref(auth->msg); - g_dbus_send_message(auth->conn, reply); - goto done; - } - - g_dbus_send_reply(auth->conn, auth->msg, - DBUS_TYPE_INVALID); - -done: - dbus_connection_unref(auth->conn); - - serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list, - auth); - g_free(auth); - - auth = next_pending(serv_adapter); - if (auth == NULL) - return; - - if (serv_adapter->adapter) - adapter_get_address(serv_adapter->adapter, &src); - else - bacpy(&src, BDADDR_ANY); - - btd_request_authorization(&src, &auth->dst, - auth->uuid, auth_cb, serv_adapter); -} - -static DBusMessage *request_authorization(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct record_data *user_record; - struct service_adapter *serv_adapter = data; - sdp_record_t *record; - sdp_list_t *services; - const char *sender; - dbus_uint32_t handle; - const char *address; - struct pending_auth *auth; - char uuid_str[MAX_LEN_UUID_STR]; - uuid_t *uuid, *uuid128; - bdaddr_t src; - - if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address, - DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_INVALID) == FALSE) - return NULL; - - sender = dbus_message_get_sender(msg); - if (find_pending_by_sender(serv_adapter, sender)) - return btd_error_does_not_exist(msg); - - user_record = find_record(serv_adapter, handle, sender); - if (!user_record) { - user_record = find_record(serv_adapter_any, handle, sender); - if (!user_record) - return btd_error_not_authorized(msg); - } - - record = sdp_record_find(user_record->handle); - if (record == NULL) - return btd_error_not_authorized(msg); - - if (sdp_get_service_classes(record, &services) < 0) { - sdp_record_free(record); - return btd_error_not_authorized(msg); - } - - if (services == NULL) - return btd_error_not_authorized(msg); - - uuid = services->data; - uuid128 = sdp_uuid_to_uuid128(uuid); - - sdp_list_free(services, bt_free); - - if (sdp_uuid2strn(uuid128, uuid_str, MAX_LEN_UUID_STR) < 0) { - bt_free(uuid128); - return btd_error_not_authorized(msg); - } - bt_free(uuid128); - - auth = g_new0(struct pending_auth, 1); - auth->msg = dbus_message_ref(msg); - auth->conn = dbus_connection_ref(connection); - auth->sender = user_record->sender; - memcpy(auth->uuid, uuid_str, MAX_LEN_UUID_STR); - str2ba(address, &auth->dst); - - serv_adapter->pending_list = g_slist_append(serv_adapter->pending_list, - auth); - - auth = next_pending(serv_adapter); - if (auth == NULL) - return btd_error_does_not_exist(msg); - - if (serv_adapter->adapter) - adapter_get_address(serv_adapter->adapter, &src); - else - bacpy(&src, BDADDR_ANY); - - if (btd_request_authorization(&src, &auth->dst, auth->uuid, auth_cb, - serv_adapter) < 0) { - serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list, - auth); - g_free(auth); - return btd_error_not_authorized(msg); - } - - return NULL; -} - -static DBusMessage *cancel_authorization(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - DBusMessage *reply; - struct service_adapter *serv_adapter = data; - struct pending_auth *auth; - const gchar *sender; - bdaddr_t src; - - sender = dbus_message_get_sender(msg); - - auth = find_pending_by_sender(serv_adapter, sender); - if (auth == NULL) - return btd_error_does_not_exist(msg); - - if (serv_adapter->adapter) - adapter_get_address(serv_adapter->adapter, &src); - else - bacpy(&src, BDADDR_ANY); - - btd_cancel_authorization(&src, &auth->dst); - - reply = btd_error_not_authorized(auth->msg); - dbus_message_unref(auth->msg); - g_dbus_send_message(auth->conn, reply); - - dbus_connection_unref(auth->conn); - - serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list, - auth); - g_free(auth); - - auth = next_pending(serv_adapter); - if (auth == NULL) - goto done; - - if (serv_adapter->adapter) - adapter_get_address(serv_adapter->adapter, &src); - else - bacpy(&src, BDADDR_ANY); - - btd_request_authorization(&src, &auth->dst, - auth->uuid, auth_cb, serv_adapter); - -done: - return dbus_message_new_method_return(msg); -} - -static const GDBusMethodTable service_methods[] = { - { GDBUS_METHOD("AddRecord", - GDBUS_ARGS({ "record", "s" }), - GDBUS_ARGS({ "handle", "u" }), - add_service_record) }, - { GDBUS_METHOD("UpdateRecord", - GDBUS_ARGS({ "handle", "u" }, { "record", "s" }), NULL, - update_service_record) }, - { GDBUS_METHOD("RemoveRecord", - GDBUS_ARGS({ "handle", "u" }), NULL, - remove_service_record) }, - { GDBUS_ASYNC_METHOD("RequestAuthorization", - GDBUS_ARGS({ "address", "s" }, { "handle", "u"}), NULL, - request_authorization) }, - { GDBUS_METHOD("CancelAuthorization", - NULL, NULL, cancel_authorization) }, - { } -}; - -static void path_unregister(void *data) -{ - struct service_adapter *serv_adapter = data; - GSList *l, *next = NULL; - - for (l = serv_adapter->records; l != NULL; l = next) { - struct record_data *user_record = l->data; - - next = l->next; - - g_dbus_remove_watch(connection, user_record->listener_id); - exit_callback(connection, user_record); - } - - g_free(serv_adapter); -} - -static int register_interface(const char *path, struct btd_adapter *adapter) -{ - struct service_adapter *serv_adapter; - - DBG("path %s", path); - - serv_adapter = g_try_new0(struct service_adapter, 1); - if (serv_adapter == NULL) - return -ENOMEM; - - serv_adapter->adapter = adapter; - serv_adapter->pending_list = NULL; - - if (g_dbus_register_interface(connection, path, SERVICE_INTERFACE, - service_methods, NULL, NULL, serv_adapter, - path_unregister) == FALSE) { - error("D-Bus failed to register %s interface", - SERVICE_INTERFACE); - g_free(serv_adapter); - return -EIO; - } - - DBG("Registered interface %s on path %s", SERVICE_INTERFACE, path); - - if (serv_adapter->adapter == NULL) - serv_adapter_any = serv_adapter; - - return 0; -} - -static void unregister_interface(const char *path) -{ - DBG("path %s", path); - - g_dbus_unregister_interface(connection, path, SERVICE_INTERFACE); -} - -static int service_probe(struct btd_adapter *adapter) -{ - register_interface(adapter_get_path(adapter), adapter); - - return 0; -} - -static void service_remove(struct btd_adapter *adapter) -{ - unregister_interface(adapter_get_path(adapter)); -} - -static struct btd_adapter_driver service_driver = { - .name = "service", - .probe = service_probe, - .remove = service_remove, -}; - -static const char *any_path; - -static int service_init(void) -{ - int err; - - connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); - if (connection == NULL) - return -EIO; - - any_path = btd_adapter_any_request_path(); - if (any_path != NULL) { - if (register_interface(any_path, NULL) < 0) { - btd_adapter_any_release_path(); - any_path = NULL; - } - } - - err = btd_register_adapter_driver(&service_driver); - if (err < 0) { - dbus_connection_unref(connection); - return err; - } - - return 0; -} - -static void service_exit(void) -{ - btd_unregister_adapter_driver(&service_driver); - - if (any_path != NULL) { - unregister_interface(any_path); - - btd_adapter_any_release_path(); - any_path = NULL; - } - - dbus_connection_unref(connection); -} - -BLUETOOTH_PLUGIN_DEFINE(service, VERSION, - BLUETOOTH_PLUGIN_PRIORITY_HIGH, service_init, service_exit) |