Skip to content

Commit

Permalink
Implement client side send operation
Browse files Browse the repository at this point in the history
Implemented send operation for client side, send operation was
introduced with the LwM2M 1.1 standard.
This PR is based on work from @sbertin-telular, specifically on
send branch: https://github.com/sbertin-telular/wakaama/tree/send
In this PR a bug that caused a memory leak has been fixed:
sbertin-telular#1
  • Loading branch information
parmi93 authored and mlasch committed Sep 1, 2023
1 parent 4bad452 commit 54249b5
Show file tree
Hide file tree
Showing 7 changed files with 583 additions and 69 deletions.
6 changes: 6 additions & 0 deletions core/internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@
#define URI_REGISTRATION_SEGMENT_LEN 2
#define URI_BOOTSTRAP_SEGMENT "bs"
#define URI_BOOTSTRAP_SEGMENT_LEN 2
#define URI_SEND_SEGMENT "dp"
#define URI_SEND_SEGMENT_LEN 2

#define QUERY_STARTER "?"
#define QUERY_NAME "ep="
Expand Down Expand Up @@ -310,6 +312,10 @@ int object_getRegisterPayload(lwm2m_context_t * contextP, uint8_t * buffer, size
int object_getServers(lwm2m_context_t * contextP, bool checkOnly);
uint8_t object_createInstance(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_data_t * dataP);
uint8_t object_writeInstance(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_data_t * dataP);
#ifndef LWM2M_VERSION_1_0
uint8_t object_readCompositeData(lwm2m_context_t *contextP, lwm2m_uri_t *uriP, size_t numUris, int *sizeP,
lwm2m_data_t **dataP);
#endif

// defined in transaction.c
lwm2m_transaction_t * transaction_new(void * sessionH, coap_method_t method, char * altPath, lwm2m_uri_t * uriP, uint16_t mID, uint8_t token_len, uint8_t* token);
Expand Down
214 changes: 214 additions & 0 deletions core/objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -1233,4 +1233,218 @@ uint8_t object_writeInstance(lwm2m_context_t * contextP,
return targetP->writeFunc(contextP, dataP->id, dataP->value.asChildren.count, dataP->value.asChildren.array, targetP, LWM2M_WRITE_REPLACE_INSTANCE);
}

#ifndef LWM2M_VERSION_1_0
uint8_t object_readCompositeData(lwm2m_context_t *contextP, lwm2m_uri_t *uriP, size_t numUris, int *sizeP,
lwm2m_data_t **dataP) {
size_t i;
int count;
uint8_t result = COAP_205_CONTENT;

*sizeP = 0;
*dataP = NULL;

for (i = 0; i < numUris; i++) {
int partialSize = 0;
lwm2m_data_t *partialDataP = NULL;
uint8_t res;
if (uriP[i].objectId == LWM2M_SECURITY_OBJECT_ID || uriP[i].objectId == LWM2M_OSCORE_OBJECT_ID) {
res = COAP_401_UNAUTHORIZED;
} else {
res = object_readData(contextP, uriP + i, &partialSize, &partialDataP);
}
if (res == COAP_205_CONTENT && partialSize > 0) {
size_t *countP;
lwm2m_data_t **childrenP;
bool finished = false;
lwm2m_data_t *parentP = NULL;
size_t j;

if (LWM2M_URI_IS_SET_OBJECT(uriP + i)) {
// Find the object
for (j = 0; (int)j < *sizeP; j++) {
if (uriP[i].objectId == (*dataP)[j].id) {
if (LWM2M_URI_IS_SET_INSTANCE(uriP + i)) {
parentP = (*dataP) + j;
} else {
// Duplicate or overlapping reads. Replace all instances.
lwm2m_data_free((*dataP)[j].value.asChildren.count, (*dataP)[j].value.asChildren.array);
(*dataP)[j].value.asChildren.count = partialSize;
(*dataP)[j].value.asChildren.array = partialDataP;
finished = true;
}
break;
}
}
if ((int)j == *sizeP) {
// Need to add a new object
if (0 != lwm2m_data_append_one(sizeP, dataP, LWM2M_TYPE_OBJECT, uriP[i].objectId)) {
parentP = *dataP + *sizeP - 1;

if (!LWM2M_URI_IS_SET_INSTANCE(uriP + i)) {
// Need to add the instances
parentP->value.asChildren.count = partialSize;
parentP->value.asChildren.array = partialDataP;
finished = true;
}
} else {
lwm2m_data_free(partialSize, partialDataP);
if (result == COAP_205_CONTENT) {
result = COAP_400_BAD_REQUEST;
}
finished = true;
}
}
} else {
// Root level. Add objects
if (0 == lwm2m_data_append(sizeP, dataP, partialSize, partialDataP)) {
lwm2m_data_free(partialSize, partialDataP);
if (result == COAP_205_CONTENT) {
result = COAP_400_BAD_REQUEST;
}
}
finished = true;
}

if (!finished) {
// Find the instance
countP = &parentP->value.asChildren.count;
childrenP = &parentP->value.asChildren.array;
for (j = 0; j < *countP; j++) {
if (uriP[i].instanceId == (*childrenP)[j].id) {
if (LWM2M_URI_IS_SET_RESOURCE(uriP + i)) {
parentP = (*childrenP) + j;
} else {
// Duplicate or overlapping reads. replace all resources.
lwm2m_data_free((*childrenP)[j].value.asChildren.count,
(*childrenP)[j].value.asChildren.array);
(*childrenP)[j].value.asChildren.count = partialSize;
(*childrenP)[j].value.asChildren.array = partialDataP;
finished = true;
}
break;
}
}
if (j == *countP) {
// Need to add a new instance
count = parentP->value.asChildren.count;
if (0 != lwm2m_data_append_one(&count, &parentP->value.asChildren.array, LWM2M_TYPE_OBJECT_INSTANCE,
uriP[i].instanceId)) {
parentP->value.asChildren.count = count;
parentP = parentP->value.asChildren.array + count - 1;

if (!LWM2M_URI_IS_SET_RESOURCE(uriP + i)) {
parentP->value.asChildren.count = partialSize;
parentP->value.asChildren.array = partialDataP;
finished = true;
}
} else {
lwm2m_data_free(partialSize, partialDataP);
if (result == COAP_205_CONTENT) {
result = COAP_400_BAD_REQUEST;
}
finished = true;
}
}
}

if (!finished) {
// Find the resource
countP = &parentP->value.asChildren.count;
childrenP = &parentP->value.asChildren.array;
for (j = 0; j < *countP; j++) {
if (uriP[i].resourceId == (*childrenP)[j].id) {
if (LWM2M_URI_IS_SET_RESOURCE_INSTANCE(uriP + i)) {
parentP = (*childrenP) + j;
} else {
// Duplicate or overlapping reads.
if ((*childrenP)[j].type == LWM2M_TYPE_MULTIPLE_RESOURCE) {
// Replace the resource instances
lwm2m_data_free((*childrenP)[j].value.asChildren.count,
(*childrenP)[j].value.asChildren.array);
(*childrenP)[j].value.asChildren.count = partialSize;
(*childrenP)[j].value.asChildren.array = partialDataP;
} else {
// Overwrite the value
memcpy((*childrenP) + j, partialDataP, sizeof(lwm2m_data_t));
// Shallow free
memset(partialDataP, 0, sizeof(lwm2m_data_t));
lwm2m_data_free(partialSize, partialDataP);
}
finished = true;
}
break;
}
}
if (j == *countP) {
// Need to add a new resource
count = parentP->value.asChildren.count;
if (0 != lwm2m_data_append_one(&count, &parentP->value.asChildren.array, LWM2M_TYPE_UNDEFINED,
uriP[i].instanceId)) {
parentP->value.asChildren.count = count;
parentP = parentP->value.asChildren.array + count - 1;

memcpy(parentP, partialDataP, sizeof(lwm2m_data_t));
// Shallow free
memset(partialDataP, 0, sizeof(lwm2m_data_t));
lwm2m_data_free(partialSize, partialDataP);
finished = true;
} else {
lwm2m_data_free(partialSize, partialDataP);
if (result == COAP_205_CONTENT) {
result = COAP_400_BAD_REQUEST;
}
finished = true;
}
}
}

if (!finished) {
// Find the resource instance
countP = &parentP->value.asChildren.count;
childrenP = &parentP->value.asChildren.array;
for (j = 0; j < *countP; j++) {
if (uriP[i].resourceInstanceId == (*childrenP)[j].id) {
// Duplicate or overlapping reads.
// Overwrite the value
memcpy((*childrenP) + j, partialDataP->value.asChildren.array, sizeof(lwm2m_data_t));
// Shallow free
memset(partialDataP, 0, sizeof(lwm2m_data_t));
lwm2m_data_free(partialSize, partialDataP);
break;
}
}
if (j == *countP) {
// Need to add a new resource instance
count = parentP->value.asChildren.count;
if (0 != lwm2m_data_append_one(&count, &parentP->value.asChildren.array, LWM2M_TYPE_UNDEFINED,
uriP[i].instanceId)) {
parentP->value.asChildren.count = count;
parentP = parentP->value.asChildren.array + count - 1;
memcpy(parentP, partialDataP->value.asChildren.array, sizeof(lwm2m_data_t));
// Shallow free
memset(partialDataP, 0, sizeof(lwm2m_data_t));
lwm2m_data_free(partialSize, partialDataP);
} else {
lwm2m_data_free(partialSize, partialDataP);
if (result == COAP_205_CONTENT) {
result = COAP_400_BAD_REQUEST;
}
}
}
}
} else if (result == COAP_205_CONTENT && res != COAP_404_NOT_FOUND) {
result = res;
}
}

if (*sizeP > 0) {
result = COAP_205_CONTENT;
} else if (result == COAP_205_CONTENT) {
result = COAP_404_NOT_FOUND;
}

return result;
}
#endif

#endif
136 changes: 136 additions & 0 deletions core/observe.c
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,142 @@ void observe_step(lwm2m_context_t * contextP,
}
}

