// // MARK: ATT_READ_BY_TYPE_REQUEST // static uint16_t handle_read_by_type_request2(uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t start_handle, uint16_t end_handle, uint16_t attribute_type_len, uint8_t * attribute_type){ printf("ATT_READ_BY_TYPE_REQUEST: from %04X to %04X, type: ", start_handle, end_handle); hexdump2(attribute_type, attribute_type_len); uint16_t offset = 1; uint16_t pair_len = 0; att_iterator_t it; att_iterator_init(&it); while (att_iterator_has_next(&it)){ att_iterator_fetch_next(&it); if (!it.handle) break; if (it.handle < start_handle) continue; if (it.handle > end_handle) break; // (1) // does current attribute match if (!att_iterator_match_uuid(&it, attribute_type, attribute_type_len)) continue; att_update_value_len(&it); // check if value has same len as last one uint16_t this_pair_len = 2 + it.value_len; if (offset > 1){ if (pair_len != this_pair_len) { break; } } // first if (offset == 1) { pair_len = this_pair_len; response_buffer[offset] = pair_len; offset++; } // space? if (offset + pair_len > response_buffer_size) { if (offset > 2) break; it.value_len = response_buffer_size - 4; } // store bt_store_16(response_buffer, offset, it.handle); offset += 2; uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len); offset += bytes_copied; } if (offset == 1){ return setup_error_atribute_not_found(response_buffer, ATT_READ_BY_TYPE_REQUEST, start_handle); } response_buffer[0] = ATT_READ_BY_TYPE_RESPONSE; return offset; }
// // MARK: ATT_FIND_INFORMATION_REQUEST // // TODO: handle other types then GATT_PRIMARY_SERVICE_UUID and GATT_SECONDARY_SERVICE_UUID // static uint16_t handle_find_information_request2(uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t start_handle, uint16_t end_handle){ printf("ATT_FIND_INFORMATION_REQUEST: from %04X to %04X\n", start_handle, end_handle); uint16_t offset = 1; uint16_t pair_len = 0; att_iterator_t it; att_iterator_init(&it); while (att_iterator_has_next(&it)){ att_iterator_fetch_next(&it); if (!it.handle) break; if (it.handle > end_handle) break; if (it.handle < start_handle) continue; att_update_value_len(&it); // printf("Handle 0x%04x\n", it.handle); // check if value has same len as last one uint16_t this_pair_len = 2 + it.value_len; if (offset > 1){ if (pair_len != this_pair_len) { break; } } // first if (offset == 1) { pair_len = this_pair_len; if (it.value_len == 2) { response_buffer[offset] = 0x01; // format } else { response_buffer[offset] = 0x02; } offset++; } // space? if (offset + pair_len > response_buffer_size) { if (offset > 2) break; it.value_len = response_buffer_size - 4; } // store bt_store_16(response_buffer, offset, it.handle); offset += 2; uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len); offset += bytes_copied; } if (offset == 1){ return setup_error_atribute_not_found(response_buffer, ATT_FIND_INFORMATION_REQUEST, start_handle); } response_buffer[0] = ATT_FIND_INFORMATION_REPLY; return offset; }
// // MARK: ATT_FIND_BY_TYPE_VALUE // // "Only attributes with attribute handles between and including the Starting Handle parameter // and the Ending Handle parameter that match the requested attri- bute type and the attribute // value that have sufficient permissions to allow reading will be returned" -> (1) // // TODO: handle other types then GATT_PRIMARY_SERVICE_UUID and GATT_SECONDARY_SERVICE_UUID // // NOTE: doesn't handle DYNAMIC values // NOTE: only supports 16 bit UUIDs // static uint16_t handle_find_by_type_value_request2(att_connection_t * att_connection, uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t start_handle, uint16_t end_handle, uint16_t attribute_type, uint16_t attribute_len, uint8_t* attribute_value){ log_info("ATT_FIND_BY_TYPE_VALUE_REQUEST: from %04X to %04X, type %04X, value: ", start_handle, end_handle, attribute_type); hexdump(attribute_value, attribute_len); uint8_t request_type = ATT_FIND_BY_TYPE_VALUE_REQUEST; if (start_handle > end_handle || start_handle == 0){ return setup_error_invalid_handle(response_buffer, request_type, start_handle); } uint16_t offset = 1; uint16_t in_group = 0; uint16_t prev_handle = 0; att_iterator_t it; att_iterator_init(&it); while (att_iterator_has_next(&it)){ att_iterator_fetch_next(&it); if (it.handle && it.handle < start_handle) continue; if (it.handle > end_handle) break; // (1) // close current tag, if within a group and a new service definition starts or we reach end of att db if (in_group && (it.handle == 0 || att_iterator_match_uuid16(&it, GATT_PRIMARY_SERVICE_UUID) || att_iterator_match_uuid16(&it, GATT_SECONDARY_SERVICE_UUID))){ log_info("End of group, handle 0x%04x", prev_handle); bt_store_16(response_buffer, offset, prev_handle); offset += 2; in_group = 0; // check if space for another handle pair available if (offset + 4 > response_buffer_size){ break; } } // keep track of previous handle prev_handle = it.handle; // does current attribute match if (it.handle && att_iterator_match_uuid16(&it, attribute_type) && attribute_len == it.value_len && memcmp(attribute_value, it.value, it.value_len) == 0){ log_info("Begin of group, handle 0x%04x", it.handle); bt_store_16(response_buffer, offset, it.handle); offset += 2; in_group = 1; } } if (offset == 1){ return setup_error_atribute_not_found(response_buffer, request_type, start_handle); } response_buffer[0] = ATT_FIND_BY_TYPE_VALUE_RESPONSE; return offset; }
// // MARK: ATT_FIND_INFORMATION_REQUEST // // TODO: handle other types then GATT_PRIMARY_SERVICE_UUID and GATT_SECONDARY_SERVICE_UUID // static uint16_t handle_find_information_request2(att_connection_t * att_connection, uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t start_handle, uint16_t end_handle){ log_info("ATT_FIND_INFORMATION_REQUEST: from %04X to %04X", start_handle, end_handle); uint8_t request_type = ATT_FIND_INFORMATION_REQUEST; if (start_handle > end_handle || start_handle == 0){ return setup_error_invalid_handle(response_buffer, request_type, start_handle); } uint16_t offset = 1; uint16_t uuid_len = 0; att_iterator_t it; att_iterator_init(&it); while (att_iterator_has_next(&it)){ att_iterator_fetch_next(&it); if (!it.handle) break; if (it.handle > end_handle) break; if (it.handle < start_handle) continue; // log_info("Handle 0x%04x", it.handle); uint16_t this_uuid_len = (it.flags & ATT_PROPERTY_UUID128) ? 16 : 2; // check if value has same len as last one if not first result if (offset > 1){ if (this_uuid_len != uuid_len) { break; } } // first if (offset == 1) { uuid_len = this_uuid_len; // set format field response_buffer[offset] = (it.flags & ATT_PROPERTY_UUID128) ? 0x02 : 0x01; offset++; } // space? if (offset + 2 + uuid_len > response_buffer_size) break; // store bt_store_16(response_buffer, offset, it.handle); offset += 2; memcpy(response_buffer + offset, it.uuid, uuid_len); offset += uuid_len; } if (offset == 1){ return setup_error_atribute_not_found(response_buffer, request_type, start_handle); } response_buffer[0] = ATT_FIND_INFORMATION_REPLY; return offset; }
int att_find_handle(att_iterator_t *it, uint16_t handle){ att_iterator_init(it); while (att_iterator_has_next(it)){ att_iterator_fetch_next(it); if (it->handle != handle) continue; return 1; } return 0; }
void att_dump_attributes(void){ att_iterator_t it; att_iterator_init(&it); while (att_iterator_has_next(&it)){ att_iterator_fetch_next(&it); if (it.handle == 0) { printf("Handle: END\n"); return; } printf("Handle: 0x%04x, flags: 0x%04x, uuid: ", it.handle, it.flags); if (it.flags & ATT_PROPERTY_UUID128){ printUUID128(it.uuid); } else { printf("%04x", READ_BT_16(it.uuid, 0)); } printf(", value_len: %u, value: ", it.value_len); hexdump2(it.value, it.value_len); } }
void att_dump_attributes(void){ att_iterator_t it; att_iterator_init(&it); uint8_t uuid128[16]; while (att_iterator_has_next(&it)){ att_iterator_fetch_next(&it); if (it.handle == 0) { log_info("Handle: END"); return; } log_info("Handle: 0x%04x, flags: 0x%04x, uuid: ", it.handle, it.flags); if (it.flags & ATT_PROPERTY_UUID128){ swap128(it.uuid, uuid128); log_info("%s", uuid128_to_str(uuid128)); } else { log_info("%04x", READ_BT_16(it.uuid, 0)); } log_info(", value_len: %u, value: ", it.value_len); hexdump(it.value, it.value_len); } }
// // MARK: ATT_READ_BY_GROUP_TYPE_REQUEST 0x10 // // Only handles GATT_PRIMARY_SERVICE_UUID and GATT_SECONDARY_SERVICE_UUID // Core v4.0, vol 3, part g, 2.5.3 // "The «Primary Service» and «Secondary Service» grouping types may be used in the Read By Group Type Request. // The «Characteristic» grouping type shall not be used in the ATT Read By Group Type Request." // // NOTE: doesn't handle DYNAMIC values // // NOTE: we don't check for security as PRIMARY and SECONDAY SERVICE definition shouldn't be protected // Core 4.0, vol 3, part g, 8.1 // "The list of services and characteristics that a device supports is not considered private or // confidential information, and therefore the Service and Characteristic Discovery procedures // shall always be permitted. " // static uint16_t handle_read_by_group_type_request2(att_connection_t * att_connection, uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t start_handle, uint16_t end_handle, uint16_t attribute_type_len, uint8_t * attribute_type){ log_info("ATT_READ_BY_GROUP_TYPE_REQUEST: from %04X to %04X, buffer size %u, type: ", start_handle, end_handle, response_buffer_size); hexdump(attribute_type, attribute_type_len); uint8_t request_type = ATT_READ_BY_GROUP_TYPE_REQUEST; if (start_handle > end_handle || start_handle == 0){ return setup_error_invalid_handle(response_buffer, request_type, start_handle); } // assert UUID is primary or secondary service uuid uint16_t uuid16 = uuid16_from_uuid(attribute_type_len, attribute_type); if (uuid16 != GATT_PRIMARY_SERVICE_UUID && uuid16 != GATT_SECONDARY_SERVICE_UUID){ return setup_error(response_buffer, request_type, start_handle, ATT_ERROR_UNSUPPORTED_GROUP_TYPE); } uint16_t offset = 1; uint16_t pair_len = 0; uint16_t in_group = 0; uint16_t group_start_handle = 0; uint8_t const * group_start_value = NULL; uint16_t prev_handle = 0; att_iterator_t it; att_iterator_init(&it); while (att_iterator_has_next(&it)){ att_iterator_fetch_next(&it); if (it.handle && it.handle < start_handle) continue; if (it.handle > end_handle) break; // (1) // log_info("Handle 0x%04x", it.handle); // close current tag, if within a group and a new service definition starts or we reach end of att db if (in_group && (it.handle == 0 || att_iterator_match_uuid16(&it, GATT_PRIMARY_SERVICE_UUID) || att_iterator_match_uuid16(&it, GATT_SECONDARY_SERVICE_UUID))){ // log_info("End of group, handle 0x%04x, val_len: %u", prev_handle, pair_len - 4); bt_store_16(response_buffer, offset, group_start_handle); offset += 2; bt_store_16(response_buffer, offset, prev_handle); offset += 2; memcpy(response_buffer + offset, group_start_value, pair_len - 4); offset += pair_len - 4; in_group = 0; // check if space for another handle pair available if (offset + pair_len > response_buffer_size){ break; } } // keep track of previous handle prev_handle = it.handle; // does current attribute match // log_info("compare: %04x == %04x", *(uint16_t*) context->attribute_type, *(uint16_t*) uuid); if (it.handle && att_iterator_match_uuid(&it, attribute_type, attribute_type_len)) { // check if value has same len as last one uint16_t this_pair_len = 4 + it.value_len; if (offset > 1){ if (this_pair_len != pair_len) { break; } } // log_info("Begin of group, handle 0x%04x", it.handle); // first if (offset == 1) { pair_len = this_pair_len; response_buffer[offset] = this_pair_len; offset++; } group_start_handle = it.handle; group_start_value = it.value; in_group = 1; } } if (offset == 1){ return setup_error_atribute_not_found(response_buffer, request_type, start_handle); } response_buffer[0] = ATT_READ_BY_GROUP_TYPE_RESPONSE; return offset; }
// // MARK: ATT_READ_BY_TYPE_REQUEST // static uint16_t handle_read_by_type_request2(att_connection_t * att_connection, uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t start_handle, uint16_t end_handle, uint16_t attribute_type_len, uint8_t * attribute_type){ log_info("ATT_READ_BY_TYPE_REQUEST: from %04X to %04X, type: ", start_handle, end_handle); hexdump(attribute_type, attribute_type_len); uint8_t request_type = ATT_READ_BY_TYPE_REQUEST; if (start_handle > end_handle || start_handle == 0){ return setup_error_invalid_handle(response_buffer, request_type, start_handle); } uint16_t offset = 1; uint16_t pair_len = 0; att_iterator_t it; att_iterator_init(&it); uint8_t error_code = 0; uint16_t first_matching_but_unreadable_handle = 0; while (att_iterator_has_next(&it)){ att_iterator_fetch_next(&it); if (!it.handle) break; if (it.handle < start_handle) continue; if (it.handle > end_handle) break; // (1) // does current attribute match if (!att_iterator_match_uuid(&it, attribute_type, attribute_type_len)) continue; // skip handles that cannot be read but rembember that there has been at least one if ((it.flags & ATT_PROPERTY_READ) == 0) { if (first_matching_but_unreadable_handle == 0) { first_matching_but_unreadable_handle = it.handle; } continue; } // check security requirements error_code = att_validate_security(att_connection, &it); if (error_code) break; att_update_value_len(&it, att_connection->con_handle); // check if value has same len as last one uint16_t this_pair_len = 2 + it.value_len; if (offset > 1){ if (pair_len != this_pair_len) { break; } } // first if (offset == 1) { pair_len = this_pair_len; response_buffer[offset] = pair_len; offset++; } // space? if (offset + pair_len > response_buffer_size) { if (offset > 2) break; it.value_len = response_buffer_size - 4; response_buffer[1] = 2 + it.value_len; } // store bt_store_16(response_buffer, offset, it.handle); offset += 2; uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len, att_connection->con_handle); offset += bytes_copied; } // at least one attribute could be read if (offset > 1){ response_buffer[0] = ATT_READ_BY_TYPE_RESPONSE; return offset; } // first attribute had an error if (error_code){ return setup_error(response_buffer, request_type, start_handle, error_code); } // no other errors, but all found attributes had been non-readable if (first_matching_but_unreadable_handle){ return setup_error_read_not_permitted(response_buffer, request_type, first_matching_but_unreadable_handle); } // attribute not found return setup_error_atribute_not_found(response_buffer, request_type, start_handle); }