| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- *
- * OBEX Server
- *
- * Copyright (C) 2007-2010 Nokia Corporation
- * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <inttypes.h>
- #include <fcntl.h>
- #include <sys/socket.h>
- #include <glib.h>
- #include "lib/bluetooth.h"
- #include "lib/uuid.h"
- #include "gdbus/gdbus.h"
- #include "btio/btio.h"
- #include "obexd/src/obexd.h"
- #include "obexd/src/plugin.h"
- #include "obexd/src/server.h"
- #include "obexd/src/obex.h"
- #include "obexd/src/transport.h"
- #include "obexd/src/service.h"
- #include "obexd/src/log.h"
- #define BT_RX_MTU 32767
- #define BT_TX_MTU 32767
- struct bluetooth_profile {
- struct obex_server *server;
- struct obex_service_driver *driver;
- char *uuid;
- char *path;
- };
- static GSList *profiles = NULL;
- static DBusConnection *connection = NULL;
- static DBusMessage *profile_release(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static void connect_event(GIOChannel *io, GError *err, void *user_data)
- {
- int sk = g_io_channel_unix_get_fd(io);
- struct bluetooth_profile *profile = user_data;
- struct obex_server *server = profile->server;
- int type;
- uint16_t omtu = BT_TX_MTU;
- uint16_t imtu = BT_RX_MTU;
- gboolean stream = TRUE;
- socklen_t len = sizeof(int);
- if (err)
- goto drop;
- if (getsockopt(sk, SOL_SOCKET, SO_TYPE, &type, &len) < 0)
- goto done;
- if (type != SOCK_SEQPACKET)
- goto done;
- stream = FALSE;
- /* Read MTU if io is an L2CAP socket */
- bt_io_get(io, NULL, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_IMTU, &imtu,
- BT_IO_OPT_INVALID);
- done:
- if (obex_server_new_connection(server, io, omtu, imtu, stream) < 0)
- g_io_channel_shutdown(io, TRUE, NULL);
- return;
- drop:
- error("%s", err->message);
- g_io_channel_shutdown(io, TRUE, NULL);
- return;
- }
- static DBusMessage *invalid_args(DBusMessage *msg)
- {
- return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments",
- "Invalid arguments in method call");
- }
- static DBusMessage *profile_new_connection(DBusConnection *conn,
- DBusMessage *msg, void *data)
- {
- DBusMessageIter args;
- const char *device;
- int fd;
- GIOChannel *io;
- dbus_message_iter_init(msg, &args);
- if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
- return invalid_args(msg);
- dbus_message_iter_get_basic(&args, &device);
- dbus_message_iter_next(&args);
- if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_UNIX_FD)
- return invalid_args(msg);
- dbus_message_iter_get_basic(&args, &fd);
- if (fd < 0) {
- error("bluetooth: NewConnection invalid fd");
- return invalid_args(msg);
- }
- /* Read fd flags to make sure it can be used */
- if (fcntl(fd, F_GETFD) < 0) {
- error("bluetooth: fcntl(%d, F_GETFD): %s (%d)", fd,
- strerror(errno), errno);
- close(fd);
- return invalid_args(msg);
- }
- io = g_io_channel_unix_new(fd);
- if (io == NULL) {
- close(fd);
- return invalid_args(msg);
- }
- DBG("device %s", device);
- connect_event(io, NULL, data);
- g_io_channel_unref(io);
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static DBusMessage *profile_request_disconnection(DBusConnection *conn,
- DBusMessage *msg, void *data)
- {
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static DBusMessage *profile_cancel(DBusConnection *conn,
- DBusMessage *msg, void *data)
- {
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static const GDBusMethodTable profile_methods[] = {
- { GDBUS_METHOD("Release",
- NULL, NULL,
- profile_release) },
- { GDBUS_METHOD("NewConnection",
- GDBUS_ARGS({ "device", "o" }, { "fd", "h" },
- { "options", "a{sv}" }), NULL,
- profile_new_connection) },
- { GDBUS_METHOD("RequestDisconnection",
- GDBUS_ARGS({ "device", "o" }), NULL,
- profile_request_disconnection) },
- { GDBUS_METHOD("Cancel",
- NULL, NULL,
- profile_cancel) },
- { }
- };
- static void unregister_profile(struct bluetooth_profile *profile)
- {
- g_dbus_unregister_interface(connection, profile->path,
- "org.bluez.Profile1");
- g_free(profile->path);
- profile->path = NULL;
- }
- static void register_profile_reply(DBusPendingCall *call, void *user_data)
- {
- struct bluetooth_profile *profile = user_data;
- DBusMessage *reply = dbus_pending_call_steal_reply(call);
- DBusError derr;
- dbus_error_init(&derr);
- if (!dbus_set_error_from_message(&derr, reply)) {
- DBG("Profile %s registered", profile->path);
- goto done;
- }
- unregister_profile(profile);
- error("bluetooth: RequestProfile error: %s, %s", derr.name,
- derr.message);
- dbus_error_free(&derr);
- done:
- dbus_message_unref(reply);
- }
- static void profile_free(void *data)
- {
- struct bluetooth_profile *profile = data;
- if (profile->path != NULL)
- unregister_profile(profile);
- g_free(profile->uuid);
- g_free(profile);
- }
- static int register_profile(struct bluetooth_profile *profile)
- {
- DBusMessage *msg;
- DBusMessageIter iter, opt;
- DBusPendingCall *call;
- dbus_bool_t auto_connect = FALSE;
- char *xml;
- int ret = 0;
- profile->path = g_strconcat("/org/bluez/obex/", profile->uuid, NULL);
- g_strdelimit(profile->path, "-", '_');
- if (!g_dbus_register_interface(connection, profile->path,
- "org.bluez.Profile1", profile_methods,
- NULL, NULL,
- profile, NULL)) {
- error("D-Bus failed to register %s", profile->path);
- g_free(profile->path);
- profile->path = NULL;
- return -1;
- }
- msg = dbus_message_new_method_call("org.bluez", "/org/bluez",
- "org.bluez.ProfileManager1",
- "RegisterProfile");
- dbus_message_iter_init_append(msg, &iter);
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
- &profile->path);
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
- &profile->uuid);
- 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,
- &opt);
- g_dbus_dict_append_entry(&opt, "AutoConnect", DBUS_TYPE_BOOLEAN,
- &auto_connect);
- if (profile->driver->record) {
- if (profile->driver->port != 0)
- xml = g_markup_printf_escaped(profile->driver->record,
- profile->driver->channel,
- profile->driver->name,
- profile->driver->port);
- else
- xml = g_markup_printf_escaped(profile->driver->record,
- profile->driver->channel,
- profile->driver->name);
- g_dbus_dict_append_entry(&opt, "ServiceRecord",
- DBUS_TYPE_STRING, &xml);
- g_free(xml);
- }
- dbus_message_iter_close_container(&iter, &opt);
- if (!g_dbus_send_message_with_reply(connection, msg, &call, -1)) {
- ret = -1;
- unregister_profile(profile);
- goto failed;
- }
- dbus_pending_call_set_notify(call, register_profile_reply, profile,
- NULL);
- dbus_pending_call_unref(call);
- failed:
- dbus_message_unref(msg);
- return ret;
- }
- static const char *service2uuid(uint16_t service)
- {
- switch (service) {
- case OBEX_OPP:
- return OBEX_OPP_UUID;
- case OBEX_FTP:
- return OBEX_FTP_UUID;
- case OBEX_PBAP:
- return OBEX_PSE_UUID;
- case OBEX_IRMC:
- return OBEX_SYNC_UUID;
- case OBEX_PCSUITE:
- return "00005005-0000-1000-8000-0002ee000001";
- case OBEX_SYNCEVOLUTION:
- return "00000002-0000-1000-8000-0002ee000002";
- case OBEX_MAS:
- return OBEX_MAS_UUID;
- case OBEX_MNS:
- return OBEX_MNS_UUID;
- }
- return NULL;
- }
- static void name_acquired(DBusConnection *conn, void *user_data)
- {
- GSList *l;
- DBG("org.bluez appeared");
- for (l = profiles; l; l = l->next) {
- struct bluetooth_profile *profile = l->data;
- if (profile->path != NULL)
- continue;
- if (register_profile(profile) < 0) {
- error("bluetooth: Failed to register profile %s",
- profile->path);
- g_free(profile->path);
- profile->path = NULL;
- }
- }
- }
- static void name_released(DBusConnection *conn, void *user_data)
- {
- GSList *l;
- DBG("org.bluez disappered");
- for (l = profiles; l; l = l->next) {
- struct bluetooth_profile *profile = l->data;
- if (profile->path == NULL)
- continue;
- unregister_profile(profile);
- }
- }
- static void *bluetooth_start(struct obex_server *server, int *err)
- {
- const GSList *l;
- for (l = server->drivers; l; l = l->next) {
- struct obex_service_driver *driver = l->data;
- struct bluetooth_profile *profile;
- const char *uuid;
- uuid = service2uuid(driver->service);
- if (uuid == NULL)
- continue;
- profile = g_new0(struct bluetooth_profile, 1);
- profile->driver = driver;
- profile->server = server;
- profile->uuid = g_strdup(uuid);
- profiles = g_slist_prepend(profiles, profile);
- }
- return profiles;
- }
- static void bluetooth_stop(void *data)
- {
- g_slist_free_full(profiles, profile_free);
- profiles = NULL;
- }
- static int bluetooth_getpeername(GIOChannel *io, char **name)
- {
- GError *gerr = NULL;
- char address[18];
- bt_io_get(io, &gerr, BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID);
- if (gerr) {
- error("%s", gerr->message);
- g_error_free(gerr);
- return -EINVAL;
- }
- *name = g_strdup(address);
- return 0;
- }
- static int bluetooth_getsockname(GIOChannel *io, char **name)
- {
- GError *gerr = NULL;
- char address[18];
- bt_io_get(io, &gerr, BT_IO_OPT_SOURCE, address, BT_IO_OPT_INVALID);
- if (gerr) {
- error("%s", gerr->message);
- g_error_free(gerr);
- return -EINVAL;
- }
- *name = g_strdup(address);
- return 0;
- }
- static struct obex_transport_driver driver = {
- .name = "bluetooth",
- .start = bluetooth_start,
- .getpeername = bluetooth_getpeername,
- .getsockname = bluetooth_getsockname,
- .stop = bluetooth_stop
- };
- static unsigned int listener_id = 0;
- static int bluetooth_init(void)
- {
- connection = g_dbus_setup_private(DBUS_BUS_SYSTEM, NULL, NULL);
- if (connection == NULL)
- return -EPERM;
- listener_id = g_dbus_add_service_watch(connection, "org.bluez",
- name_acquired, name_released, NULL, NULL);
- return obex_transport_driver_register(&driver);
- }
- static void bluetooth_exit(void)
- {
- g_dbus_remove_watch(connection, listener_id);
- g_slist_free_full(profiles, profile_free);
- if (connection)
- dbus_connection_unref(connection);
- obex_transport_driver_unregister(&driver);
- }
- OBEX_PLUGIN_DEFINE(bluetooth, bluetooth_init, bluetooth_exit)
|