#ifndef LWM2M_VERSION_1_0
int lwm2m_send(lwm2m_context_t *contextP, uint16_t shortServerID, lwm2m_uri_t *urisP, size_t numUris,
lwm2m_transaction_callback_t callback, void *userData) {
#if defined(LWM2M_SUPPORT_SENML_CBOR) || defined(LWM2M_SUPPORT_SENML_JSON)
lwm2m_transaction_t *transactionP;
lwm2m_server_t *targetP;
lwm2m_data_t *dataP = NULL;
#ifdef LWM2M_SUPPORT_SENML_CBOR
lwm2m_media_type_t format = LWM2M_CONTENT_SENML_CBOR;
#else
lwm2m_media_type_t format = LWM2M_CONTENT_SENML_JSON;
#endif
lwm2m_uri_t uri;
int ret;
int size = 0;
uint8_t *buffer = NULL;
int length;
size_t i;
bool oneGood = false;

LOG_ARG("shortServerID: %d", shortServerID);
for (i = 0; i < numUris; i++) {
LOG_URI(urisP + i);
}

for (i = 0; i < numUris; i++) {
if (!LWM2M_URI_IS_SET_OBJECT(urisP + i))
return COAP_400_BAD_REQUEST;
if (!LWM2M_URI_IS_SET_INSTANCE(urisP + i) && LWM2M_URI_IS_SET_RESOURCE(urisP + i))
return COAP_400_BAD_REQUEST;
}

ret = object_readCompositeData(contextP, urisP, numUris, &size, &dataP);
if (ret != COAP_205_CONTENT)
return ret;

LWM2M_URI_RESET(&uri);
if (size == 1) {
uri.objectId = dataP->id;
if (dataP->value.asChildren.count == 1) {
uri.instanceId = dataP->value.asChildren.array->id;
}
}
ret = lwm2m_data_serialize(&uri, size, dataP, &format, &buffer);
lwm2m_data_free(size, dataP);
if (ret < 0) {
return COAP_500_INTERNAL_SERVER_ERROR;
} else {
length = ret;
}

if (shortServerID == 0 && contextP->serverList != NULL && contextP->serverList->next == NULL) {
// Only 1 server
shortServerID = contextP->serverList->shortID;
}

ret = COAP_404_NOT_FOUND;
for (targetP = contextP->serverList; targetP != NULL; targetP = targetP->next) {
bool mute = true;
lwm2m_data_t *muteDataP;
int muteSize;

if (shortServerID != 0 && shortServerID != targetP->shortID)
continue;
if (targetP->sessionH == NULL ||
(targetP->status != STATE_REGISTERED && targetP->status != STATE_REG_UPDATE_PENDING &&
targetP->status != STATE_REG_UPDATE_NEEDED && targetP->status != STATE_REG_FULL_UPDATE_NEEDED)) {
if (ret == COAP_404_NOT_FOUND)
ret = COAP_405_METHOD_NOT_ALLOWED;
if (shortServerID == 0)
continue;
break;
}

LWM2M_URI_RESET(&uri);
uri.objectId = LWM2M_SERVER_OBJECT_ID;
uri.instanceId = targetP->servObjInstID;
uri.resourceId = LWM2M_SERVER_MUTE_SEND_ID;
if (COAP_205_CONTENT == object_readData(contextP, &uri, &muteSize, &muteDataP)) {
lwm2m_data_decode_bool(muteDataP, &mute);
lwm2m_data_free(muteSize, muteDataP);
}
if (mute) {
if (ret == COAP_404_NOT_FOUND)
ret = COAP_405_METHOD_NOT_ALLOWED;
if (shortServerID == 0)
continue;
break;
}

LWM2M_URI_RESET(&uri);
transactionP = transaction_new(targetP->sessionH, COAP_POST, NULL, &uri, contextP->nextMID++, 0, NULL);
if (transactionP == NULL) {
ret = COAP_500_INTERNAL_SERVER_ERROR;
// Going to the next server likely won't fix this, just get out.
break;
}

coap_set_header_uri_path(transactionP->message, "/" URI_SEND_SEGMENT);
coap_set_header_content_type(transactionP->message, format);
coap_set_payload(transactionP->message, buffer, length);

transactionP->callback = callback;
transactionP->userData = userData;

contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transactionP);

ret = transaction_send(contextP, transactionP);
if (ret == NO_ERROR) {
oneGood = true;
} else {
LOG_ARG("transaction_send failed for %d: 0x%02X!", targetP->shortID, ret);
}
coap_set_payload(transactionP->message, NULL, 0); // Clear the payload
if (shortServerID != 0)
break;
}
if (buffer) {
lwm2m_free(buffer);
}
if (oneGood)
ret = NO_ERROR;
return ret;
#else
/* Unused parameters */
(void)contextP;
(void)shortServerID;
(void)urisP;
(void)numUris;
(void)callback;
(void)userData;
return COAP_415_UNSUPPORTED_CONTENT_FORMAT;
#endif
}
#endif

#endif

#ifdef LWM2M_SERVER_MODE
Expand Down
Loading

0 comments on commit 54249b5

Please sign in to comment.