Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ agentic_kit_require_dependency(cJSON)
agentic_kit_require_dependency(coreHTTP)
agentic_kit_require_dependency(coreMQTT)

# mbedTLS uses its vendored default config. The SDK deliberately does not set
# MBEDTLS_USER_CONFIG_FILE or alter mbedTLS's global config (allocator, record
# sizes): the host application may share the same mbedTLS, so those are the
# integrator's to own (see common/tls.h).

if(NOT TARGET MbedTLS::mbedtls)
add_subdirectory("${mbedtls_source_dir}" "${CMAKE_BINARY_DIR}/third_party/mbedtls" EXCLUDE_FROM_ALL)
endif()
Expand Down
12 changes: 12 additions & 0 deletions common/tls.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@
* recursive mutex, releasing it during tcp_poll waits, so a worker thread and a
* sender thread may interleave freely (the rtc model). The handshake DRBG is a
* process-wide, lazily-seeded singleton shared by all connections.
*
* mbedTLS configuration is the integrator's responsibility. This SDK does NOT
* touch mbedTLS's process-global configuration -- the allocator
* (mbedtls_platform_set_calloc_free), threading callbacks
* (mbedtls_threading_set_alt), and record-buffer sizes
* (MBEDTLS_SSL_{IN,OUT}_CONTENT_LEN) are owned by the application, because the
* host may share the same mbedTLS instance and last-writer-wins on those
* globals would corrupt its state. To route mbedTLS through your own heap
* (e.g. the same allocator the SDK's pal uses, to keep one coalescing heap),
* call mbedtls_platform_set_calloc_free() yourself at startup -- once,
* single-threaded, before any SDK init -- and build mbedTLS with
* MBEDTLS_PLATFORM_MEMORY.
*/

#ifndef COMMON_TLS_H
Expand Down
2 changes: 1 addition & 1 deletion examples/posix/dp-management/dp_management_demo.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ static void on_schema_update(const char *schema_id, const char *new_schema, void
* Returns the allocated cert (assign to client->cacert; free at shutdown) or NULL. */
static char *ensure_mqtt_ca(iot_client_t *client)
{
if (client->mqtt_disable_tls || client->cacert || !client->mqtt_url)
if (client->mqtt_disable_tls || client->cacert || client->mqtt_url[0] == '\0')
return NULL;

char scheme[8] = {0};
Expand Down
12 changes: 8 additions & 4 deletions modules/iot-client/include/iot_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,11 @@ typedef struct {
} iot_on_boarding_config_t;


/* Opaque DP-layer context; defined privately in src/iot_dp.c. */
/* Opaque DP-layer context; defined privately in src/iot_dp.c. Its storage is
* inlined into iot_client_t (dp_storage below) to avoid a per-session heap
* allocation; iot_dp.c _Static_asserts that the real struct fits. */
struct iot_dp_context;
#define IOT_DP_CONTEXT_STORAGE 128

/**
* @brief IoT client instance structure
Expand All @@ -139,8 +142,8 @@ struct iot_dp_context;
char local_key[32]; // Local encryption key for LAN communication
char schema_id[64]; // Device schema ID (from activation; stable key for schema upgrade)

char *https_url; // HTTPS endpoint URL for ATOP requests (SDK-owned)
char *mqtt_url; // MQTT broker URL (SDK-owned, mqtt:// or mqtts://)
char https_url[64]; // HTTPS endpoint URL for ATOP (inline; "" = unresolved)
char mqtt_url[64]; // MQTT broker URL (inline, mqtt://|mqtts://; "" = unresolved)
char *schema; // Device schema JSON (dynamically allocated)

iot_region_t region; // Server region (CN, US, EU, IN)
Expand All @@ -153,7 +156,8 @@ struct iot_dp_context;
struct mqtt_client *mqtt; // Internal MQTT client handle
iot_message_callback_t message_callback; // User callback for incoming messages

struct iot_dp_context *dp; // DP management layer state (SDK-owned, opaque; NULL when inactive)
struct iot_dp_context *dp; // DP layer state; points into dp_storage, NULL when inactive
void *dp_storage[IOT_DP_CONTEXT_STORAGE / sizeof(void *)]; // inline storage for *dp (no heap)
} iot_client_t;

/**
Expand Down
28 changes: 12 additions & 16 deletions modules/iot-client/src/atop.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,23 +231,21 @@ int atop_activate_request(const pal_t *pal, const activite_request_t *request, a
.cert_bundle_attach = request->cert_bundle_attach};

/* ATOP service request send */
atop_base_response_t *atop_response = pal->malloc(sizeof(atop_base_response_t));
if (atop_response == NULL) {
return OPRT_MALLOC_FAILED;
}
rt = atop_base_request(pal,&atop_request, atop_response);
/* Stack-allocated like the other ATOP service calls; atop_base_response_free
* still releases the detached cJSON `result` subtree. Heap-allocating it was
* the lone inconsistency here and leaked `buffer` if that malloc failed. */
atop_base_response_t atop_response = {0};
rt = atop_base_request(pal, &atop_request, &atop_response);
pal->free(buffer);
if (OPRT_OK != rt) {
log_error("atop_base_request error:%d", rt);
pal->free(atop_response);
return rt;
}

cJSON *result = atop_response->result;
cJSON *result = atop_response.result;
if (result == NULL) {
log_error("activate response no result");
atop_base_response_free(pal,atop_response);
pal->free(atop_response);
atop_base_response_free(pal, &atop_response);
return OPRT_COMMUNICATION_ERROR;
}

Expand All @@ -266,13 +264,11 @@ int atop_activate_request(const pal_t *pal, const activite_request_t *request, a
if (!response->devid || !response->secret_key || !response->local_key) {
log_error("activate response missing required fields");
atop_activate_response_free(pal, response);
atop_base_response_free(pal,atop_response);
pal->free(atop_response);
atop_base_response_free(pal, &atop_response);
return OPRT_COMMUNICATION_ERROR;
}

atop_base_response_free(pal,atop_response);
pal->free(atop_response);
atop_base_response_free(pal, &atop_response);
return rt;
}

Expand Down Expand Up @@ -568,7 +564,7 @@ int atop_ai_token_get(const pal_t *pal, const ai_token_request_t *request, ai_to
cJSON_free(json_str);
}
}
atop_base_response_free(pal,&atop_response);
atop_base_response_free(pal, &atop_response);

if (response->token == NULL) {
return OPRT_MALLOC_FAILED;
Expand Down Expand Up @@ -794,7 +790,7 @@ int atop_upgrade_get(const pal_t *pal, const ota_upgrade_request_t *request, ota
/* success=true but result=null → cloud has no upgrade configured for this device */
log_debug("atop_upgrade_get: no upgrade available (success=%d)", atop_response.success);
atop_base_response_free(pal, &atop_response);
return OPRT_OK;
return OPRT_OK; /* no-upgrade is success; response->has_upgrade stays false */
}

/* The result object contains firmware upgrade info.
Expand All @@ -812,7 +808,7 @@ int atop_upgrade_get(const pal_t *pal, const ota_upgrade_request_t *request, ota
if (chosen_url == NULL) {
log_debug("atop_upgrade_get: no cdnUrl/httpsUrl (no upgrade)");
atop_base_response_free(pal, &atop_response);
return OPRT_OK;
return OPRT_OK; /* no-upgrade is success; response->has_upgrade stays false */
}

response->has_upgrade = true;
Expand Down
9 changes: 3 additions & 6 deletions modules/iot-client/src/atop_base.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
#define DEFAULT_RESPONSE_BUFFER_LEN (1024)
#define AES_GCM128_NONCE_LEN 12
#define AES_GCM128_TAG_LEN 16
#define URL_PARAM_SIGN_BUFFER_LEN (512)
#define URL_PARAM_SIGN_BUFFER_LEN (256)


// Error check macro
Expand All @@ -62,21 +62,19 @@
size_t printlen = 0;
int i = 0;
uint8_t digest[MD5SUM_LENGTH];
(void)pal; /* sign buffer is stack-allocated now; pal unused here */

char *buffer = pal->malloc(URL_PARAM_SIGN_BUFFER_LEN);
TUYA_CHECK_NULL_RETURN(buffer, OPRT_MALLOC_FAILED);
char buffer[URL_PARAM_SIGN_BUFFER_LEN];

for (i = 0; i < param_num; ++i) {
int ret = snprintf(buffer + printlen, URL_PARAM_SIGN_BUFFER_LEN - printlen, "%s=%s||", params[i].key, params[i].value);
if (ret < 0 || (size_t)ret >= URL_PARAM_SIGN_BUFFER_LEN - printlen) {
pal->free(buffer);
return OPRT_COMMUNICATION_ERROR;
}
printlen += (size_t)ret;
}
int ret = snprintf(buffer + printlen, URL_PARAM_SIGN_BUFFER_LEN - printlen, "%s", (char *)key);
if (ret < 0 || (size_t)ret >= URL_PARAM_SIGN_BUFFER_LEN - printlen) {
pal->free(buffer);
return OPRT_COMMUNICATION_ERROR;
}
printlen += (size_t)ret;
Expand All @@ -88,7 +86,6 @@
if (md5_ret == 0) md5_ret = mbedtls_md5_update(&ctx, (const uint8_t *)buffer, printlen);
if (md5_ret == 0) md5_ret = mbedtls_md5_finish(&ctx, digest);
mbedtls_md5_free(&ctx);
pal->free(buffer);
if (md5_ret != 0) {
log_error("MD5 computation failed: -0x%04x", -md5_ret);
return OPRT_COMMUNICATION_ERROR;
Expand Down
49 changes: 16 additions & 33 deletions modules/iot-client/src/http_client_interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,12 +183,9 @@ http_client_status_t http_client_request(const http_client_request_t *request,
use_tls = false;
}

// Allocate network context
struct HTTPNetworkContext *network_ctx = (struct HTTPNetworkContext *)pal->malloc(sizeof(struct HTTPNetworkContext));
if (!network_ctx) {
log_error("Failed to allocate network context");
return HTTP_CLIENT_ERROR;
}
// Network context lives on the stack (call-local, never returned).
struct HTTPNetworkContext network_ctx_buf;
struct HTTPNetworkContext *network_ctx = &network_ctx_buf;

network_ctx->tcp_handle = NULL;
network_ctx->use_tls = false;
Expand All @@ -211,7 +208,6 @@ http_client_status_t http_client_request(const http_client_request_t *request,

if (connect_ret != 0) {
log_error("Failed to connect to %s:%d", request->host, request->port);
pal->free(network_ctx);
return (connect_ret == OPRT_TLS_HANDSHAKE_FAILED) ? HTTP_CLIENT_TLS_ERROR : HTTP_CLIENT_ERROR;
}

Expand All @@ -223,15 +219,18 @@ http_client_status_t http_client_request(const http_client_request_t *request,
.pNetworkContext = (NetworkContext_t *)network_ctx
};

// Prepare request headers buffer (allocate on heap to avoid stack overflow)
// One block holds both the request-header and response buffers (both alive
// through HTTPClient_Send) -- one allocation instead of two.
#define REQUEST_HEADER_BUFFER_SIZE 1024
uint8_t *request_header_buffer = (uint8_t *)pal->malloc(REQUEST_HEADER_BUFFER_SIZE);
if (!request_header_buffer) {
log_error("Failed to allocate request header buffer");
#define RESPONSE_BUFFER_SIZE 4096
uint8_t *http_buf = (uint8_t *)pal->malloc(REQUEST_HEADER_BUFFER_SIZE + RESPONSE_BUFFER_SIZE);
if (!http_buf) {
log_error("Failed to allocate HTTP buffers");
disconnect(network_ctx);
pal->free(network_ctx);
return HTTP_CLIENT_ERROR;
}
uint8_t *request_header_buffer = http_buf;
uint8_t *response_buffer = http_buf + REQUEST_HEADER_BUFFER_SIZE;
HTTPRequestHeaders_t request_headers = {
.pBuffer = request_header_buffer,
.bufferLen = REQUEST_HEADER_BUFFER_SIZE,
Expand All @@ -253,9 +252,8 @@ http_client_status_t http_client_request(const http_client_request_t *request,
HTTPStatus_t http_status = HTTPClient_InitializeRequestHeaders(&request_headers, &request_info);
if (http_status != HTTPSuccess) {
log_error("Failed to initialize request headers: %d", http_status);
pal->free(request_header_buffer);
pal->free(http_buf);
disconnect(network_ctx);
pal->free(network_ctx);
return HTTP_CLIENT_ERROR;
}

Expand All @@ -268,23 +266,12 @@ http_client_status_t http_client_request(const http_client_request_t *request,
strlen(request->headers[i].value));
if (http_status != HTTPSuccess) {
log_error("Failed to add header %s: %d", request->headers[i].key, http_status);
pal->free(request_header_buffer);
pal->free(http_buf);
disconnect(network_ctx);
pal->free(network_ctx);
return HTTP_CLIENT_ERROR;
}
}

// Prepare response buffer (allocate on heap to avoid stack overflow)
#define RESPONSE_BUFFER_SIZE 4096
uint8_t *response_buffer = (uint8_t *)pal->malloc(RESPONSE_BUFFER_SIZE);
if (!response_buffer) {
log_error("Failed to allocate response buffer");
pal->free(request_header_buffer);
disconnect(network_ctx);
pal->free(network_ctx);
return HTTP_CLIENT_ERROR;
}
HTTPResponse_t http_response = {
.pBuffer = response_buffer,
.bufferLen = RESPONSE_BUFFER_SIZE,
Expand Down Expand Up @@ -330,11 +317,9 @@ http_client_status_t http_client_request(const http_client_request_t *request,
} else {
log_error("Failed to allocate response body buffer");
ret_status = HTTP_CLIENT_ERROR;
disconnect(network_ctx);
pal->free(network_ctx);
response->internal = NULL;
pal->free(request_header_buffer);
pal->free(response_buffer);
pal->free(http_buf);
disconnect(network_ctx);
return ret_status;
}
}
Expand Down Expand Up @@ -363,10 +348,8 @@ http_client_status_t http_client_request(const http_client_request_t *request,
}

}
pal->free(request_header_buffer);
pal->free(response_buffer);
pal->free(http_buf);
disconnect(network_ctx);
pal->free(network_ctx);
return ret_status;
}

Expand Down
39 changes: 18 additions & 21 deletions modules/iot-client/src/iot_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ void iot_client_resolve_atop_host(iot_client_t *client, char *host_out, size_t h
*port_out = IOT_DEFAULT_PORT;
if (host_len == 0) return;
host_out[0] = '\0';
if (client->https_url) {
if (client->https_url[0] != '\0') {
parse_host_port(client->https_url, host_out, host_len, port_out);
} else {
const char *h = iot_region_to_host(client->region, client->env);
Expand Down Expand Up @@ -130,20 +130,23 @@ static int iot_client_dns_resolve(iot_client_t *client)
if (strcmp(dns_resp.endpoints[i].key, mqtt_dns_key) == 0) {
const char *scheme = client->mqtt_disable_tls ? "mqtt" : "mqtts";
const char *addr = dns_resp.endpoints[i].addr;
size_t url_len = strlen(scheme) + 3 + strlen(addr) + 1;
char *mqtt_url = (char *)client->pal->malloc(url_len);
if (mqtt_url) {
int sn = snprintf(mqtt_url, url_len, "%s://%s", scheme, addr);
if (sn < 0 || (size_t)sn >= url_len) {
client->pal->free(mqtt_url);
} else {
client->mqtt_url = mqtt_url;
log_info("IoT DNS %s: %s", mqtt_dns_key, mqtt_url);
}
int sn = snprintf(client->mqtt_url, sizeof(client->mqtt_url),
"%s://%s", scheme, addr);
if (sn < 0 || (size_t)sn >= sizeof(client->mqtt_url)) {
client->mqtt_url[0] = '\0'; /* too long: leave unresolved */
log_warn("IoT DNS %s url too long, ignored", mqtt_dns_key);
} else {
log_info("IoT DNS %s: %s", mqtt_dns_key, client->mqtt_url);
}
} else if (strcmp(dns_resp.endpoints[i].key, "httpsUrl") == 0) {
client->https_url = pal_strdup(client->pal, dns_resp.endpoints[i].addr);
log_info("IoT DNS httpsUrl: %s", dns_resp.endpoints[i].addr);
const char *addr = dns_resp.endpoints[i].addr;
int sn = snprintf(client->https_url, sizeof(client->https_url), "%s", addr);
if (sn < 0 || (size_t)sn >= sizeof(client->https_url)) {
client->https_url[0] = '\0';
log_warn("IoT DNS httpsUrl too long, ignored");
} else {
log_info("IoT DNS httpsUrl: %s", client->https_url);
}
}
}
iot_dns_url_config_response_free(client->pal, &dns_resp);
Expand Down Expand Up @@ -225,7 +228,7 @@ IOT_API iot_client_t *iot_client_init(const iot_client_config_t *config)
iot_client_dns_resolve(client);
}

if (client->mqtt_url && config->mqtt_auto_connect) {
if (client->mqtt_url[0] != '\0' && config->mqtt_auto_connect) {
int ret = iot_client_message_connect(client);
if (ret != OPRT_OK) {
log_error("MQTT connect failed: %d", ret);
Expand All @@ -239,7 +242,7 @@ IOT_API iot_client_t *iot_client_init(const iot_client_config_t *config)
char meta_host[64] = {0};
uint16_t meta_port = IOT_DEFAULT_PORT;
const char *host;
if (client->https_url) {
if (client->https_url[0] != '\0') {
parse_host_port(client->https_url, meta_host, sizeof(meta_host), &meta_port);
host = meta_host;
} else {
Expand Down Expand Up @@ -286,12 +289,6 @@ IOT_API void iot_client_deinit(iot_client_t *client)
}
iot_client_message_disconnect(client);
iot_dp_deinit(client); /* after disconnect: no more dispatch on the process thread */
if (client->https_url) {
client->pal->free(client->https_url);
}
if (client->mqtt_url) {
client->pal->free(client->mqtt_url);
}
if (client->schema) {
client->pal->free(client->schema);
}
Expand Down
Loading
Loading