mirror of
https://github.com/obgm/libcoap.git
synced 2026-10-26 12:41:22 +03:00
coap_block.c: Use get_func wherever a request body is added
For a request set up with coap_add_data_large_request_app(), the data is provided by the application's get_func and there is no data pointer. Three places still took the body from that NULL pointer, so coap_add_data() was passed a NULL pointer: - check_freshness(), re-sending the next block with the Echo option from a 4.01 response. - coap_cancel_observe_lkd(), re-adding the observe request's body to the cancel request. - coap_add_data_large_internal(), when the application sets a Block1 option with a non-zero block number to send a single block. Use get_func in all of these, as the other places that send a block of an lg_xmit already do. The single block and the whole-body cases now share add_get_func_data(), which also checks the allocation and no longer leaks the buffer when get_func fails. Add unit tests.
This commit is contained in:
1 parent
85ed6ebb93
commit
92fc71e908
9 files changed
+312
-17
No files matched your search
@@ -953,6 +953,8 @@ if(ENABLE_TESTS)
|
||||
${CMAKE_CURRENT_LIST_DIR}/tests/test_common.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/tests/test_encode.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/tests/test_encode.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/tests/test_block.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/tests/test_block.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/tests/test_error_response.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/tests/test_error_response.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/tests/test_options.c
|
||||
|
||||
@@ -152,6 +152,7 @@ EXTRA_DIST = \
|
||||
src/coap_strm_contiki.c \
|
||||
src/coap_strm_lwip.c \
|
||||
src/coap_strm_riot.c \
|
||||
tests/test_block.h \
|
||||
tests/test_common.h \
|
||||
tests/test_error_response.h \
|
||||
tests/test_encode.h \
|
||||
|
||||
+62
-17
@@ -621,7 +621,13 @@ coap_cancel_observe_lkd(coap_session_t *session, coap_binary_t *token,
|
||||
lg_crcv->o_blk_size),
|
||||
buf);
|
||||
}
|
||||
if (lg_crcv->obs_data) {
|
||||
if (lg_crcv->obs_data && lg_crcv->obs_data->get_func) {
|
||||
coap_add_data_large_request_app_lkd(session, pdu,
|
||||
lg_crcv->obs_data->length,
|
||||
NULL,
|
||||
lg_crcv->obs_data->get_func,
|
||||
lg_crcv->obs_data->app_ptr);
|
||||
} else if (lg_crcv->obs_data) {
|
||||
coap_add_data_large_request_lkd(session, pdu,
|
||||
lg_crcv->obs_data->length,
|
||||
lg_crcv->obs_data->data, NULL, NULL);
|
||||
@@ -799,6 +805,31 @@ coap_find_lg_xmit_response(const coap_session_t *session,
|
||||
}
|
||||
#endif /* COAP_SERVER_SUPPORT */
|
||||
|
||||
/*
|
||||
* Add up to length bytes of the body starting at offset, as provided by
|
||||
* the application's get_func, to pdu.
|
||||
*/
|
||||
static int
|
||||
add_get_func_data(coap_session_t *session, coap_pdu_t *pdu,
|
||||
coap_get_large_data_t get_func, void *app_ptr,
|
||||
size_t offset, size_t length) {
|
||||
uint8_t *l_data;
|
||||
size_t l_length;
|
||||
int ret = 1;
|
||||
|
||||
if (length == 0)
|
||||
return 1;
|
||||
l_data = coap_malloc_type(COAP_STRING, length);
|
||||
if (!l_data) {
|
||||
coap_log_warn("out of memory allocating %" PRIuS " bytes for block data\n", length);
|
||||
return 0;
|
||||
}
|
||||
if (get_func(session, length, offset, l_data, &l_length, app_ptr))
|
||||
ret = coap_add_data(pdu, l_length, l_data);
|
||||
coap_free_type(COAP_STRING, l_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
coap_add_data_large_internal(coap_session_t *session,
|
||||
const coap_pdu_t *request,
|
||||
@@ -1094,8 +1125,14 @@ coap_add_data_large_internal(coap_session_t *session,
|
||||
rem = chunk;
|
||||
if (chunk > length - block.num * chunk)
|
||||
rem = length - block.num * chunk;
|
||||
if (!coap_add_data(pdu, rem, &data[block.num * chunk]))
|
||||
goto fail;
|
||||
if (get_func) {
|
||||
if (!add_get_func_data(session, pdu, get_func, app_ptr,
|
||||
block.num * chunk, rem))
|
||||
goto fail;
|
||||
} else {
|
||||
if (!coap_add_data(pdu, rem, &data[block.num * chunk]))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (release_func) {
|
||||
coap_lock_callback(release_func(session, app_ptr));
|
||||
@@ -1329,16 +1366,8 @@ coap_add_data_large_internal(coap_session_t *session,
|
||||
}
|
||||
add_data:
|
||||
if (get_func) {
|
||||
uint8_t *l_data = coap_malloc_type(COAP_STRING, length);
|
||||
size_t l_length;
|
||||
|
||||
if (get_func(session, length, 0, l_data, &l_length, app_ptr)) {
|
||||
if (!coap_add_data(pdu, l_length, l_data)) {
|
||||
coap_free_type(COAP_STRING, l_data);
|
||||
goto fail;
|
||||
}
|
||||
coap_free_type(COAP_STRING, l_data);
|
||||
}
|
||||
if (!add_get_func_data(session, pdu, get_func, app_ptr, 0, length))
|
||||
goto fail;
|
||||
} else {
|
||||
if (!coap_add_data(pdu, length, data))
|
||||
goto fail;
|
||||
@@ -4092,6 +4121,12 @@ check_freshness(coap_session_t *session, coap_pdu_t *rcvd, coap_pdu_t *sent,
|
||||
uint8_t ltoken[8];
|
||||
size_t ltoken_len;
|
||||
uint64_t token;
|
||||
#if COAP_CONSTRAINED_STACK
|
||||
/* Protected by global_lock if needed */
|
||||
static uint8_t l_data[1024];
|
||||
#else /* ! COAP_CONSTRAINED_STACK */
|
||||
uint8_t l_data[1024];
|
||||
#endif /* ! COAP_CONSTRAINED_STACK */
|
||||
|
||||
if (sent) {
|
||||
if (coap_get_data(sent, &data_len, &data))
|
||||
@@ -4101,10 +4136,20 @@ check_freshness(coap_session_t *session, coap_pdu_t *rcvd, coap_pdu_t *sent,
|
||||
if (lg_xmit->data_info->length) {
|
||||
size_t blk_size = (size_t)1 << (lg_xmit->blk_size + 4);
|
||||
size_t offset = (lg_xmit->last_block + 1) * blk_size;
|
||||
have_data = 1;
|
||||
data = &lg_xmit->data_info->data[offset];
|
||||
data_len = (lg_xmit->data_info->length - offset) > blk_size ? blk_size :
|
||||
lg_xmit->data_info->length - offset;
|
||||
if (lg_xmit->data_info->get_func) {
|
||||
assert(blk_size <= sizeof(l_data));
|
||||
if (lg_xmit->data_info->get_func(session, blk_size, offset, l_data,
|
||||
&data_len,
|
||||
lg_xmit->data_info->app_ptr)) {
|
||||
have_data = 1;
|
||||
data = l_data;
|
||||
}
|
||||
} else {
|
||||
have_data = 1;
|
||||
data = &lg_xmit->data_info->data[offset];
|
||||
data_len = (lg_xmit->data_info->length - offset) > blk_size ? blk_size :
|
||||
lg_xmit->data_info->length - offset;
|
||||
}
|
||||
}
|
||||
} else { /* lg_crcv */
|
||||
sent = lg_crcv->sent_pdu;
|
||||
|
||||
@@ -18,6 +18,7 @@ noinst_PROGRAMS = \
|
||||
|
||||
testdriver_SOURCES = \
|
||||
testdriver.c \
|
||||
test_block.c \
|
||||
test_error_response.c \
|
||||
test_encode.c \
|
||||
test_options.c \
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
/* libcoap unit tests
|
||||
*
|
||||
* Copyright (C) 2026 Alejandro Pacheco <alepacheco99@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* This file is part of the CoAP library libcoap. Please see
|
||||
* README for terms of use.
|
||||
*/
|
||||
|
||||
#include "test_common.h"
|
||||
#include "test_block.h"
|
||||
|
||||
#if COAP_CLIENT_SUPPORT
|
||||
#include <stdio.h>
|
||||
|
||||
#define ReturnIf_CU_ASSERT_PTR_NOT_NULL(value) \
|
||||
CU_ASSERT_PTR_NOT_NULL(value); \
|
||||
if ((void*)value == NULL) return;
|
||||
|
||||
#if COAP_SERVER_SUPPORT
|
||||
static size_t large_app_calls;
|
||||
|
||||
static int
|
||||
large_app_get_data(coap_session_t *s COAP_UNUSED, size_t max, size_t offset,
|
||||
uint8_t *data, size_t *length, void *app_ptr COAP_UNUSED) {
|
||||
large_app_calls++;
|
||||
if (offset >= 4096)
|
||||
return 0;
|
||||
*length = max < 4096 - offset ? max : 4096 - offset;
|
||||
memset(data, 'x', *length);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* A 4.01 with Echo for a request built by coap_add_data_large_request_app()
|
||||
* must get the re-sent block from the application callback. */
|
||||
static void
|
||||
t_block_echo_get_func(void) {
|
||||
coap_context_t *l_ctx;
|
||||
coap_session_t *l_session;
|
||||
coap_address_t saddr;
|
||||
coap_pdu_t *pdu;
|
||||
size_t calls;
|
||||
uint8_t token = 0x5a;
|
||||
uint8_t rsp[] = { 0x51, 0x81, 0x12, 0x34, 0x5a,
|
||||
0xd8, 0xef, 1, 2, 3, 4, 5, 6, 7, 8
|
||||
};
|
||||
|
||||
coap_address_init(&saddr);
|
||||
saddr.size = sizeof(struct sockaddr_in6);
|
||||
saddr.addr.sin6.sin6_family = AF_INET6;
|
||||
saddr.addr.sin6.sin6_addr = in6addr_loopback;
|
||||
saddr.addr.sin6.sin6_port = htons(20011);
|
||||
|
||||
l_ctx = coap_new_context(NULL);
|
||||
ReturnIf_CU_ASSERT_PTR_NOT_NULL(l_ctx);
|
||||
coap_context_set_block_mode(l_ctx, COAP_BLOCK_USE_LIBCOAP);
|
||||
/* Something to receive the request so that the re-send succeeds */
|
||||
CU_ASSERT_PTR_NOT_NULL(coap_new_endpoint(l_ctx, &saddr, COAP_PROTO_UDP));
|
||||
l_session = coap_new_client_session(l_ctx, NULL, &saddr, COAP_PROTO_UDP);
|
||||
CU_ASSERT_PTR_NOT_NULL(l_session);
|
||||
if (!l_session)
|
||||
goto done;
|
||||
|
||||
pdu = coap_pdu_init(COAP_MESSAGE_NON, COAP_REQUEST_CODE_PUT,
|
||||
coap_new_message_id(l_session),
|
||||
coap_session_max_pdu_size(l_session));
|
||||
CU_ASSERT_PTR_NOT_NULL(pdu);
|
||||
if (!pdu)
|
||||
goto done;
|
||||
coap_add_token(pdu, 1, &token);
|
||||
CU_ASSERT(coap_add_data_large_request_app(l_session, pdu, 4096, NULL,
|
||||
large_app_get_data, NULL) == 1);
|
||||
CU_ASSERT(coap_send(l_session, pdu) != COAP_INVALID_MID);
|
||||
|
||||
calls = large_app_calls;
|
||||
coap_lock_lock(goto done);
|
||||
coap_handle_dgram(l_ctx, l_session, rsp, sizeof(rsp));
|
||||
coap_lock_unlock();
|
||||
CU_ASSERT(large_app_calls > calls);
|
||||
|
||||
done:
|
||||
coap_free_context(l_ctx);
|
||||
}
|
||||
|
||||
/* Cancelling an observe whose request body was built by
|
||||
* coap_add_data_large_request_app() must get the body from the
|
||||
* application callback again. */
|
||||
static void
|
||||
t_block_cancel_observe_get_func(void) {
|
||||
coap_context_t *l_ctx;
|
||||
coap_session_t *l_session;
|
||||
coap_address_t saddr;
|
||||
coap_pdu_t *pdu;
|
||||
coap_binary_t tok;
|
||||
size_t calls;
|
||||
uint8_t token = 0x5b;
|
||||
uint8_t obs = 0;
|
||||
/* NON 2.05, token 0x5b, Observe 1 */
|
||||
uint8_t rsp[] = { 0x51, 0x45, 0x12, 0x35, 0x5b, 0x61, 0x01 };
|
||||
|
||||
coap_address_init(&saddr);
|
||||
saddr.size = sizeof(struct sockaddr_in6);
|
||||
saddr.addr.sin6.sin6_family = AF_INET6;
|
||||
saddr.addr.sin6.sin6_addr = in6addr_loopback;
|
||||
saddr.addr.sin6.sin6_port = htons(20012);
|
||||
|
||||
l_ctx = coap_new_context(NULL);
|
||||
ReturnIf_CU_ASSERT_PTR_NOT_NULL(l_ctx);
|
||||
coap_context_set_block_mode(l_ctx, COAP_BLOCK_USE_LIBCOAP);
|
||||
/* Something to receive the requests so that the sends succeed */
|
||||
CU_ASSERT_PTR_NOT_NULL(coap_new_endpoint(l_ctx, &saddr, COAP_PROTO_UDP));
|
||||
l_session = coap_new_client_session(l_ctx, NULL, &saddr, COAP_PROTO_UDP);
|
||||
CU_ASSERT_PTR_NOT_NULL(l_session);
|
||||
if (!l_session)
|
||||
goto done;
|
||||
|
||||
pdu = coap_pdu_init(COAP_MESSAGE_NON, COAP_REQUEST_CODE_GET,
|
||||
coap_new_message_id(l_session),
|
||||
coap_session_max_pdu_size(l_session));
|
||||
CU_ASSERT_PTR_NOT_NULL(pdu);
|
||||
if (!pdu)
|
||||
goto done;
|
||||
coap_add_token(pdu, 1, &token);
|
||||
coap_add_option(pdu, COAP_OPTION_OBSERVE, 0, &obs);
|
||||
CU_ASSERT(coap_add_data_large_request_app(l_session, pdu, 4096, NULL,
|
||||
large_app_get_data, NULL) == 1);
|
||||
CU_ASSERT(coap_send(l_session, pdu) != COAP_INVALID_MID);
|
||||
|
||||
/* Observe is now established */
|
||||
coap_lock_lock(goto done);
|
||||
coap_handle_dgram(l_ctx, l_session, rsp, sizeof(rsp));
|
||||
coap_lock_unlock();
|
||||
|
||||
calls = large_app_calls;
|
||||
tok.length = 1;
|
||||
tok.s = &token;
|
||||
CU_ASSERT(coap_cancel_observe(l_session, &tok, COAP_MESSAGE_NON) == 1);
|
||||
CU_ASSERT(large_app_calls > calls);
|
||||
|
||||
done:
|
||||
coap_free_context(l_ctx);
|
||||
}
|
||||
|
||||
/* A request built by coap_add_data_large_request_app() for a single
|
||||
* block chosen by the application must get that block from the
|
||||
* application callback. */
|
||||
static void
|
||||
t_block_large_app_block_num(void) {
|
||||
coap_context_t *l_ctx;
|
||||
coap_session_t *l_session;
|
||||
coap_address_t saddr;
|
||||
coap_pdu_t *pdu;
|
||||
size_t calls;
|
||||
size_t len;
|
||||
const uint8_t *data;
|
||||
uint8_t token = 0x5c;
|
||||
uint8_t buf[4];
|
||||
|
||||
coap_address_init(&saddr);
|
||||
saddr.size = sizeof(struct sockaddr_in6);
|
||||
saddr.addr.sin6.sin6_family = AF_INET6;
|
||||
saddr.addr.sin6.sin6_addr = in6addr_loopback;
|
||||
saddr.addr.sin6.sin6_port = htons(20013);
|
||||
|
||||
l_ctx = coap_new_context(NULL);
|
||||
ReturnIf_CU_ASSERT_PTR_NOT_NULL(l_ctx);
|
||||
coap_context_set_block_mode(l_ctx, COAP_BLOCK_USE_LIBCOAP);
|
||||
l_session = coap_new_client_session(l_ctx, NULL, &saddr, COAP_PROTO_UDP);
|
||||
CU_ASSERT_PTR_NOT_NULL(l_session);
|
||||
if (!l_session)
|
||||
goto done;
|
||||
|
||||
pdu = coap_pdu_init(COAP_MESSAGE_NON, COAP_REQUEST_CODE_PUT,
|
||||
coap_new_message_id(l_session),
|
||||
coap_session_max_pdu_size(l_session));
|
||||
CU_ASSERT_PTR_NOT_NULL(pdu);
|
||||
if (!pdu)
|
||||
goto done;
|
||||
coap_add_token(pdu, 1, &token);
|
||||
/* Block1 NUM 1, M 1, SZX 6 */
|
||||
coap_add_option(pdu, COAP_OPTION_BLOCK1,
|
||||
coap_encode_var_safe(buf, sizeof(buf), (1 << 4) | (1 << 3) | 6),
|
||||
buf);
|
||||
calls = large_app_calls;
|
||||
CU_ASSERT(coap_add_data_large_request_app(l_session, pdu, 4096, NULL,
|
||||
large_app_get_data, NULL) == 1);
|
||||
CU_ASSERT(large_app_calls > calls);
|
||||
CU_ASSERT(coap_get_data(pdu, &len, &data) && len > 0 && data[0] == 'x');
|
||||
coap_delete_pdu(pdu);
|
||||
|
||||
done:
|
||||
coap_free_context(l_ctx);
|
||||
}
|
||||
#endif /* COAP_SERVER_SUPPORT */
|
||||
|
||||
CU_pSuite
|
||||
t_init_block_tests(void) {
|
||||
CU_pSuite suite;
|
||||
|
||||
suite = CU_add_suite("block", NULL, NULL);
|
||||
if (!suite) { /* signal error */
|
||||
fprintf(stderr, "W: cannot add block test suite (%s)\n",
|
||||
CU_get_error_msg());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define BLOCK_TEST(s,t) \
|
||||
if (!CU_ADD_TEST(s,t)) { \
|
||||
fprintf(stderr, "W: cannot add block test (%s)\n", \
|
||||
CU_get_error_msg()); \
|
||||
}
|
||||
|
||||
#if COAP_SERVER_SUPPORT
|
||||
BLOCK_TEST(suite, t_block_echo_get_func);
|
||||
BLOCK_TEST(suite, t_block_cancel_observe_get_func);
|
||||
BLOCK_TEST(suite, t_block_large_app_block_num);
|
||||
#endif /* COAP_SERVER_SUPPORT */
|
||||
|
||||
return suite;
|
||||
}
|
||||
#endif /* COAP_CLIENT_SUPPORT */
|
||||
@@ -0,0 +1,13 @@
|
||||
/* libcoap unit tests
|
||||
*
|
||||
* Copyright (C) 2026 Alejandro Pacheco <alepacheco99@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* This file is part of the CoAP library libcoap. Please see
|
||||
* README for terms of use.
|
||||
*/
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
CU_pSuite t_init_block_tests(void);
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <CUnit/Basic.h>
|
||||
|
||||
#include "test_common.h"
|
||||
#include "test_block.h"
|
||||
#include "test_uri.h"
|
||||
#include "test_encode.h"
|
||||
#include "test_options.h"
|
||||
@@ -47,6 +48,7 @@ main(int argc COAP_UNUSED, char **argv COAP_UNUSED) {
|
||||
#if COAP_CLIENT_SUPPORT
|
||||
t_init_session_tests();
|
||||
t_init_sendqueue_tests();
|
||||
t_init_block_tests();
|
||||
#endif /* COAP_CLIENT_SUPPORT */
|
||||
#if COAP_SERVER_SUPPORT && COAP_CLIENT_SUPPORT
|
||||
t_init_wellknown_tests();
|
||||
|
||||
@@ -376,6 +376,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\tests\testdriver.c" />
|
||||
<ClCompile Include="..\..\tests\test_block.c" />
|
||||
<ClCompile Include="..\..\tests\test_error_response.c" />
|
||||
<ClCompile Include="..\..\tests\test_options.c" />
|
||||
<ClCompile Include="..\..\tests\test_oscore.c" />
|
||||
@@ -387,6 +388,7 @@
|
||||
<ClCompile Include="..\..\tests\test_wellknown.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\tests\test_block.h" />
|
||||
<ClInclude Include="..\..\tests\test_error_response.h" />
|
||||
<ClInclude Include="..\..\tests\test_options.h" />
|
||||
<ClInclude Include="..\..\tests\test_oscore.h" />
|
||||
|
||||
@@ -45,6 +45,9 @@
|
||||
<ClCompile Include="..\..\tests\test_tls.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\tests\test_block.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\tests\test_wellknown.h">
|
||||
@@ -74,5 +77,8 @@
|
||||
<ClInclude Include="..\..\tests\test_tls.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\tests\test_block.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in new issue
Block a user