// SPDX-License-Identifier: GPL-2.0-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2011 Nokia Corporation * Copyright (C) 2011 Marcel Holtmann * * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "lib/bluetooth.h" #include "lib/hci.h" #include "lib/sdp.h" #include "src/shared/util.h" #include "uuid-helper.h" #include "eir.h" #define EIR_OOB_MIN (2 + 6) static void sd_free(void *data) { struct eir_sd *sd = data; free(sd->uuid); g_free(sd); } static void data_free(void *data) { struct eir_ad *ad = data; g_free(ad->data); g_free(ad); } void eir_data_free(struct eir_data *eir) { g_slist_free_full(eir->services, free); eir->services = NULL; g_free(eir->name); eir->name = NULL; free(eir->hash); eir->hash = NULL; free(eir->randomizer); eir->randomizer = NULL; g_slist_free_full(eir->msd_list, g_free); eir->msd_list = NULL; g_slist_free_full(eir->sd_list, sd_free); eir->sd_list = NULL; g_slist_free_full(eir->data_list, data_free); eir->data_list = NULL; } static void eir_parse_uuid16(struct eir_data *eir, const void *data, uint8_t len) { const uint16_t *uuid16 = data; uuid_t service; char *uuid_str; unsigned int i; service.type = SDP_UUID16; for (i = 0; i < len / 2; i++, uuid16++) { service.value.uuid16 = get_le16(uuid16); uuid_str = bt_uuid2string(&service); if (!uuid_str) continue; eir->services = g_slist_append(eir->services, uuid_str); } } static void eir_parse_uuid32(struct eir_data *eir, const void *data, uint8_t len) { const uint32_t *uuid32 = data; uuid_t service; char *uuid_str; unsigned int i; service.type = SDP_UUID32; for (i = 0; i < len / 4; i++, uuid32++) { service.value.uuid32 = get_le32(uuid32); uuid_str = bt_uuid2string(&service); if (!uuid_str) continue; eir->services = g_slist_append(eir->services, uuid_str); } } static void eir_parse_uuid128(struct eir_data *eir, const uint8_t *data, uint8_t len) { const uint8_t *uuid_ptr = data; uuid_t service; char *uuid_str; unsigned int i; int k; service.type = SDP_UUID128; for (i = 0; i < len / 16; i++) { for (k = 0; k < 16; k++) service.value.uuid128.data[k] = uuid_ptr[16 - k - 1]; uuid_str = bt_uuid2string(&service); if (!uuid_str) continue; eir->services = g_slist_append(eir->services, uuid_str); uuid_ptr += 16; } } static char *name2utf8(const uint8_t *name, uint8_t len) { char utf8_name[HCI_MAX_NAME_LENGTH + 2]; int i; if (g_utf8_validate((const char *) name, len, NULL)) return g_strndup((char *) name, len); memset(utf8_name, 0, sizeof(utf8_name)); strncpy(utf8_name, (char *) name, len); /* Assume ASCII, and replace all non-ASCII with spaces */ for (i = 0; utf8_name[i] != '\0'; i++) { if (!isascii(utf8_name[i])) utf8_name[i] = ' '; } /* Remove leading and trailing whitespace characters */ g_strstrip(utf8_name); return g_strdup(utf8_name); } static void eir_parse_msd(struct eir_data *eir, const uint8_t *data, uint8_t len) { struct eir_msd *msd; if (len < 2 || len > 2 + sizeof(msd->data)) return; msd = g_malloc(sizeof(*msd)); msd->company = get_le16(data); msd->data_len = len - 2; memcpy(&msd->data, data + 2, msd->data_len); eir->msd_list = g_slist_append(eir->msd_list, msd); } static void eir_parse_sd(struct eir_data *eir, uuid_t *service, const uint8_t *data, uint8_t len) { struct eir_sd *sd; char *uuid; uuid = bt_uuid2string(service); if (!uuid) return; sd = g_malloc(sizeof(*sd)); sd->uuid = uuid; sd->data_len = len; memcpy(&sd->data, data, sd->data_len); eir->sd_list = g_slist_append(eir->sd_list, sd); } static void eir_parse_uuid16_data(struct eir_data *eir, const uint8_t *data, uint8_t len) { uuid_t service; if (len < 2 || len > EIR_SD_MAX_LEN) return; service.type = SDP_UUID16; service.value.uuid16 = get_le16(data); eir_parse_sd(eir, &service, data + 2, len - 2); } static void eir_parse_uuid32_data(struct eir_data *eir, const uint8_t *data, uint8_t len) { uuid_t service; if (len < 4 || len > EIR_SD_MAX_LEN) return; service.type = SDP_UUID32; service.value.uuid32 = get_le32(data); eir_parse_sd(eir, &service, data + 4, len - 4); } static void eir_parse_uuid128_data(struct eir_data *eir, const uint8_t *data, uint8_t len) { uuid_t service; int k; if (len < 16 || len > EIR_SD_MAX_LEN) return; service.type = SDP_UUID128; for (k = 0; k < 16; k++) service.value.uuid128.data[k] = data[16 - k - 1]; eir_parse_sd(eir, &service, data + 16, len - 16); } static void eir_parse_data(struct eir_data *eir, uint8_t type, const uint8_t *data, uint8_t len) { struct eir_ad *ad; ad = g_malloc(sizeof(*ad)); ad->type = type; ad->len = len; ad->data = g_malloc(len); memcpy(ad->data, data, len); eir->data_list = g_slist_append(eir->data_list, ad); if (type == EIR_CSIP_RSI) eir->rsi = true; } void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len) { uint16_t len = 0; eir->flags = 0; eir->tx_power = 127; /* No EIR data to parse */ if (eir_data == NULL) return; while (len < eir_len - 1) { uint8_t field_len = eir_data[0]; const uint8_t *data; uint8_t data_len; /* Check for the end of EIR */ if (field_len == 0) break; len += field_len + 1; /* Do not continue EIR Data parsing if got incorrect length */ if (len > eir_len) break; data = &eir_data[2]; data_len = field_len - 1; switch (eir_data[1]) { case EIR_UUID16_SOME: case EIR_UUID16_ALL: eir_parse_uuid16(eir, data, data_len); break; case EIR_UUID32_SOME: case EIR_UUID32_ALL: eir_parse_uuid32(eir, data, data_len); break; case EIR_UUID128_SOME: case EIR_UUID128_ALL: eir_parse_uuid128(eir, data, data_len); break; case EIR_FLAGS: if (data_len > 0) eir->flags = *data; break; case EIR_NAME_SHORT: case EIR_NAME_COMPLETE: /* Some vendors put a NUL byte terminator into * the name */ while (data_len > 0 && data[data_len - 1] == '\0') data_len--; g_free(eir->name); eir->name = name2utf8(data, data_len); eir->name_complete = eir_data[1] == EIR_NAME_COMPLETE; break; case EIR_TX_POWER: if (data_len < 1) break; eir->tx_power = (int8_t) data[0]; break; case EIR_CLASS_OF_DEV: if (data_len < 3) break; eir->class = data[0] | (data[1] << 8) | (data[2] << 16); break; case EIR_GAP_APPEARANCE: if (data_len < 2) break; eir->appearance = get_le16(data); break; case EIR_SSP_HASH: if (data_len < 16) break; eir->hash = util_memdup(data, 16); break; case EIR_SSP_RANDOMIZER: if (data_len < 16) break; eir->randomizer = util_memdup(data, 16); break; case EIR_DEVICE_ID: if (data_len < 8) break; eir->did_source = data[0] | (data[1] << 8); eir->did_vendor = data[2] | (data[3] << 8); eir->did_product = data[4] | (data[5] << 8); eir->did_version = data[6] | (data[7] << 8); break; case EIR_SVC_DATA16: eir_parse_uuid16_data(eir, data, data_len); break; case EIR_SVC_DATA32: eir_parse_uuid32_data(eir, data, data_len); break; case EIR_SVC_DATA128: eir_parse_uuid128_data(eir, data, data_len); break; case EIR_MANUFACTURER_DATA: eir_parse_msd(eir, data, data_len); break; default: eir_parse_data(eir, eir_data[1], data, data_len); break; } eir_data += field_len + 1; } } int eir_parse_oob(struct eir_data *eir, uint8_t *eir_data, uint16_t eir_len) { if (eir_len < EIR_OOB_MIN) return -1; if (eir_len != get_le16(eir_data)) return -1; eir_data += sizeof(uint16_t); eir_len -= sizeof(uint16_t); memcpy(&eir->addr, eir_data, sizeof(bdaddr_t)); eir_data += sizeof(bdaddr_t); eir_len -= sizeof(bdaddr_t); /* optional OOB EIR data */ if (eir_len > 0) eir_parse(eir, eir_data, eir_len); return 0; } #define SIZEOF_UUID128 16 static void eir_generate_uuid128(sdp_list_t *list, uint8_t *ptr, uint16_t *eir_len) { int i, k, uuid_count = 0; uint16_t len = *eir_len; uint8_t *uuid128; bool truncated = false; /* Store UUIDs in place, skip 2 bytes to write type and length later */ uuid128 = ptr + 2; for (; list; list = list->next) { sdp_record_t *rec = list->data; uuid_t *uuid = &rec->svclass; uint8_t *uuid128_data = uuid->value.uuid128.data; if (uuid->type != SDP_UUID128) continue; /* Stop if not enough space to put next UUID128 */ if ((len + 2 + SIZEOF_UUID128) > HCI_MAX_EIR_LENGTH) { truncated = true; break; } /* Check for duplicates, EIR data is Little Endian */ for (i = 0; i < uuid_count; i++) { for (k = 0; k < SIZEOF_UUID128; k++) { if (uuid128[i * SIZEOF_UUID128 + k] != uuid128_data[SIZEOF_UUID128 - 1 - k]) break; } if (k == SIZEOF_UUID128) break; } if (i < uuid_count) continue; /* EIR data is Little Endian */ for (k = 0; k < SIZEOF_UUID128; k++) uuid128[uuid_count * SIZEOF_UUID128 + k] = uuid128_data[SIZEOF_UUID128 - 1 - k]; len += SIZEOF_UUID128; uuid_count++; } if (uuid_count > 0 || truncated) { /* EIR Data length */ ptr[0] = (uuid_count * SIZEOF_UUID128) + 1; /* EIR Data type */ ptr[1] = truncated ? EIR_UUID128_SOME : EIR_UUID128_ALL; len += 2; *eir_len = len; } } int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod, const uint8_t *hash, const uint8_t *randomizer, uint16_t did_vendor, uint16_t did_product, uint16_t did_version, uint16_t did_source, sdp_list_t *uuids, uint8_t *data) { sdp_list_t *l; uint8_t *ptr = data; uint16_t eir_optional_len = 0; uint16_t eir_total_len; uint16_t uuid16[HCI_MAX_EIR_LENGTH / 2]; int i, uuid_count = 0; bool truncated = false; size_t name_len; eir_total_len = sizeof(uint16_t) + sizeof(bdaddr_t); ptr += sizeof(uint16_t); memcpy(ptr, addr, sizeof(bdaddr_t)); ptr += sizeof(bdaddr_t); if (cod > 0) { uint8_t class[3]; class[0] = (uint8_t) cod; class[1] = (uint8_t) (cod >> 8); class[2] = (uint8_t) (cod >> 16); *ptr++ = 4; *ptr++ = EIR_CLASS_OF_DEV; memcpy(ptr, class, sizeof(class)); ptr += sizeof(class); eir_optional_len += sizeof(class) + 2; } if (hash) { *ptr++ = 17; *ptr++ = EIR_SSP_HASH; memcpy(ptr, hash, 16); ptr += 16; eir_optional_len += 16 + 2; } if (randomizer) { *ptr++ = 17; *ptr++ = EIR_SSP_RANDOMIZER; memcpy(ptr, randomizer, 16); ptr += 16; eir_optional_len += 16 + 2; } name_len = strlen(name); if (name_len > 0) { /* EIR Data type */ if (name_len > 48) { name_len = 48; ptr[1] = EIR_NAME_SHORT; } else ptr[1] = EIR_NAME_COMPLETE; /* EIR Data length */ ptr[0] = name_len + 1; memcpy(ptr + 2, name, name_len); eir_optional_len += (name_len + 2); ptr += (name_len + 2); } if (did_vendor != 0x0000) { *ptr++ = 9; *ptr++ = EIR_DEVICE_ID; *ptr++ = (did_source & 0x00ff); *ptr++ = (did_source & 0xff00) >> 8; *ptr++ = (did_vendor & 0x00ff); *ptr++ = (did_vendor & 0xff00) >> 8; *ptr++ = (did_product & 0x00ff); *ptr++ = (did_product & 0xff00) >> 8; *ptr++ = (did_version & 0x00ff); *ptr++ = (did_version & 0xff00) >> 8; eir_optional_len += 10; } /* Group all UUID16 types */ for (l = uuids; l != NULL; l = l->next) { sdp_record_t *rec = l->data; uuid_t *uuid = &rec->svclass; if (uuid->type != SDP_UUID16) continue; if (uuid->value.uuid16 < 0x1100) continue; if (uuid->value.uuid16 == PNP_INFO_SVCLASS_ID) continue; /* Stop if not enough space to put next UUID16 */ if ((eir_optional_len + 2 + sizeof(uint16_t)) > HCI_MAX_EIR_LENGTH) { truncated = true; break; } /* Check for duplicates */ for (i = 0; i < uuid_count; i++) if (uuid16[i] == uuid->value.uuid16) break; if (i < uuid_count) continue; uuid16[uuid_count++] = uuid->value.uuid16; eir_optional_len += sizeof(uint16_t); } if (uuid_count > 0) { /* EIR Data length */ ptr[0] = (uuid_count * sizeof(uint16_t)) + 1; /* EIR Data type */ ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL; ptr += 2; eir_optional_len += 2; for (i = 0; i < uuid_count; i++) { *ptr++ = (uuid16[i] & 0x00ff); *ptr++ = (uuid16[i] & 0xff00) >> 8; } } /* Group all UUID128 types */ if (eir_optional_len <= HCI_MAX_EIR_LENGTH - 2) eir_generate_uuid128(uuids, ptr, &eir_optional_len); eir_total_len += eir_optional_len; /* store total length */ put_le16(eir_total_len, data); return eir_total_len; }