From 9b31df63510a796c255c9cdd2559d8e594e82df0 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Tue, 19 Sep 2017 11:33:38 +0200 Subject: [PATCH] att_db: validate prepared writes before execution. Additional att_write_callback with ATT_TRANSACTION_MODE_VALIDATE --- src/ble/att_db.c | 18 ++++++++++++++++++ src/ble/att_db.h | 11 +++++++++-- src/bluetooth.h | 3 ++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/ble/att_db.c b/src/ble/att_db.c index de4dd0ab8..59d4f0035 100644 --- a/src/ble/att_db.c +++ b/src/ble/att_db.c @@ -986,6 +986,20 @@ static void att_notify_write_callbacks(att_connection_t * att_connection, uint16 (*att_write_callback)(att_connection->con_handle, 0, transaction_mode, 0, NULL, 0); } +// returns first reported error or 0 +static uint8_t att_validate_prepared_write(att_connection_t * att_connection){ + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, &service_handlers); + while (btstack_linked_list_iterator_has_next(&it)){ + att_service_handler_t * handler = (att_service_handler_t*) btstack_linked_list_iterator_next(&it); + if (!handler->write_callback) continue; + uint8_t error_code = (*handler->write_callback)(att_connection->con_handle, 0, ATT_TRANSACTION_MODE_VALIDATE, 0, NULL, 0); + if (error_code) return error_code; + } + if (!att_write_callback) return 0; + return (*att_write_callback)(att_connection->con_handle, 0, ATT_TRANSACTION_MODE_VALIDATE, 0, NULL, 0); +} + /* * @brief transcation queue of prepared writes, e.g., after disconnect */ @@ -1003,6 +1017,10 @@ static uint16_t handle_execute_write_request(att_connection_t * att_connection, uint8_t request_type = ATT_EXECUTE_WRITE_REQUEST; if (request_buffer[1]) { + // validate queued write + if (att_prepare_write_error_code == 0){ + att_prepare_write_error_code = att_validate_prepared_write(att_connection); + } // deliver queued errors if (att_prepare_write_error_code){ att_clear_transaction_queue(att_connection); diff --git a/src/ble/att_db.h b/src/ble/att_db.h index 02b6123e6..f81989fed 100644 --- a/src/ble/att_db.h +++ b/src/ble/att_db.h @@ -76,15 +76,22 @@ typedef uint16_t (*att_read_callback_t)(hci_con_handle_t con_handle, uint16_t at // ATT Client Write Callback for Dynamic Data // @param con_handle of hci le connection // @param attribute_handle to be written -// @param transaction - ATT_TRANSACTION_MODE_NONE for regular writes, ATT_TRANSACTION_MODE_ACTIVE for prepared writes and ATT_TRANSACTION_MODE_EXECUTE +// @param transaction - ATT_TRANSACTION_MODE_NONE for regular writes. For prepared writes: ATT_TRANSACTION_MODE_ACTIVE, ATT_TRANSACTION_MODE_VALIDATE, ATT_TRANSACTION_MODE_EXECUTE, ATT_TRANSACTION_MODE_CANCEL // @param offset into the value - used for queued writes and long attributes // @param buffer // @param buffer_size // @param signature used for signed write commmands // @returns 0 if write was ok, ATT_ERROR_PREPARE_QUEUE_FULL if no space in queue, ATT_ERROR_INVALID_OFFSET if offset is larger than max buffer +// +// Each Prepared Write Request triggers a callback with transaction mode ATT_TRANSACTION_MODE_ACTIVE. +// On Execute Write, the callback will be called with ATT_TRANSACTION_MODE_VALIDATE and allows to validate all queued writes and return an application error. +// If none of the registered callbacks return an error for ATT_TRANSACTION_MODE_VALIDATE and the callback will be called with ATT_TRANSACTION_MODE_EXECUTE. +// Otherwise, all callbacks will be called with ATT_TRANSACTION_MODE_CANCEL. +// +// If the additional validation step is not needed, just return 0 for all callbacks with transaction mode ATT_TRANSACTION_MODE_VALIDATE. +// typedef int (*att_write_callback_t)(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size); - // Read & Write Callbacks for handle range typedef struct att_service_handler { btstack_linked_item_t * item; diff --git a/src/bluetooth.h b/src/bluetooth.h index e0e09de0f..9e9f1b257 100644 --- a/src/bluetooth.h +++ b/src/bluetooth.h @@ -1081,13 +1081,14 @@ typedef enum { #define ATT_PROPERTY_AUTHORIZATION_REQUIRED 0x800 // Encryption key size stored in upper 4 bits, 0 == no encryption, encryption key size - 1 otherwise -// ATT Transaction Timeout of 30 seconds for Command/Response or Incidationc/Confirmation +// ATT Transaction Timeout of 30 seconds for Command/Response or Indication/Confirmation #define ATT_TRANSACTION_TIMEOUT_MS 30000 #define ATT_TRANSACTION_MODE_NONE 0x0 #define ATT_TRANSACTION_MODE_ACTIVE 0x1 #define ATT_TRANSACTION_MODE_EXECUTE 0x2 #define ATT_TRANSACTION_MODE_CANCEL 0x3 +#define ATT_TRANSACTION_MODE_VALIDATE 0x4 // MARK: GATT UUIDs #define GATT_PRIMARY_SERVICE_UUID 0x2800