Files
esp-idf/components/bt/host/bluedroid/stack/obex/obex_api.c
linruihao a0ae67ef8b fix(bt/bluedroid): Fix some bugs in Bluedroid
- Fix PBAC may free undefine memory when conn failed
- Fix some memory in OBEX not being freed when bluedroid deinit
- Fix pre-commit not check some source file that we added to bluedroid
2025-03-21 11:12:07 +08:00

777 lines
23 KiB
C

/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "osi/osi.h"
#include "osi/allocator.h"
#include "common/bt_target.h"
#include "stack/obex_api.h"
#include "obex_int.h"
#include "obex_tl.h"
#include "obex_tl_l2cap.h"
#include "obex_tl_rfcomm.h"
#if (OBEX_INCLUDED == TRUE)
static inline void obex_server_to_tl_server(tOBEX_SVR_INFO *server, tOBEX_TL_SVR_INFO *tl_server)
{
if (server->tl == OBEX_OVER_L2CAP) {
tl_server->l2cap.psm = server->l2cap.psm;
tl_server->l2cap.sec_mask = server->l2cap.sec_mask;
tl_server->l2cap.pref_mtu = server->l2cap.pref_mtu;
bdcpy(tl_server->l2cap.addr, server->l2cap.addr);
}
else if (server->tl == OBEX_OVER_RFCOMM) {
tl_server->rfcomm.scn = server->rfcomm.scn;
tl_server->rfcomm.sec_mask = server->rfcomm.sec_mask;
tl_server->rfcomm.pref_mtu = server->rfcomm.pref_mtu;
bdcpy(tl_server->rfcomm.addr, server->rfcomm.addr);
}
else {
OBEX_TRACE_ERROR("Unsupported OBEX transport type\n");
assert(0);
}
}
static inline void obex_updata_packet_length(BT_HDR *p_buf, UINT16 len)
{
UINT8 *p_pkt_len = (UINT8 *)(p_buf + 1) + p_buf->offset + 1;
UINT16_TO_BE_FIELD(p_pkt_len, len);
}
/*******************************************************************************
**
** Function OBEX_Init
**
** Description Initialize OBEX Profile, must call before using any other
** OBEX APIs
**
** Returns OBEX_SUCCESS if successful, otherwise failed
**
*******************************************************************************/
UINT16 OBEX_Init(void)
{
#if (OBEX_DYNAMIC_MEMORY)
if (!obex_cb_ptr) {
obex_cb_ptr = (tOBEX_CB *)osi_malloc(sizeof(tOBEX_CB));
if (!obex_cb_ptr) {
return OBEX_NO_RESOURCES;
}
}
#endif /* #if (OBEX_DYNAMIC_MEMORY) */
memset(&obex_cb, 0, sizeof(tOBEX_CB));
obex_cb.tl_ops[OBEX_OVER_L2CAP] = obex_tl_l2cap_ops_get();
if (obex_cb.tl_ops[OBEX_OVER_L2CAP]->init != NULL) {
obex_cb.tl_ops[OBEX_OVER_L2CAP]->init(obex_tl_l2cap_callback);
}
#if (RFCOMM_INCLUDED == TRUE)
obex_cb.tl_ops[OBEX_OVER_RFCOMM] = obex_tl_rfcomm_ops_get();
if (obex_cb.tl_ops[OBEX_OVER_RFCOMM]->init != NULL) {
obex_cb.tl_ops[OBEX_OVER_RFCOMM]->init(obex_tl_rfcomm_callback);
}
#endif
obex_cb.trace_level = BT_TRACE_LEVEL_ERROR;
return OBEX_SUCCESS;
}
/*******************************************************************************
**
** Function OBEX_Deinit
**
** Description Deinit OBEX profile, once deinit, can not use any other
** APIs until call OBEX_Init again
**
*******************************************************************************/
void OBEX_Deinit(void)
{
if (obex_cb.tl_ops[OBEX_OVER_L2CAP]->deinit != NULL) {
obex_cb.tl_ops[OBEX_OVER_L2CAP]->deinit();
}
#if (RFCOMM_INCLUDED == TRUE)
if (obex_cb.tl_ops[OBEX_OVER_RFCOMM]->deinit != NULL) {
obex_cb.tl_ops[OBEX_OVER_RFCOMM]->deinit();
}
#endif
#if (OBEX_DYNAMIC_MEMORY)
if (obex_cb_ptr) {
osi_free(obex_cb_ptr);
obex_cb_ptr = NULL;
}
#endif /* #if (OBEX_DYNAMIC_MEMORY) */
}
/*******************************************************************************
**
** Function OBEX_CreateConn
**
** Description Start the progress of creating an OBEX connection
**
** Returns OBEX_SUCCESS if successful, otherwise failed, when the
** connection is opened, an OBEX_CONNECT_EVT will come
**
*******************************************************************************/
UINT16 OBEX_CreateConn(tOBEX_SVR_INFO *server, tOBEX_MSG_CBACK callback, UINT16 *out_handle)
{
UINT16 ret = OBEX_SUCCESS;
tOBEX_CCB *p_ccb = NULL;
do {
if (server->tl >= OBEX_NUM_TL) {
ret = OBEX_INVALID_PARAM;
break;
}
p_ccb = obex_allocate_ccb();
if (p_ccb == NULL) {
ret = OBEX_NO_RESOURCES;
break;
}
tOBEX_TL_SVR_INFO tl_server = {0};
obex_server_to_tl_server(server, &tl_server);
p_ccb->tl = server->tl;
p_ccb->tl_hdl = obex_cb.tl_ops[p_ccb->tl]->connect(&tl_server);
if (p_ccb->tl_hdl == 0) {
ret = OBEX_ERROR_TL;
break;
}
p_ccb->callback = callback;
p_ccb->role = OBEX_ROLE_CLIENT;
p_ccb->state = OBEX_STATE_OPENING;
*out_handle = p_ccb->allocated;
} while (0);
if (ret != OBEX_SUCCESS && p_ccb != NULL) {
obex_free_ccb(p_ccb);
}
return ret;
}
/*******************************************************************************
**
** Function OBEX_RemoveConn
**
** Description Remove an OBEX connection
**
** Returns OBEX_SUCCESS if successful, otherwise failed
**
*******************************************************************************/
UINT16 OBEX_RemoveConn(UINT16 handle)
{
tOBEX_CCB *p_ccb = NULL;
UINT16 ccb_idx = handle - 1;
if (ccb_idx >= OBEX_MAX_CONNECTION || !obex_cb.ccb[ccb_idx].allocated) {
return OBEX_BAD_HANDLE;
}
p_ccb = &obex_cb.ccb[ccb_idx];
obex_cb.tl_ops[p_ccb->tl]->disconnect(p_ccb->tl_hdl);
obex_free_ccb(p_ccb);
return OBEX_SUCCESS;
}
/*******************************************************************************
**
** Function OBEX_RegisterServer
**
** Description Register an OBEX server and listen the incoming connection
**
** Returns OBEX_SUCCESS if successful, otherwise failed
**
*******************************************************************************/
UINT16 OBEX_RegisterServer(tOBEX_SVR_INFO *server, tOBEX_MSG_CBACK callback, UINT16 *out_svr_handle)
{
UINT8 ret = OBEX_SUCCESS;
tOBEX_SCB *p_scb = NULL;
do {
if (server->tl >= OBEX_NUM_TL) {
ret = OBEX_INVALID_PARAM;
break;
}
p_scb = obex_allocate_scb();
if (p_scb == NULL) {
ret = OBEX_NO_RESOURCES;
break;
}
tOBEX_TL_SVR_INFO tl_server = {0};
obex_server_to_tl_server(server, &tl_server);
p_scb->tl = server->tl;
p_scb->tl_hdl = obex_cb.tl_ops[p_scb->tl]->bind(&tl_server);
if (p_scb->tl_hdl == 0) {
ret = OBEX_ERROR_TL;
break;
}
p_scb->callback = callback;
if (out_svr_handle) {
/* To avoid confuse with connection handle, left shift 8 bit */
*out_svr_handle = p_scb->allocated << 8;
}
} while (0);
if (ret != OBEX_SUCCESS && p_scb != NULL) {
obex_free_scb(p_scb);
}
return ret;
}
/*******************************************************************************
**
** Function OBEX_DeregisterServer
**
** Description Deregister an OBEX server, if there are still a connection
** alive, the behavior depend on the implementation of transport
** layer
**
** Returns OBEX_SUCCESS if successful, otherwise failed
**
*******************************************************************************/
UINT16 OBEX_DeregisterServer(UINT16 svr_handle)
{
tOBEX_SCB *p_scb = NULL;
UINT16 scb_idx = (svr_handle >> 8) - 1;
if (scb_idx >= OBEX_MAX_SERVER || !obex_cb.scb[scb_idx].allocated) {
return OBEX_BAD_HANDLE;
}
p_scb = &obex_cb.scb[scb_idx];
obex_cb.tl_ops[p_scb->tl]->unbind(p_scb->tl_hdl);
obex_free_scb(p_scb);
return OBEX_SUCCESS;
}
/*******************************************************************************
**
** Function OBEX_SendPacket
**
** Description Send a packet to peer OBEX server or client, once call
** this function, the ownership of pkt is lost, do not free
** or modify the pkt any more
**
** Returns OBEX_SUCCESS if successful, otherwise failed
**
*******************************************************************************/
UINT16 OBEX_SendPacket(UINT16 handle, BT_HDR *pkt)
{
UINT16 ret = OBEX_SUCCESS;
BOOLEAN free_pkt = true;
tOBEX_CCB *p_ccb = NULL;
do {
if (pkt == NULL) {
ret = OBEX_INVALID_PARAM;
break;
}
UINT16 ccb_idx = handle - 1;
if (ccb_idx >= OBEX_MAX_CONNECTION || !obex_cb.ccb[ccb_idx].allocated) {
ret = OBEX_BAD_HANDLE;
break;
}
p_ccb = &obex_cb.ccb[ccb_idx];
if (p_ccb->state != OBEX_STATE_OPENED) {
ret = OBEX_NOT_OPEN;
break;
}
if (pkt->len > p_ccb->tl_peer_mtu) {
ret = OBEX_PACKET_TOO_LARGE;
break;
}
ret = obex_cb.tl_ops[p_ccb->tl]->send(p_ccb->tl_hdl, pkt);
/* packet has pass to lower layer, do not free it */
free_pkt = false;
if (ret == OBEX_TL_SUCCESS || ret == OBEX_TL_CONGESTED) {
ret = OBEX_SUCCESS;
}
else {
ret = OBEX_ERROR_TL;
}
} while (0);
if (free_pkt) {
osi_free(pkt);
}
return ret;
}
/*******************************************************************************
**
** Function OBEX_BuildRequest
**
** Description Build a request packet with opcode and additional info,
** packet can free by osi_free
**
** Returns OBEX_SUCCESS if successful, otherwise failed
**
*******************************************************************************/
UINT16 OBEX_BuildRequest(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_pkt)
{
if (buff_size < OBEX_MIN_PACKET_SIZE || info == NULL || out_pkt == NULL) {
return OBEX_INVALID_PARAM;
}
buff_size += sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET + OBEX_BT_HDR_RESERVE_LEN;
BT_HDR *p_buf= (BT_HDR *)osi_malloc(buff_size);
if (p_buf == NULL) {
return OBEX_NO_RESOURCES;
}
UINT16 pkt_len = OBEX_MIN_PACKET_SIZE;
p_buf->offset = OBEX_BT_HDR_MIN_OFFSET;
/* use layer_specific to store the max data length allowed */
p_buf->layer_specific = buff_size - sizeof(BT_HDR) - OBEX_BT_HDR_MIN_OFFSET - OBEX_BT_HDR_RESERVE_LEN;
UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
/* byte 0: opcode */
*p_data++ = info->opcode;
/* byte 1, 2: packet length, skip, we will update at last */
UINT8 *p_pkt_len = p_data;
p_data += 2;
switch (info->opcode)
{
case OBEX_OPCODE_CONNECT:
/* byte 3: OBEX version number */
*p_data++ = info->obex_version_number;
/* byte 4: flags */
*p_data++ = info->flags;
/* byte 5, 6: maximum OBEX packet length, recommend to set as our mtu*/
UINT16_TO_BE_FIELD(p_data, info->max_packet_length);
pkt_len += 4;
break;
case OBEX_OPCODE_SETPATH:
/* byte 3: flags */
*p_data++ = info->flags;
/* byte 4: constants, reserved, must be zero */
*p_data++ = 0;
pkt_len += 2;
break;
default:
break;
}
UINT16_TO_BE_FIELD(p_pkt_len, pkt_len);
p_buf->len = pkt_len;
*out_pkt = p_buf;
return OBEX_SUCCESS;
}
/*******************************************************************************
**
** Function OBEX_BuildResponse
**
** Description Build a response packet with opcode and response and additional
** info, packet can by free by osi_free
**
** Returns OBEX_SUCCESS if successful, otherwise failed
**
*******************************************************************************/
UINT16 OBEX_BuildResponse(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_pkt)
{
if (buff_size < OBEX_MIN_PACKET_SIZE || info == NULL || out_pkt == NULL) {
return OBEX_INVALID_PARAM;
}
buff_size += sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET + OBEX_BT_HDR_RESERVE_LEN;
BT_HDR *p_buf= (BT_HDR *)osi_malloc(buff_size);
if (p_buf == NULL) {
return OBEX_NO_RESOURCES;
}
UINT16 pkt_len = OBEX_MIN_PACKET_SIZE;
p_buf->offset = OBEX_BT_HDR_MIN_OFFSET;
/* use layer_specific to store the max data length allowed */
p_buf->layer_specific = buff_size - sizeof(BT_HDR) - OBEX_BT_HDR_MIN_OFFSET - OBEX_BT_HDR_RESERVE_LEN;
UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
/* byte 0: response code */
*p_data++ = info->response_code;
/* byte 1, 2: packet length, skip, we will update at last */
UINT8 *p_pkt_len = p_data;
p_data += 2;
/* we need to use opcode to decide the response format */
switch (info->opcode)
{
case OBEX_OPCODE_CONNECT:
/* byte 3: OBEX version number */
*p_data++ = info->obex_version_number;
/* byte 4: flags */
*p_data++ = info->flags;
/* byte 5, 6: maximum OBEX packet length, recommend to set as our mtu */
UINT16_TO_BE_FIELD(p_data, info->max_packet_length);
pkt_len += 4;
break;
default:
break;
}
UINT16_TO_BE_FIELD(p_pkt_len, pkt_len);
p_buf->len = pkt_len;
*out_pkt = p_buf;
return OBEX_SUCCESS;
}
/*******************************************************************************
**
** Function OBEX_AppendHeader
**
** Description Append a header to specific packet, packet can be request
** or response, the format of header must be valid
**
** Returns OBEX_SUCCESS if successful, otherwise failed
**
*******************************************************************************/
UINT16 OBEX_AppendHeader(BT_HDR *pkt, const UINT8 *header)
{
if (pkt == NULL || header == NULL) {
return OBEX_INVALID_PARAM;
}
UINT16 header_len = 0;
UINT8 header_id = *header;
switch (header_id & OBEX_HEADER_ID_U2B_MASK)
{
case OBEX_HEADER_ID_U2B_TYPE1:
case OBEX_HEADER_ID_U2B_TYPE2:
header_len = (header[1] << 8) + header[2];
break;
case OBEX_HEADER_ID_U2B_TYPE3:
header_len = 2;
break;
case OBEX_HEADER_ID_U2B_TYPE4:
header_len = 5;
break;
default:
break;
}
if (header_len == 0) {
return OBEX_INVALID_PARAM;
}
if (pkt->layer_specific - pkt->len < header_len) {
/* the packet can not hold this header */
return OBEX_NO_RESOURCES;
}
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
UINT8 *p_start = p_data + pkt->len;
memcpy(p_start, header, header_len);
pkt->len += header_len;
/* point to packet len */
p_data++;
UINT16_TO_BE_FIELD(p_data, pkt->len);
return OBEX_SUCCESS;
}
/*******************************************************************************
**
** Function OBEX_AppendHeaderRaw
**
** Description Append a header to specific packet, packet can be request
** or response, data not include 2 byte length prefixed
**
** Returns OBEX_SUCCESS if successful, otherwise failed
**
*******************************************************************************/
UINT16 OBEX_AppendHeaderRaw(BT_HDR *pkt, UINT8 header_id, const UINT8 *data, UINT16 data_len)
{
if (pkt == NULL) {
return OBEX_INVALID_PARAM;
}
if ((data == NULL && data_len != 0) || (data != NULL && data_len == 0)) {
return OBEX_INVALID_PARAM;
}
UINT16 header_len = 0;
BOOLEAN store_header_len = FALSE;
switch (header_id & OBEX_HEADER_ID_U2B_MASK)
{
case OBEX_HEADER_ID_U2B_TYPE1:
case OBEX_HEADER_ID_U2B_TYPE2:
/* header id + 2 byte length prefixed + data */
header_len = data_len + 3;
store_header_len = TRUE;
break;
case OBEX_HEADER_ID_U2B_TYPE3:
header_len = 2;
if (data_len != 1) {
return OBEX_INVALID_PARAM;
}
break;
case OBEX_HEADER_ID_U2B_TYPE4:
header_len = 5;
if (data_len != 4) {
return OBEX_INVALID_PARAM;
}
break;
default:
break;
}
if (header_len == 0) {
return OBEX_INVALID_PARAM;
}
if (pkt->layer_specific - pkt->len < header_len) {
/* the packet can not hold this header */
return OBEX_NO_RESOURCES;
}
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
UINT8 *p_start = p_data + pkt->len;
/* store header id */
*p_start++ = header_id;
if (store_header_len) {
/* store header length */
UINT16_TO_BE_FIELD(p_start, header_len);
p_start+= 2;
}
if (data != NULL) {
/* store data */
memcpy(p_start, data, data_len);
}
pkt->len += header_len;
/* point to packet len */
p_data++;
UINT16_TO_BE_FIELD(p_data, pkt->len);
return OBEX_SUCCESS;
}
/*******************************************************************************
**
** Function OBEX_AppendHeaderSRM
**
** Description Append a Single Response Mode header
**
** Returns OBEX_SUCCESS if successful, otherwise failed
**
*******************************************************************************/
UINT16 OBEX_AppendHeaderSRM(BT_HDR *pkt, UINT8 value)
{
return OBEX_AppendHeaderRaw(pkt, OBEX_HEADER_ID_SRM, &value, 1);
}
/*******************************************************************************
**
** Function OBEX_AppendHeaderSRMP
**
** Description Append a Single Response Mode Parameters header
**
** Returns OBEX_SUCCESS if successful, otherwise failed
**
*******************************************************************************/
UINT16 OBEX_AppendHeaderSRMP(BT_HDR *pkt, UINT8 value)
{
return OBEX_AppendHeaderRaw(pkt, OBEX_HEADER_ID_SRM_PARAM, &value, 1);
}
/*******************************************************************************
**
** Function OBEX_GetPacketFreeSpace
**
** Description Get the current free space of a packet, use this to check
** if a packet can hold a header
**
** Returns Current free space of a packet, in bytes
**
*******************************************************************************/
UINT16 OBEX_GetPacketFreeSpace(BT_HDR *pkt)
{
if (pkt == NULL) {
return 0;
}
return pkt->layer_specific - pkt->len;
}
/*******************************************************************************
**
** Function OBEX_GetPacketLength
**
** Description Get the current packet length
**
** Returns Current packet length, in bytes
**
*******************************************************************************/
UINT16 OBEX_GetPacketLength(BT_HDR *pkt)
{
if (pkt == NULL) {
return 0;
}
return pkt->len;
}
/*******************************************************************************
**
** Function OBEX_ParseRequest
**
** Description Parse a request packet
**
** Returns OBEX_SUCCESS if successful, otherwise failed
**
*******************************************************************************/
UINT16 OBEX_ParseRequest(BT_HDR *pkt, tOBEX_PARSE_INFO *info)
{
if (pkt == NULL || info == NULL) {
return OBEX_INVALID_PARAM;
}
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
info->opcode = *p_data;
switch (info->opcode)
{
case OBEX_OPCODE_CONNECT:
info->obex_version_number = p_data[3];
info->flags = p_data[4];
info->max_packet_length = (p_data[5] << 8) + p_data[6];
info->next_header_pos = 7;
break;
case OBEX_OPCODE_SETPATH:
info->flags = p_data[3];
info->next_header_pos = 5;
break;
default:
info->next_header_pos = 3;
break;
}
return OBEX_SUCCESS;
}
/*******************************************************************************
**
** Function OBEX_ParseResponse
**
** Description Parse a request response packet
**
** Returns OBEX_SUCCESS if successful, otherwise failed
**
*******************************************************************************/
UINT16 OBEX_ParseResponse(BT_HDR *pkt, UINT8 opcode, tOBEX_PARSE_INFO *info)
{
if (pkt == NULL || info == NULL) {
return OBEX_INVALID_PARAM;
}
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
info->opcode = opcode;
info->response_code = *p_data;
switch (opcode)
{
case OBEX_OPCODE_CONNECT:
info->obex_version_number = p_data[3];
info->flags = p_data[4];
info->max_packet_length = (p_data[5] << 8) + p_data[6];
info->next_header_pos = 7;
break;
default:
info->next_header_pos = 3;
break;
}
return OBEX_SUCCESS;
}
/*******************************************************************************
**
** Function OBEX_CheckFinalBit
**
** Description Check whether a packet had set the final bit
**
** Returns TRUE if final bit set, otherwise, false
**
*******************************************************************************/
BOOLEAN OBEX_CheckFinalBit(BT_HDR *pkt)
{
if (pkt == NULL) {
return FALSE;
}
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
return (*p_data) & OBEX_FINAL_BIT_MASK;
}
/*******************************************************************************
**
** Function OBEX_CheckContinueResponse
**
** Description Check whether a packet is continue response
**
** Returns TRUE if continue response, otherwise, false
**
*******************************************************************************/
BOOLEAN OBEX_CheckContinueResponse(BT_HDR *pkt)
{
if (pkt == NULL) {
return FALSE;
}
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
return (*p_data == 0x90) || (*p_data == 0x10);
}
/*******************************************************************************
**
** Function OBEX_GetHeaderLength
**
** Description Get header length
**
** Returns header length
**
*******************************************************************************/
UINT16 OBEX_GetHeaderLength(UINT8 *header)
{
UINT16 header_len = 0;
UINT8 header_id = *header;
switch (header_id & OBEX_HEADER_ID_U2B_MASK)
{
case OBEX_HEADER_ID_U2B_TYPE1:
case OBEX_HEADER_ID_U2B_TYPE2:
header_len = (header[1] << 8) + header[2];
break;
case OBEX_HEADER_ID_U2B_TYPE3:
header_len = 2;
break;
case OBEX_HEADER_ID_U2B_TYPE4:
header_len = 5;
break;
default:
/* unreachable */
break;
}
return header_len;
}
/*******************************************************************************
**
** Function OBEX_GetNextHeader
**
** Description Get next header pointer from a packet
**
** Returns Pointer to start address of a header, NULL if no more header
** or failed
**
*******************************************************************************/
UINT8 *OBEX_GetNextHeader(BT_HDR *pkt, tOBEX_PARSE_INFO *info)
{
if (pkt == NULL || info == NULL) {
return NULL;
}
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
if (info->next_header_pos == 0 || info->next_header_pos >= pkt->len) {
return NULL;
}
UINT8 *header = p_data + info->next_header_pos;
UINT16 header_len = OBEX_GetHeaderLength(header);
info->next_header_pos += header_len;
return header;
}
#endif /* #if (OBEX_INCLUDED == TRUE) */