static void find_by_type_val_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end, uuid16; struct find_by_type_val_data data; uint16_t mtu = bt_att_get_mtu(server->att); uint8_t rsp_pdu[mtu]; uint16_t ehandle = 0; bt_uuid_t uuid; if (length < 6) { data.ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } data.pdu = rsp_pdu; data.len = 0; data.mtu = mtu; data.ecode = 0; start = get_le16(pdu); end = get_le16(pdu + 2); uuid16 = get_le16(pdu + 4); util_debug(server->debug_callback, server->debug_data, "Find By Type Value - start: 0x%04x end: 0x%04x uuid: 0x%04x", start, end, uuid16); ehandle = start; if (start > end) { data.ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } bt_uuid16_create(&uuid, uuid16); gatt_db_find_by_type_value(server->db, start, end, &uuid, pdu + 6, length - 6, find_by_type_val_att_cb, &data); if (!data.len) data.ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND; if (data.ecode) goto error; bt_att_send(server->att, BT_ATT_OP_FIND_BY_TYPE_RSP, data.pdu, data.len, NULL, NULL, NULL); return; error: bt_att_send_error_rsp(server->att, opcode, ehandle, data.ecode); }
static void respond_not_supported(struct bt_att *att, uint8_t opcode) { struct bt_att_pdu_error_rsp pdu; pdu.opcode = opcode; pdu.handle = 0x0000; pdu.ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; bt_att_send(att, BT_ATT_OP_ERROR_RSP, &pdu, sizeof(pdu), NULL, NULL, NULL); }
static void respond_not_supported(struct bt_att *att, uint8_t opcode) { uint8_t pdu[4]; pdu[0] = opcode; pdu[1] = 0; pdu[2] = 0; pdu[3] = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; bt_att_send(att, BT_ATT_OP_ERROR_RSP, pdu, sizeof(pdu), NULL, NULL, NULL); }
static int gatt_send(struct bt_conn *conn, struct net_buf *buf, bt_att_func_t func, void *user_data, bt_att_destroy_t destroy) { int err; err = bt_att_send(conn, buf, func, user_data, destroy); if (err) { BT_ERR("Error sending ATT PDU: %d", err); net_buf_unref(buf); } return err; }
static void prep_write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t handle = 0; uint16_t offset; struct gatt_db_attribute *attr; uint8_t ecode; if (length < 4) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } if (queue_length(server->prep_queue) >= server->max_prep_queue_len) { ecode = BT_ATT_ERROR_PREPARE_QUEUE_FULL; goto error; } handle = get_le16(pdu); offset = get_le16(pdu + 2); attr = gatt_db_get_attribute(server->db, handle); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } util_debug(server->debug_callback, server->debug_data, "Prep Write Req - handle: 0x%04x", handle); ecode = check_permissions(server, attr, BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN | BT_ATT_PERM_WRITE_ENCRYPT); if (ecode) goto error; if (!store_prep_data(server, handle, offset, length - 4, &((uint8_t *) pdu)[4])) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } bt_att_send(server->att, BT_ATT_OP_PREP_WRITE_RSP, pdu, length, NULL, NULL, NULL); return; error: bt_att_send_error_rsp(server->att, opcode, handle, ecode); }
bool bt_gatt_server_send_indication(struct bt_gatt_server *server, uint16_t handle, const uint8_t *value, uint16_t length, bt_gatt_server_conf_func_t callback, void *user_data, bt_gatt_server_destroy_func_t destroy) { uint16_t pdu_len; uint8_t *pdu; struct ind_data *data; bool result; if (!server || (length && !value)) return false; pdu_len = MIN(bt_att_get_mtu(server->att) - 1, length + 2); pdu = malloc(pdu_len); if (!pdu) return false; data = new0(struct ind_data, 1); if (!data) { free(pdu); return false; } data->callback = callback; data->destroy = destroy; data->user_data = user_data; put_le16(handle, pdu); memcpy(pdu + 2, value, pdu_len - 2); result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_VAL_IND, pdu, pdu_len, conf_cb, data, destroy_ind_data); if (!result) destroy_ind_data(data); free(pdu); return result; }
static void exec_next_prep_write(struct bt_gatt_server *server, uint16_t ehandle, int err) { struct prep_write_data *next = NULL; struct gatt_db_attribute *attr; bool status; if (err) goto error; next = queue_pop_head(server->prep_queue); if (!next) { bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0, NULL, NULL, NULL); return; } attr = gatt_db_get_attribute(server->db, next->handle); if (!attr) { err = BT_ATT_ERROR_UNLIKELY; goto error; } status = gatt_db_attribute_write(attr, next->offset, next->value, next->length, BT_ATT_OP_EXEC_WRITE_REQ, server->att, exec_write_complete_cb, server); prep_write_data_destroy(next); if (status) return; err = BT_ATT_ERROR_UNLIKELY; error: queue_remove_all(server->prep_queue, NULL, NULL, prep_write_data_destroy); bt_att_send_error_rsp(server->att, BT_ATT_OP_EXEC_WRITE_REQ, ehandle, err); }
static void process_read_by_type(struct async_read_op *op) { struct bt_gatt_server *server = op->server; uint8_t ecode; struct gatt_db_attribute *attr; uint32_t perm; attr = queue_pop_head(op->db_data); if (op->done || !attr) { bt_att_send(server->att, BT_ATT_OP_READ_BY_TYPE_RSP, op->pdu, op->pdu_len, NULL, NULL, NULL); async_read_op_destroy(op); return; } perm = gatt_db_attribute_get_permissions(attr); /* * Check for the READ access permission. Encryption, * authentication, and authorization permissions need to be * checked by the read handler, since bt_att is agnostic to * connection type and doesn't have security information on it. */ if (perm && !(perm & BT_ATT_PERM_READ)) { ecode = BT_ATT_ERROR_READ_NOT_PERMITTED; goto error; } if (gatt_db_attribute_read(attr, 0, op->opcode, server->att, read_by_type_read_complete_cb, op)) return; ecode = BT_ATT_ERROR_UNLIKELY; error: bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ, gatt_db_attribute_get_handle(attr), ecode); async_read_op_destroy(op); }
static void exec_write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint8_t flags; uint8_t ecode; bool write; if (length != 1) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } flags = ((uint8_t *) pdu)[0]; util_debug(server->debug_callback, server->debug_data, "Exec Write Req - flags: 0x%02x", flags); if (flags == 0x00) write = false; else if (flags == 0x01) write = true; else { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } if (!write) { queue_remove_all(server->prep_queue, NULL, NULL, prep_write_data_destroy); bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0, NULL, NULL, NULL); return; } exec_next_prep_write(server, 0, 0); return; error: bt_att_send_error_rsp(server->att, opcode, 0, ecode); }
unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode, uint16_t handle, int error) { struct bt_att_pdu_error_rsp pdu; uint8_t ecode; if (!att || !opcode) return 0; ecode = att_ecode_from_error(error); memset(&pdu, 0, sizeof(pdu)); pdu.opcode = opcode; put_le16(handle, &pdu.handle); pdu.ecode = ecode; return bt_att_send(att, BT_ATT_OP_ERROR_RSP, &pdu, sizeof(pdu), NULL, NULL, NULL); }
static void write_complete_cb(struct gatt_db_attribute *attr, int err, void *user_data) { struct async_write_op *op = user_data; struct bt_gatt_server *server = op->server; uint16_t handle; if (!server || op->opcode == BT_ATT_OP_WRITE_CMD) { async_write_op_destroy(op); return; } handle = gatt_db_attribute_get_handle(attr); if (err) bt_att_send_error_rsp(server->att, op->opcode, handle, err); else bt_att_send(server->att, BT_ATT_OP_WRITE_RSP, NULL, 0, NULL, NULL, NULL); async_write_op_destroy(op); }
static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, const uint8_t *value, size_t len, void *user_data) { struct read_multiple_resp_data *data = user_data; struct gatt_db_attribute *next_attr; uint32_t perm; uint16_t handle = gatt_db_attribute_get_handle(attr); if (err != 0) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, handle, err); read_multiple_resp_data_free(data); return; } perm = gatt_db_attribute_get_permissions(attr); if (perm && !(perm & BT_ATT_PERM_READ)) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, handle, BT_ATT_ERROR_READ_NOT_PERMITTED); read_multiple_resp_data_free(data); return; } len = MIN(len, data->mtu - data->length - 1); memcpy(data->rsp_data + data->length, value, len); data->length += len; data->cur_handle++; if ((data->length >= data->mtu - 1) || (data->cur_handle == data->num_handles)) { bt_att_send(data->server->att, BT_ATT_OP_READ_MULT_RSP, data->rsp_data, data->length, NULL, NULL, NULL); read_multiple_resp_data_free(data); return; } util_debug(data->server->debug_callback, data->server->debug_data, "Read Multiple Req - #%zu of %zu: 0x%04x", data->cur_handle + 1, data->num_handles, data->handles[data->cur_handle]); next_attr = gatt_db_get_attribute(data->server->db, data->handles[data->cur_handle]); if (!next_attr) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, data->handles[data->cur_handle], BT_ATT_ERROR_INVALID_HANDLE); read_multiple_resp_data_free(data); return; } if (!gatt_db_attribute_read(next_attr, 0, BT_ATT_OP_READ_MULT_REQ, data->server->att, read_multiple_complete_cb, data)) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, data->handles[data->cur_handle], BT_ATT_ERROR_UNLIKELY); read_multiple_resp_data_free(data); } }
static void find_info_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; uint16_t mtu = bt_att_get_mtu(server->att); uint8_t rsp_pdu[mtu]; uint16_t rsp_len; uint8_t ecode = 0; uint16_t ehandle = 0; struct queue *q = NULL; if (length != 4) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } q = queue_new(); if (!q) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } start = get_le16(pdu); end = get_le16(pdu + 2); util_debug(server->debug_callback, server->debug_data, "Find Info - start: 0x%04x end: 0x%04x", start, end); if (!start || !end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } ehandle = start; if (start > end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } gatt_db_find_information(server->db, start, end, q); if (queue_isempty(q)) { ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND; goto error; } if (!encode_find_info_rsp(server->db, q, mtu, rsp_pdu, &rsp_len)) { ecode = BT_ATT_ERROR_UNLIKELY; goto error; } bt_att_send(server->att, BT_ATT_OP_FIND_INFO_RSP, rsp_pdu, rsp_len, NULL, NULL, NULL); queue_destroy(q, NULL); return; error: bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); queue_destroy(q, NULL); }
static void read_by_grp_type_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; bt_uuid_t type; bt_uuid_t prim, snd; uint16_t mtu = bt_att_get_mtu(server->att); uint8_t rsp_pdu[mtu]; uint16_t rsp_len; uint8_t ecode = 0; uint16_t ehandle = 0; struct queue *q = NULL; if (length != 6 && length != 20) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } q = queue_new(); if (!q) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } start = get_le16(pdu); end = get_le16(pdu + 2); get_uuid_le(pdu + 4, length - 4, &type); util_debug(server->debug_callback, server->debug_data, "Read By Grp Type - start: 0x%04x end: 0x%04x", start, end); if (!start || !end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } ehandle = start; if (start > end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } /* * GATT defines that only the <<Primary Service>> and * <<Secondary Service>> group types can be used for the * "Read By Group Type" request (Core v4.1, Vol 3, sec 2.5.3). Return an * error if any other group type is given. */ bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID); bt_uuid16_create(&snd, GATT_SND_SVC_UUID); if (bt_uuid_cmp(&type, &prim) && bt_uuid_cmp(&type, &snd)) { ecode = BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE; goto error; } gatt_db_read_by_group_type(server->db, start, end, type, q); if (queue_isempty(q)) { ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND; goto error; } if (!encode_read_by_grp_type_rsp(server->db, q, server->att, mtu, rsp_pdu, &rsp_len)) { ecode = BT_ATT_ERROR_UNLIKELY; goto error; } queue_destroy(q, NULL); bt_att_send(server->att, BT_ATT_OP_READ_BY_GRP_TYPE_RSP, rsp_pdu, rsp_len, NULL, NULL, NULL); return; error: queue_destroy(q, NULL); bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); }
static void prep_write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; struct prep_write_data *prep_data = NULL; uint16_t handle = 0; uint16_t offset; struct gatt_db_attribute *attr; uint8_t ecode; uint32_t perm; if (length < 4) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } if (queue_length(server->prep_queue) >= server->max_prep_queue_len) { ecode = BT_ATT_ERROR_PREPARE_QUEUE_FULL; goto error; } handle = get_le16(pdu); offset = get_le16(pdu + 2); attr = gatt_db_get_attribute(server->db, handle); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } util_debug(server->debug_callback, server->debug_data, "Prep Write Req - handle: 0x%04x", handle); perm = gatt_db_attribute_get_permissions(attr); /* * TODO: The "Prepare Write" request requires security permission checks * to be performed before the write is executed. I.e., we can't leave * the permission check to the upper layer since we can't call * gatt_db_write until the entire queue is atomically processed during * an "Execute Write" request. Figure out how to make this check here. */ if (!(perm & BT_ATT_PERM_WRITE)) { ecode = BT_ATT_ERROR_WRITE_NOT_PERMITTED; goto error; } prep_data = new0(struct prep_write_data, 1); if (!prep_data) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } prep_data->length = length - 4; if (prep_data->length) { prep_data->value = malloc(prep_data->length); if (!prep_data->value) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } } prep_data->server = server; prep_data->handle = handle; prep_data->offset = offset; memcpy(prep_data->value, pdu + 4, prep_data->length); queue_push_tail(server->prep_queue, prep_data); bt_att_send(server->att, BT_ATT_OP_PREP_WRITE_RSP, pdu, length, NULL, NULL, NULL); return; error: if (prep_data) prep_write_data_destroy(prep_data); bt_att_send_error_rsp(server->att, opcode, handle, ecode); }
if (func || notify) { cb = new0(struct attrib_callbacks, 1); if (!cb) return 0; cb->result_func = func; cb->user_data = user_data; cb->destroy_func = notify; cb->parent = attrib; queue_push_head(attrib->callbacks, cb); response_cb = attrib_callback_result; destroy_cb = attrib_callbacks_remove; } pend_id = bt_att_send(attrib->att, pdu[0], (void *) pdu + 1, len - 1, response_cb, cb, destroy_cb); /* * We store here pair as it is easier to handle it in response and in * case where user request us to use specific id request - see below. */ if (id == 0) id = pend_id; /* * If user what us to use given id, lets keep track on that so we give * user a possibility to cancel ongoing request. */ if (cb) cb->id = store_id(attrib, id, pend_id);