pick up wakaama files from https://github.com/eclipse/wakaama
Revision 0:1fa43ab66921, committed 2017-04-19
- Comitter:
- terencez
- Date:
- Wed Apr 19 11:30:02 2017 +0000
- Commit message:
- Initial commit
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/block1.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,153 @@ +/******************************************************************************* + * + * Copyright (c) 2016 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Simon Bernard - initial API and implementation + * + *******************************************************************************/ +/* + Copyright (c) 2016 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include "internals.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +// the maximum payload transferred by block1 we accumulate per server +#define MAX_BLOCK1_SIZE 4096 + +coap_status_t coap_block1_handler(lwm2m_block1_data_t ** pBlock1Data, + uint16_t mid, + uint8_t * buffer, + size_t length, + uint16_t blockSize, + uint32_t blockNum, + bool blockMore, + uint8_t ** outputBuffer, + size_t * outputLength) +{ + lwm2m_block1_data_t * block1Data = *pBlock1Data;; + + // manage new block1 transfer + if (blockNum == 0) + { + // we already have block1 data for this server, clear it + if (block1Data != NULL) + { + lwm2m_free(block1Data->block1buffer); + } + else + { + block1Data = lwm2m_malloc(sizeof(lwm2m_block1_data_t)); + *pBlock1Data = block1Data; + if (NULL == block1Data) return COAP_500_INTERNAL_SERVER_ERROR; + } + + block1Data->block1buffer = lwm2m_malloc(length); + block1Data->block1bufferSize = length; + + // write new block in buffer + memcpy(block1Data->block1buffer, buffer, length); + block1Data->lastmid = mid; + } + // manage already started block1 transfer + else + { + if (block1Data == NULL) + { + // we never receive the first block + // TODO should we clean block1 data for this server ? + return COAP_408_REQ_ENTITY_INCOMPLETE; + } + + // If this is a retransmission, we already did that. + if (block1Data->lastmid != mid) + { + uint8_t * oldBuffer = block1Data->block1buffer; + size_t oldSize = block1Data->block1bufferSize; + + if (block1Data->block1bufferSize != blockSize * blockNum) + { + // we don't receive block in right order + // TODO should we clean block1 data for this server ? + return COAP_408_REQ_ENTITY_INCOMPLETE; + } + + // is it too large? + if (block1Data->block1bufferSize + length >= MAX_BLOCK1_SIZE) { + return COAP_413_ENTITY_TOO_LARGE; + } + // re-alloc new buffer + block1Data->block1bufferSize = oldSize+length; + block1Data->block1buffer = lwm2m_malloc(block1Data->block1bufferSize); + if (NULL == block1Data->block1buffer) return COAP_500_INTERNAL_SERVER_ERROR; + memcpy(block1Data->block1buffer, oldBuffer, oldSize); + lwm2m_free(oldBuffer); + + // write new block in buffer + memcpy(block1Data->block1buffer + oldSize, buffer, length); + block1Data->lastmid = mid; + } + } + + if (blockMore) + { + *outputLength = -1; + return COAP_231_CONTINUE; + } + else + { + // buffer is full, set output parameter + // we don't free it to be able to send retransmission + *outputLength = block1Data->block1bufferSize; + *outputBuffer = block1Data->block1buffer; + + return NO_ERROR; + } +} + +void free_block1_buffer(lwm2m_block1_data_t * block1Data) +{ + if (block1Data != NULL) + { + // free block1 buffer + lwm2m_free(block1Data->block1buffer); + block1Data->block1bufferSize = 0 ; + + // free current element + lwm2m_free(block1Data); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/bootstrap.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,751 @@ +/******************************************************************************* + * + * Copyright (c) 2015 Sierra Wireless and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Pascal Rieux - Please refer to git log + * Bosch Software Innovations GmbH - Please refer to git log + * David Navarro, Intel Corporation - Please refer to git log + * + *******************************************************************************/ + +#include "internals.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#ifdef LWM2M_CLIENT_MODE +#ifdef LWM2M_BOOTSTRAP + +#define PRV_QUERY_BUFFER_LENGTH 200 + + +static void prv_handleResponse(lwm2m_server_t * bootstrapServer, + coap_packet_t * message) +{ + if (COAP_204_CHANGED == message->code) + { + LOG("Received ACK/2.04, Bootstrap pending, waiting for DEL/PUT from BS server..."); + bootstrapServer->status = STATE_BS_PENDING; + } + else + { + bootstrapServer->status = STATE_BS_FAILING; + } +} + +static void prv_handleBootstrapReply(lwm2m_transaction_t * transaction, + void * message) +{ + lwm2m_server_t * bootstrapServer = (lwm2m_server_t *)transaction->userData; + coap_packet_t * coapMessage = (coap_packet_t *)message; + + LOG("Entering"); + + if (bootstrapServer->status == STATE_BS_INITIATED) + { + if (NULL != coapMessage && COAP_TYPE_RST != coapMessage->type) + { + prv_handleResponse(bootstrapServer, coapMessage); + } + else + { + bootstrapServer->status = STATE_BS_FAILING; + } + } +} + +// start a device initiated bootstrap +static void prv_requestBootstrap(lwm2m_context_t * context, + lwm2m_server_t * bootstrapServer) +{ + char query[PRV_QUERY_BUFFER_LENGTH]; + int query_length = 0; + int res; + + LOG("Entering"); + + query_length = utils_stringCopy(query, PRV_QUERY_BUFFER_LENGTH, QUERY_STARTER QUERY_NAME); + if (query_length < 0) + { + bootstrapServer->status = STATE_BS_FAILING; + return; + } + res = utils_stringCopy(query + query_length, PRV_QUERY_BUFFER_LENGTH - query_length, context->endpointName); + if (res < 0) + { + bootstrapServer->status = STATE_BS_FAILING; + return; + } + query_length += res; + + if (bootstrapServer->sessionH == NULL) + { + bootstrapServer->sessionH = lwm2m_connect_server(bootstrapServer->secObjInstID, context->userData); + } + + if (bootstrapServer->sessionH != NULL) + { + lwm2m_transaction_t * transaction = NULL; + + LOG("Bootstrap server connection opened"); + + transaction = transaction_new(bootstrapServer->sessionH, COAP_POST, NULL, NULL, context->nextMID++, 4, NULL); + if (transaction == NULL) + { + bootstrapServer->status = STATE_BS_FAILING; + return; + } + + coap_set_header_uri_path(transaction->message, "/"URI_BOOTSTRAP_SEGMENT); + coap_set_header_uri_query(transaction->message, query); + transaction->callback = prv_handleBootstrapReply; + transaction->userData = (void *)bootstrapServer; + context->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(context->transactionList, transaction); + if (transaction_send(context, transaction) == 0) + { + LOG("CI bootstrap requested to BS server"); + bootstrapServer->status = STATE_BS_INITIATED; + } + } + else + { + LOG("Connecting bootstrap server failed"); + bootstrapServer->status = STATE_BS_FAILED; + } +} + +void bootstrap_step(lwm2m_context_t * contextP, + uint32_t currentTime, + time_t * timeoutP) +{ + lwm2m_server_t * targetP; + + LOG("entering"); + targetP = contextP->bootstrapServerList; + while (targetP != NULL) + { + LOG_ARG("Initial status: %s", STR_STATUS(targetP->status)); + switch (targetP->status) + { + case STATE_DEREGISTERED: + targetP->registration = currentTime + targetP->lifetime; + targetP->status = STATE_BS_HOLD_OFF; + if (*timeoutP > targetP->lifetime) + { + *timeoutP = targetP->lifetime; + } + break; + + case STATE_BS_HOLD_OFF: + if (targetP->registration <= currentTime) + { + prv_requestBootstrap(contextP, targetP); + } + else if (*timeoutP > targetP->registration - currentTime) + { + *timeoutP = targetP->registration - currentTime; + } + break; + + case STATE_BS_INITIATED: + // waiting + break; + + case STATE_BS_PENDING: + // waiting + break; + + case STATE_BS_FINISHING: + if (targetP->sessionH != NULL) + { + lwm2m_close_connection(targetP->sessionH, contextP->userData); + targetP->sessionH = NULL; + } + targetP->status = STATE_BS_FINISHED; + *timeoutP = 0; + break; + + case STATE_BS_FAILING: + if (targetP->sessionH != NULL) + { + lwm2m_close_connection(targetP->sessionH, contextP->userData); + targetP->sessionH = NULL; + } + targetP->status = STATE_BS_FAILED; + *timeoutP = 0; + break; + + default: + break; + } + LOG_ARG("Finalal status: %s", STR_STATUS(targetP->status)); + targetP = targetP->next; + } +} + +coap_status_t bootstrap_handleFinish(lwm2m_context_t * context, + void * fromSessionH) +{ + lwm2m_server_t * bootstrapServer; + + LOG("Entering"); + bootstrapServer = utils_findBootstrapServer(context, fromSessionH); + if (bootstrapServer != NULL + && bootstrapServer->status == STATE_BS_PENDING) + { + LOG("Bootstrap server status changed to STATE_BS_FINISHING"); + bootstrapServer->status = STATE_BS_FINISHING; + return COAP_204_CHANGED; + } + + return COAP_IGNORE; +} + +/* + * Reset the bootstrap servers statuses + * + * TODO: handle LWM2M Servers the client is registered to ? + * + */ +void bootstrap_start(lwm2m_context_t * contextP) +{ + lwm2m_server_t * targetP; + + LOG("Entering"); + targetP = contextP->bootstrapServerList; + while (targetP != NULL) + { + targetP->status = STATE_DEREGISTERED; + if (targetP->sessionH == NULL) + { + targetP->sessionH = lwm2m_connect_server(targetP->secObjInstID, contextP->userData); + } + targetP = targetP->next; + } +} + +/* + * Returns STATE_BS_PENDING if at least one bootstrap is still pending + * Returns STATE_BS_FINISHED if at least one bootstrap succeeded and no bootstrap is pending + * Returns STATE_BS_FAILED if all bootstrap failed. + */ +lwm2m_status_t bootstrap_getStatus(lwm2m_context_t * contextP) +{ + lwm2m_server_t * targetP; + lwm2m_status_t bs_status; + + LOG("Entering"); + targetP = contextP->bootstrapServerList; + bs_status = STATE_BS_FAILED; + + while (targetP != NULL) + { + switch (targetP->status) + { + case STATE_BS_FINISHED: + if (bs_status == STATE_BS_FAILED) + { + bs_status = STATE_BS_FINISHED; + } + break; + + case STATE_BS_HOLD_OFF: + case STATE_BS_INITIATED: + case STATE_BS_PENDING: + case STATE_BS_FINISHING: + bs_status = STATE_BS_PENDING; + break; + + default: + break; + } + targetP = targetP->next; + } + + LOG_ARG("Returned status: %s", STR_STATUS(bs_status)); + + return bs_status; +} + +static coap_status_t prv_checkServerStatus(lwm2m_server_t * serverP) +{ + LOG_ARG("Initial status: %s", STR_STATUS(serverP->status)); + + switch (serverP->status) + { + case STATE_BS_HOLD_OFF: + serverP->status = STATE_BS_PENDING; + LOG_ARG("Status changed to: %s", STR_STATUS(serverP->status)); + break; + + case STATE_BS_INITIATED: + // The ACK was probably lost + serverP->status = STATE_BS_PENDING; + LOG_ARG("Status changed to: %s", STR_STATUS(serverP->status)); + break; + + case STATE_DEREGISTERED: + // server initiated bootstrap + case STATE_BS_PENDING: + // do nothing + break; + + case STATE_BS_FINISHED: + case STATE_BS_FINISHING: + case STATE_BS_FAILING: + case STATE_BS_FAILED: + default: + LOG("Returning COAP_IGNORE"); + return COAP_IGNORE; + } + + return COAP_NO_ERROR; +} + +static void prv_tagServer(lwm2m_context_t * contextP, + uint16_t id) +{ + lwm2m_server_t * targetP; + + targetP = (lwm2m_server_t *)LWM2M_LIST_FIND(contextP->bootstrapServerList, id); + if (targetP == NULL) + { + targetP = (lwm2m_server_t *)LWM2M_LIST_FIND(contextP->serverList, id); + } + if (targetP != NULL) + { + targetP->dirty = true; + } +} + +static void prv_tagAllServer(lwm2m_context_t * contextP, + lwm2m_server_t * serverP) +{ + lwm2m_server_t * targetP; + + targetP = contextP->bootstrapServerList; + while (targetP != NULL) + { + if (targetP != serverP) + { + targetP->dirty = true; + } + targetP = targetP->next; + } + targetP = contextP->serverList; + while (targetP != NULL) + { + targetP->dirty = true; + targetP = targetP->next; + } +} + +coap_status_t bootstrap_handleCommand(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_server_t * serverP, + coap_packet_t * message, + coap_packet_t * response) +{ + coap_status_t result; + lwm2m_media_type_t format; + + LOG_ARG("Code: %02X", message->code); + LOG_URI(uriP); + format = utils_convertMediaType(message->content_type); + + result = prv_checkServerStatus(serverP); + if (result != COAP_NO_ERROR) return result; + + switch (message->code) + { + case COAP_PUT: + { + if (LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + if (object_isInstanceNew(contextP, uriP->objectId, uriP->instanceId)) + { + result = object_create(contextP, uriP, format, message->payload, message->payload_len); + if (COAP_201_CREATED == result) + { + result = COAP_204_CHANGED; + } + } + else + { + result = object_write(contextP, uriP, format, message->payload, message->payload_len); + if (uriP->objectId == LWM2M_SECURITY_OBJECT_ID + && result == COAP_204_CHANGED) + { + prv_tagServer(contextP, uriP->instanceId); + } + } + } + else + { + lwm2m_data_t * dataP = NULL; + int size = 0; + int i; + + if (message->payload_len == 0 || message->payload == 0) + { + result = COAP_400_BAD_REQUEST; + } + else + { + size = lwm2m_data_parse(uriP, message->payload, message->payload_len, format, &dataP); + if (size == 0) + { + result = COAP_500_INTERNAL_SERVER_ERROR; + break; + } + + for (i = 0 ; i < size ; i++) + { + if(dataP[i].type == LWM2M_TYPE_OBJECT_INSTANCE) + { + if (object_isInstanceNew(contextP, uriP->objectId, dataP[i].id)) + { + result = object_createInstance(contextP, uriP, &dataP[i]); + if (COAP_201_CREATED == result) + { + result = COAP_204_CHANGED; + } + } + else + { + result = object_writeInstance(contextP, uriP, &dataP[i]); + if (uriP->objectId == LWM2M_SECURITY_OBJECT_ID + && result == COAP_204_CHANGED) + { + prv_tagServer(contextP, dataP[i].id); + } + } + + if(result != COAP_204_CHANGED) // Stop object create or write when result is error + { + break; + } + } + else + { + result = COAP_400_BAD_REQUEST; + } + } + lwm2m_data_free(size, dataP); + } + } + } + break; + + case COAP_DELETE: + { + if (LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + result = COAP_400_BAD_REQUEST; + } + else + { + result = object_delete(contextP, uriP); + if (uriP->objectId == LWM2M_SECURITY_OBJECT_ID + && result == COAP_202_DELETED) + { + if (LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + prv_tagServer(contextP, uriP->instanceId); + } + else + { + prv_tagAllServer(contextP, NULL); + } + } + } + } + break; + + case COAP_GET: + case COAP_POST: + default: + result = COAP_400_BAD_REQUEST; + break; + } + + if (result == COAP_202_DELETED + || result == COAP_204_CHANGED) + { + if (serverP->status != STATE_BS_PENDING) + { + serverP->status = STATE_BS_PENDING; + contextP->state = STATE_BOOTSTRAPPING; + } + } + LOG_ARG("Server status: %s", STR_STATUS(serverP->status)); + + return result; +} + +coap_status_t bootstrap_handleDeleteAll(lwm2m_context_t * contextP, + void * fromSessionH) +{ + lwm2m_server_t * serverP; + coap_status_t result; + lwm2m_object_t * objectP; + + LOG("Entering"); + serverP = utils_findBootstrapServer(contextP, fromSessionH); + if (serverP == NULL) return COAP_IGNORE; + result = prv_checkServerStatus(serverP); + if (result != COAP_NO_ERROR) return result; + + result = COAP_202_DELETED; + for (objectP = contextP->objectList; objectP != NULL; objectP = objectP->next) + { + lwm2m_uri_t uri; + + memset(&uri, 0, sizeof(lwm2m_uri_t)); + uri.flag = LWM2M_URI_FLAG_OBJECT_ID; + uri.objectId = objectP->objID; + + if (objectP->objID == LWM2M_SECURITY_OBJECT_ID) + { + lwm2m_list_t * instanceP; + + instanceP = objectP->instanceList; + while (NULL != instanceP + && result == COAP_202_DELETED) + { + if (instanceP->id == serverP->secObjInstID) + { + instanceP = instanceP->next; + } + else + { + uri.flag = LWM2M_URI_FLAG_OBJECT_ID | LWM2M_URI_FLAG_INSTANCE_ID; + uri.instanceId = instanceP->id; + result = object_delete(contextP, &uri); + instanceP = objectP->instanceList; + } + } + if (result == COAP_202_DELETED) + { + prv_tagAllServer(contextP, serverP); + } + } + else + { + result = object_delete(contextP, &uri); + if (result == COAP_405_METHOD_NOT_ALLOWED) + { + // Fake a successful deletion for static objects like the Device object. + result = COAP_202_DELETED; + } + } + } + + return result; +} +#endif +#endif + +#ifdef LWM2M_BOOTSTRAP_SERVER_MODE +uint8_t bootstrap_handleRequest(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + void * fromSessionH, + coap_packet_t * message, + coap_packet_t * response) +{ + uint8_t result; + char * name; + + LOG_URI(uriP); + if (contextP->bootstrapCallback == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + if (message->code != COAP_POST) return COAP_400_BAD_REQUEST; + if (message->uri_query == NULL) return COAP_400_BAD_REQUEST; + if (message->payload != NULL) return COAP_400_BAD_REQUEST; + + if (lwm2m_strncmp((char *)message->uri_query->data, QUERY_NAME, QUERY_NAME_LEN) != 0) + { + return COAP_400_BAD_REQUEST; + } + + if (message->uri_query->len == QUERY_NAME_LEN) return COAP_400_BAD_REQUEST; + if (message->uri_query->next != NULL) return COAP_400_BAD_REQUEST; + + name = (char *)lwm2m_malloc(message->uri_query->len - QUERY_NAME_LEN + 1); + if (name == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + memcpy(name, message->uri_query->data + QUERY_NAME_LEN, message->uri_query->len - QUERY_NAME_LEN); + name[message->uri_query->len - QUERY_NAME_LEN] = 0; + + result = contextP->bootstrapCallback(fromSessionH, COAP_NO_ERROR, NULL, name, contextP->bootstrapUserData); + + lwm2m_free(name); + + return result; +} + +void lwm2m_set_bootstrap_callback(lwm2m_context_t * contextP, + lwm2m_bootstrap_callback_t callback, + void * userData) +{ + LOG("Entering"); + contextP->bootstrapCallback = callback; + contextP->bootstrapUserData = userData; +} + +static void prv_resultCallback(lwm2m_transaction_t * transacP, + void * message) +{ + bs_data_t * dataP = (bs_data_t *)transacP->userData; + lwm2m_uri_t * uriP; + + if (dataP->isUri == true) + { + uriP = &dataP->uri; + } + else + { + uriP = NULL; + } + + if (message == NULL) + { + dataP->callback(transacP->peerH, + COAP_503_SERVICE_UNAVAILABLE, + uriP, + NULL, + dataP->userData); + } + else + { + coap_packet_t * packet = (coap_packet_t *)message; + + dataP->callback(transacP->peerH, + packet->code, + uriP, + NULL, + dataP->userData); + } + lwm2m_free(dataP); +} + +int lwm2m_bootstrap_delete(lwm2m_context_t * contextP, + void * sessionH, + lwm2m_uri_t * uriP) +{ + lwm2m_transaction_t * transaction; + bs_data_t * dataP; + + LOG_URI(uriP); + transaction = transaction_new(sessionH, COAP_DELETE, NULL, uriP, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + dataP = (bs_data_t *)lwm2m_malloc(sizeof(bs_data_t)); + if (dataP == NULL) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + if (uriP == NULL) + { + dataP->isUri = false; + } + else + { + dataP->isUri = true; + memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t)); + } + dataP->callback = contextP->bootstrapCallback; + dataP->userData = contextP->bootstrapUserData; + + transaction->callback = prv_resultCallback; + transaction->userData = (void *)dataP; + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + + return transaction_send(contextP, transaction); +} + +int lwm2m_bootstrap_write(lwm2m_context_t * contextP, + void * sessionH, + lwm2m_uri_t * uriP, + lwm2m_media_type_t format, + uint8_t * buffer, + size_t length) +{ + lwm2m_transaction_t * transaction; + bs_data_t * dataP; + + LOG_URI(uriP); + if (uriP == NULL + || buffer == NULL + || length == 0) + { + return COAP_400_BAD_REQUEST; + } + + transaction = transaction_new(sessionH, COAP_PUT, NULL, uriP, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + coap_set_header_content_type(transaction->message, format); + coap_set_payload(transaction->message, buffer, length); + + dataP = (bs_data_t *)lwm2m_malloc(sizeof(bs_data_t)); + if (dataP == NULL) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + dataP->isUri = true; + memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t)); + dataP->callback = contextP->bootstrapCallback; + dataP->userData = contextP->bootstrapUserData; + + transaction->callback = prv_resultCallback; + transaction->userData = (void *)dataP; + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + + return transaction_send(contextP, transaction); +} + +int lwm2m_bootstrap_finish(lwm2m_context_t * contextP, + void * sessionH) +{ + lwm2m_transaction_t * transaction; + bs_data_t * dataP; + + LOG("Entering"); + transaction = transaction_new(sessionH, COAP_POST, NULL, NULL, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + coap_set_header_uri_path(transaction->message, "/"URI_BOOTSTRAP_SEGMENT); + + dataP = (bs_data_t *)lwm2m_malloc(sizeof(bs_data_t)); + if (dataP == NULL) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + dataP->isUri = false; + dataP->callback = contextP->bootstrapCallback; + dataP->userData = contextP->bootstrapUserData; + + transaction->callback = prv_resultCallback; + transaction->userData = (void *)dataP; + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + + return transaction_send(contextP, transaction); +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/data.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,545 @@ +/******************************************************************************* +* +* Copyright (c) 2013, 2014 Intel Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* and Eclipse Distribution License v1.0 which accompany this distribution. +* +* The Eclipse Public License is available at +* http://www.eclipse.org/legal/epl-v10.html +* The Eclipse Distribution License is available at +* http://www.eclipse.org/org/documents/edl-v10.php. +* +* Contributors: +* David Navarro, Intel Corporation - initial API and implementation +* Fabien Fleutot - Please refer to git log +* Bosch Software Innovations GmbH - Please refer to git log +* +*******************************************************************************/ + +#include "internals.h" +#include <float.h> + +// dataP array length is assumed to be 1. +static int prv_textSerialize(lwm2m_data_t * dataP, + uint8_t ** bufferP) +{ + size_t res; + + switch (dataP->type) + { + case LWM2M_TYPE_STRING: + *bufferP = (uint8_t *)lwm2m_malloc(dataP->value.asBuffer.length); + if (*bufferP == NULL) return 0; + memcpy(*bufferP, dataP->value.asBuffer.buffer, dataP->value.asBuffer.length); + return (int)dataP->value.asBuffer.length; + + case LWM2M_TYPE_INTEGER: + res = utils_int64ToPlainText(dataP->value.asInteger, bufferP); + if (res == 0) return -1; + return (int)res; + + case LWM2M_TYPE_FLOAT: + res = utils_float64ToPlainText(dataP->value.asFloat, bufferP); + if (res == 0) return -1; + return (int)res; + + case LWM2M_TYPE_BOOLEAN: + res = utils_boolToPlainText(dataP->value.asBoolean, bufferP); + if (res == 0) return -1; + return (int)res; + + case LWM2M_TYPE_OBJECT_LINK: + { + char stringBuffer[20]; + int len = snprintf(stringBuffer, 20, "%d:%d", + dataP->value.asObjLink.objectId, + dataP->value.asObjLink.objectInstanceId); + *bufferP = (uint8_t *)lwm2m_malloc(len); + if (*bufferP == NULL) return -1; + memcpy(*bufferP, stringBuffer, len); + return len; + } + case LWM2M_TYPE_OPAQUE: + case LWM2M_TYPE_UNDEFINED: + default: + return -1; + } +} + +static int prv_setBuffer(lwm2m_data_t * dataP, + uint8_t * buffer, + size_t bufferLen) +{ + dataP->value.asBuffer.buffer = (uint8_t *)lwm2m_malloc(bufferLen); + if (dataP->value.asBuffer.buffer == NULL) + { + lwm2m_data_free(1, dataP); + return 0; + } + dataP->value.asBuffer.length = bufferLen; + memcpy(dataP->value.asBuffer.buffer, buffer, bufferLen); + + return 1; +} + +lwm2m_data_t * lwm2m_data_new(int size) +{ + lwm2m_data_t * dataP; + + LOG_ARG("size: %d", size); + if (size <= 0) return NULL; + + dataP = (lwm2m_data_t *)lwm2m_malloc(size * sizeof(lwm2m_data_t)); + + if (dataP != NULL) + { + memset(dataP, 0, size * sizeof(lwm2m_data_t)); + } + + return dataP; +} + +void lwm2m_data_free(int size, + lwm2m_data_t * dataP) +{ + int i; + + LOG_ARG("size: %d", size); + if (size == 0 || dataP == NULL) return; + + for (i = 0; i < size; i++) + { + switch (dataP[i].type) + { + case LWM2M_TYPE_MULTIPLE_RESOURCE: + case LWM2M_TYPE_OBJECT_INSTANCE: + case LWM2M_TYPE_OBJECT: + lwm2m_data_free(dataP[i].value.asChildren.count, dataP[i].value.asChildren.array); + break; + + case LWM2M_TYPE_STRING: + case LWM2M_TYPE_OPAQUE: + if (dataP[i].value.asBuffer.buffer != NULL) + { + lwm2m_free(dataP[i].value.asBuffer.buffer); + } + + default: + // do nothing + break; + } + } + lwm2m_free(dataP); +} + +void lwm2m_data_encode_string(const char * string, + lwm2m_data_t * dataP) +{ + size_t len; + int res; + + LOG_ARG("\"%s\"", string); + if (string == NULL) + { + len = 0; + } + else + { + for (len = 0; string[len] != 0; len++); + } + + if (len == 0) + { + dataP->value.asBuffer.length = 0; + dataP->value.asBuffer.buffer = NULL; + res = 1; + } + else + { + res = prv_setBuffer(dataP, (uint8_t *)string, len); + } + + if (res == 1) + { + dataP->type = LWM2M_TYPE_STRING; + } + else + { + dataP->type = LWM2M_TYPE_UNDEFINED; + } +} + +void lwm2m_data_encode_opaque(uint8_t * buffer, + size_t length, + lwm2m_data_t * dataP) +{ + int res; + + LOG_ARG("length: %d", length); + if (length == 0) + { + dataP->value.asBuffer.length = 0; + dataP->value.asBuffer.buffer = NULL; + res = 1; + } + else + { + res = prv_setBuffer(dataP, buffer, length); + } + + if (res == 1) + { + dataP->type = LWM2M_TYPE_OPAQUE; + } + else + { + dataP->type = LWM2M_TYPE_UNDEFINED; + } +} + +void lwm2m_data_encode_nstring(const char * string, + size_t length, + lwm2m_data_t * dataP) +{ + LOG_ARG("length: %d, string: \"%s\"", length, string); + lwm2m_data_encode_opaque((uint8_t *)string, length, dataP); + + if (dataP->type == LWM2M_TYPE_OPAQUE) + { + dataP->type = LWM2M_TYPE_STRING; + } +} + +void lwm2m_data_encode_int(int64_t value, + lwm2m_data_t * dataP) +{ + LOG_ARG("value: %" PRId64 "", value); + dataP->type = LWM2M_TYPE_INTEGER; + dataP->value.asInteger = value; +} + +int lwm2m_data_decode_int(const lwm2m_data_t * dataP, + int64_t * valueP) +{ + int result; + + LOG("Entering"); + switch (dataP->type) + { + case LWM2M_TYPE_INTEGER: + *valueP = dataP->value.asInteger; + result = 1; + break; + + case LWM2M_TYPE_STRING: + result = utils_plainTextToInt64(dataP->value.asBuffer.buffer, dataP->value.asBuffer.length, valueP); + break; + + case LWM2M_TYPE_OPAQUE: + result = utils_opaqueToInt(dataP->value.asBuffer.buffer, dataP->value.asBuffer.length, valueP); + if (result == (int)dataP->value.asBuffer.length) + { + result = 1; + } + else + { + result = 0; + } + break; + + default: + return 0; + } + LOG_ARG("result: %d, value: %" PRId64, result, *valueP); + + return result; +} + +void lwm2m_data_encode_float(double value, + lwm2m_data_t * dataP) +{ + LOG_ARG("value: %f", value); + dataP->type = LWM2M_TYPE_FLOAT; + dataP->value.asFloat = value; +} + +int lwm2m_data_decode_float(const lwm2m_data_t * dataP, + double * valueP) +{ + int result; + + LOG("Entering"); + switch (dataP->type) + { + case LWM2M_TYPE_FLOAT: + *valueP = dataP->value.asFloat; + result = 1; + break; + + case LWM2M_TYPE_INTEGER: + *valueP = (double)dataP->value.asInteger; + result = 1; + break; + + case LWM2M_TYPE_STRING: + result = utils_plainTextToFloat64(dataP->value.asBuffer.buffer, dataP->value.asBuffer.length, valueP); + break; + + case LWM2M_TYPE_OPAQUE: + result = utils_opaqueToFloat(dataP->value.asBuffer.buffer, dataP->value.asBuffer.length, valueP); + if (result == (int)dataP->value.asBuffer.length) + { + result = 1; + } + else + { + result = 0; + } + break; + + default: + return 0; + } + + LOG_ARG("result: %d, value: %f", result, *valueP); + + return result; +} + +void lwm2m_data_encode_bool(bool value, + lwm2m_data_t * dataP) +{ + LOG_ARG("value: %s", value?"true":"false"); + dataP->type = LWM2M_TYPE_BOOLEAN; + dataP->value.asBoolean = value; +} + +int lwm2m_data_decode_bool(const lwm2m_data_t * dataP, + bool * valueP) +{ + int result; + + LOG("Entering"); + switch (dataP->type) + { + case LWM2M_TYPE_BOOLEAN: + *valueP = dataP->value.asBoolean; + result = 1; + break; + + case LWM2M_TYPE_STRING: + if (dataP->value.asBuffer.length != 1) return 0; + + switch (dataP->value.asBuffer.buffer[0]) + { + case '0': + *valueP = false; + result = 1; + break; + case '1': + *valueP = true; + result = 1; + break; + default: + result = 0; + break; + } + break; + + case LWM2M_TYPE_OPAQUE: + if (dataP->value.asBuffer.length != 1) return 0; + + switch (dataP->value.asBuffer.buffer[0]) + { + case 0: + *valueP = false; + result = 1; + break; + case 1: + *valueP = true; + result = 1; + break; + default: + result = 0; + break; + } + break; + + default: + result = 0; + break; + } + + LOG_ARG("result: %d, value: %s", result, *valueP ? "true" : "false"); + + return result; +} + +void lwm2m_data_encode_objlink(uint16_t objectId, + uint16_t objectInstanceId, + lwm2m_data_t * dataP) +{ + LOG_ARG("value: %d/%d", objectId, objectInstanceId); + dataP->type = LWM2M_TYPE_OBJECT_LINK; + dataP->value.asObjLink.objectId = objectId; + dataP->value.asObjLink.objectInstanceId = objectInstanceId; +} + +void lwm2m_data_include(lwm2m_data_t * subDataP, + size_t count, + lwm2m_data_t * dataP) +{ + LOG_ARG("count: %d", count); + if (subDataP == NULL || count == 0) return; + + switch (subDataP[0].type) + { + case LWM2M_TYPE_STRING: + case LWM2M_TYPE_OPAQUE: + case LWM2M_TYPE_INTEGER: + case LWM2M_TYPE_FLOAT: + case LWM2M_TYPE_BOOLEAN: + case LWM2M_TYPE_OBJECT_LINK: + case LWM2M_TYPE_MULTIPLE_RESOURCE: + dataP->type = LWM2M_TYPE_OBJECT_INSTANCE; + break; + case LWM2M_TYPE_OBJECT_INSTANCE: + dataP->type = LWM2M_TYPE_OBJECT; + break; + default: + return; + } + dataP->value.asChildren.count = count; + dataP->value.asChildren.array = subDataP; +} + +void lwm2m_data_encode_instances(lwm2m_data_t * subDataP, + size_t count, + lwm2m_data_t * dataP) +{ + LOG_ARG("count: %d", count); + lwm2m_data_include(subDataP, count, dataP); + dataP->type = LWM2M_TYPE_MULTIPLE_RESOURCE; +} + +int lwm2m_data_parse(lwm2m_uri_t * uriP, + uint8_t * buffer, + size_t bufferLen, + lwm2m_media_type_t format, + lwm2m_data_t ** dataP) +{ + LOG_ARG("format: %s, bufferLen: %d", STR_MEDIA_TYPE(format), bufferLen); + LOG_URI(uriP); + switch (format) + { + case LWM2M_CONTENT_TEXT: + if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) return 0; + *dataP = lwm2m_data_new(1); + if (*dataP == NULL) return 0; + (*dataP)->id = uriP->resourceId; + (*dataP)->type = LWM2M_TYPE_STRING; + return prv_setBuffer(*dataP, buffer, bufferLen); + + case LWM2M_CONTENT_OPAQUE: + if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) return 0; + *dataP = lwm2m_data_new(1); + if (*dataP == NULL) return 0; + (*dataP)->id = uriP->resourceId; + (*dataP)->type = LWM2M_TYPE_OPAQUE; + return prv_setBuffer(*dataP, buffer, bufferLen); + +#ifdef LWM2M_OLD_CONTENT_FORMAT_SUPPORT + case LWM2M_CONTENT_TLV_OLD: +#endif + case LWM2M_CONTENT_TLV: + return tlv_parse(buffer, bufferLen, dataP); + +#ifdef LWM2M_SUPPORT_JSON +#ifdef LWM2M_OLD_CONTENT_FORMAT_SUPPORT + case LWM2M_CONTENT_JSON_OLD: +#endif + case LWM2M_CONTENT_JSON: + return json_parse(uriP, buffer, bufferLen, dataP); +#endif + + default: + return 0; + } +} + +int lwm2m_data_serialize(lwm2m_uri_t * uriP, + int size, + lwm2m_data_t * dataP, + lwm2m_media_type_t * formatP, + uint8_t ** bufferP) +{ + LOG_URI(uriP); + LOG_ARG("size: %d, formatP: %s", size, STR_MEDIA_TYPE(*formatP)); + + // Check format + if (*formatP == LWM2M_CONTENT_TEXT + || *formatP == LWM2M_CONTENT_OPAQUE) + { + if (size != 1 + || (uriP != NULL && !LWM2M_URI_IS_SET_RESOURCE(uriP)) + || dataP->type == LWM2M_TYPE_OBJECT + || dataP->type == LWM2M_TYPE_OBJECT_INSTANCE + || dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { +#ifdef LWM2M_SUPPORT_JSON + *formatP = LWM2M_CONTENT_JSON; +#else + *formatP = LWM2M_CONTENT_TLV; +#endif + } + } + + if (*formatP == LWM2M_CONTENT_TEXT + && dataP->type == LWM2M_TYPE_OPAQUE) + { + *formatP = LWM2M_CONTENT_OPAQUE; + } + LOG_ARG("Final format: %s", STR_MEDIA_TYPE(*formatP)); + + switch (*formatP) + { + case LWM2M_CONTENT_TEXT: + return prv_textSerialize(dataP, bufferP); + + case LWM2M_CONTENT_OPAQUE: + *bufferP = (uint8_t *)lwm2m_malloc(dataP->value.asBuffer.length); + if (*bufferP == NULL) return -1; + memcpy(*bufferP, dataP->value.asBuffer.buffer, dataP->value.asBuffer.length); + return (int)dataP->value.asBuffer.length; + + case LWM2M_CONTENT_TLV: + { + bool isResourceInstance; + + if (uriP != NULL && LWM2M_URI_IS_SET_RESOURCE(uriP) + && (size != 1 || dataP->id != uriP->resourceId)) + { + isResourceInstance = true; + } + else + { + isResourceInstance = false; + } + return tlv_serialize(isResourceInstance, size, dataP, bufferP); + } + +#ifdef LWM2M_CLIENT_MODE + case LWM2M_CONTENT_LINK: + return discover_serialize(NULL, uriP, NULL, size, dataP, bufferP); +#endif +#ifdef LWM2M_SUPPORT_JSON + case LWM2M_CONTENT_JSON: + return json_serialize(uriP, size, dataP, bufferP); +#endif + + default: + return -1; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/discover.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,442 @@ +/******************************************************************************* +* +* Copyright (c) 2015 Intel Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* and Eclipse Distribution License v1.0 which accompany this distribution. +* +* The Eclipse Public License is available at +* http://www.eclipse.org/legal/epl-v10.html +* The Eclipse Distribution License is available at +* http://www.eclipse.org/org/documents/edl-v10.php. +* +* Contributors: +* David Navarro, Intel Corporation - initial API and implementation +* +*******************************************************************************/ + + +#include "internals.h" + +#define PRV_LINK_BUFFER_SIZE 1024 + + +#define PRV_CONCAT_STR(buf, len, index, str, str_len) \ + { \ + if ((len)-(index) < (str_len)) return -1; \ + memcpy((buf)+(index), (str), (str_len)); \ + (index) += (str_len); \ + } + + +#ifdef LWM2M_CLIENT_MODE + +static lwm2m_attributes_t * prv_findAttributes(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_server_t * serverP) +{ + lwm2m_observed_t * observedP; + lwm2m_watcher_t * watcherP; + lwm2m_attributes_t * paramP; + + paramP = NULL; + + if (contextP == NULL) return NULL; + if (serverP == NULL) return NULL; + + observedP = observe_findByUri(contextP, uriP); + if (observedP == NULL || observedP->watcherList == NULL) return NULL; + + for (watcherP = observedP->watcherList; watcherP != NULL; watcherP = watcherP->next) + { + if (watcherP->server == serverP) + { + paramP = watcherP->parameters; + } + } + + return paramP; +} + +static int prv_serializeAttributes(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_server_t * serverP, + lwm2m_attributes_t * objectParamP, + uint8_t * buffer, + size_t uriLen, + size_t bufferLen) +{ + int head; + int res; + lwm2m_attributes_t * paramP; + + head = 0; + + paramP = prv_findAttributes(contextP, uriP, serverP); + if (paramP == NULL) paramP = objectParamP; + + if (paramP != NULL) + { + head = uriLen; + + if (paramP->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD) + { + PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE); + PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN); + + res = utils_intToText(paramP->minPeriod, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + } + else if (objectParamP->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD) + { + PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE); + PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN); + + res = utils_intToText(objectParamP->minPeriod, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + } + + if (paramP->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD) + { + PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE); + PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN); + + res = utils_intToText(paramP->maxPeriod, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + } + else if (objectParamP->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD) + { + PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE); + PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN); + + res = utils_intToText(objectParamP->maxPeriod, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + } + + if (paramP->toSet & LWM2M_ATTR_FLAG_GREATER_THAN) + { + PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE); + PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_GREATER_THAN_STR, ATTR_GREATER_THAN_LEN); + + res = utils_floatToText(paramP->greaterThan, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + } + if (paramP->toSet & LWM2M_ATTR_FLAG_LESS_THAN) + { + PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE); + PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_LESS_THAN_STR, ATTR_LESS_THAN_LEN); + + res = utils_floatToText(paramP->lessThan, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + } + if (paramP->toSet & LWM2M_ATTR_FLAG_STEP) + { + PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE); + PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_STEP_STR, ATTR_STEP_LEN); + + res = utils_floatToText(paramP->step, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + } + PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ITEM_ATTR_END, LINK_ITEM_ATTR_END_SIZE); + } + + if (head > 0) head -= uriLen + 1; + + return head; +} + +static int prv_serializeLinkData(lwm2m_context_t * contextP, + lwm2m_data_t * tlvP, + lwm2m_server_t * serverP, + lwm2m_attributes_t * objectParamP, + lwm2m_uri_t * parentUriP, + uint8_t * parentUriStr, + size_t parentUriLen, + uint8_t * buffer, + size_t bufferLen) +{ + int head; + int res; + lwm2m_uri_t uri; + + head = 0; + + switch (tlvP->type) + { + case LWM2M_TYPE_UNDEFINED: + case LWM2M_TYPE_STRING: + case LWM2M_TYPE_OPAQUE: + case LWM2M_TYPE_INTEGER: + case LWM2M_TYPE_FLOAT: + case LWM2M_TYPE_BOOLEAN: + case LWM2M_TYPE_OBJECT_LINK: + case LWM2M_TYPE_MULTIPLE_RESOURCE: + if (bufferLen < LINK_ITEM_START_SIZE) return -1; + memcpy(buffer + head, LINK_ITEM_START, LINK_ITEM_START_SIZE); + head = LINK_ITEM_START_SIZE; + + if (parentUriLen > 0) + { + if (bufferLen - head < parentUriLen) return -1; + memcpy(buffer + head, parentUriStr, parentUriLen); + head += parentUriLen; + } + + if (bufferLen - head < LINK_URI_SEPARATOR_SIZE) return -1; + memcpy(buffer + head, LINK_URI_SEPARATOR, LINK_URI_SEPARATOR_SIZE); + head += LINK_URI_SEPARATOR_SIZE; + + res = utils_intToText(tlvP->id, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + + if (tlvP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + if (bufferLen - head < LINK_ITEM_DIM_START_SIZE) return -1; + memcpy(buffer + head, LINK_ITEM_DIM_START, LINK_ITEM_DIM_START_SIZE); + head += LINK_ITEM_DIM_START_SIZE; + + res = utils_intToText(tlvP->value.asChildren.count, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + + if (bufferLen - head < LINK_ITEM_ATTR_END_SIZE) return -1; + memcpy(buffer + head, LINK_ITEM_ATTR_END, LINK_ITEM_ATTR_END_SIZE); + head += LINK_ITEM_ATTR_END_SIZE; + } + else + { + if (bufferLen - head < LINK_ITEM_END_SIZE) return -1; + memcpy(buffer + head, LINK_ITEM_END, LINK_ITEM_END_SIZE); + head += LINK_ITEM_END_SIZE; + } + + if (serverP != NULL) + { + memcpy(&uri, parentUriP, sizeof(lwm2m_uri_t)); + uri.resourceId = tlvP->id; + uri.flag |= LWM2M_URI_FLAG_RESOURCE_ID; + res = prv_serializeAttributes(contextP, &uri, serverP, objectParamP, buffer, head - 1, bufferLen); + if (res < 0) return -1; // careful, 0 is valid + if (res > 0) head += res; + } + break; + + case LWM2M_TYPE_OBJECT_INSTANCE: + { + uint8_t uriStr[URI_MAX_STRING_LEN]; + size_t uriLen; + size_t index; + + if (parentUriLen > 0) + { + if (URI_MAX_STRING_LEN < parentUriLen) return -1; + memcpy(uriStr, parentUriStr, parentUriLen); + uriLen = parentUriLen; + } + else + { + uriLen = 0; + } + + if (URI_MAX_STRING_LEN - uriLen < LINK_URI_SEPARATOR_SIZE) return -1; + memcpy(uriStr + uriLen, LINK_URI_SEPARATOR, LINK_URI_SEPARATOR_SIZE); + uriLen += LINK_URI_SEPARATOR_SIZE; + + res = utils_intToText(tlvP->id, uriStr + uriLen, URI_MAX_STRING_LEN - uriLen); + if (res <= 0) return -1; + uriLen += res; + + memcpy(&uri, parentUriP, sizeof(lwm2m_uri_t)); + uri.instanceId = tlvP->id; + uri.flag |= LWM2M_URI_FLAG_INSTANCE_ID; + + head = 0; + PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ITEM_START, LINK_ITEM_START_SIZE); + PRV_CONCAT_STR(buffer, bufferLen, head, uriStr, uriLen); + PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ITEM_END, LINK_ITEM_END_SIZE); + if (serverP != NULL) + { + res = prv_serializeAttributes(contextP, &uri, serverP, NULL, buffer, head - 1, bufferLen); + if (res < 0) return -1; // careful, 0 is valid + if (res == 0) head = 0; // rewind + else head += res; + } + for (index = 0; index < tlvP->value.asChildren.count; index++) + { + res = prv_serializeLinkData(contextP, tlvP->value.asChildren.array + index, serverP, objectParamP, &uri, uriStr, uriLen, buffer + head, bufferLen - head); + if (res < 0) return -1; + head += res; + } + } + break; + + case LWM2M_TYPE_OBJECT: + default: + return -1; + } + + return head; +} + +int discover_serialize(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_server_t * serverP, + int size, + lwm2m_data_t * dataP, + uint8_t ** bufferP) +{ + uint8_t bufferLink[PRV_LINK_BUFFER_SIZE]; + uint8_t baseUriStr[URI_MAX_STRING_LEN]; + int baseUriLen; + int index; + size_t head; + int res; + lwm2m_uri_t parentUri; + lwm2m_attributes_t * paramP; + lwm2m_attributes_t mergedParam; + + LOG_ARG("size: %d", size); + LOG_URI(uriP); + + head = 0; + memset(&parentUri, 0, sizeof(lwm2m_uri_t)); + parentUri.objectId = uriP->objectId; + parentUri.flag = LWM2M_URI_FLAG_OBJECT_ID; + + if (LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + lwm2m_uri_t tempUri; + lwm2m_attributes_t * objParamP; + lwm2m_attributes_t * instParamP; + + memset(&parentUri, 0, sizeof(lwm2m_uri_t)); + tempUri.objectId = uriP->objectId; + tempUri.flag = LWM2M_URI_FLAG_OBJECT_ID; + + // get object level attributes + objParamP = prv_findAttributes(contextP, &tempUri, serverP); + + // get object instance level attributes + tempUri.instanceId = uriP->instanceId; + tempUri.flag = LWM2M_URI_FLAG_INSTANCE_ID; + instParamP = prv_findAttributes(contextP, &tempUri, serverP); + + if (objParamP != NULL) + { + if (instParamP != NULL) + { + memset(&mergedParam, 0, sizeof(lwm2m_attributes_t)); + mergedParam.toSet = objParamP->toSet | instParamP->toSet; + if (mergedParam.toSet & LWM2M_ATTR_FLAG_MIN_PERIOD) + { + if (instParamP->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD) + { + mergedParam.minPeriod = instParamP->minPeriod; + } + else + { + mergedParam.minPeriod = objParamP->minPeriod; + } + } + if (mergedParam.toSet & LWM2M_ATTR_FLAG_MAX_PERIOD) + { + if (instParamP->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD) + { + mergedParam.maxPeriod = instParamP->maxPeriod; + } + else + { + mergedParam.maxPeriod = objParamP->maxPeriod; + } + } + paramP = &mergedParam; + } + else + { + paramP = objParamP; + } + } + else + { + paramP = instParamP; + } + uriP->flag &= ~LWM2M_URI_FLAG_RESOURCE_ID; + } + else + { + paramP = NULL; + + if (LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + PRV_CONCAT_STR(bufferLink, PRV_LINK_BUFFER_SIZE, head, LINK_ITEM_START, LINK_ITEM_START_SIZE); + PRV_CONCAT_STR(bufferLink, PRV_LINK_BUFFER_SIZE, head, LINK_URI_SEPARATOR, LINK_URI_SEPARATOR_SIZE); + res = utils_intToText(uriP->objectId, bufferLink + head, PRV_LINK_BUFFER_SIZE - head); + if (res <= 0) return -1; + head += res; + PRV_CONCAT_STR(bufferLink, PRV_LINK_BUFFER_SIZE, head, LINK_URI_SEPARATOR, LINK_URI_SEPARATOR_SIZE); + res = utils_intToText(uriP->instanceId, bufferLink + head, PRV_LINK_BUFFER_SIZE - head); + if (res <= 0) return -1; + head += res; + PRV_CONCAT_STR(bufferLink, PRV_LINK_BUFFER_SIZE, head, LINK_ITEM_END, LINK_ITEM_END_SIZE); + parentUri.instanceId = uriP->instanceId; + parentUri.flag = LWM2M_URI_FLAG_INSTANCE_ID; + if (serverP != NULL) + { + res = prv_serializeAttributes(contextP, &parentUri, serverP, NULL, bufferLink, head - 1, PRV_LINK_BUFFER_SIZE); + if (res < 0) return -1; // careful, 0 is valid + } + else + { + res = 0; + } + head += res; + } + else + { + PRV_CONCAT_STR(bufferLink, PRV_LINK_BUFFER_SIZE, head, LINK_ITEM_START, LINK_ITEM_START_SIZE); + PRV_CONCAT_STR(bufferLink, PRV_LINK_BUFFER_SIZE, head, LINK_URI_SEPARATOR, LINK_URI_SEPARATOR_SIZE); + res = utils_intToText(uriP->objectId, bufferLink + head, PRV_LINK_BUFFER_SIZE - head); + if (res <= 0) return -1; + head += res; + PRV_CONCAT_STR(bufferLink, PRV_LINK_BUFFER_SIZE, head, LINK_ITEM_END, LINK_ITEM_END_SIZE); + + if (serverP != NULL) + { + res = prv_serializeAttributes(contextP, &parentUri, serverP, NULL, bufferLink, head - 1, PRV_LINK_BUFFER_SIZE); + if (res < 0) return -1; // careful, 0 is valid + head += res; + } + } + } + + baseUriLen = uri_toString(uriP, baseUriStr, URI_MAX_STRING_LEN, NULL); + if (baseUriLen < 0) return -1; + baseUriLen -= 1; + + for (index = 0; index < size && head < PRV_LINK_BUFFER_SIZE; index++) + { + res = prv_serializeLinkData(contextP, dataP + index, serverP, paramP, uriP, baseUriStr, baseUriLen, bufferLink + head, PRV_LINK_BUFFER_SIZE - head); + if (res < 0) return -1; + head += res; + } + + if (head > 0) + { + head -= 1; + + *bufferP = (uint8_t *)lwm2m_malloc(head); + if (*bufferP == NULL) return 0; + memcpy(*bufferP, bufferLink, head); + } + + return (int)head; +} +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/er-coap-13/er-coap-13.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,1371 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * An implementation of the Constrained Application Protocol (draft 12) + * \author + * Matthias Kovatsch <kovatsch@inf.ethz.ch> + * \contributors + * David Navarro, Intel Corporation - Adapt to usage in liblwm2m + */ + + +#include <stdlib.h> + +#include <string.h> +#include <stdio.h> + +#include "er-coap-13.h" + +#include "liblwm2m.h" /* for lwm2m_malloc() and lwm2m_free() */ + +#define DEBUG 0 +#if DEBUG +#include <stdio.h> +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +/*-----------------------------------------------------------------------------------*/ +/*- Variables -----------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +static uint16_t current_mid = 0; + +coap_status_t coap_error_code = NO_ERROR; +const char *coap_error_message = ""; +/*-----------------------------------------------------------------------------------*/ +/*- LOCAL HELP FUNCTIONS ------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +static +uint16_t +coap_log_2(uint16_t value) +{ + uint16_t result = 0; + do { + value = value >> 1; + result++; + } while (value); + + return result ? result - 1 : result; +} +/*-----------------------------------------------------------------------------------*/ +static +uint32_t +coap_parse_int_option(uint8_t *bytes, size_t length) +{ + uint32_t var = 0; + size_t i = 0; + while (i<length) + { + var <<= 8; + var |= bytes[i++]; + } + return var; +} +/*-----------------------------------------------------------------------------------*/ +static +uint8_t +coap_option_nibble(unsigned int value) +{ + if (value<13) + { + return value; + } + else if (value<=0xFF+13) + { + return 13; + } + else + { + return 14; + } +} +/*-----------------------------------------------------------------------------------*/ +static +size_t +coap_set_option_header(unsigned int delta, size_t length, uint8_t *buffer) +{ + size_t written = 0; + unsigned int *x = δ + + buffer[0] = coap_option_nibble(delta)<<4 | coap_option_nibble(length); + + /* avoids code duplication without function overhead */ + do + { + if (*x>268) + { + buffer[++written] = (*x-269)>>8; + buffer[++written] = (*x-269); + } + else if (*x>12) + { + buffer[++written] = (*x-13); + } + } + while (x!=(unsigned int *)&length && (x=(unsigned int *)&length)); + + PRINTF("WRITTEN %u B opt header\n", written); + + return ++written; +} +/*-----------------------------------------------------------------------------------*/ +static +size_t +coap_serialize_int_option(unsigned int number, unsigned int current_number, uint8_t *buffer, uint32_t value) +{ + size_t i = 0; + + if (0xFF000000 & value) ++i; + if (0xFFFF0000 & value) ++i; + if (0xFFFFFF00 & value) ++i; + if (0xFFFFFFFF & value) ++i; + + PRINTF("OPTION %u (delta %u, len %u)\n", number, number - current_number, i); + + i = coap_set_option_header(number - current_number, i, buffer); + + if (0xFF000000 & value) buffer[i++] = (uint8_t) (value>>24); + if (0xFFFF0000 & value) buffer[i++] = (uint8_t) (value>>16); + if (0xFFFFFF00 & value) buffer[i++] = (uint8_t) (value>>8); + if (0xFFFFFFFF & value) buffer[i++] = (uint8_t) (value); + + return i; +} +/*-----------------------------------------------------------------------------------*/ +static +size_t +coap_serialize_array_option(unsigned int number, unsigned int current_number, uint8_t *buffer, uint8_t *array, size_t length, char split_char) +{ + size_t i = 0; + + if (split_char!='\0') + { + size_t j; + uint8_t *part_start = array; + uint8_t *part_end = NULL; + size_t temp_length; + + for (j = 0; j<=length; ++j) + { + if (array[j]==split_char || j==length) + { + part_end = array + j; + temp_length = part_end-part_start; + + i += coap_set_option_header(number - current_number, temp_length, &buffer[i]); + memcpy(&buffer[i], part_start, temp_length); + i += temp_length; + + PRINTF("OPTION type %u, delta %u, len %u, part [%.*s]\n", number, number - current_number, i, temp_length, part_start); + + ++j; /* skip the splitter */ + current_number = number; + part_start = array + j; + } + } /* for */ + } + else + { + i += coap_set_option_header(number - current_number, length, &buffer[i]); + memcpy(&buffer[i], array, length); + i += length; + + PRINTF("OPTION type %u, delta %u, len %u\n", number, number - current_number, length); + } + + return i; +} +/*-----------------------------------------------------------------------------------*/ +static +size_t +coap_serialize_multi_option(unsigned int number, unsigned int current_number, uint8_t *buffer, multi_option_t *array) +{ + size_t i = 0; + multi_option_t * j; + + for (j = array; j != NULL; j= j->next) + { + i += coap_set_option_header(number - current_number, j->len, &buffer[i]); + current_number = number; + memcpy(&buffer[i], j->data, j->len); + i += j->len; + } /* for */ + + return i; +} +/*-----------------------------------------------------------------------------------*/ +static +void +coap_merge_multi_option(uint8_t **dst, size_t *dst_len, uint8_t *option, size_t option_len, char separator) +{ + /* Merge multiple options. */ + if (*dst_len > 0) + { + /* dst already contains an option: concatenate */ + (*dst)[*dst_len] = separator; + *dst_len += 1; + + /* memmove handles 2-byte option headers */ + memmove((*dst)+(*dst_len), option, option_len); + + *dst_len += option_len; + } + else + { + /* dst is empty: set to option */ + *dst = option; + *dst_len = option_len; + } +} + +void +coap_add_multi_option(multi_option_t **dst, uint8_t *option, size_t option_len, uint8_t is_static) +{ + multi_option_t *opt = (multi_option_t *)lwm2m_malloc(sizeof(multi_option_t)); + + if (opt) + { + opt->next = NULL; + opt->len = (uint8_t)option_len; + if (is_static) + { + opt->data = option; + opt->is_static = 1; + } + else + { + opt->is_static = 0; + opt->data = (uint8_t *)lwm2m_malloc(option_len); + if (opt->data == NULL) + { + lwm2m_free(opt); + return; + } + memcpy(opt->data, option, option_len); + } + + if (*dst) + { + multi_option_t * i = *dst; + while (i->next) + { + i = i->next; + } + i->next = opt; + } + else + { + *dst = opt; + } + } +} + +void +free_multi_option(multi_option_t *dst) +{ + if (dst) + { + multi_option_t *n = dst->next; + dst->next = NULL; + if (dst->is_static == 0) + { + lwm2m_free(dst->data); + } + lwm2m_free(dst); + free_multi_option(n); + } +} + +char * coap_get_multi_option_as_string(multi_option_t * option) +{ + size_t len = 0; + multi_option_t * opt; + char * output; + + for (opt = option; opt != NULL; opt = opt->next) + { + len += opt->len + 1; // for separator + } + + output = lwm2m_malloc(len + 1); // for String terminator + if (output != NULL) + { + size_t i = 0; + + for (opt = option; opt != NULL; opt = opt->next) + { + output[i] = '/'; + i += 1; + + memmove(output + i, opt->data, opt->len); + i += opt->len; + } + output[i] = 0; + } + + return output; +} + +/*-----------------------------------------------------------------------------------*/ +static +int +coap_get_variable(const uint8_t *buffer, size_t length, const char *name, const char **output) +{ + const uint8_t *start = NULL; + const uint8_t *end = NULL; + const uint8_t *value_end = NULL; + size_t name_len = 0; + + /*initialize the output buffer first*/ + *output = 0; + + name_len = strlen(name); + end = buffer + length; + + for (start = buffer; start + name_len < end; ++start){ + if ((start == buffer || start[-1] == '&') && start[name_len] == '=' && + strncmp(name, (char *)start, name_len)==0) { + + /* Point start to variable value */ + start += name_len + 1; + + /* Point end to the end of the value */ + value_end = (const uint8_t *) memchr(start, '&', end - start); + if (value_end == NULL) { + value_end = end; + } + + *output = (char *)start; + + return (value_end - start); + } + } + + return 0; +} + +/*-----------------------------------------------------------------------------------*/ +uint16_t +coap_get_mid() +{ + return ++current_mid; +} +/*-----------------------------------------------------------------------------------*/ +/*- MEASSAGE PROCESSING -------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +void +coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t mid) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + /* Important thing */ + memset(coap_pkt, 0, sizeof(coap_packet_t)); + + coap_pkt->type = type; + coap_pkt->code = code; + coap_pkt->mid = mid; +} + +void +coap_free_header(void *packet) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + free_multi_option(coap_pkt->uri_path); + free_multi_option(coap_pkt->uri_query); + free_multi_option(coap_pkt->location_path); + coap_pkt->uri_path = NULL; + coap_pkt->uri_query = NULL; + coap_pkt->location_path = NULL; +} + +/*-----------------------------------------------------------------------------------*/ +size_t coap_serialize_get_size(void *packet) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + size_t length = 0; + + length = COAP_HEADER_LEN + coap_pkt->payload_len + coap_pkt->token_len; + + if (IS_OPTION(coap_pkt, COAP_OPTION_IF_MATCH)) + { + length += COAP_MAX_OPTION_HEADER_LEN + coap_pkt->if_match_len; + } + if (IS_OPTION(coap_pkt, COAP_OPTION_URI_HOST)) + { + length += COAP_MAX_OPTION_HEADER_LEN + coap_pkt->uri_host_len; + } + if (IS_OPTION(coap_pkt, COAP_OPTION_ETAG)) + { + length += COAP_MAX_OPTION_HEADER_LEN + coap_pkt->etag_len; + } + if (IS_OPTION(coap_pkt, COAP_OPTION_IF_NONE_MATCH)) + { + // can be stored in extended fields + length += COAP_MAX_OPTION_HEADER_LEN; + } + if (IS_OPTION(coap_pkt, COAP_OPTION_OBSERVE)) + { + // can be stored in extended fields + length += COAP_MAX_OPTION_HEADER_LEN; + } + if (IS_OPTION(coap_pkt, COAP_OPTION_URI_PORT)) + { + // can be stored in extended fields + length += COAP_MAX_OPTION_HEADER_LEN; + } + if (IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH)) + { + multi_option_t * optP; + + for (optP = coap_pkt->location_path ; optP != NULL ; optP = optP->next) + { + length += COAP_MAX_OPTION_HEADER_LEN + optP->len; + } + } + if (IS_OPTION(coap_pkt, COAP_OPTION_URI_PATH)) + { + multi_option_t * optP; + + for (optP = coap_pkt->uri_path ; optP != NULL ; optP = optP->next) + { + length += COAP_MAX_OPTION_HEADER_LEN + optP->len; + } + } + if (IS_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE)) + { + // can be stored in extended fields + length += COAP_MAX_OPTION_HEADER_LEN; + } + if (IS_OPTION(coap_pkt, COAP_OPTION_MAX_AGE)) + { + // can be stored in extended fields + length += COAP_MAX_OPTION_HEADER_LEN; + } + if (IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) + { + multi_option_t * optP; + + for (optP = coap_pkt->uri_query ; optP != NULL ; optP = optP->next) + { + length += COAP_MAX_OPTION_HEADER_LEN + optP->len; + } + } + if (IS_OPTION(coap_pkt, COAP_OPTION_ACCEPT)) + { + length += coap_pkt->accept_num * COAP_MAX_OPTION_HEADER_LEN; + } + if (IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY)) + { + length += COAP_MAX_OPTION_HEADER_LEN + coap_pkt->location_query_len; + } + if (IS_OPTION(coap_pkt, COAP_OPTION_BLOCK2)) + { + // can be stored in extended fields + length += COAP_MAX_OPTION_HEADER_LEN; + } + if (IS_OPTION(coap_pkt, COAP_OPTION_BLOCK1)) + { + // can be stored in extended fields + length += COAP_MAX_OPTION_HEADER_LEN; + } + if (IS_OPTION(coap_pkt, COAP_OPTION_SIZE)) + { + // can be stored in extended fields + length += COAP_MAX_OPTION_HEADER_LEN; + } + if (IS_OPTION(coap_pkt, COAP_OPTION_PROXY_URI)) + { + length += COAP_MAX_OPTION_HEADER_LEN + coap_pkt->proxy_uri_len; + } + + return length; +} + +/*-----------------------------------------------------------------------------------*/ +size_t +coap_serialize_message(void *packet, uint8_t *buffer) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + uint8_t *option; + unsigned int current_number = 0; + + /* Initialize */ + coap_pkt->buffer = buffer; + coap_pkt->version = 1; + + PRINTF("-Serializing MID %u to %p, ", coap_pkt->mid, coap_pkt->buffer); + + /* set header fields */ + coap_pkt->buffer[0] = 0x00; + coap_pkt->buffer[0] |= COAP_HEADER_VERSION_MASK & (coap_pkt->version)<<COAP_HEADER_VERSION_POSITION; + coap_pkt->buffer[0] |= COAP_HEADER_TYPE_MASK & (coap_pkt->type)<<COAP_HEADER_TYPE_POSITION; + coap_pkt->buffer[0] |= COAP_HEADER_TOKEN_LEN_MASK & (coap_pkt->token_len)<<COAP_HEADER_TOKEN_LEN_POSITION; + coap_pkt->buffer[1] = coap_pkt->code; + coap_pkt->buffer[2] = (uint8_t) ((coap_pkt->mid)>>8); + coap_pkt->buffer[3] = (uint8_t) (coap_pkt->mid); + + /* set Token */ + PRINTF("Token (len %u)", coap_pkt->token_len); + option = coap_pkt->buffer + COAP_HEADER_LEN; + for (current_number=0; current_number<coap_pkt->token_len; ++current_number) + { + PRINTF(" %02X", coap_pkt->token[current_number]); + *option = coap_pkt->token[current_number]; + ++option; + } + PRINTF("-\n"); + + /* Serialize options */ + current_number = 0; + + PRINTF("-Serializing options at %p-\n", option); + + /* The options must be serialized in the order of their number */ + COAP_SERIALIZE_BYTE_OPTION( COAP_OPTION_IF_MATCH, if_match, "If-Match") + COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_URI_HOST, uri_host, '\0', "Uri-Host") + COAP_SERIALIZE_BYTE_OPTION( COAP_OPTION_ETAG, etag, "ETag") + COAP_SERIALIZE_INT_OPTION( COAP_OPTION_IF_NONE_MATCH, content_type-coap_pkt->content_type, "If-None-Match") /* hack to get a zero field */ + COAP_SERIALIZE_INT_OPTION( COAP_OPTION_OBSERVE, observe, "Observe") + COAP_SERIALIZE_INT_OPTION( COAP_OPTION_URI_PORT, uri_port, "Uri-Port") + COAP_SERIALIZE_MULTI_OPTION( COAP_OPTION_LOCATION_PATH, location_path, "Location-Path") + COAP_SERIALIZE_MULTI_OPTION( COAP_OPTION_URI_PATH, uri_path, "Uri-Path") + COAP_SERIALIZE_INT_OPTION( COAP_OPTION_CONTENT_TYPE, content_type, "Content-Format") + COAP_SERIALIZE_INT_OPTION( COAP_OPTION_MAX_AGE, max_age, "Max-Age") + COAP_SERIALIZE_MULTI_OPTION( COAP_OPTION_URI_QUERY, uri_query, "Uri-Query") + COAP_SERIALIZE_ACCEPT_OPTION( COAP_OPTION_ACCEPT, accept, "Accept") + COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_LOCATION_QUERY, location_query, '&', "Location-Query") + COAP_SERIALIZE_BLOCK_OPTION( COAP_OPTION_BLOCK2, block2, "Block2") + COAP_SERIALIZE_BLOCK_OPTION( COAP_OPTION_BLOCK1, block1, "Block1") + COAP_SERIALIZE_INT_OPTION( COAP_OPTION_SIZE, size, "Size") + COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_PROXY_URI, proxy_uri, '\0', "Proxy-Uri") + + PRINTF("-Done serializing at %p----\n", option); + + /* Free allocated header fields */ + coap_free_header(packet); + + /* Pack payload */ + /* Payload marker */ + if (coap_pkt->payload_len) + { + *option = 0xFF; + ++option; + } + + memmove(option, coap_pkt->payload, coap_pkt->payload_len); + + PRINTF("-Done %u B (header len %u, payload len %u)-\n", coap_pkt->payload_len + option - buffer, option - buffer, coap_pkt->payload_len); + + PRINTF("Dump [0x%02X %02X %02X %02X %02X %02X %02X %02X]\n", + coap_pkt->buffer[0], + coap_pkt->buffer[1], + coap_pkt->buffer[2], + coap_pkt->buffer[3], + coap_pkt->buffer[4], + coap_pkt->buffer[5], + coap_pkt->buffer[6], + coap_pkt->buffer[7] + ); + + return (option - buffer) + coap_pkt->payload_len; /* packet length */ +} +/*-----------------------------------------------------------------------------------*/ +coap_status_t +coap_parse_message(void *packet, uint8_t *data, uint16_t data_len) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + uint8_t *current_option; + unsigned int option_number = 0; + unsigned int option_delta = 0; + size_t option_length = 0; + unsigned int *x; + + /* Initialize packet */ + memset(coap_pkt, 0, sizeof(coap_packet_t)); + + /* pointer to packet bytes */ + coap_pkt->buffer = data; + + /* parse header fields */ + coap_pkt->version = (COAP_HEADER_VERSION_MASK & coap_pkt->buffer[0])>>COAP_HEADER_VERSION_POSITION; + coap_pkt->type = (COAP_HEADER_TYPE_MASK & coap_pkt->buffer[0])>>COAP_HEADER_TYPE_POSITION; + coap_pkt->token_len = MIN(COAP_TOKEN_LEN, (COAP_HEADER_TOKEN_LEN_MASK & coap_pkt->buffer[0])>>COAP_HEADER_TOKEN_LEN_POSITION); + coap_pkt->code = coap_pkt->buffer[1]; + coap_pkt->mid = coap_pkt->buffer[2]<<8 | coap_pkt->buffer[3]; + + if (coap_pkt->version != 1) + { + coap_error_message = "CoAP version must be 1"; + return BAD_REQUEST_4_00; + } + + current_option = data + COAP_HEADER_LEN; + + if (coap_pkt->token_len != 0) + { + memcpy(coap_pkt->token, current_option, coap_pkt->token_len); + SET_OPTION(coap_pkt, COAP_OPTION_TOKEN); + + PRINTF("Token (len %u) [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->token_len, + coap_pkt->token[0], + coap_pkt->token[1], + coap_pkt->token[2], + coap_pkt->token[3], + coap_pkt->token[4], + coap_pkt->token[5], + coap_pkt->token[6], + coap_pkt->token[7] + ); /*FIXME always prints 8 bytes */ + } + + /* parse options */ + current_option += coap_pkt->token_len; + + while (current_option < data+data_len) + { + /* Payload marker 0xFF, currently only checking for 0xF* because rest is reserved */ + if ((current_option[0] & 0xF0)==0xF0) + { + coap_pkt->payload = ++current_option; + coap_pkt->payload_len = data_len - (coap_pkt->payload - data); + + break; + } + + option_delta = current_option[0]>>4; + option_length = current_option[0] & 0x0F; + ++current_option; + + /* avoids code duplication without function overhead */ + x = &option_delta; + do + { + if (*x==13) + { + *x += current_option[0]; + ++current_option; + } + else if (*x==14) + { + *x += 255; + *x += current_option[0]<<8; + ++current_option; + *x += current_option[0]; + ++current_option; + } + } + while (x!=(unsigned int *)&option_length && (x=(unsigned int *)&option_length)); + + option_number += option_delta; + + PRINTF("OPTION %u (delta %u, len %u): ", option_number, option_delta, option_length); + + SET_OPTION(coap_pkt, option_number); + + switch (option_number) + { + case COAP_OPTION_CONTENT_TYPE: + coap_pkt->content_type = coap_parse_int_option(current_option, option_length); + PRINTF("Content-Format [%u]\n", coap_pkt->content_type); + break; + case COAP_OPTION_MAX_AGE: + coap_pkt->max_age = coap_parse_int_option(current_option, option_length); + PRINTF("Max-Age [%lu]\n", coap_pkt->max_age); + break; + case COAP_OPTION_ETAG: + coap_pkt->etag_len = (uint8_t)(MIN(COAP_ETAG_LEN, option_length)); + memcpy(coap_pkt->etag, current_option, coap_pkt->etag_len); + PRINTF("ETag %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->etag_len, + coap_pkt->etag[0], + coap_pkt->etag[1], + coap_pkt->etag[2], + coap_pkt->etag[3], + coap_pkt->etag[4], + coap_pkt->etag[5], + coap_pkt->etag[6], + coap_pkt->etag[7] + ); /*FIXME always prints 8 bytes */ + break; + case COAP_OPTION_ACCEPT: + if (coap_pkt->accept_num < COAP_MAX_ACCEPT_NUM) + { + coap_pkt->accept[coap_pkt->accept_num] = coap_parse_int_option(current_option, option_length); + coap_pkt->accept_num += 1; + PRINTF("Accept [%u]\n", coap_pkt->content_type); + } + break; + case COAP_OPTION_IF_MATCH: + /*FIXME support multiple ETags */ + coap_pkt->if_match_len = (uint8_t)(MIN(COAP_ETAG_LEN, option_length)); + memcpy(coap_pkt->if_match, current_option, coap_pkt->if_match_len); + PRINTF("If-Match %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->if_match_len, + coap_pkt->if_match[0], + coap_pkt->if_match[1], + coap_pkt->if_match[2], + coap_pkt->if_match[3], + coap_pkt->if_match[4], + coap_pkt->if_match[5], + coap_pkt->if_match[6], + coap_pkt->if_match[7] + ); /*FIXME always prints 8 bytes */ + break; + case COAP_OPTION_IF_NONE_MATCH: + coap_pkt->if_none_match = 1; + PRINTF("If-None-Match\n"); + break; + + case COAP_OPTION_URI_HOST: + coap_pkt->uri_host = current_option; + coap_pkt->uri_host_len = option_length; + PRINTF("Uri-Host [%.*s]\n", coap_pkt->uri_host_len, coap_pkt->uri_host); + break; + case COAP_OPTION_URI_PORT: + coap_pkt->uri_port = coap_parse_int_option(current_option, option_length); + PRINTF("Uri-Port [%u]\n", coap_pkt->uri_port); + break; + case COAP_OPTION_URI_PATH: + /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ + // coap_merge_multi_option( (char **) &(coap_pkt->uri_path), &(coap_pkt->uri_path_len), current_option, option_length, 0); + coap_add_multi_option( &(coap_pkt->uri_path), current_option, option_length, 1); + PRINTF("Uri-Path [%.*s]\n", option_length, current_option); + break; + case COAP_OPTION_URI_QUERY: + /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ + // coap_merge_multi_option( (char **) &(coap_pkt->uri_query), &(coap_pkt->uri_query_len), current_option, option_length, '&'); + coap_add_multi_option( &(coap_pkt->uri_query), current_option, option_length, 1); + PRINTF("Uri-Query [%.*s]\n", option_length, current_option); + break; + + case COAP_OPTION_LOCATION_PATH: + coap_add_multi_option( &(coap_pkt->location_path), current_option, option_length, 1); + break; + case COAP_OPTION_LOCATION_QUERY: + /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */ + coap_merge_multi_option( &(coap_pkt->location_query), &(coap_pkt->location_query_len), current_option, option_length, '&'); + PRINTF("Location-Query [%.*s]\n", option_length, current_option); + break; + + case COAP_OPTION_PROXY_URI: + /*FIXME check for own end-point */ + coap_pkt->proxy_uri = current_option; + coap_pkt->proxy_uri_len = option_length; + /*TODO length > 270 not implemented (actually not required) */ + PRINTF("Proxy-Uri NOT IMPLEMENTED [%.*s]\n", coap_pkt->proxy_uri_len, coap_pkt->proxy_uri); + coap_error_message = "This is a constrained server (Contiki)"; + return PROXYING_NOT_SUPPORTED_5_05; + break; + + case COAP_OPTION_OBSERVE: + coap_pkt->observe = coap_parse_int_option(current_option, option_length); + PRINTF("Observe [%lu]\n", coap_pkt->observe); + break; + case COAP_OPTION_BLOCK2: + coap_pkt->block2_num = coap_parse_int_option(current_option, option_length); + coap_pkt->block2_more = (coap_pkt->block2_num & 0x08)>>3; + coap_pkt->block2_size = 16 << (coap_pkt->block2_num & 0x07); + coap_pkt->block2_offset = (coap_pkt->block2_num & ~0x0000000F)<<(coap_pkt->block2_num & 0x07); + coap_pkt->block2_num >>= 4; + PRINTF("Block2 [%lu%s (%u B/blk)]\n", coap_pkt->block2_num, coap_pkt->block2_more ? "+" : "", coap_pkt->block2_size); + break; + case COAP_OPTION_BLOCK1: + coap_pkt->block1_num = coap_parse_int_option(current_option, option_length); + coap_pkt->block1_more = (coap_pkt->block1_num & 0x08)>>3; + coap_pkt->block1_size = 16 << (coap_pkt->block1_num & 0x07); + coap_pkt->block1_offset = (coap_pkt->block1_num & ~0x0000000F)<<(coap_pkt->block1_num & 0x07); + coap_pkt->block1_num >>= 4; + PRINTF("Block1 [%lu%s (%u B/blk)]\n", coap_pkt->block1_num, coap_pkt->block1_more ? "+" : "", coap_pkt->block1_size); + break; + case COAP_OPTION_SIZE: + coap_pkt->size = coap_parse_int_option(current_option, option_length); + PRINTF("Size [%lu]\n", coap_pkt->size); + break; + default: + PRINTF("unknown (%u)\n", option_number); + /* Check if critical (odd) */ + if (option_number & 1) + { + coap_error_message = "Unsupported critical option"; + return BAD_OPTION_4_02; + } + } + + current_option += option_length; + } /* for */ + PRINTF("-Done parsing-------\n"); + + + + return NO_ERROR; +} +/*-----------------------------------------------------------------------------------*/ +/*- REST FRAMEWORK FUNCTIONS --------------------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +int +coap_get_query_variable(void *packet, const char *name, const char **output) +{ +/* + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) { + return coap_get_variable(coap_pkt->uri_query, coap_pkt->uri_query_len, name, output); + } +*/ + return 0; +} + +int +coap_get_post_variable(void *packet, const char *name, const char **output) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (coap_pkt->payload_len) { + return coap_get_variable(coap_pkt->payload, coap_pkt->payload_len, name, output); + } + return 0; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_set_status_code(void *packet, unsigned int code) +{ + if (code <= 0xFF) + { + ((coap_packet_t *)packet)->code = (uint8_t) code; + return 1; + } + else + { + return 0; + } +} +/*-----------------------------------------------------------------------------------*/ +/*- HEADER OPTION GETTERS AND SETTERS -----------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +unsigned int +coap_get_header_content_type(void *packet) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE)) return -1; + + return coap_pkt->content_type; +} + +int +coap_set_header_content_type(void *packet, unsigned int content_type) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + coap_pkt->content_type = (coap_content_type_t) content_type; + SET_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE); + return 1; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_accept(void *packet, const uint16_t **accept) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_ACCEPT)) return 0; + + *accept = coap_pkt->accept; + return coap_pkt->accept_num; +} + +int +coap_set_header_accept(void *packet, uint16_t accept) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (coap_pkt->accept_num < COAP_MAX_ACCEPT_NUM) + { + coap_pkt->accept[coap_pkt->accept_num] = accept; + coap_pkt->accept_num += 1; + + SET_OPTION(coap_pkt, COAP_OPTION_ACCEPT); + } + return coap_pkt->accept_num; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_max_age(void *packet, uint32_t *age) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_MAX_AGE)) { + *age = COAP_DEFAULT_MAX_AGE; + } else { + *age = coap_pkt->max_age; + } + return 1; +} + +int +coap_set_header_max_age(void *packet, uint32_t age) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + coap_pkt->max_age = age; + SET_OPTION(coap_pkt, COAP_OPTION_MAX_AGE); + return 1; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_etag(void *packet, const uint8_t **etag) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_ETAG)) return 0; + + *etag = coap_pkt->etag; + return coap_pkt->etag_len; +} + +int +coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + coap_pkt->etag_len = (uint8_t)(MIN(COAP_ETAG_LEN, etag_len)); + memcpy(coap_pkt->etag, etag, coap_pkt->etag_len); + + SET_OPTION(coap_pkt, COAP_OPTION_ETAG); + return coap_pkt->etag_len; +} +/*-----------------------------------------------------------------------------------*/ +/*FIXME support multiple ETags */ +int +coap_get_header_if_match(void *packet, const uint8_t **etag) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_IF_MATCH)) return 0; + + *etag = coap_pkt->if_match; + return coap_pkt->if_match_len; +} + +int +coap_set_header_if_match(void *packet, const uint8_t *etag, size_t etag_len) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + coap_pkt->if_match_len = (uint8_t)(MIN(COAP_ETAG_LEN, etag_len)); + memcpy(coap_pkt->if_match, etag, coap_pkt->if_match_len); + + SET_OPTION(coap_pkt, COAP_OPTION_IF_MATCH); + return coap_pkt->if_match_len; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_if_none_match(void *packet) +{ + return IS_OPTION((coap_packet_t *)packet, COAP_OPTION_IF_NONE_MATCH) ? 1 : 0; +} + +int +coap_set_header_if_none_match(void *packet) +{ + SET_OPTION((coap_packet_t *)packet, COAP_OPTION_IF_NONE_MATCH); + return 1; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_token(void *packet, const uint8_t **token) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_TOKEN)) return 0; + + *token = coap_pkt->token; + return coap_pkt->token_len; +} + +int +coap_set_header_token(void *packet, const uint8_t *token, size_t token_len) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + coap_pkt->token_len = (uint8_t)(MIN(COAP_TOKEN_LEN, token_len)); + memcpy(coap_pkt->token, token, coap_pkt->token_len); + + SET_OPTION(coap_pkt, COAP_OPTION_TOKEN); + return coap_pkt->token_len; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_proxy_uri(void *packet, const char **uri) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_PROXY_URI)) return 0; + + *uri = (const char *)coap_pkt->proxy_uri; + return coap_pkt->proxy_uri_len; +} + +int +coap_set_header_proxy_uri(void *packet, const char *uri) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + coap_pkt->proxy_uri = (uint8_t *)uri; + coap_pkt->proxy_uri_len = strlen(uri); + + SET_OPTION(coap_pkt, COAP_OPTION_PROXY_URI); + return coap_pkt->proxy_uri_len; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_uri_host(void *packet, const char **host) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_HOST)) return 0; + + *host = (char *)coap_pkt->uri_host; + return coap_pkt->uri_host_len; +} + +int +coap_set_header_uri_host(void *packet, const char *host) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + coap_pkt->uri_host = (uint8_t *)host; + coap_pkt->uri_host_len = strlen(host); + + SET_OPTION(coap_pkt, COAP_OPTION_URI_HOST); + return coap_pkt->uri_host_len; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_uri_path(void *packet, const char **path) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_PATH)) return 0; + + *path = NULL; //coap_pkt->uri_path; + return 0; //coap_pkt->uri_path_len; +} + +int +coap_set_header_uri_path(void *packet, const char *path) +{ + coap_packet_t *coap_pkt = (coap_packet_t *) packet; + int length = 0; + + free_multi_option(coap_pkt->uri_path); + coap_pkt->uri_path = NULL; + + if (path[0]=='/') ++path; + + do + { + int i = 0; + + while (path[i] != 0 && path[i] != '/') i++; + coap_add_multi_option(&(coap_pkt->uri_path), (uint8_t *)path, i, 0); + + if (path[i] == '/') i++; + path += i; + length += i; + } while (path[0] != 0); + + SET_OPTION(coap_pkt, COAP_OPTION_URI_PATH); + return length; +} + +int +coap_set_header_uri_path_segment(void *packet, const char *segment) +{ + coap_packet_t *coap_pkt = (coap_packet_t *) packet; + int length; + + if (segment == NULL || segment[0] == 0) + { + coap_add_multi_option(&(coap_pkt->uri_path), NULL, 0, 1); + length = 0; + } + else + { + length = strlen(segment); + coap_add_multi_option(&(coap_pkt->uri_path), (uint8_t *)segment, length, 0); + } + + SET_OPTION(coap_pkt, COAP_OPTION_URI_PATH); + return length; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_uri_query(void *packet, const char **query) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) return 0; + + *query = NULL; //coap_pkt->uri_query; + return 0; //coap_pkt->uri_query_len; +} + +int +coap_set_header_uri_query(void *packet, const char *query) +{ + int length = 0; + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + free_multi_option(coap_pkt->uri_query); + coap_pkt->uri_query = NULL; + + if (query[0]=='?') ++query; + + do + { + int i = 0; + + while (query[i] != 0 && query[i] != '&') i++; + coap_add_multi_option(&(coap_pkt->uri_query), (uint8_t *)query, i, 0); + + if (query[i] == '&') i++; + query += i; + length += i; + } while (query[0] != 0); + + SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); + return length; + } +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_location_path(void *packet, const char **path) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH)) return 0; + + *path = NULL; //coap_pkt->location_path; + return 0; //coap_pkt->location_path_len; +} + +int +coap_set_header_location_path(void *packet, const char *path) +{ + coap_packet_t *coap_pkt = (coap_packet_t *) packet; + int length = 0; + + free_multi_option(coap_pkt->location_path); + coap_pkt->location_path = NULL; + + if (path[0]=='/') ++path; + + do + { + int i = 0; + + while (path[i] != 0 && path[i] != '/') i++; + coap_add_multi_option(&(coap_pkt->location_path), (uint8_t *)path, i, 0); + + if (path[i] == '/') i++; + path += i; + length += i; + } while (path[0] != 0); + + SET_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH); + return length; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_location_query(void *packet, const char **query) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY)) return 0; + + *query = (const char*)coap_pkt->location_query; + return coap_pkt->location_query_len; +} + +int +coap_set_header_location_query(void *packet, char *query) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + while (query[0]=='?') ++query; + + coap_pkt->location_query = (uint8_t *)query; + coap_pkt->location_query_len = strlen(query); + + SET_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY); + return coap_pkt->location_query_len; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_observe(void *packet, uint32_t *observe) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_OBSERVE)) return 0; + + *observe = coap_pkt->observe; + return 1; +} + +int +coap_set_header_observe(void *packet, uint32_t observe) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + coap_pkt->observe = 0x00FFFFFF & observe; + SET_OPTION(coap_pkt, COAP_OPTION_OBSERVE); + return 1; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_BLOCK2)) return 0; + + /* pointers may be NULL to get only specific block parameters */ + if (num!=NULL) *num = coap_pkt->block2_num; + if (more!=NULL) *more = coap_pkt->block2_more; + if (size!=NULL) *size = coap_pkt->block2_size; + if (offset!=NULL) *offset = coap_pkt->block2_offset; + + return 1; +} + +int +coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t size) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (size<16) return 0; + if (size>2048) return 0; + if (num>0x0FFFFF) return 0; + + coap_pkt->block2_num = num; + coap_pkt->block2_more = more ? 1 : 0; + coap_pkt->block2_size = size; + + SET_OPTION(coap_pkt, COAP_OPTION_BLOCK2); + return 1; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_BLOCK1)) return 0; + + /* pointers may be NULL to get only specific block parameters */ + if (num!=NULL) *num = coap_pkt->block1_num; + if (more!=NULL) *more = coap_pkt->block1_more; + if (size!=NULL) *size = coap_pkt->block1_size; + if (offset!=NULL) *offset = coap_pkt->block1_offset; + + return 1; +} + +int +coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (size<16) return 0; + if (size>2048) return 0; + if (num>0x0FFFFF) return 0; + + coap_pkt->block1_num = num; + coap_pkt->block1_more = more; + coap_pkt->block1_size = size; + + SET_OPTION(coap_pkt, COAP_OPTION_BLOCK1); + return 1; +} +/*-----------------------------------------------------------------------------------*/ +int +coap_get_header_size(void *packet, uint32_t *size) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (!IS_OPTION(coap_pkt, COAP_OPTION_SIZE)) return 0; + + *size = coap_pkt->size; + return 1; +} + +int +coap_set_header_size(void *packet, uint32_t size) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + coap_pkt->size = size; + SET_OPTION(coap_pkt, COAP_OPTION_SIZE); + return 1; +} +/*-----------------------------------------------------------------------------------*/ +/*- PAYLOAD -------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +int +coap_get_payload(void *packet, const uint8_t **payload) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + if (coap_pkt->payload) { + *payload = coap_pkt->payload; + return coap_pkt->payload_len; + } else { + *payload = NULL; + return 0; + } +} + +int +coap_set_payload(void *packet, const void *payload, size_t length) +{ + coap_packet_t *const coap_pkt = (coap_packet_t *) packet; + + coap_pkt->payload = (uint8_t *) payload; + coap_pkt->payload_len = (uint16_t)(length); + + return coap_pkt->payload_len; +} +/*-----------------------------------------------------------------------------------*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/er-coap-13/er-coap-13.h Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * An implementation of the Constrained Application Protocol (draft 12) + * \author + * Matthias Kovatsch <kovatsch@inf.ethz.ch> + * \contributors + * David Navarro, Intel Corporation - Adapt to usage in liblwm2m + */ + + +#ifndef COAP_13_H_ +#define COAP_13_H_ + +#include <stdint.h> +#include <stddef.h> /* for size_t */ + +/* + * The maximum buffer size that is provided for resource responses and must be respected due to the limited IP buffer. + * Larger data must be handled by the resource and will be sent chunk-wise through a TCP stream or CoAP blocks. + */ +#ifndef REST_MAX_CHUNK_SIZE +#define REST_MAX_CHUNK_SIZE 128 +#endif + +#define COAP_DEFAULT_MAX_AGE 60 +#define COAP_RESPONSE_TIMEOUT 2 +#define COAP_MAX_RETRANSMIT 4 +#define COAP_ACK_RANDOM_FACTOR 1.5 + +#define COAP_MAX_TRANSMIT_WAIT ((COAP_RESPONSE_TIMEOUT * ( (1 << (COAP_MAX_RETRANSMIT + 1) ) - 1) * COAP_ACK_RANDOM_FACTOR)) + +#define COAP_HEADER_LEN 4 /* | version:0x03 type:0x0C tkl:0xF0 | code | mid:0x00FF | mid:0xFF00 | */ +#define COAP_ETAG_LEN 8 /* The maximum number of bytes for the ETag */ +#define COAP_TOKEN_LEN 8 /* The maximum number of bytes for the Token */ +#define COAP_MAX_ACCEPT_NUM 2 /* The maximum number of accept preferences to parse/store */ + +#define COAP_MAX_OPTION_HEADER_LEN 5 + +#define COAP_HEADER_VERSION_MASK 0xC0 +#define COAP_HEADER_VERSION_POSITION 6 +#define COAP_HEADER_TYPE_MASK 0x30 +#define COAP_HEADER_TYPE_POSITION 4 +#define COAP_HEADER_TOKEN_LEN_MASK 0x0F +#define COAP_HEADER_TOKEN_LEN_POSITION 0 + +#define COAP_HEADER_OPTION_DELTA_MASK 0xF0 +#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F + +/* + * Conservative size limit, as not all options have to be set at the same time. + */ +#ifndef COAP_MAX_HEADER_SIZE +/* Hdr CoT Age Tag Obs Tok Blo strings */ +#define COAP_MAX_HEADER_SIZE (4 + 3 + 5 + 1+COAP_ETAG_LEN + 3 + 1+COAP_TOKEN_LEN + 4 + 30) /* 70 */ +#endif /* COAP_MAX_HEADER_SIZE */ + +#define COAP_MAX_PACKET_SIZE (COAP_MAX_HEADER_SIZE + REST_MAX_CHUNK_SIZE) +/* 0/14 48 for IPv6 (28 for IPv4) */ +#if COAP_MAX_PACKET_SIZE > (UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN) +//#error "UIP_CONF_BUFFER_SIZE too small for REST_MAX_CHUNK_SIZE" +#endif + + +/* Bitmap for set options */ +enum { OPTION_MAP_SIZE = sizeof(uint8_t) * 8 }; +#define SET_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] |= 1 << (opt % OPTION_MAP_SIZE)) +#define IS_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] & (1 << (opt % OPTION_MAP_SIZE))) + +#ifndef MIN +#define MIN(a, b) ((a) < (b)? (a) : (b)) +#endif /* MIN */ + +/* CoAP message types */ +typedef enum { + COAP_TYPE_CON, /* confirmables */ + COAP_TYPE_NON, /* non-confirmables */ + COAP_TYPE_ACK, /* acknowledgements */ + COAP_TYPE_RST /* reset */ +} coap_message_type_t; + +/* CoAP request method codes */ +typedef enum { + COAP_GET = 1, + COAP_POST, + COAP_PUT, + COAP_DELETE +} coap_method_t; + +/* CoAP response codes */ +typedef enum { + NO_ERROR = 0, + + CREATED_2_01 = 65, /* CREATED */ + DELETED_2_02 = 66, /* DELETED */ + VALID_2_03 = 67, /* NOT_MODIFIED */ + CHANGED_2_04 = 68, /* CHANGED */ + CONTENT_2_05 = 69, /* OK */ + + BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */ + UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */ + BAD_OPTION_4_02 = 130, /* BAD_OPTION */ + FORBIDDEN_4_03 = 131, /* FORBIDDEN */ + NOT_FOUND_4_04 = 132, /* NOT_FOUND */ + METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */ + NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */ + PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */ + REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */ + UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */ + + INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */ + NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */ + BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */ + SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */ + GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */ + PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */ + + /* Erbium errors */ + MEMORY_ALLOCATION_ERROR = 192, + PACKET_SERIALIZATION_ERROR, + + /* Erbium hooks */ + MANUAL_RESPONSE + +} coap_status_t; + +/* CoAP header options */ +typedef enum { + COAP_OPTION_IF_MATCH = 1, /* 0-8 B */ + COAP_OPTION_URI_HOST = 3, /* 1-255 B */ + COAP_OPTION_ETAG = 4, /* 1-8 B */ + COAP_OPTION_IF_NONE_MATCH = 5, /* 0 B */ + COAP_OPTION_OBSERVE = 6, /* 0-3 B */ + COAP_OPTION_URI_PORT = 7, /* 0-2 B */ + COAP_OPTION_LOCATION_PATH = 8, /* 0-255 B */ + COAP_OPTION_URI_PATH = 11, /* 0-255 B */ + COAP_OPTION_CONTENT_TYPE = 12, /* 0-2 B */ + COAP_OPTION_MAX_AGE = 14, /* 0-4 B */ + COAP_OPTION_URI_QUERY = 15, /* 0-270 B */ + COAP_OPTION_ACCEPT = 17, /* 0-2 B */ + COAP_OPTION_TOKEN = 19, /* 1-8 B */ + COAP_OPTION_LOCATION_QUERY = 20, /* 1-270 B */ + COAP_OPTION_BLOCK2 = 23, /* 1-3 B */ + COAP_OPTION_BLOCK1 = 27, /* 1-3 B */ + COAP_OPTION_SIZE = 28, /* 0-4 B */ + COAP_OPTION_PROXY_URI = 35, /* 1-270 B */ + OPTION_MAX_VALUE = 0xFFFF +} coap_option_t; + +/* CoAP Content-Types */ +typedef enum { + TEXT_PLAIN = 0, + TEXT_XML = 1, /* Indented types are not in the initial registry. */ + TEXT_CSV = 2, + TEXT_HTML = 3, + IMAGE_GIF = 21, + IMAGE_JPEG = 22, + IMAGE_PNG = 23, + IMAGE_TIFF = 24, + AUDIO_RAW = 25, + VIDEO_RAW = 26, + APPLICATION_LINK_FORMAT = 40, + APPLICATION_XML = 41, + APPLICATION_OCTET_STREAM = 42, + APPLICATION_RDF_XML = 43, + APPLICATION_SOAP_XML = 44, + APPLICATION_ATOM_XML = 45, + APPLICATION_XMPP_XML = 46, + APPLICATION_EXI = 47, + APPLICATION_FASTINFOSET = 48, + APPLICATION_SOAP_FASTINFOSET = 49, + APPLICATION_JSON = 50, + APPLICATION_X_OBIX_BINARY = 51, + CONTENT_MAX_VALUE = 0xFFFF +} coap_content_type_t; + +typedef struct _multi_option_t { + struct _multi_option_t *next; + uint8_t is_static; + uint8_t len; + uint8_t *data; +} multi_option_t; + +/* Parsed message struct */ +typedef struct { + uint8_t *buffer; /* pointer to CoAP header / incoming packet buffer / memory to serialize packet */ + + uint8_t version; + coap_message_type_t type; + uint8_t code; + uint16_t mid; + + uint8_t options[COAP_OPTION_PROXY_URI / OPTION_MAP_SIZE + 1]; /* Bitmap to check if option is set */ + + coap_content_type_t content_type; /* Parse options once and store; allows setting options in random order */ + uint32_t max_age; + size_t proxy_uri_len; + const uint8_t *proxy_uri; + uint8_t etag_len; + uint8_t etag[COAP_ETAG_LEN]; + size_t uri_host_len; + const uint8_t *uri_host; + multi_option_t *location_path; + uint16_t uri_port; + size_t location_query_len; + uint8_t *location_query; + multi_option_t *uri_path; + uint32_t observe; + uint8_t token_len; + uint8_t token[COAP_TOKEN_LEN]; + uint8_t accept_num; + uint16_t accept[COAP_MAX_ACCEPT_NUM]; + uint8_t if_match_len; + uint8_t if_match[COAP_ETAG_LEN]; + uint32_t block2_num; + uint8_t block2_more; + uint16_t block2_size; + uint32_t block2_offset; + uint32_t block1_num; + uint8_t block1_more; + uint16_t block1_size; + uint32_t block1_offset; + uint32_t size; + multi_option_t *uri_query; + uint8_t if_none_match; + + uint16_t payload_len; + uint8_t *payload; + +} coap_packet_t; + +/* Option format serialization*/ +#define COAP_SERIALIZE_INT_OPTION(number, field, text) \ + if (IS_OPTION(coap_pkt, number)) { \ + PRINTF(text" [%u]\n", coap_pkt->field); \ + option += coap_serialize_int_option(number, current_number, option, coap_pkt->field); \ + current_number = number; \ + } +#define COAP_SERIALIZE_BYTE_OPTION(number, field, text) \ + if (IS_OPTION(coap_pkt, number)) { \ + PRINTF(text" %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->field##_len, \ + coap_pkt->field[0], \ + coap_pkt->field[1], \ + coap_pkt->field[2], \ + coap_pkt->field[3], \ + coap_pkt->field[4], \ + coap_pkt->field[5], \ + coap_pkt->field[6], \ + coap_pkt->field[7] \ + ); /*FIXME always prints 8 bytes */ \ + option += coap_serialize_array_option(number, current_number, option, coap_pkt->field, coap_pkt->field##_len, '\0'); \ + current_number = number; \ + } +#define COAP_SERIALIZE_STRING_OPTION(number, field, splitter, text) \ + if (IS_OPTION(coap_pkt, number)) { \ + PRINTF(text" [%.*s]\n", coap_pkt->field##_len, coap_pkt->field); \ + option += coap_serialize_array_option(number, current_number, option, (uint8_t *) coap_pkt->field, coap_pkt->field##_len, splitter); \ + current_number = number; \ + } +#define COAP_SERIALIZE_MULTI_OPTION(number, field, text) \ + if (IS_OPTION(coap_pkt, number)) { \ + PRINTF(text); \ + option += coap_serialize_multi_option(number, current_number, option, coap_pkt->field); \ + current_number = number; \ + } +#define COAP_SERIALIZE_ACCEPT_OPTION(number, field, text) \ + if (IS_OPTION(coap_pkt, number)) { \ + int i; \ + for (i=0; i<coap_pkt->field##_num; ++i) \ + { \ + PRINTF(text" [%u]\n", coap_pkt->field[i]); \ + option += coap_serialize_int_option(number, current_number, option, coap_pkt->field[i]); \ + current_number = number; \ + } \ + } +#define COAP_SERIALIZE_BLOCK_OPTION(number, field, text) \ + if (IS_OPTION(coap_pkt, number)) \ + { \ + uint32_t block = coap_pkt->field##_num << 4; \ + PRINTF(text" [%lu%s (%u B/blk)]\n", coap_pkt->field##_num, coap_pkt->field##_more ? "+" : "", coap_pkt->field##_size); \ + if (coap_pkt->field##_more) block |= 0x8; \ + block |= 0xF & coap_log_2(coap_pkt->field##_size/16); \ + PRINTF(text" encoded: 0x%lX\n", block); \ + option += coap_serialize_int_option(number, current_number, option, block); \ + current_number = number; \ + } + +/* To store error code and human-readable payload */ +extern const char *coap_error_message; + +uint16_t coap_get_mid(void); + +void coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t mid); +size_t coap_serialize_get_size(void *packet); +size_t coap_serialize_message(void *packet, uint8_t *buffer); +coap_status_t coap_parse_message(void *request, uint8_t *data, uint16_t data_len); +void coap_free_header(void *packet); + +char * coap_get_multi_option_as_string(multi_option_t * option); +void coap_add_multi_option(multi_option_t **dst, uint8_t *option, size_t option_len, uint8_t is_static); +void free_multi_option(multi_option_t *dst); + +int coap_get_query_variable(void *packet, const char *name, const char **output); +int coap_get_post_variable(void *packet, const char *name, const char **output); + +/*-----------------------------------------------------------------------------------*/ + +int coap_set_status_code(void *packet, unsigned int code); + +unsigned int coap_get_header_content_type(void *packet); +int coap_set_header_content_type(void *packet, unsigned int content_type); + +int coap_get_header_accept(void *packet, const uint16_t **accept); +int coap_set_header_accept(void *packet, uint16_t accept); + +int coap_get_header_max_age(void *packet, uint32_t *age); +int coap_set_header_max_age(void *packet, uint32_t age); + +int coap_get_header_etag(void *packet, const uint8_t **etag); +int coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len); + +int coap_get_header_if_match(void *packet, const uint8_t **etag); +int coap_set_header_if_match(void *packet, const uint8_t *etag, size_t etag_len); + +int coap_get_header_if_none_match(void *packet); +int coap_set_header_if_none_match(void *packet); + +int coap_get_header_token(void *packet, const uint8_t **token); +int coap_set_header_token(void *packet, const uint8_t *token, size_t token_len); + +int coap_get_header_proxy_uri(void *packet, const char **uri); /* In-place string might not be 0-terminated. */ +int coap_set_header_proxy_uri(void *packet, const char *uri); + +int coap_get_header_uri_host(void *packet, const char **host); /* In-place string might not be 0-terminated. */ +int coap_set_header_uri_host(void *packet, const char *host); + +int coap_get_header_uri_path(void *packet, const char **path); /* In-place string might not be 0-terminated. */ +int coap_set_header_uri_path(void *packet, const char *path); +int coap_set_header_uri_path_segment(void *packet, const char *path); + +int coap_get_header_uri_query(void *packet, const char **query); /* In-place string might not be 0-terminated. */ +int coap_set_header_uri_query(void *packet, const char *query); + +int coap_get_header_location_path(void *packet, const char **path); /* In-place string might not be 0-terminated. */ +int coap_set_header_location_path(void *packet, const char *path); /* Also splits optional query into Location-Query option. */ + +int coap_get_header_location_query(void *packet, const char **query); /* In-place string might not be 0-terminated. */ +int coap_set_header_location_query(void *packet, char *query); + +int coap_get_header_observe(void *packet, uint32_t *observe); +int coap_set_header_observe(void *packet, uint32_t observe); + +int coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset); +int coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t size); + +int coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset); +int coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size); + +int coap_get_header_size(void *packet, uint32_t *size); +int coap_set_header_size(void *packet, uint32_t size); + +int coap_get_payload(void *packet, const uint8_t **payload); +int coap_set_payload(void *packet, const void *payload, size_t length); + +#endif /* COAP_13_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/internals.h Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,347 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * Fabien Fleutot - Please refer to git log + * Toby Jaffey - Please refer to git log + * Bosch Software Innovations GmbH - Please refer to git log + * Pascal Rieux - Please refer to git log + * + *******************************************************************************/ +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro <david.navarro@intel.com> + +*/ + +#ifndef _LWM2M_INTERNALS_H_ +#define _LWM2M_INTERNALS_H_ + +#include "liblwm2m.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> + +#include "er-coap-13/er-coap-13.h" + +#ifdef LWM2M_WITH_LOGS +#include <inttypes.h> +#define LOG(STR) lwm2m_printf("[%s:%d] " STR "\r\n", __func__ , __LINE__) +#define LOG_ARG(FMT, ...) lwm2m_printf("[%s:%d] " FMT "\r\n", __func__ , __LINE__ , __VA_ARGS__) +#define LOG_URI(URI) \ +{ \ + if ((URI) == NULL) lwm2m_printf("[%s:%d] NULL\r\n", __func__ , __LINE__); \ + else \ + { \ + lwm2m_printf("[%s:%d] /%d", __func__ , __LINE__ , (URI)->objectId); \ + if (LWM2M_URI_IS_SET_INSTANCE(URI)) lwm2m_printf("/%d", (URI)->instanceId); \ + if (LWM2M_URI_IS_SET_RESOURCE(URI)) lwm2m_printf("/%d", (URI)->resourceId); \ + lwm2m_printf("\r\n"); \ + } \ +} +#define STR_STATUS(S) \ +((S) == STATE_DEREGISTERED ? "STATE_DEREGISTERED" : \ +((S) == STATE_REG_PENDING ? "STATE_REG_PENDING" : \ +((S) == STATE_REGISTERED ? "STATE_REGISTERED" : \ +((S) == STATE_REG_FAILED ? "STATE_REG_FAILED" : \ +((S) == STATE_REG_UPDATE_PENDING ? "STATE_REG_UPDATE_PENDING" : \ +((S) == STATE_REG_UPDATE_NEEDED ? "STATE_REG_UPDATE_NEEDED" : \ +((S) == STATE_REG_FULL_UPDATE_NEEDED ? "STATE_REG_FULL_UPDATE_NEEDED" : \ +((S) == STATE_DEREG_PENDING ? "STATE_DEREG_PENDING" : \ +((S) == STATE_BS_HOLD_OFF ? "STATE_BS_HOLD_OFF" : \ +((S) == STATE_BS_INITIATED ? "STATE_BS_INITIATED" : \ +((S) == STATE_BS_PENDING ? "STATE_BS_PENDING" : \ +((S) == STATE_BS_FINISHED ? "STATE_BS_FINISHED" : \ +((S) == STATE_BS_FINISHING ? "STATE_BS_FINISHING" : \ +((S) == STATE_BS_FAILING ? "STATE_BS_FAILING" : \ +((S) == STATE_BS_FAILED ? "STATE_BS_FAILED" : \ +"Unknown"))))))))))))))) +#define STR_MEDIA_TYPE(M) \ +((M) == LWM2M_CONTENT_TEXT ? "LWM2M_CONTENT_TEXT" : \ +((M) == LWM2M_CONTENT_LINK ? "LWM2M_CONTENT_LINK" : \ +((M) == LWM2M_CONTENT_OPAQUE ? "LWM2M_CONTENT_OPAQUE" : \ +((M) == LWM2M_CONTENT_TLV ? "LWM2M_CONTENT_TLV" : \ +((M) == LWM2M_CONTENT_JSON ? "LWM2M_CONTENT_JSON" : \ +"Unknown"))))) +#define STR_STATE(S) \ +((S) == STATE_INITIAL ? "STATE_INITIAL" : \ +((S) == STATE_BOOTSTRAP_REQUIRED ? "STATE_BOOTSTRAP_REQUIRED" : \ +((S) == STATE_BOOTSTRAPPING ? "STATE_BOOTSTRAPPING" : \ +((S) == STATE_REGISTER_REQUIRED ? "STATE_REGISTER_REQUIRED" : \ +((S) == STATE_REGISTERING ? "STATE_REGISTERING" : \ +((S) == STATE_READY ? "STATE_READY" : \ +"Unknown")))))) +#else +#define LOG_ARG(FMT, ...) +#define LOG(STR) +#define LOG_URI(URI) +#endif + +#define LWM2M_DEFAULT_LIFETIME 86400 + +#ifdef LWM2M_SUPPORT_JSON +#define REG_LWM2M_RESOURCE_TYPE ">;rt=\"oma.lwm2m\";ct=11543," +#define REG_LWM2M_RESOURCE_TYPE_LEN 25 +#else +#define REG_LWM2M_RESOURCE_TYPE ">;rt=\"oma.lwm2m\"," +#define REG_LWM2M_RESOURCE_TYPE_LEN 17 +#endif +#define REG_START "<" +#define REG_DEFAULT_PATH "/" + +#define REG_OBJECT_MIN_LEN 5 // "</n>," +#define REG_PATH_END ">," +#define REG_PATH_SEPARATOR "/" + +#define REG_OBJECT_PATH "<%s/%hu>," +#define REG_OBJECT_INSTANCE_PATH "<%s/%hu/%hu>," + +#define URI_REGISTRATION_SEGMENT "rd" +#define URI_REGISTRATION_SEGMENT_LEN 2 +#define URI_BOOTSTRAP_SEGMENT "bs" +#define URI_BOOTSTRAP_SEGMENT_LEN 2 + +#define QUERY_STARTER "?" +#define QUERY_NAME "ep=" +#define QUERY_NAME_LEN 3 // strlen("ep=") +#define QUERY_SMS "sms=" +#define QUERY_SMS_LEN 4 +#define QUERY_LIFETIME "lt=" +#define QUERY_LIFETIME_LEN 3 +#define QUERY_VERSION "lwm2m=" +#define QUERY_VERSION_LEN 6 +#define QUERY_BINDING "b=" +#define QUERY_BINDING_LEN 2 +#define QUERY_DELIMITER "&" + +#define LWM2M_VERSION "1.0" +#define LWM2M_VERSION_LEN 3 + +#define QUERY_VERSION_FULL QUERY_VERSION LWM2M_VERSION +#define QUERY_VERSION_FULL_LEN QUERY_VERSION_LEN+LWM2M_VERSION_LEN + +#define REG_URI_START '<' +#define REG_URI_END '>' +#define REG_DELIMITER ',' +#define REG_ATTR_SEPARATOR ';' +#define REG_ATTR_EQUALS '=' +#define REG_ATTR_TYPE_KEY "rt" +#define REG_ATTR_TYPE_KEY_LEN 2 +#define REG_ATTR_TYPE_VALUE "\"oma.lwm2m\"" +#define REG_ATTR_TYPE_VALUE_LEN 11 +#define REG_ATTR_CONTENT_KEY "ct" +#define REG_ATTR_CONTENT_KEY_LEN 2 +#define REG_ATTR_CONTENT_JSON "11543" // Temporary value +#define REG_ATTR_CONTENT_JSON_LEN 5 + +#define ATTR_SERVER_ID_STR "ep=" +#define ATTR_SERVER_ID_LEN 3 +#define ATTR_MIN_PERIOD_STR "pmin=" +#define ATTR_MIN_PERIOD_LEN 5 +#define ATTR_MAX_PERIOD_STR "pmax=" +#define ATTR_MAX_PERIOD_LEN 5 +#define ATTR_GREATER_THAN_STR "gt=" +#define ATTR_GREATER_THAN_LEN 3 +#define ATTR_LESS_THAN_STR "lt=" +#define ATTR_LESS_THAN_LEN 3 +#define ATTR_STEP_STR "stp=" +#define ATTR_STEP_LEN 4 +#define ATTR_DIMENSION_STR "dim=" +#define ATTR_DIMENSION_LEN 4 + +#define URI_MAX_STRING_LEN 18 // /65535/65535/65535 +#define _PRV_64BIT_BUFFER_SIZE 8 + +#define LINK_ITEM_START "<" +#define LINK_ITEM_START_SIZE 1 +#define LINK_ITEM_END ">," +#define LINK_ITEM_END_SIZE 2 +#define LINK_ITEM_DIM_START ">;dim=" +#define LINK_ITEM_DIM_START_SIZE 6 +#define LINK_ITEM_ATTR_END "," +#define LINK_ITEM_ATTR_END_SIZE 1 +#define LINK_URI_SEPARATOR "/" +#define LINK_URI_SEPARATOR_SIZE 1 +#define LINK_ATTR_SEPARATOR ";" +#define LINK_ATTR_SEPARATOR_SIZE 1 + +#define ATTR_FLAG_NUMERIC (uint8_t)(LWM2M_ATTR_FLAG_LESS_THAN | LWM2M_ATTR_FLAG_GREATER_THAN | LWM2M_ATTR_FLAG_STEP) + +#define LWM2M_URI_FLAG_DM (uint8_t)0x00 +#define LWM2M_URI_FLAG_DELETE_ALL (uint8_t)0x10 +#define LWM2M_URI_FLAG_REGISTRATION (uint8_t)0x20 +#define LWM2M_URI_FLAG_BOOTSTRAP (uint8_t)0x40 + +#define LWM2M_URI_MASK_TYPE (uint8_t)0x70 +#define LWM2M_URI_MASK_ID (uint8_t)0x07 + +typedef struct +{ + uint16_t clientID; + lwm2m_uri_t uri; + lwm2m_result_callback_t callback; + void * userData; +} dm_data_t; + +typedef enum +{ + URI_DEPTH_OBJECT, + URI_DEPTH_OBJECT_INSTANCE, + URI_DEPTH_RESOURCE, + URI_DEPTH_RESOURCE_INSTANCE +} uri_depth_t; + +#ifdef LWM2M_BOOTSTRAP_SERVER_MODE +typedef struct +{ + bool isUri; + lwm2m_uri_t uri; + lwm2m_bootstrap_callback_t callback; + void * userData; +} bs_data_t; +#endif + +// defined in uri.c +lwm2m_uri_t * uri_decode(char * altPath, multi_option_t *uriPath); +int uri_getNumber(uint8_t * uriString, size_t uriLength); +int uri_toString(lwm2m_uri_t * uriP, uint8_t * buffer, size_t bufferLen, uri_depth_t * depthP); + +// defined in objects.c +coap_status_t object_readData(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, int * sizeP, lwm2m_data_t ** dataP); +coap_status_t object_read(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_media_type_t * formatP, uint8_t ** bufferP, size_t * lengthP); +coap_status_t object_write(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, size_t length); +coap_status_t object_create(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, size_t length); +coap_status_t object_execute(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, uint8_t * buffer, size_t length); +coap_status_t object_delete(lwm2m_context_t * contextP, lwm2m_uri_t * uriP); +coap_status_t object_discover(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_server_t * serverP, uint8_t ** bufferP, size_t * lengthP); +uint8_t object_checkReadable(lwm2m_context_t * contextP, lwm2m_uri_t * uriP); +uint8_t object_checkNumeric(lwm2m_context_t * contextP, lwm2m_uri_t * uriP); +bool object_isInstanceNew(lwm2m_context_t * contextP, uint16_t objectId, uint16_t instanceId); +int object_getRegisterPayload(lwm2m_context_t * contextP, uint8_t * buffer, size_t length); +int object_getServers(lwm2m_context_t * contextP); +coap_status_t object_createInstance(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_data_t * dataP); +coap_status_t object_writeInstance(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_data_t * dataP); + +// 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); +int transaction_send(lwm2m_context_t * contextP, lwm2m_transaction_t * transacP); +void transaction_free(lwm2m_transaction_t * transacP); +void transaction_remove(lwm2m_context_t * contextP, lwm2m_transaction_t * transacP); +bool transaction_handleResponse(lwm2m_context_t * contextP, void * fromSessionH, coap_packet_t * message, coap_packet_t * response); +void transaction_step(lwm2m_context_t * contextP, time_t currentTime, time_t * timeoutP); + +// defined in management.c +coap_status_t dm_handleRequest(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_server_t * serverP, coap_packet_t * message, coap_packet_t * response); + +// defined in observe.c +coap_status_t observe_handleRequest(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_server_t * serverP, int size, lwm2m_data_t * dataP, coap_packet_t * message, coap_packet_t * response); +void observe_cancel(lwm2m_context_t * contextP, uint16_t mid, void * fromSessionH); +coap_status_t observe_setParameters(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_server_t * serverP, lwm2m_attributes_t * attrP); +void observe_step(lwm2m_context_t * contextP, time_t currentTime, time_t * timeoutP); +bool observe_handleNotify(lwm2m_context_t * contextP, void * fromSessionH, coap_packet_t * message, coap_packet_t * response); +void observe_remove(lwm2m_observation_t * observationP); +lwm2m_observed_t * observe_findByUri(lwm2m_context_t * contextP, lwm2m_uri_t * uriP); + +// defined in registration.c +coap_status_t registration_handleRequest(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, void * fromSessionH, coap_packet_t * message, coap_packet_t * response); +void registration_deregister(lwm2m_context_t * contextP, lwm2m_server_t * serverP); +void registration_freeClient(lwm2m_client_t * clientP); +uint8_t registration_start(lwm2m_context_t * contextP); +void registration_step(lwm2m_context_t * contextP, time_t currentTime, time_t * timeoutP); +lwm2m_status_t registration_getStatus(lwm2m_context_t * contextP); + +// defined in packet.c +coap_status_t message_send(lwm2m_context_t * contextP, coap_packet_t * message, void * sessionH); + +// defined in bootstrap.c +void bootstrap_step(lwm2m_context_t * contextP, uint32_t currentTime, time_t* timeoutP); +coap_status_t bootstrap_handleCommand(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_server_t * serverP, coap_packet_t * message, coap_packet_t * response); +coap_status_t bootstrap_handleDeleteAll(lwm2m_context_t * context, void * fromSessionH); +coap_status_t bootstrap_handleFinish(lwm2m_context_t * context, void * fromSessionH); +uint8_t bootstrap_handleRequest(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, void * fromSessionH, coap_packet_t * message, coap_packet_t * response); +void bootstrap_start(lwm2m_context_t * contextP); +lwm2m_status_t bootstrap_getStatus(lwm2m_context_t * contextP); + +// defined in tlv.c +int tlv_parse(uint8_t * buffer, size_t bufferLen, lwm2m_data_t ** dataP); +int tlv_serialize(bool isResourceInstance, int size, lwm2m_data_t * dataP, uint8_t ** bufferP); + +// defined in json.c +#ifdef LWM2M_SUPPORT_JSON +int json_parse(lwm2m_uri_t * uriP, uint8_t * buffer, size_t bufferLen, lwm2m_data_t ** dataP); +int json_serialize(lwm2m_uri_t * uriP, int size, lwm2m_data_t * tlvP, uint8_t ** bufferP); +#endif + +// defined in discover.c +int discover_serialize(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_server_t * serverP, int size, lwm2m_data_t * dataP, uint8_t ** bufferP); + +// defined in block1.c +coap_status_t coap_block1_handler(lwm2m_block1_data_t ** block1Data, uint16_t mid, uint8_t * buffer, size_t length, uint16_t blockSize, uint32_t blockNum, bool blockMore, uint8_t ** outputBuffer, size_t * outputLength); +void free_block1_buffer(lwm2m_block1_data_t * block1Data); + +// defined in utils.c +lwm2m_data_type_t utils_depthToDatatype(uri_depth_t depth); +lwm2m_binding_t utils_stringToBinding(uint8_t *buffer, size_t length); +lwm2m_media_type_t utils_convertMediaType(coap_content_type_t type); +int utils_isAltPathValid(const char * altPath); +int utils_stringCopy(char * buffer, size_t length, const char * str); +int utils_intCopy(char * buffer, size_t length, int32_t value); +size_t utils_intToText(int64_t data, uint8_t * string, size_t length); +size_t utils_floatToText(double data, uint8_t * string, size_t length); +int utils_plainTextToInt64(uint8_t * buffer, int length, int64_t * dataP); +int utils_plainTextToFloat64(uint8_t * buffer, int length, double * dataP); +size_t utils_int64ToPlainText(int64_t data, uint8_t ** bufferP); +size_t utils_float64ToPlainText(double data, uint8_t ** bufferP); +size_t utils_boolToPlainText(bool data, uint8_t ** bufferP); +void utils_copyValue(void * dst, const void * src, size_t len); +int utils_opaqueToInt(const uint8_t * buffer, size_t buffer_len, int64_t * dataP); +int utils_opaqueToFloat(const uint8_t * buffer, size_t buffer_len, double * dataP); +size_t utils_encodeInt(int64_t data, uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE]); +size_t utils_encodeFloat(double data, uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE]); +size_t utils_base64ToOpaque(uint8_t * dataP, size_t dataLen, uint8_t ** bufferP); +size_t utils_opaqueToBase64(uint8_t * dataP, size_t dataLen, uint8_t ** bufferP); +size_t utils_base64Encode(uint8_t * dataP, size_t dataLen, uint8_t * bufferP, size_t bufferLen); +#ifdef LWM2M_CLIENT_MODE +lwm2m_server_t * utils_findServer(lwm2m_context_t * contextP, void * fromSessionH); +lwm2m_server_t * utils_findBootstrapServer(lwm2m_context_t * contextP, void * fromSessionH); +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/json.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,1436 @@ +/******************************************************************************* + * + * Copyright (c) 2015 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * + *******************************************************************************/ + + +#include "internals.h" +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <inttypes.h> + + +#ifdef LWM2M_SUPPORT_JSON + +#define PRV_JSON_BUFFER_SIZE 1024 + +#define JSON_MIN_ARRAY_LEN 21 // e":[{"n":"N","v":X}]} +#define JSON_MIN_BASE_LEN 7 // n":"N", +#define JSON_ITEM_MAX_SIZE 36 // with ten characters for value +#define JSON_MIN_BX_LEN 5 // bt":1 + +#define JSON_FALSE_STRING "false" +#define JSON_TRUE_STRING "true" + +#define JSON_RES_ITEM_URI "{\"n\":\"" +#define JSON_RES_ITEM_URI_SIZE 6 +#define JSON_ITEM_BOOL_TRUE "\",\"bv\":true}," +#define JSON_ITEM_BOOL_TRUE_SIZE 13 +#define JSON_ITEM_BOOL_FALSE "\",\"bv\":false}," +#define JSON_ITEM_BOOL_FALSE_SIZE 14 +#define JSON_ITEM_NUM "\",\"v\":" +#define JSON_ITEM_NUM_SIZE 6 +#define JSON_ITEM_NUM_END "}," +#define JSON_ITEM_NUM_END_SIZE 2 +#define JSON_ITEM_STRING_BEGIN "\",\"sv\":\"" +#define JSON_ITEM_STRING_BEGIN_SIZE 8 +#define JSON_ITEM_STRING_END "\"}," +#define JSON_ITEM_STRING_END_SIZE 3 + +#define JSON_BN_HEADER_1 "{\"bn\":\"" +#define JSON_BN_HEADER_1_SIZE 7 +#define JSON_BN_HEADER_2 "\",\"e\":[" +#define JSON_BN_HEADER_2_SIZE 7 +#define JSON_HEADER "{\"e\":[" +#define JSON_HEADER_SIZE 6 +#define JSON_FOOTER "]}" +#define JSON_FOOTER_SIZE 2 + + +#define _GO_TO_NEXT_CHAR(I,B,L) \ + { \ + I++; \ + I += prv_skipSpace(B+I, L-I); \ + if (I == L) goto error; \ + } + +typedef enum +{ + _STEP_START, + _STEP_TOKEN, + _STEP_ANY_SEPARATOR, + _STEP_SEPARATOR, + _STEP_QUOTED_VALUE, + _STEP_VALUE, + _STEP_DONE +} _itemState; + +typedef enum +{ + _TYPE_UNSET, + _TYPE_FALSE, + _TYPE_TRUE, + _TYPE_FLOAT, + _TYPE_STRING +} _type; + +typedef struct +{ + uint16_t ids[4]; + _type type; + uint8_t * value; + size_t valueLen; +} _record_t; + +static int prv_isReserved(char sign) +{ + if (sign == '[' + || sign == '{' + || sign == ']' + || sign == '}' + || sign == ':' + || sign == ',' + || sign == '"') + { + return 1; + } + + return 0; +} + +static int prv_isWhiteSpace(uint8_t sign) +{ + if (sign == 0x20 + || sign == 0x09 + || sign == 0x0A + || sign == 0x0D) + { + return 1; + } + + return 0; +} + +static size_t prv_skipSpace(uint8_t * buffer, + size_t bufferLen) +{ + size_t i; + + i = 0; + while ((i < bufferLen) + && prv_isWhiteSpace(buffer[i])) + { + i++; + } + + return i; +} + +static int prv_split(uint8_t * buffer, + size_t bufferLen, + int * tokenStartP, + int * tokenLenP, + int * valueStartP, + int * valueLenP) +{ + size_t index; + _itemState step; + + index = 0; + step = _STEP_START; + + index = prv_skipSpace(buffer + index, bufferLen - index); + if (index == bufferLen) return -1; + + while ((index < bufferLen) + && (buffer[index] != ',')) + { + switch (step) + { + case _STEP_START: + if (buffer[index] != '"') return -1; + *tokenStartP = index+1; + step = _STEP_TOKEN; + break; + + case _STEP_TOKEN: + if (buffer[index] == '"') + { + *tokenLenP = index - *tokenStartP; + step = _STEP_ANY_SEPARATOR; + } + break; + + case _STEP_ANY_SEPARATOR: + if (buffer[index] != ':') return -1; + step = _STEP_SEPARATOR; + break; + + case _STEP_SEPARATOR: + if (buffer[index] == '"') + { + *valueStartP = index; + step = _STEP_QUOTED_VALUE; + } else if (!prv_isReserved(buffer[index])) + { + *valueStartP = index; + step = _STEP_VALUE; + } else + { + return -1; + } + break; + + case _STEP_QUOTED_VALUE: + if (buffer[index] == '"' && buffer[index-1] != '\\' ) + { + *valueLenP = index - *valueStartP + 1; + step = _STEP_DONE; + } + break; + + case _STEP_VALUE: + if (prv_isWhiteSpace(buffer[index])) + { + *valueLenP = index - *valueStartP; + step = _STEP_DONE; + } + break; + + case _STEP_DONE: + default: + return -1; + } + + index++; + if (step == _STEP_START + || step == _STEP_ANY_SEPARATOR + || step == _STEP_SEPARATOR + || step == _STEP_DONE) + { + index += prv_skipSpace(buffer + index, bufferLen - index); + } + } + + if (step == _STEP_VALUE) + { + *valueLenP = index - *valueStartP; + step = _STEP_DONE; + } + + if (step != _STEP_DONE) return -1; + + return (int)index; +} + +static int prv_countItems(uint8_t * buffer, + size_t bufferLen) +{ + int count; + size_t index; + int in; + + count = 0; + index = 0; + in = 0; + + while (index < bufferLen) + { + if (in == 0) + { + if (buffer[index] != '{') return -1; + in = 1; + _GO_TO_NEXT_CHAR(index, buffer, bufferLen); + } + else + { + if (buffer[index] == '{') return -1; + if (buffer[index] == '}') + { + in = 0; + count++; + _GO_TO_NEXT_CHAR(index, buffer, bufferLen); + if (buffer[index] == ']') + { + break; + } + if (buffer[index] != ',') return -1; + _GO_TO_NEXT_CHAR(index, buffer, bufferLen); + } + else + { + _GO_TO_NEXT_CHAR(index, buffer, bufferLen); + } + } + } + if (in == 1) return -1; + + return count; + +error: + return -1; +} + +static int prv_parseItem(uint8_t * buffer, + size_t bufferLen, + _record_t * recordP) +{ + size_t index; + + memset(recordP->ids, 0xFF, 4*sizeof(uint16_t)); + recordP->type = _TYPE_UNSET; + recordP->value = NULL; + recordP->valueLen = 0; + + index = 0; + do + { + int tokenStart; + int tokenLen; + int valueStart; + int valueLen; + int next; + + next = prv_split(buffer+index, bufferLen-index, &tokenStart, &tokenLen, &valueStart, &valueLen); + if (next < 0) return -1; + + switch (tokenLen) + { + case 1: + { + switch (buffer[index+tokenStart]) + { + case 'n': + { + int i; + int j; + + if (recordP->ids[0] != LWM2M_MAX_ID) return -1; + + // Check for " around URI + if (valueLen < 3 + || buffer[index+valueStart] != '"' + || buffer[index+valueStart+valueLen-1] != '"') + { + return -1; + } + // Ignore starting / + if (buffer[index + valueStart + 1] == '/') + { + if (valueLen < 4) + { + return -1; + } + valueStart += 1; + valueLen -= 1; + } + i = 0; + j = 0; + do { + uint32_t readId; + + readId = 0; + i++; + while (i < valueLen-1 && buffer[index+valueStart+i] != '/') + { + if (buffer[index+valueStart+i] < '0' + || buffer[index+valueStart+i] > '9') + { + return -1; + } + readId *= 10; + readId += buffer[index+valueStart+i] - '0'; + if (readId > LWM2M_MAX_ID) return -1; + i++; + } + recordP->ids[j] = readId; + j++; + } while (i < valueLen-1 && j < 4 && buffer[index+valueStart+i] == '/'); + if (i < valueLen-1 ) return -1; + } + break; + + case 'v': + if (recordP->type != _TYPE_UNSET) return -1; + recordP->type = _TYPE_FLOAT; + recordP->value = buffer + index + valueStart; + recordP->valueLen = valueLen; + break; + + case 't': + // TODO: support time + break; + + default: + return -1; + } + } + break; + + case 2: + { + // "bv", "ov", or "sv" + if (buffer[index+tokenStart+1] != 'v') return -1; + switch (buffer[index+tokenStart]) + { + case 'b': + if (recordP->type != _TYPE_UNSET) return -1; + if (0 == lwm2m_strncmp(JSON_TRUE_STRING, (char *)buffer + index + valueStart, valueLen)) + { + recordP->type = _TYPE_TRUE; + } + else if (0 == lwm2m_strncmp(JSON_FALSE_STRING, (char *)buffer + index + valueStart, valueLen)) + { + recordP->type = _TYPE_FALSE; + } + else + { + return -1; + } + break; + + case 'o': + if (recordP->type != _TYPE_UNSET) return -1; + // TODO: support object link + break; + + case 's': + if (recordP->type != _TYPE_UNSET) return -1; + // Check for " around value + if (valueLen < 2 + || buffer[index+valueStart] != '"' + || buffer[index+valueStart+valueLen-1] != '"') + { + return -1; + } + recordP->type = _TYPE_STRING; + recordP->value = buffer + index + valueStart + 1; + recordP->valueLen = valueLen - 2; + break; + + default: + return -1; + } + } + break; + + default: + return -1; + } + + index += next + 1; + } while (index < bufferLen); + + return 0; +} + +static bool prv_convertValue(_record_t * recordP, + lwm2m_data_t * targetP) +{ + switch (recordP->type) + { + case _TYPE_FALSE: + lwm2m_data_encode_bool(false, targetP); + break; + case _TYPE_TRUE: + lwm2m_data_encode_bool(true, targetP); + break; + case _TYPE_FLOAT: + { + size_t i; + + i = 0; + while (i < recordP->valueLen + && recordP->value[i] != '.') + { + i++; + } + if (i == recordP->valueLen) + { + int64_t value; + + if ( 1 != utils_plainTextToInt64(recordP->value, + recordP->valueLen, + &value)) + { + return false; + } + + lwm2m_data_encode_int(value, targetP); + } + else + { + double value; + + if ( 1 != utils_plainTextToFloat64(recordP->value, + recordP->valueLen, + &value)) + { + return false; + } + + lwm2m_data_encode_float(value, targetP); + } + } + break; + + case _TYPE_STRING: + lwm2m_data_encode_opaque(recordP->value, recordP->valueLen, targetP); + targetP->type = LWM2M_TYPE_STRING; + break; + + case _TYPE_UNSET: + default: + return false; + } + + return true; +} + +static lwm2m_data_t * prv_findDataItem(lwm2m_data_t * listP, + int count, + uint16_t id) +{ + int i; + + i = 0; + while (i < count) + { + if (listP[i].type != LWM2M_TYPE_UNDEFINED && listP[i].id == id) + { + return listP + i; + } + i++; + } + + return NULL; +} + +static uri_depth_t prv_decreaseLevel(uri_depth_t level) +{ + switch(level) + { + case URI_DEPTH_OBJECT: + return URI_DEPTH_OBJECT_INSTANCE; + case URI_DEPTH_OBJECT_INSTANCE: + return URI_DEPTH_RESOURCE; + case URI_DEPTH_RESOURCE: + return URI_DEPTH_RESOURCE_INSTANCE; + case URI_DEPTH_RESOURCE_INSTANCE: + return URI_DEPTH_RESOURCE_INSTANCE; + default: + return URI_DEPTH_RESOURCE; + } +} + +static lwm2m_data_t * prv_extendData(lwm2m_data_t * parentP) +{ + lwm2m_data_t * newP; + + newP = lwm2m_data_new(parentP->value.asChildren.count + 1); + if (newP == NULL) return NULL; + if (parentP->value.asChildren.array != NULL) + { + memcpy(newP, parentP->value.asChildren.array, parentP->value.asChildren.count * sizeof(lwm2m_data_t)); + lwm2m_free(parentP->value.asChildren.array); // do not use lwm2m_data_free() to keep pointed values + } + parentP->value.asChildren.array = newP; + parentP->value.asChildren.count += 1; + + return newP + parentP->value.asChildren.count - 1; +} + +static int prv_convertRecord(lwm2m_uri_t * uriP, + _record_t * recordArray, + int count, + lwm2m_data_t ** dataP) +{ + int index; + int freeIndex; + lwm2m_data_t * rootP; + int size; + uri_depth_t rootLevel; + + if (uriP == NULL) + { + size = count; + *dataP = lwm2m_data_new(count); + if (NULL == *dataP) return -1; + rootLevel = URI_DEPTH_OBJECT; + rootP = *dataP; + } + else + { + lwm2m_data_t * parentP; + size = 1; + + *dataP = lwm2m_data_new(1); + if (NULL == *dataP) return -1; + (*dataP)->type = LWM2M_TYPE_OBJECT; + (*dataP)->id = uriP->objectId; + rootLevel = URI_DEPTH_OBJECT_INSTANCE; + parentP = *dataP; + if (LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + parentP->value.asChildren.count = 1; + parentP->value.asChildren.array = lwm2m_data_new(1); + if (NULL == parentP->value.asChildren.array) goto error; + parentP = parentP->value.asChildren.array; + parentP->type = LWM2M_TYPE_OBJECT_INSTANCE; + parentP->id = uriP->instanceId; + rootLevel = URI_DEPTH_RESOURCE; + if (LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + parentP->value.asChildren.count = 1; + parentP->value.asChildren.array = lwm2m_data_new(1); + if (NULL == parentP->value.asChildren.array) goto error; + parentP = parentP->value.asChildren.array; + parentP->type = LWM2M_TYPE_UNDEFINED; + parentP->id = uriP->resourceId; + rootLevel = URI_DEPTH_RESOURCE_INSTANCE; + } + } + parentP->value.asChildren.count = count; + parentP->value.asChildren.array = lwm2m_data_new(count); + if (NULL == parentP->value.asChildren.array) goto error; + rootP = parentP->value.asChildren.array; + } + + freeIndex = 0; + for (index = 0 ; index < count ; index++) + { + lwm2m_data_t * targetP; + int resSegmentIndex; + int i; + + // check URI depth + // resSegmentIndex is set to the resource segment position + switch(rootLevel) + { + case URI_DEPTH_OBJECT: + resSegmentIndex = 2; + break; + case URI_DEPTH_OBJECT_INSTANCE: + resSegmentIndex = 1; + break; + case URI_DEPTH_RESOURCE: + resSegmentIndex = 0; + break; + case URI_DEPTH_RESOURCE_INSTANCE: + resSegmentIndex = -1; + break; + default: + goto error; + } + for (i = 0 ; i <= resSegmentIndex ; i++) + { + if (recordArray[index].ids[i] == LWM2M_MAX_ID) goto error; + } + if (resSegmentIndex < 2) + { + if (recordArray[index].ids[resSegmentIndex + 2] != LWM2M_MAX_ID) goto error; + } + + targetP = prv_findDataItem(rootP, count, recordArray[index].ids[0]); + if (targetP == NULL) + { + targetP = rootP + freeIndex; + freeIndex++; + targetP->id = recordArray[index].ids[0]; + targetP->type = utils_depthToDatatype(rootLevel); + } + if (recordArray[index].ids[1] != LWM2M_MAX_ID) + { + lwm2m_data_t * parentP; + uri_depth_t level; + + parentP = targetP; + level = prv_decreaseLevel(rootLevel); + for (i = 1 ; i <= resSegmentIndex ; i++) + { + targetP = prv_findDataItem(parentP->value.asChildren.array, parentP->value.asChildren.count, recordArray[index].ids[i]); + if (targetP == NULL) + { + targetP = prv_extendData(parentP); + if (targetP == NULL) goto error; + targetP->id = recordArray[index].ids[i]; + targetP->type = utils_depthToDatatype(level); + } + level = prv_decreaseLevel(level); + parentP = targetP; + } + if (recordArray[index].ids[resSegmentIndex + 1] != LWM2M_MAX_ID) + { + targetP->type = LWM2M_TYPE_MULTIPLE_RESOURCE; + targetP = prv_extendData(targetP); + if (targetP == NULL) goto error; + targetP->id = recordArray[index].ids[resSegmentIndex + 1]; + targetP->type = LWM2M_TYPE_UNDEFINED; + } + } + + if (true != prv_convertValue(recordArray + index, targetP)) goto error; + } + + return size; + +error: + lwm2m_data_free(size, *dataP); + *dataP = NULL; + + return -1; +} + +static int prv_dataStrip(int size, + lwm2m_data_t * dataP, + lwm2m_data_t ** resultP) +{ + int i; + int j; + int realSize; + + realSize = 0; + for (i = 0 ; i < size ; i++) + { + if (dataP[i].type != LWM2M_TYPE_UNDEFINED) + { + realSize++; + } + } + + *resultP = lwm2m_data_new(realSize); + if (*resultP == NULL) return -1; + + j = 0; + for (i = 0 ; i < size ; i++) + { + if (dataP[i].type != LWM2M_TYPE_UNDEFINED) + { + memcpy((*resultP) + j, dataP + i, sizeof(lwm2m_data_t)); + + if (dataP[i].type == LWM2M_TYPE_OBJECT + || dataP[i].type == LWM2M_TYPE_OBJECT_INSTANCE + || dataP[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + int childLen; + + childLen = prv_dataStrip(dataP[i].value.asChildren.count, dataP[i].value.asChildren.array, &((*resultP)[j].value.asChildren.array)); + if (childLen <= 0) + { + // skip this one + j--; + } + else + { + (*resultP)[j].value.asChildren.count = childLen; + } + } + else + { + dataP[i].value.asBuffer.buffer = NULL; + } + + j++; + } + } + + return realSize; +} + +int json_parse(lwm2m_uri_t * uriP, + uint8_t * buffer, + size_t bufferLen, + lwm2m_data_t ** dataP) +{ + size_t index; + int count = 0; + bool eFound = false; + bool bnFound = false; + bool btFound = false; + int bnStart; + int bnLen; + _record_t * recordArray; + lwm2m_data_t * parsedP; + + LOG_ARG("bufferLen: %d, buffer: \"%s\"", bufferLen, (char *)buffer); + LOG_URI(uriP); + *dataP = NULL; + recordArray = NULL; + parsedP = NULL; + + index = prv_skipSpace(buffer, bufferLen); + if (index == bufferLen) return -1; + + if (buffer[index] != '{') return -1; + do + { + _GO_TO_NEXT_CHAR(index, buffer, bufferLen); + if (buffer[index] != '"') goto error; + if (index++ >= bufferLen) goto error; + switch (buffer[index]) + { + case 'e': + { + int recordIndex; + + if (bufferLen-index < JSON_MIN_ARRAY_LEN) goto error; + index++; + if (buffer[index] != '"') goto error; + if (eFound == true) goto error; + eFound = true; + + _GO_TO_NEXT_CHAR(index, buffer, bufferLen); + if (buffer[index] != ':') goto error; + _GO_TO_NEXT_CHAR(index, buffer, bufferLen); + if (buffer[index] != '[') goto error; + _GO_TO_NEXT_CHAR(index, buffer, bufferLen); + count = prv_countItems(buffer + index, bufferLen - index); + if (count <= 0) goto error; + recordArray = (_record_t*)lwm2m_malloc(count * sizeof(_record_t)); + if (recordArray == NULL) goto error; + // at this point we are sure buffer[index] is '{' and all { and } are matching + recordIndex = 0; + while (recordIndex < count) + { + int itemLen; + + if (buffer[index] != '{') goto error; + itemLen = 0; + while (buffer[index + itemLen] != '}') itemLen++; + if (0 != prv_parseItem(buffer + index + 1, itemLen - 1, recordArray + recordIndex)) + { + goto error; + } + recordIndex++; + index += itemLen; + _GO_TO_NEXT_CHAR(index, buffer, bufferLen); + switch (buffer[index]) + { + case ',': + _GO_TO_NEXT_CHAR(index, buffer, bufferLen); + break; + case ']': + if (recordIndex == count) break; + // else this is an error + default: + goto error; + } + } + if (buffer[index] != ']') goto error; + } + break; + + case 'b': + if (bufferLen-index < JSON_MIN_BX_LEN) goto error; + index++; + switch (buffer[index]) + { + case 't': + index++; + if (buffer[index] != '"') goto error; + if (btFound == true) goto error; + btFound = true; + + // TODO: handle timed values + // temp: skip this token + while(index < bufferLen && buffer[index] != ',' && buffer[index] != '}') index++; + if (index == bufferLen) goto error; + index--; + // end temp + break; + case 'n': + { + int next; + int tokenStart; + int tokenLen; + int itemLen; + + index++; + if (buffer[index] != '"') goto error; + if (bnFound == true) goto error; + bnFound = true; + index -= 3; + itemLen = 0; + while (buffer[index + itemLen] != '}' + && buffer[index + itemLen] != ',' + && index + itemLen < bufferLen) + { + itemLen++; + } + if (index + itemLen == bufferLen) goto error; + next = prv_split(buffer+index, itemLen, &tokenStart, &tokenLen, &bnStart, &bnLen); + if (next < 0) goto error; + bnStart += index; + index += next - 1; + } + break; + default: + goto error; + } + break; + + default: + goto error; + } + + _GO_TO_NEXT_CHAR(index, buffer, bufferLen); + } while (buffer[index] == ','); + + if (buffer[index] != '}') goto error; + + if (eFound == true) + { + lwm2m_uri_t baseURI; + lwm2m_uri_t * baseUriP; + lwm2m_data_t * resultP; + int size; + + memset(&baseURI, 0, sizeof(lwm2m_uri_t)); + if (bnFound == false) + { + baseUriP = uriP; + } + else + { + int res; + + // we ignore the request URI and use the bn one. + + // Check for " around URI + if (bnLen < 3 + || buffer[bnStart] != '"' + || buffer[bnStart+bnLen-1] != '"') + { + goto error; + } + bnStart += 1; + bnLen -= 2; + + if (bnLen == 1) + { + if (buffer[bnStart] != '/') goto error; + baseUriP = NULL; + } + else + { + res = lwm2m_stringToUri((char *)buffer + bnStart, bnLen, &baseURI); + if (res < 0 || res != bnLen) goto error; + baseUriP = &baseURI; + } + } + + count = prv_convertRecord(baseUriP, recordArray, count, &parsedP); + lwm2m_free(recordArray); + recordArray = NULL; + + if (count > 0 && uriP != NULL) + { + if (parsedP->type != LWM2M_TYPE_OBJECT || parsedP->id != uriP->objectId) goto error; + if (!LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + size = parsedP->value.asChildren.count; + resultP = parsedP->value.asChildren.array; + } + else + { + int i; + + resultP = NULL; + // be permissive and allow full object JSON when requesting for a single instance + for (i = 0 ; i < (int)parsedP->value.asChildren.count && resultP == NULL; i++) + { + lwm2m_data_t * targetP; + + targetP = parsedP->value.asChildren.array + i; + if (targetP->id == uriP->instanceId) + { + resultP = targetP->value.asChildren.array; + size = targetP->value.asChildren.count; + } + } + if (resultP == NULL) goto error; + if (LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + lwm2m_data_t * resP; + + resP = NULL; + for (i = 0 ; i < size && resP == NULL; i++) + { + lwm2m_data_t * targetP; + + targetP = resultP + i; + if (targetP->id == uriP->resourceId) + { + if (targetP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + resP = targetP->value.asChildren.array; + size = targetP->value.asChildren.count; + } + else + { + size = prv_dataStrip(1, targetP, &resP); + if (size <= 0) goto error; + lwm2m_data_free(count, parsedP); + parsedP = NULL; + } + } + } + if (resP == NULL) goto error; + resultP = resP; + } + } + } + else + { + resultP = parsedP; + size = count; + } + + if (parsedP != NULL) + { + lwm2m_data_t * tempP; + + size = prv_dataStrip(size, resultP, &tempP); + if (size <= 0) goto error; + lwm2m_data_free(count, parsedP); + resultP = tempP; + } + count = size; + *dataP = resultP; + } + + LOG_ARG("Parsing successful. count: %d", count); + return count; + +error: + LOG("Parsing failed"); + if (parsedP != NULL) + { + lwm2m_data_free(count, parsedP); + parsedP = NULL; + } + if (recordArray != NULL) + { + lwm2m_free(recordArray); + } + return -1; +} + +static int prv_serializeValue(lwm2m_data_t * tlvP, + uint8_t * buffer, + size_t bufferLen) +{ + int res; + int head; + + switch (tlvP->type) + { + case LWM2M_TYPE_STRING: + if (bufferLen < JSON_ITEM_STRING_BEGIN_SIZE) return -1; + memcpy(buffer, JSON_ITEM_STRING_BEGIN, JSON_ITEM_STRING_BEGIN_SIZE); + head = JSON_ITEM_STRING_BEGIN_SIZE; + + if (bufferLen - head < tlvP->value.asBuffer.length) return -1; + memcpy(buffer + head, tlvP->value.asBuffer.buffer, tlvP->value.asBuffer.length); + head += tlvP->value.asBuffer.length; + + if (bufferLen - head < JSON_ITEM_STRING_END_SIZE) return -1; + memcpy(buffer + head, JSON_ITEM_STRING_END, JSON_ITEM_STRING_END_SIZE); + head += JSON_ITEM_STRING_END_SIZE; + + break; + + case LWM2M_TYPE_INTEGER: + { + int64_t value; + + if (0 == lwm2m_data_decode_int(tlvP, &value)) return -1; + + if (bufferLen < JSON_ITEM_NUM_SIZE) return -1; + memcpy(buffer, JSON_ITEM_NUM, JSON_ITEM_NUM_SIZE); + head = JSON_ITEM_NUM_SIZE; + + res = utils_intToText(value, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + + if (bufferLen - head < JSON_ITEM_NUM_END_SIZE) return -1; + memcpy(buffer + head, JSON_ITEM_NUM_END, JSON_ITEM_NUM_END_SIZE); + head += JSON_ITEM_NUM_END_SIZE; + } + break; + + case LWM2M_TYPE_FLOAT: + { + double value; + + if (0 == lwm2m_data_decode_float(tlvP, &value)) return -1; + + if (bufferLen < JSON_ITEM_NUM_SIZE) return -1; + memcpy(buffer, JSON_ITEM_NUM, JSON_ITEM_NUM_SIZE); + head = JSON_ITEM_NUM_SIZE; + + res = utils_floatToText(value, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + + if (bufferLen - head < JSON_ITEM_NUM_END_SIZE) return -1; + memcpy(buffer + head, JSON_ITEM_NUM_END, JSON_ITEM_NUM_END_SIZE); + head += JSON_ITEM_NUM_END_SIZE; + } + break; + + case LWM2M_TYPE_BOOLEAN: + { + bool value; + + if (0 == lwm2m_data_decode_bool(tlvP, &value)) return -1; + + if (value == true) + { + if (bufferLen < JSON_ITEM_BOOL_TRUE_SIZE) return -1; + memcpy(buffer, JSON_ITEM_BOOL_TRUE, JSON_ITEM_BOOL_TRUE_SIZE); + head = JSON_ITEM_BOOL_TRUE_SIZE; + } + else + { + if (bufferLen < JSON_ITEM_BOOL_FALSE_SIZE) return -1; + memcpy(buffer, JSON_ITEM_BOOL_FALSE, JSON_ITEM_BOOL_FALSE_SIZE); + head = JSON_ITEM_BOOL_FALSE_SIZE; + } + } + break; + + case LWM2M_TYPE_OPAQUE: + if (bufferLen < JSON_ITEM_STRING_BEGIN_SIZE) return -1; + memcpy(buffer, JSON_ITEM_STRING_BEGIN, JSON_ITEM_STRING_BEGIN_SIZE); + head = JSON_ITEM_STRING_BEGIN_SIZE; + + res = utils_base64Encode(tlvP->value.asBuffer.buffer, tlvP->value.asBuffer.length, buffer+head, bufferLen - head); + if (res == 0) return -1; + head += res; + + if (bufferLen - head < JSON_ITEM_STRING_END_SIZE) return -1; + memcpy(buffer + head, JSON_ITEM_STRING_END, JSON_ITEM_STRING_END_SIZE); + head += JSON_ITEM_STRING_END_SIZE; + break; + + case LWM2M_TYPE_OBJECT_LINK: + // TODO: implement + return -1; + + default: + return -1; + } + + return head; +} + +int prv_serializeData(lwm2m_data_t * tlvP, + uint8_t * parentUriStr, + size_t parentUriLen, + uint8_t * buffer, + size_t bufferLen) +{ + int head; + int res; + + head = 0; + + switch (tlvP->type) + { + case LWM2M_TYPE_OBJECT: + case LWM2M_TYPE_OBJECT_INSTANCE: + case LWM2M_TYPE_MULTIPLE_RESOURCE: + { + uint8_t uriStr[URI_MAX_STRING_LEN]; + size_t uriLen; + size_t index; + + if (parentUriLen > 0) + { + if (URI_MAX_STRING_LEN < parentUriLen) return -1; + memcpy(uriStr, parentUriStr, parentUriLen); + uriLen = parentUriLen; + } + else + { + uriLen = 0; + } + res = utils_intToText(tlvP->id, uriStr + uriLen, URI_MAX_STRING_LEN - uriLen); + if (res <= 0) return -1; + uriLen += res; + uriStr[uriLen] = '/'; + uriLen++; + + head = 0; + for (index = 0 ; index < tlvP->value.asChildren.count; index++) + { + res = prv_serializeData(tlvP->value.asChildren.array + index, uriStr, uriLen, buffer + head, bufferLen - head); + if (res < 0) return -1; + head += res; + } + } + break; + + default: + if (bufferLen < JSON_RES_ITEM_URI_SIZE) return -1; + memcpy(buffer, JSON_RES_ITEM_URI, JSON_RES_ITEM_URI_SIZE); + head = JSON_RES_ITEM_URI_SIZE; + + if (parentUriLen > 0) + { + if (bufferLen - head < parentUriLen) return -1; + memcpy(buffer + head, parentUriStr, parentUriLen); + head += parentUriLen; + } + + res = utils_intToText(tlvP->id, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + + res = prv_serializeValue(tlvP, buffer + head, bufferLen - head); + if (res < 0) return -1; + head += res; + break; + } + + return head; +} + +static int prv_findAndCheckData(lwm2m_uri_t * uriP, + uri_depth_t level, + size_t size, + lwm2m_data_t * tlvP, + lwm2m_data_t ** targetP) +{ + size_t index; + int result; + + if (size == 0) return 0; + + if (size > 1) + { + if (tlvP[0].type == LWM2M_TYPE_OBJECT || tlvP[0].type == LWM2M_TYPE_OBJECT_INSTANCE) + { + for (index = 0; index < size; index++) + { + if (tlvP[index].type != tlvP[0].type) + { + *targetP = NULL; + return -1; + } + } + } + else + { + for (index = 0; index < size; index++) + { + if (tlvP[index].type == LWM2M_TYPE_OBJECT || tlvP[index].type == LWM2M_TYPE_OBJECT_INSTANCE) + { + *targetP = NULL; + return -1; + } + } + } + } + + *targetP = NULL; + result = -1; + switch (level) + { + case URI_DEPTH_OBJECT: + if (tlvP[0].type == LWM2M_TYPE_OBJECT) + { + *targetP = tlvP; + result = (int)size; + } + break; + + case URI_DEPTH_OBJECT_INSTANCE: + switch (tlvP[0].type) + { + case LWM2M_TYPE_OBJECT: + for (index = 0; index < size; index++) + { + if (tlvP[index].id == uriP->objectId) + { + return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP); + } + } + break; + case LWM2M_TYPE_OBJECT_INSTANCE: + *targetP = tlvP; + result = (int)size; + break; + default: + break; + } + break; + + case URI_DEPTH_RESOURCE: + switch (tlvP[0].type) + { + case LWM2M_TYPE_OBJECT: + for (index = 0; index < size; index++) + { + if (tlvP[index].id == uriP->objectId) + { + return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP); + } + } + break; + case LWM2M_TYPE_OBJECT_INSTANCE: + for (index = 0; index < size; index++) + { + if (tlvP[index].id == uriP->instanceId) + { + return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP); + } + } + break; + default: + *targetP = tlvP; + result = (int)size; + break; + } + break; + + case URI_DEPTH_RESOURCE_INSTANCE: + switch (tlvP[0].type) + { + case LWM2M_TYPE_OBJECT: + for (index = 0; index < size; index++) + { + if (tlvP[index].id == uriP->objectId) + { + return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP); + } + } + break; + case LWM2M_TYPE_OBJECT_INSTANCE: + for (index = 0; index < size; index++) + { + if (tlvP[index].id == uriP->instanceId) + { + return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP); + } + } + break; + case LWM2M_TYPE_MULTIPLE_RESOURCE: + for (index = 0; index < size; index++) + { + if (tlvP[index].id == uriP->resourceId) + { + return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP); + } + } + break; + default: + *targetP = tlvP; + result = (int)size; + break; + } + break; + + default: + break; + } + + return result; +} + +int json_serialize(lwm2m_uri_t * uriP, + int size, + lwm2m_data_t * tlvP, + uint8_t ** bufferP) +{ + int index; + size_t head; + uint8_t bufferJSON[PRV_JSON_BUFFER_SIZE]; + uint8_t baseUriStr[URI_MAX_STRING_LEN]; + int baseUriLen; + uri_depth_t rootLevel; + int num; + lwm2m_data_t * targetP; + + LOG_ARG("size: %d", size); + LOG_URI(uriP); + if (size != 0 && tlvP == NULL) return -1; + + baseUriLen = uri_toString(uriP, baseUriStr, URI_MAX_STRING_LEN, &rootLevel); + if (baseUriLen < 0) return -1; + + num = prv_findAndCheckData(uriP, rootLevel, size, tlvP, &targetP); + if (num < 0) return -1; + + while (num == 1 + && (targetP->type == LWM2M_TYPE_OBJECT + || targetP->type == LWM2M_TYPE_OBJECT_INSTANCE + || targetP->type == LWM2M_TYPE_MULTIPLE_RESOURCE)) + { + int res; + + res = utils_intToText(targetP->id, baseUriStr + baseUriLen, URI_MAX_STRING_LEN - baseUriLen); + if (res <= 0) return 0; + baseUriLen += res; + if (baseUriLen >= URI_MAX_STRING_LEN -1) return 0; + num = targetP->value.asChildren.count; + targetP = targetP->value.asChildren.array; + baseUriStr[baseUriLen] = '/'; + baseUriLen++; + } + + if (baseUriLen > 0) + { + memcpy(bufferJSON, JSON_BN_HEADER_1, JSON_BN_HEADER_1_SIZE); + head = JSON_BN_HEADER_1_SIZE; + memcpy(bufferJSON + head, baseUriStr, baseUriLen); + head += baseUriLen; + memcpy(bufferJSON + head, JSON_BN_HEADER_2, JSON_BN_HEADER_2_SIZE); + head += JSON_BN_HEADER_2_SIZE; + } + else + { + memcpy(bufferJSON, JSON_HEADER, JSON_HEADER_SIZE); + head = JSON_HEADER_SIZE; + } + + for (index = 0 ; index < num && head < PRV_JSON_BUFFER_SIZE ; index++) + { + int res; + + res = prv_serializeData(targetP + index, NULL, 0, bufferJSON + head, PRV_JSON_BUFFER_SIZE - head); + if (res < 0) return 0; + head += res; + } + + if (head + JSON_FOOTER_SIZE - 1 > PRV_JSON_BUFFER_SIZE) return 0; + + if (num > 0) head = head - 1; + + memcpy(bufferJSON + head, JSON_FOOTER, JSON_FOOTER_SIZE); + head = head + JSON_FOOTER_SIZE; + + *bufferP = (uint8_t *)lwm2m_malloc(head); + if (*bufferP == NULL) return 0; + memcpy(*bufferP, bufferJSON, head); + + return head; +} + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/liblwm2m.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,475 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * Fabien Fleutot - Please refer to git log + * Simon Bernard - Please refer to git log + * Toby Jaffey - Please refer to git log + * Pascal Rieux - Please refer to git log + * + *******************************************************************************/ + +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro <david.navarro@intel.com> + +*/ + +#include "internals.h" + +#include <stdlib.h> +#include <string.h> + +#include <stdio.h> + + +lwm2m_context_t * lwm2m_init(void * userData) +{ + lwm2m_context_t * contextP; + + LOG("Entering"); + contextP = (lwm2m_context_t *)lwm2m_malloc(sizeof(lwm2m_context_t)); + if (NULL != contextP) + { + memset(contextP, 0, sizeof(lwm2m_context_t)); + contextP->userData = userData; + srand((int)lwm2m_gettime()); + contextP->nextMID = rand(); + } + + return contextP; +} + +#ifdef LWM2M_CLIENT_MODE +void lwm2m_deregister(lwm2m_context_t * context) +{ + lwm2m_server_t * server = context->serverList; + + LOG("Entering"); + while (NULL != server) + { + registration_deregister(context, server); + server = server->next; + } +} + +static void prv_deleteServer(lwm2m_server_t * serverP) +{ + // TODO parse transaction and observation to remove the ones related to this server + if (NULL != serverP->location) + { + lwm2m_free(serverP->location); + } + free_block1_buffer(serverP->block1Data); + lwm2m_free(serverP); +} + +static void prv_deleteServerList(lwm2m_context_t * context) +{ + while (NULL != context->serverList) + { + lwm2m_server_t * server; + server = context->serverList; + context->serverList = server->next; + prv_deleteServer(server); + } +} + +static void prv_deleteBootstrapServer(lwm2m_server_t * serverP) +{ + // TODO should we free location as in prv_deleteServer ? + // TODO should we parse transaction and observation to remove the ones related to this server ? + free_block1_buffer(serverP->block1Data); + lwm2m_free(serverP); +} + +static void prv_deleteBootstrapServerList(lwm2m_context_t * context) +{ + while (NULL != context->bootstrapServerList) + { + lwm2m_server_t * server; + server = context->bootstrapServerList; + context->bootstrapServerList = server->next; + prv_deleteBootstrapServer(server); + } +} + +static void prv_deleteObservedList(lwm2m_context_t * contextP) +{ + while (NULL != contextP->observedList) + { + lwm2m_observed_t * targetP; + lwm2m_watcher_t * watcherP; + + targetP = contextP->observedList; + contextP->observedList = contextP->observedList->next; + + for (watcherP = targetP->watcherList ; watcherP != NULL ; watcherP = watcherP->next) + { + if (watcherP->parameters != NULL) lwm2m_free(watcherP->parameters); + } + LWM2M_LIST_FREE(targetP->watcherList); + + lwm2m_free(targetP); + } +} +#endif + +void prv_deleteTransactionList(lwm2m_context_t * context) +{ + while (NULL != context->transactionList) + { + lwm2m_transaction_t * transaction; + + transaction = context->transactionList; + context->transactionList = context->transactionList->next; + transaction_free(transaction); + } +} + +void lwm2m_close(lwm2m_context_t * contextP) +{ +#ifdef LWM2M_CLIENT_MODE + + LOG("Entering"); + lwm2m_deregister(contextP); + prv_deleteServerList(contextP); + prv_deleteBootstrapServerList(contextP); + prv_deleteObservedList(contextP); + lwm2m_free(contextP->endpointName); + if (contextP->msisdn != NULL) + { + lwm2m_free(contextP->msisdn); + } + if (contextP->altPath != NULL) + { + lwm2m_free(contextP->altPath); + } + +#endif + +#ifdef LWM2M_SERVER_MODE + while (NULL != contextP->clientList) + { + lwm2m_client_t * clientP; + + clientP = contextP->clientList; + contextP->clientList = contextP->clientList->next; + + registration_freeClient(clientP); + } +#endif + + prv_deleteTransactionList(contextP); + lwm2m_free(contextP); +} + +#ifdef LWM2M_CLIENT_MODE +static int prv_refreshServerList(lwm2m_context_t * contextP) +{ + lwm2m_server_t * targetP; + lwm2m_server_t * nextP; + + // Remove all servers marked as dirty + targetP = contextP->bootstrapServerList; + contextP->bootstrapServerList = NULL; + while (targetP != NULL) + { + nextP = targetP->next; + targetP->next = NULL; + if (!targetP->dirty) + { + targetP->status = STATE_DEREGISTERED; + contextP->bootstrapServerList = (lwm2m_server_t *)LWM2M_LIST_ADD(contextP->bootstrapServerList, targetP); + } + else + { + prv_deleteServer(targetP); + } + targetP = nextP; + } + targetP = contextP->serverList; + contextP->serverList = NULL; + while (targetP != NULL) + { + nextP = targetP->next; + targetP->next = NULL; + if (!targetP->dirty) + { + // TODO: Should we revert the status to STATE_DEREGISTERED ? + contextP->serverList = (lwm2m_server_t *)LWM2M_LIST_ADD(contextP->serverList, targetP); + } + else + { + prv_deleteServer(targetP); + } + targetP = nextP; + } + + return object_getServers(contextP); +} + +int lwm2m_configure(lwm2m_context_t * contextP, + const char * endpointName, + const char * msisdn, + const char * altPath, + uint16_t numObject, + lwm2m_object_t * objectList[]) +{ + int i; + uint8_t found; + + LOG_ARG("endpointName: \"%s\", msisdn: \"%s\", altPath: \"%s\", numObject: %d", endpointName, msisdn, altPath, numObject); + // This API can be called only once for now + if (contextP->endpointName != NULL || contextP->objectList != NULL) return COAP_400_BAD_REQUEST; + + if (endpointName == NULL) return COAP_400_BAD_REQUEST; + if (numObject < 3) return COAP_400_BAD_REQUEST; + // Check that mandatory objects are present + found = 0; + for (i = 0 ; i < numObject ; i++) + { + if (objectList[i]->objID == LWM2M_SECURITY_OBJECT_ID) found |= 0x01; + if (objectList[i]->objID == LWM2M_SERVER_OBJECT_ID) found |= 0x02; + if (objectList[i]->objID == LWM2M_DEVICE_OBJECT_ID) found |= 0x04; + } + if (found != 0x07) return COAP_400_BAD_REQUEST; + if (altPath != NULL) + { + if (0 == utils_isAltPathValid(altPath)) + { + return COAP_400_BAD_REQUEST; + } + if (altPath[1] == 0) + { + altPath = NULL; + } + } + contextP->endpointName = lwm2m_strdup(endpointName); + if (contextP->endpointName == NULL) + { + return COAP_500_INTERNAL_SERVER_ERROR; + } + + if (msisdn != NULL) + { + contextP->msisdn = lwm2m_strdup(msisdn); + if (contextP->msisdn == NULL) + { + return COAP_500_INTERNAL_SERVER_ERROR; + } + } + + if (altPath != NULL) + { + contextP->altPath = lwm2m_strdup(altPath); + if (contextP->altPath == NULL) + { + return COAP_500_INTERNAL_SERVER_ERROR; + } + } + + for (i = 0; i < numObject; i++) + { + objectList[i]->next = NULL; + contextP->objectList = (lwm2m_object_t *)LWM2M_LIST_ADD(contextP->objectList, objectList[i]); + } + + return COAP_NO_ERROR; +} + +int lwm2m_add_object(lwm2m_context_t * contextP, + lwm2m_object_t * objectP) +{ + lwm2m_object_t * targetP; + + LOG_ARG("ID: %d", objectP->objID); + targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, objectP->objID); + if (targetP != NULL) return COAP_406_NOT_ACCEPTABLE; + objectP->next = NULL; + + contextP->objectList = (lwm2m_object_t *)LWM2M_LIST_ADD(contextP->objectList, objectP); + + if (contextP->state == STATE_READY) + { + return lwm2m_update_registration(contextP, 0, true); + } + + return COAP_NO_ERROR; +} + +int lwm2m_remove_object(lwm2m_context_t * contextP, + uint16_t id) +{ + lwm2m_object_t * targetP; + + LOG_ARG("ID: %d", id); + contextP->objectList = (lwm2m_object_t *)LWM2M_LIST_RM(contextP->objectList, id, &targetP); + + if (targetP == NULL) return COAP_404_NOT_FOUND; + + if (contextP->state == STATE_READY) + { + return lwm2m_update_registration(contextP, 0, true); + } + + return 0; +} + +#endif + + +int lwm2m_step(lwm2m_context_t * contextP, + time_t * timeoutP) +{ + time_t tv_sec; + int result; + + LOG_ARG("timeoutP: %" PRId64, *timeoutP); + tv_sec = lwm2m_gettime(); + if (tv_sec < 0) return COAP_500_INTERNAL_SERVER_ERROR; + +#ifdef LWM2M_CLIENT_MODE + LOG_ARG("State: %s", STR_STATE(contextP->state)); + // state can also be modified in bootstrap_handleCommand(). + +next_step: + switch (contextP->state) + { + case STATE_INITIAL: + if (0 != prv_refreshServerList(contextP)) return COAP_503_SERVICE_UNAVAILABLE; + if (contextP->serverList != NULL) + { + contextP->state = STATE_REGISTER_REQUIRED; + } + else + { + // Bootstrapping + contextP->state = STATE_BOOTSTRAP_REQUIRED; + } + goto next_step; + break; + + case STATE_BOOTSTRAP_REQUIRED: +#ifdef LWM2M_BOOTSTRAP + if (contextP->bootstrapServerList != NULL) + { + bootstrap_start(contextP); + contextP->state = STATE_BOOTSTRAPPING; + bootstrap_step(contextP, tv_sec, timeoutP); + } + else +#endif + { + return COAP_503_SERVICE_UNAVAILABLE; + } + break; + +#ifdef LWM2M_BOOTSTRAP + case STATE_BOOTSTRAPPING: + switch (bootstrap_getStatus(contextP)) + { + case STATE_BS_FINISHED: + contextP->state = STATE_INITIAL; + goto next_step; + break; + + case STATE_BS_FAILED: + return COAP_503_SERVICE_UNAVAILABLE; + + default: + // keep on waiting + bootstrap_step(contextP, tv_sec, timeoutP); + break; + } + break; +#endif + case STATE_REGISTER_REQUIRED: + result = registration_start(contextP); + if (COAP_NO_ERROR != result) return result; + contextP->state = STATE_REGISTERING; + break; + + case STATE_REGISTERING: + { + switch (registration_getStatus(contextP)) + { + case STATE_REGISTERED: + contextP->state = STATE_READY; + break; + + case STATE_REG_FAILED: + // TODO avoid infinite loop by checking the bootstrap info is different + contextP->state = STATE_BOOTSTRAP_REQUIRED; + goto next_step; + break; + + case STATE_REG_PENDING: + default: + // keep on waiting + break; + } + } + break; + + case STATE_READY: + if (registration_getStatus(contextP) == STATE_REG_FAILED) + { + // TODO avoid infinite loop by checking the bootstrap info is different + contextP->state = STATE_BOOTSTRAP_REQUIRED; + goto next_step; + break; + } + break; + + default: + // do nothing + break; + } + + observe_step(contextP, tv_sec, timeoutP); +#endif + + registration_step(contextP, tv_sec, timeoutP); + transaction_step(contextP, tv_sec, timeoutP); + + LOG_ARG("Final timeoutP: %" PRId64, *timeoutP); +#ifdef LWM2M_CLIENT_MODE + LOG_ARG("Final state: %s", STR_STATE(contextP->state)); +#endif + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/liblwm2m.h Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,740 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * Fabien Fleutot - Please refer to git log + * Simon Bernard - Please refer to git log + * Toby Jaffey - Please refer to git log + * Julien Vermillard - Please refer to git log + * Bosch Software Innovations GmbH - Please refer to git log + * Pascal Rieux - Please refer to git log + *******************************************************************************/ + +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro <david.navarro@intel.com> + +*/ + +#ifndef _LWM2M_CLIENT_H_ +#define _LWM2M_CLIENT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include <time.h> + +#ifdef LWM2M_SERVER_MODE +#ifndef LWM2M_SUPPORT_JSON +#define LWM2M_SUPPORT_JSON +#endif +#endif + +#if defined(LWM2M_BOOTSTRAP) && defined(LWM2M_BOOTSTRAP_SERVER_MODE) +#error "LWM2M_BOOTSTRAP and LWM2M_BOOTSTRAP_SERVER_MODE cannot be defined at the same time!" +#endif + +/* + * Platform abstraction functions to be implemented by the user + */ + +#ifndef LWM2M_MEMORY_TRACE +// Allocate a block of size bytes of memory, returning a pointer to the beginning of the block. +void * lwm2m_malloc(size_t s); +// Deallocate a block of memory previously allocated by lwm2m_malloc() or lwm2m_strdup() +void lwm2m_free(void * p); +// Allocate a memory block, duplicate the string str in it and return a pointer to this new block. +char * lwm2m_strdup(const char * str); +#else +// same functions as above with caller location for debugging purposes +char * lwm2m_trace_strdup(const char * str, const char * file, const char * function, int lineno); +void * lwm2m_trace_malloc(size_t size, const char * file, const char * function, int lineno); +void lwm2m_trace_free(void * mem, const char * file, const char * function, int lineno); + +#define lwm2m_strdup(S) lwm2m_trace_strdup(S, __FILE__, __FUNCTION__, __LINE__) +#define lwm2m_malloc(S) lwm2m_trace_malloc(S, __FILE__, __FUNCTION__, __LINE__) +#define lwm2m_free(M) lwm2m_trace_free(M, __FILE__, __FUNCTION__, __LINE__) +#endif +// Compare at most the n first bytes of s1 and s2, return 0 if they match +int lwm2m_strncmp(const char * s1, const char * s2, size_t n); +// This function must return the number of seconds elapsed since origin. +// The origin (Epoch, system boot, etc...) does not matter as this +// function is used only to determine the elapsed time since the last +// call to it. +// In case of error, this must return a negative value. +// Per POSIX specifications, time_t is a signed integer. +time_t lwm2m_gettime(void); + +#ifdef LWM2M_WITH_LOGS +// Same usage as C89 printf() +void lwm2m_printf(const char * format, ...); +#endif + +// communication layer +#ifdef LWM2M_CLIENT_MODE +// Returns a session handle that MUST uniquely identify a peer. +// secObjInstID: ID of the Securty Object instance to open a connection to +// userData: parameter to lwm2m_init() +void * lwm2m_connect_server(uint16_t secObjInstID, void * userData); +// Close a session created by lwm2m_connect_server() +// sessionH: session handle identifying the peer (opaque to the core) +// userData: parameter to lwm2m_init() +void lwm2m_close_connection(void * sessionH, void * userData); +#endif +// Send data to a peer +// Returns COAP_NO_ERROR or a COAP_NNN error code +// sessionH: session handle identifying the peer (opaque to the core) +// buffer, length: data to send +// userData: parameter to lwm2m_init() +uint8_t lwm2m_buffer_send(void * sessionH, uint8_t * buffer, size_t length, void * userData); +// Compare two session handles +// Returns true if the two sessions identify the same peer. false otherwise. +// userData: parameter to lwm2m_init() +bool lwm2m_session_is_equal(void * session1, void * session2, void * userData); + +/* + * Error code + */ + +#define COAP_NO_ERROR (uint8_t)0x00 +#define COAP_IGNORE (uint8_t)0x01 + +#define COAP_201_CREATED (uint8_t)0x41 +#define COAP_202_DELETED (uint8_t)0x42 +#define COAP_204_CHANGED (uint8_t)0x44 +#define COAP_205_CONTENT (uint8_t)0x45 +#define COAP_231_CONTINUE (uint8_t)0x5F +#define COAP_400_BAD_REQUEST (uint8_t)0x80 +#define COAP_401_UNAUTHORIZED (uint8_t)0x81 +#define COAP_402_BAD_OPTION (uint8_t)0x82 +#define COAP_404_NOT_FOUND (uint8_t)0x84 +#define COAP_405_METHOD_NOT_ALLOWED (uint8_t)0x85 +#define COAP_406_NOT_ACCEPTABLE (uint8_t)0x86 +#define COAP_408_REQ_ENTITY_INCOMPLETE (uint8_t)0x88 +#define COAP_412_PRECONDITION_FAILED (uint8_t)0x8C +#define COAP_413_ENTITY_TOO_LARGE (uint8_t)0x8D +#define COAP_500_INTERNAL_SERVER_ERROR (uint8_t)0xA0 +#define COAP_501_NOT_IMPLEMENTED (uint8_t)0xA1 +#define COAP_503_SERVICE_UNAVAILABLE (uint8_t)0xA3 + +/* + * Standard Object IDs + */ +#define LWM2M_SECURITY_OBJECT_ID 0 +#define LWM2M_SERVER_OBJECT_ID 1 +#define LWM2M_ACL_OBJECT_ID 2 +#define LWM2M_DEVICE_OBJECT_ID 3 +#define LWM2M_CONN_MONITOR_OBJECT_ID 4 +#define LWM2M_FIRMWARE_UPDATE_OBJECT_ID 5 +#define LWM2M_LOCATION_OBJECT_ID 6 +#define LWM2M_CONN_STATS_OBJECT_ID 7 + +/* + * Ressource IDs for the LWM2M Security Object + */ +#define LWM2M_SECURITY_URI_ID 0 +#define LWM2M_SECURITY_BOOTSTRAP_ID 1 +#define LWM2M_SECURITY_SECURITY_ID 2 +#define LWM2M_SECURITY_PUBLIC_KEY_ID 3 +#define LWM2M_SECURITY_SERVER_PUBLIC_KEY_ID 4 +#define LWM2M_SECURITY_SECRET_KEY_ID 5 +#define LWM2M_SECURITY_SMS_SECURITY_ID 6 +#define LWM2M_SECURITY_SMS_KEY_PARAM_ID 7 +#define LWM2M_SECURITY_SMS_SECRET_KEY_ID 8 +#define LWM2M_SECURITY_SMS_SERVER_NUMBER_ID 9 +#define LWM2M_SECURITY_SHORT_SERVER_ID 10 +#define LWM2M_SECURITY_HOLD_OFF_ID 11 +#define LWM2M_SECURITY_BOOTSTRAP_TIMEOUT_ID 12 + +/* + * Ressource IDs for the LWM2M Server Object + */ +#define LWM2M_SERVER_SHORT_ID_ID 0 +#define LWM2M_SERVER_LIFETIME_ID 1 +#define LWM2M_SERVER_MIN_PERIOD_ID 2 +#define LWM2M_SERVER_MAX_PERIOD_ID 3 +#define LWM2M_SERVER_DISABLE_ID 4 +#define LWM2M_SERVER_TIMEOUT_ID 5 +#define LWM2M_SERVER_STORING_ID 6 +#define LWM2M_SERVER_BINDING_ID 7 +#define LWM2M_SERVER_UPDATE_ID 8 + +#define LWM2M_SECURITY_MODE_PRE_SHARED_KEY 0 +#define LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY 1 +#define LWM2M_SECURITY_MODE_CERTIFICATE 2 +#define LWM2M_SECURITY_MODE_NONE 3 + + +/* + * Utility functions for sorted linked list + */ + +typedef struct _lwm2m_list_t +{ + struct _lwm2m_list_t * next; + uint16_t id; +} lwm2m_list_t; + +// defined in list.c +// Add 'node' to the list 'head' and return the new list +lwm2m_list_t * lwm2m_list_add(lwm2m_list_t * head, lwm2m_list_t * node); +// Return the node with ID 'id' from the list 'head' or NULL if not found +lwm2m_list_t * lwm2m_list_find(lwm2m_list_t * head, uint16_t id); +// Remove the node with ID 'id' from the list 'head' and return the new list +lwm2m_list_t * lwm2m_list_remove(lwm2m_list_t * head, uint16_t id, lwm2m_list_t ** nodeP); +// Return the lowest unused ID in the list 'head' +uint16_t lwm2m_list_newId(lwm2m_list_t * head); +// Free a list. Do not use if nodes contain allocated pointers as it calls lwm2m_free on nodes only. +// If the nodes of the list need to do more than just "free()" their instances, don't use lwm2m_list_free(). +void lwm2m_list_free(lwm2m_list_t * head); + +#define LWM2M_LIST_ADD(H,N) lwm2m_list_add((lwm2m_list_t *)H, (lwm2m_list_t *)N); +#define LWM2M_LIST_RM(H,I,N) lwm2m_list_remove((lwm2m_list_t *)H, I, (lwm2m_list_t **)N); +#define LWM2M_LIST_FIND(H,I) lwm2m_list_find((lwm2m_list_t *)H, I) +#define LWM2M_LIST_FREE(H) lwm2m_list_free((lwm2m_list_t *)H) + +/* + * URI + * + * objectId is always set + * instanceId or resourceId are set according to the flag bit-field + * + */ + +#define LWM2M_MAX_ID ((uint16_t)0xFFFF) + +#define LWM2M_URI_FLAG_OBJECT_ID (uint8_t)0x04 +#define LWM2M_URI_FLAG_INSTANCE_ID (uint8_t)0x02 +#define LWM2M_URI_FLAG_RESOURCE_ID (uint8_t)0x01 + +#define LWM2M_URI_IS_SET_INSTANCE(uri) (((uri)->flag & LWM2M_URI_FLAG_INSTANCE_ID) != 0) +#define LWM2M_URI_IS_SET_RESOURCE(uri) (((uri)->flag & LWM2M_URI_FLAG_RESOURCE_ID) != 0) + +typedef struct +{ + uint8_t flag; // indicates which segments are set + uint16_t objectId; + uint16_t instanceId; + uint16_t resourceId; +} lwm2m_uri_t; + + +#define LWM2M_STRING_ID_MAX_LEN 6 + +// Parse an URI in LWM2M format and fill the lwm2m_uri_t. +// Return the number of characters read from buffer or 0 in case of error. +// Valid URIs: /1, /1/, /1/2, /1/2/, /1/2/3 +// Invalid URIs: /, //, //2, /1//, /1//3, /1/2/3/, /1/2/3/4 +int lwm2m_stringToUri(const char * buffer, size_t buffer_len, lwm2m_uri_t * uriP); + +/* + * The lwm2m_data_t is used to store LWM2M resource values in a hierarchical way. + * Depending on the type the value is different: + * - LWM2M_TYPE_OBJECT, LWM2M_TYPE_OBJECT_INSTANCE, LWM2M_TYPE_MULTIPLE_RESOURCE: value.asChildren + * - LWM2M_TYPE_STRING, LWM2M_TYPE_OPAQUE: value.asBuffer + * - LWM2M_TYPE_INTEGER, LWM2M_TYPE_TIME: value.asInteger + * - LWM2M_TYPE_FLOAT: value.asFloat + * - LWM2M_TYPE_BOOLEAN: value.asBoolean + * + * LWM2M_TYPE_STRING is also used when the data is in text format. + */ + +typedef enum +{ + LWM2M_TYPE_UNDEFINED = 0, + LWM2M_TYPE_OBJECT, + LWM2M_TYPE_OBJECT_INSTANCE, + LWM2M_TYPE_MULTIPLE_RESOURCE, + + LWM2M_TYPE_STRING, + LWM2M_TYPE_OPAQUE, + LWM2M_TYPE_INTEGER, + LWM2M_TYPE_FLOAT, + LWM2M_TYPE_BOOLEAN, + + LWM2M_TYPE_OBJECT_LINK +} lwm2m_data_type_t; + +typedef struct _lwm2m_data_t lwm2m_data_t; + +struct _lwm2m_data_t +{ + lwm2m_data_type_t type; + uint16_t id; + union + { + bool asBoolean; + int64_t asInteger; + double asFloat; + struct + { + size_t length; + uint8_t * buffer; + } asBuffer; + struct + { + size_t count; + lwm2m_data_t * array; + } asChildren; + struct + { + uint16_t objectId; + uint16_t objectInstanceId; + } asObjLink; + } value; +}; + +typedef enum +{ + LWM2M_CONTENT_TEXT = 0, // Also used as undefined + LWM2M_CONTENT_LINK = 40, + LWM2M_CONTENT_OPAQUE = 42, + LWM2M_CONTENT_TLV_OLD = 1542, // Keep old value for backward-compatibility + LWM2M_CONTENT_TLV = 11542, + LWM2M_CONTENT_JSON_OLD = 1543, // Keep old value for backward-compatibility + LWM2M_CONTENT_JSON = 11543 +} lwm2m_media_type_t; + +lwm2m_data_t * lwm2m_data_new(int size); +int lwm2m_data_parse(lwm2m_uri_t * uriP, uint8_t * buffer, size_t bufferLen, lwm2m_media_type_t format, lwm2m_data_t ** dataP); +int lwm2m_data_serialize(lwm2m_uri_t * uriP, int size, lwm2m_data_t * dataP, lwm2m_media_type_t * formatP, uint8_t ** bufferP); +void lwm2m_data_free(int size, lwm2m_data_t * dataP); + +void lwm2m_data_encode_string(const char * string, lwm2m_data_t * dataP); +void lwm2m_data_encode_nstring(const char * string, size_t length, lwm2m_data_t * dataP); +void lwm2m_data_encode_opaque(uint8_t * buffer, size_t length, lwm2m_data_t * dataP); +void lwm2m_data_encode_int(int64_t value, lwm2m_data_t * dataP); +int lwm2m_data_decode_int(const lwm2m_data_t * dataP, int64_t * valueP); +void lwm2m_data_encode_float(double value, lwm2m_data_t * dataP); +int lwm2m_data_decode_float(const lwm2m_data_t * dataP, double * valueP); +void lwm2m_data_encode_bool(bool value, lwm2m_data_t * dataP); +int lwm2m_data_decode_bool(const lwm2m_data_t * dataP, bool * valueP); +void lwm2m_data_encode_objlink(uint16_t objectId, uint16_t objectInstanceId, lwm2m_data_t * dataP); +void lwm2m_data_encode_instances(lwm2m_data_t * subDataP, size_t count, lwm2m_data_t * dataP); +void lwm2m_data_include(lwm2m_data_t * subDataP, size_t count, lwm2m_data_t * dataP); + + +/* + * Utility function to parse TLV buffers directly + * + * Returned value: number of bytes parsed + * buffer: buffer to parse + * buffer_len: length in bytes of buffer + * oType: (OUT) type of the parsed TLV record. can be: + * - LWM2M_TYPE_OBJECT + * - LWM2M_TYPE_OBJECT_INSTANCE + * - LWM2M_TYPE_MULTIPLE_RESOURCE + * - LWM2M_TYPE_OPAQUE + * oID: (OUT) ID of the parsed TLV record + * oDataIndex: (OUT) index of the data of the parsed TLV record in the buffer + * oDataLen: (OUT) length of the data of the parsed TLV record + */ + +#define LWM2M_TLV_HEADER_MAX_LENGTH 6 + +int lwm2m_decode_TLV(const uint8_t * buffer, size_t buffer_len, lwm2m_data_type_t * oType, uint16_t * oID, size_t * oDataIndex, size_t * oDataLen); + + +/* + * LWM2M Objects + * + * For the read callback, if *numDataP is not zero, *dataArrayP is pre-allocated + * and contains the list of resources to read. + * + */ + +typedef struct _lwm2m_object_t lwm2m_object_t; + +typedef uint8_t (*lwm2m_read_callback_t) (uint16_t instanceId, int * numDataP, lwm2m_data_t ** dataArrayP, lwm2m_object_t * objectP); +typedef uint8_t (*lwm2m_discover_callback_t) (uint16_t instanceId, int * numDataP, lwm2m_data_t ** dataArrayP, lwm2m_object_t * objectP); +typedef uint8_t (*lwm2m_write_callback_t) (uint16_t instanceId, int numData, lwm2m_data_t * dataArray, lwm2m_object_t * objectP); +typedef uint8_t (*lwm2m_execute_callback_t) (uint16_t instanceId, uint16_t resourceId, uint8_t * buffer, int length, lwm2m_object_t * objectP); +typedef uint8_t (*lwm2m_create_callback_t) (uint16_t instanceId, int numData, lwm2m_data_t * dataArray, lwm2m_object_t * objectP); +typedef uint8_t (*lwm2m_delete_callback_t) (uint16_t instanceId, lwm2m_object_t * objectP); + +struct _lwm2m_object_t +{ + struct _lwm2m_object_t * next; // for internal use only. + uint16_t objID; + lwm2m_list_t * instanceList; + lwm2m_read_callback_t readFunc; + lwm2m_write_callback_t writeFunc; + lwm2m_execute_callback_t executeFunc; + lwm2m_create_callback_t createFunc; + lwm2m_delete_callback_t deleteFunc; + lwm2m_discover_callback_t discoverFunc; + void * userData; +}; + +/* + * LWM2M Servers + * + * Since LWM2M Server Object instances are not accessible to LWM2M servers, + * there is no need to store them as lwm2m_objects_t + */ + +typedef enum +{ + STATE_DEREGISTERED = 0, // not registered or boostrap not started + STATE_REG_PENDING, // registration pending + STATE_REGISTERED, // successfully registered + STATE_REG_FAILED, // last registration failed + STATE_REG_UPDATE_PENDING, // registration update pending + STATE_REG_UPDATE_NEEDED, // registration update required + STATE_REG_FULL_UPDATE_NEEDED, // registration update with objects required + STATE_DEREG_PENDING, // deregistration pending + STATE_BS_HOLD_OFF, // bootstrap hold off time + STATE_BS_INITIATED, // bootstrap request sent + STATE_BS_PENDING, // boostrap ongoing + STATE_BS_FINISHING, // boostrap finish received + STATE_BS_FINISHED, // bootstrap done + STATE_BS_FAILING, // bootstrap error occurred + STATE_BS_FAILED, // bootstrap failed +} lwm2m_status_t; + +typedef enum +{ + BINDING_UNKNOWN = 0, + BINDING_U, // UDP + BINDING_UQ, // UDP queue mode + BINDING_S, // SMS + BINDING_SQ, // SMS queue mode + BINDING_US, // UDP plus SMS + BINDING_UQS // UDP queue mode plus SMS +} lwm2m_binding_t; + +/* + * LWM2M block1 data + * + * Temporary data needed to handle block1 request. + * Currently support only one block1 request by server. + */ +typedef struct _lwm2m_block1_data_ lwm2m_block1_data_t; + +struct _lwm2m_block1_data_ +{ + uint8_t * block1buffer; // data buffer + size_t block1bufferSize; // buffer size + uint16_t lastmid; // mid of the last message received +}; + +typedef struct _lwm2m_server_ +{ + struct _lwm2m_server_ * next; // matches lwm2m_list_t::next + uint16_t secObjInstID; // matches lwm2m_list_t::id + uint16_t shortID; // servers short ID, may be 0 for bootstrap server + time_t lifetime; // lifetime of the registration in sec or 0 if default value (86400 sec), also used as hold off time for bootstrap servers + time_t registration; // date of the last registration in sec or end of client hold off time for bootstrap servers + lwm2m_binding_t binding; // client connection mode with this server + void * sessionH; + lwm2m_status_t status; + char * location; + bool dirty; + lwm2m_block1_data_t * block1Data; // buffer to handle block1 data, should be replace by a list to support several block1 transfer by server. +} lwm2m_server_t; + + +/* + * LWM2M result callback + * + * When used with an observe, if 'data' is not nil, 'status' holds the observe counter. + */ +typedef void (*lwm2m_result_callback_t) (uint16_t clientID, lwm2m_uri_t * uriP, int status, lwm2m_media_type_t format, uint8_t * data, int dataLength, void * userData); + +/* + * LWM2M Observations + * + * Used to store observation of remote clients resources. + * status STATE_REG_PENDING means the observe request was sent to the client but not yet answered. + * status STATE_REGISTERED means the client acknowledged the observe request. + * status STATE_DEREG_PENDING means the user canceled the request before the client answered it. + */ + +typedef struct _lwm2m_observation_ +{ + struct _lwm2m_observation_ * next; // matches lwm2m_list_t::next + uint16_t id; // matches lwm2m_list_t::id + struct _lwm2m_client_ * clientP; + lwm2m_uri_t uri; + lwm2m_status_t status; + lwm2m_result_callback_t callback; + void * userData; +} lwm2m_observation_t; + +/* + * LWM2M Link Attributes + * + * Used for observation parameters. + * + */ + +#define LWM2M_ATTR_FLAG_MIN_PERIOD (uint8_t)0x01 +#define LWM2M_ATTR_FLAG_MAX_PERIOD (uint8_t)0x02 +#define LWM2M_ATTR_FLAG_GREATER_THAN (uint8_t)0x04 +#define LWM2M_ATTR_FLAG_LESS_THAN (uint8_t)0x08 +#define LWM2M_ATTR_FLAG_STEP (uint8_t)0x10 + +typedef struct +{ + uint8_t toSet; + uint8_t toClear; + uint32_t minPeriod; + uint32_t maxPeriod; + double greaterThan; + double lessThan; + double step; +} lwm2m_attributes_t; + +/* + * LWM2M Clients + * + * Be careful not to mix lwm2m_client_object_t used to store list of objects of remote clients + * and lwm2m_object_t describing objects exposed to remote servers. + * + */ + +typedef struct _lwm2m_client_object_ +{ + struct _lwm2m_client_object_ * next; // matches lwm2m_list_t::next + uint16_t id; // matches lwm2m_list_t::id + lwm2m_list_t * instanceList; +} lwm2m_client_object_t; + +typedef struct _lwm2m_client_ +{ + struct _lwm2m_client_ * next; // matches lwm2m_list_t::next + uint16_t internalID; // matches lwm2m_list_t::id + char * name; + lwm2m_binding_t binding; + char * msisdn; + char * altPath; + bool supportJSON; + uint32_t lifetime; + time_t endOfLife; + void * sessionH; + lwm2m_client_object_t * objectList; + lwm2m_observation_t * observationList; +} lwm2m_client_t; + + +/* + * LWM2M transaction + * + * Adaptation of Erbium's coap_transaction_t + */ + +typedef struct _lwm2m_transaction_ lwm2m_transaction_t; + +typedef void (*lwm2m_transaction_callback_t) (lwm2m_transaction_t * transacP, void * message); + +struct _lwm2m_transaction_ +{ + lwm2m_transaction_t * next; // matches lwm2m_list_t::next + uint16_t mID; // matches lwm2m_list_t::id + void * peerH; + uint8_t ack_received; // indicates, that the ACK was received + time_t response_timeout; // timeout to wait for response, if token is used. When 0, use calculated acknowledge timeout. + uint8_t retrans_counter; + time_t retrans_time; + char objStringID[LWM2M_STRING_ID_MAX_LEN]; + char instanceStringID[LWM2M_STRING_ID_MAX_LEN]; + char resourceStringID[LWM2M_STRING_ID_MAX_LEN]; + void * message; + uint16_t buffer_len; + uint8_t * buffer; + lwm2m_transaction_callback_t callback; + void * userData; +}; + +/* + * LWM2M observed resources + */ +typedef struct _lwm2m_watcher_ +{ + struct _lwm2m_watcher_ * next; + + bool active; + bool update; + lwm2m_server_t * server; + lwm2m_attributes_t * parameters; + uint8_t token[8]; + size_t tokenLen; + time_t lastTime; + uint32_t counter; + uint16_t lastMid; + union + { + int64_t asInteger; + double asFloat; + } lastValue; +} lwm2m_watcher_t; + +typedef struct _lwm2m_observed_ +{ + struct _lwm2m_observed_ * next; + + lwm2m_uri_t uri; + lwm2m_watcher_t * watcherList; +} lwm2m_observed_t; + +#ifdef LWM2M_CLIENT_MODE + +typedef enum +{ + STATE_INITIAL = 0, + STATE_BOOTSTRAP_REQUIRED, + STATE_BOOTSTRAPPING, + STATE_REGISTER_REQUIRED, + STATE_REGISTERING, + STATE_READY +} lwm2m_client_state_t; + +#endif +/* + * LWM2M Context + */ + +#ifdef LWM2M_BOOTSTRAP_SERVER_MODE +// In all the following APIs, the session handle MUST uniquely identify a peer. + +// LWM2M bootstrap callback +// When a LWM2M client requests bootstrap information, the callback is called with status COAP_NO_ERROR, uriP is nil and +// name is set. The callback must return a COAP_* error code. COAP_204_CHANGED for success. +// After a lwm2m_bootstrap_delete() or a lwm2m_bootstrap_write(), the callback is called with the status returned by the +// client, the URI of the operation (may be nil) and name is nil. The callback return value is ignored. +typedef int (*lwm2m_bootstrap_callback_t) (void * sessionH, uint8_t status, lwm2m_uri_t * uriP, char * name, void * userData); +#endif + +typedef struct +{ +#ifdef LWM2M_CLIENT_MODE + lwm2m_client_state_t state; + char * endpointName; + char * msisdn; + char * altPath; + lwm2m_server_t * bootstrapServerList; + lwm2m_server_t * serverList; + lwm2m_object_t * objectList; + lwm2m_observed_t * observedList; +#endif +#ifdef LWM2M_SERVER_MODE + lwm2m_client_t * clientList; + lwm2m_result_callback_t monitorCallback; + void * monitorUserData; +#endif +#ifdef LWM2M_BOOTSTRAP_SERVER_MODE + lwm2m_bootstrap_callback_t bootstrapCallback; + void * bootstrapUserData; +#endif + uint16_t nextMID; + lwm2m_transaction_t * transactionList; + void * userData; +} lwm2m_context_t; + + +// initialize a liblwm2m context. +lwm2m_context_t * lwm2m_init(void * userData); +// close a liblwm2m context. +void lwm2m_close(lwm2m_context_t * contextP); + +// perform any required pending operation and adjust timeoutP to the maximal time interval to wait in seconds. +int lwm2m_step(lwm2m_context_t * contextP, time_t * timeoutP); +// dispatch received data to liblwm2m +void lwm2m_handle_packet(lwm2m_context_t * contextP, uint8_t * buffer, int length, void * fromSessionH); + +#ifdef LWM2M_CLIENT_MODE +// configure the client side with the Endpoint Name, binding, MSISDN (can be nil), alternative path +// for objects (can be nil) and a list of objects. +// LWM2M Security Object (ID 0) must be present with either a bootstrap server or a LWM2M server and +// its matching LWM2M Server Object (ID 1) instance +int lwm2m_configure(lwm2m_context_t * contextP, const char * endpointName, const char * msisdn, const char * altPath, uint16_t numObject, lwm2m_object_t * objectList[]); +int lwm2m_add_object(lwm2m_context_t * contextP, lwm2m_object_t * objectP); +int lwm2m_remove_object(lwm2m_context_t * contextP, uint16_t id); + +// send a registration update to the server specified by the server short identifier +// or all if the ID is 0. +// If withObjects is true, the registration update contains the object list. +int lwm2m_update_registration(lwm2m_context_t * contextP, uint16_t shortServerID, bool withObjects); + +void lwm2m_resource_value_changed(lwm2m_context_t * contextP, lwm2m_uri_t * uriP); +#endif + +#ifdef LWM2M_SERVER_MODE +// Clients registration/deregistration monitoring API. +// When a LWM2M client registers, the callback is called with status COAP_201_CREATED. +// When a LWM2M client deregisters, the callback is called with status COAP_202_DELETED. +// clientID is the internal ID of the LWM2M Client. +// The callback's parameters uri, data, dataLength are always NULL. +// The lwm2m_client_t is present in the lwm2m_context_t's clientList when the callback is called. On a deregistration, it deleted when the callback returns. +void lwm2m_set_monitoring_callback(lwm2m_context_t * contextP, lwm2m_result_callback_t callback, void * userData); + +// Device Management APIs +int lwm2m_dm_read(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_result_callback_t callback, void * userData); +int lwm2m_dm_discover(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_result_callback_t callback, void * userData); +int lwm2m_dm_write(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, int length, lwm2m_result_callback_t callback, void * userData); +int lwm2m_dm_write_attributes(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_attributes_t * attrP, lwm2m_result_callback_t callback, void * userData); +int lwm2m_dm_execute(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, int length, lwm2m_result_callback_t callback, void * userData); +int lwm2m_dm_create(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, int length, lwm2m_result_callback_t callback, void * userData); +int lwm2m_dm_delete(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_result_callback_t callback, void * userData); + +// Information Reporting APIs +int lwm2m_observe(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_result_callback_t callback, void * userData); +int lwm2m_observe_cancel(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_result_callback_t callback, void * userData); +#endif + +#ifdef LWM2M_BOOTSTRAP_SERVER_MODE +// Clients bootstrap request monitoring API. +// When a LWM2M client sends a bootstrap request, the callback is called with the client's endpoint name. +void lwm2m_set_bootstrap_callback(lwm2m_context_t * contextP, lwm2m_bootstrap_callback_t callback, void * userData); + +// Boostrap Interface APIs +// if uriP is nil, a "Delete /" is sent to the client +int lwm2m_bootstrap_delete(lwm2m_context_t * contextP, void * sessionH, lwm2m_uri_t * uriP); +int lwm2m_bootstrap_write(lwm2m_context_t * contextP, void * sessionH, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, size_t length); +int lwm2m_bootstrap_finish(lwm2m_context_t * contextP, void * sessionH); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/list.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,125 @@ +/******************************************************************************* + * + * Copyright (c) 2013 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * + *******************************************************************************/ + +#include "internals.h" + + +lwm2m_list_t * lwm2m_list_add(lwm2m_list_t * head, + lwm2m_list_t * node) +{ + lwm2m_list_t * target; + + if (NULL == head) return node; + + if (head->id > node->id) + { + node->next = head; + return node; + } + + target = head; + while (NULL != target->next && target->next->id < node->id) + { + target = target->next; + } + + node->next = target->next; + target->next = node; + + return head; +} + + +lwm2m_list_t * lwm2m_list_find(lwm2m_list_t * head, + uint16_t id) +{ + while (NULL != head && head->id < id) + { + head = head->next; + } + + if (NULL != head && head->id == id) return head; + + return NULL; +} + + +lwm2m_list_t * lwm2m_list_remove(lwm2m_list_t * head, + uint16_t id, + lwm2m_list_t ** nodeP) +{ + lwm2m_list_t * target; + + if (head == NULL) + { + if (nodeP) *nodeP = NULL; + return NULL; + } + + if (head->id == id) + { + if (nodeP) *nodeP = head; + return head->next; + } + + target = head; + while (NULL != target->next && target->next->id < id) + { + target = target->next; + } + + if (NULL != target->next && target->next->id == id) + { + if (nodeP) *nodeP = target->next; + target->next = target->next->next; + } + else + { + if (nodeP) *nodeP = NULL; + } + + return head; +} + +uint16_t lwm2m_list_newId(lwm2m_list_t * head) +{ + uint16_t id; + lwm2m_list_t * target; + + id = 0; + target = head; + + while (target != NULL && id == target->id) + { + id = target->id + 1; + target = target->next; + } + + return id; +} + +void lwm2m_list_free(lwm2m_list_t * head) +{ + if (head != NULL) + { + lwm2m_list_t * nextP; + + nextP = head->next; + lwm2m_free(head); + lwm2m_list_free(nextP); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/management.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,777 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * domedambrosio - Please refer to git log + * Toby Jaffey - Please refer to git log + * Bosch Software Innovations GmbH - Please refer to git log + * Pascal Rieux - Please refer to git log + * + *******************************************************************************/ +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro <david.navarro@intel.com> + +*/ + +#include "internals.h" +#include <stdio.h> + + +#ifdef LWM2M_CLIENT_MODE +static int prv_readAttributes(multi_option_t * query, + lwm2m_attributes_t * attrP) +{ + int64_t intValue; + double floatValue; + + memset(attrP, 0, sizeof(lwm2m_attributes_t)); + + while (query != NULL) + { + if (lwm2m_strncmp((char *)query->data, ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN) == 0) + { + if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_MIN_PERIOD)) return -1; + if (query->len == ATTR_MIN_PERIOD_LEN) return -1; + + if (1 != utils_plainTextToInt64(query->data + ATTR_MIN_PERIOD_LEN, query->len - ATTR_MIN_PERIOD_LEN, &intValue)) return -1; + if (intValue < 0) return -1; + + attrP->toSet |= LWM2M_ATTR_FLAG_MIN_PERIOD; + attrP->minPeriod = intValue; + } + else if (lwm2m_strncmp((char *)query->data, ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN - 1) == 0) + { + if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_MIN_PERIOD)) return -1; + if (query->len != ATTR_MIN_PERIOD_LEN - 1) return -1; + + attrP->toClear |= LWM2M_ATTR_FLAG_MIN_PERIOD; + } + else if (lwm2m_strncmp((char *)query->data, ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN) == 0) + { + if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_MAX_PERIOD)) return -1; + if (query->len == ATTR_MAX_PERIOD_LEN) return -1; + + if (1 != utils_plainTextToInt64(query->data + ATTR_MAX_PERIOD_LEN, query->len - ATTR_MAX_PERIOD_LEN, &intValue)) return -1; + if (intValue < 0) return -1; + + attrP->toSet |= LWM2M_ATTR_FLAG_MAX_PERIOD; + attrP->maxPeriod = intValue; + } + else if (lwm2m_strncmp((char *)query->data, ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN - 1) == 0) + { + if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_MAX_PERIOD)) return -1; + if (query->len != ATTR_MAX_PERIOD_LEN - 1) return -1; + + attrP->toClear |= LWM2M_ATTR_FLAG_MAX_PERIOD; + } + else if (lwm2m_strncmp((char *)query->data, ATTR_GREATER_THAN_STR, ATTR_GREATER_THAN_LEN) == 0) + { + if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_GREATER_THAN)) return -1; + if (query->len == ATTR_GREATER_THAN_LEN) return -1; + + if (1 != utils_plainTextToFloat64(query->data + ATTR_GREATER_THAN_LEN, query->len - ATTR_GREATER_THAN_LEN, &floatValue)) return -1; + + attrP->toSet |= LWM2M_ATTR_FLAG_GREATER_THAN; + attrP->greaterThan = floatValue; + } + else if (lwm2m_strncmp((char *)query->data, ATTR_GREATER_THAN_STR, ATTR_GREATER_THAN_LEN - 1) == 0) + { + if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_GREATER_THAN)) return -1; + if (query->len != ATTR_GREATER_THAN_LEN - 1) return -1; + + attrP->toClear |= LWM2M_ATTR_FLAG_GREATER_THAN; + } + else if (lwm2m_strncmp((char *)query->data, ATTR_LESS_THAN_STR, ATTR_LESS_THAN_LEN) == 0) + { + if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_LESS_THAN)) return -1; + if (query->len == ATTR_LESS_THAN_LEN) return -1; + + if (1 != utils_plainTextToFloat64(query->data + ATTR_LESS_THAN_LEN, query->len - ATTR_LESS_THAN_LEN, &floatValue)) return -1; + + attrP->toSet |= LWM2M_ATTR_FLAG_LESS_THAN; + attrP->lessThan = floatValue; + } + else if (lwm2m_strncmp((char *)query->data, ATTR_LESS_THAN_STR, ATTR_LESS_THAN_LEN - 1) == 0) + { + if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_LESS_THAN)) return -1; + if (query->len != ATTR_LESS_THAN_LEN - 1) return -1; + + attrP->toClear |= LWM2M_ATTR_FLAG_LESS_THAN; + } + else if (lwm2m_strncmp((char *)query->data, ATTR_STEP_STR, ATTR_STEP_LEN) == 0) + { + if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_STEP)) return -1; + if (query->len == ATTR_STEP_LEN) return -1; + + if (1 != utils_plainTextToFloat64(query->data + ATTR_STEP_LEN, query->len - ATTR_STEP_LEN, &floatValue)) return -1; + if (floatValue < 0) return -1; + + attrP->toSet |= LWM2M_ATTR_FLAG_STEP; + attrP->step = floatValue; + } + else if (lwm2m_strncmp((char *)query->data, ATTR_STEP_STR, ATTR_STEP_LEN - 1) == 0) + { + if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_STEP)) return -1; + if (query->len != ATTR_STEP_LEN - 1) return -1; + + attrP->toClear |= LWM2M_ATTR_FLAG_STEP; + } + else return -1; + + query = query->next; + } + + return 0; +} + +coap_status_t dm_handleRequest(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_server_t * serverP, + coap_packet_t * message, + coap_packet_t * response) +{ + coap_status_t result; + lwm2m_media_type_t format; + + LOG_ARG("Code: %02X, server status: %s", message->code, STR_STATUS(serverP->status)); + LOG_URI(uriP); + + if (IS_OPTION(message, COAP_OPTION_CONTENT_TYPE)) + { + format = utils_convertMediaType(message->content_type); + } + else + { + format = LWM2M_CONTENT_TLV; + } + + if (uriP->objectId == LWM2M_SECURITY_OBJECT_ID) + { + return COAP_404_NOT_FOUND; + } + + if (serverP->status != STATE_REGISTERED + && serverP->status != STATE_REG_UPDATE_NEEDED + && serverP->status != STATE_REG_FULL_UPDATE_NEEDED + && serverP->status != STATE_REG_UPDATE_PENDING) + { + return COAP_IGNORE; + } + + // TODO: check ACL + + switch (message->code) + { + case COAP_GET: + { + uint8_t * buffer = NULL; + size_t length = 0; + int res; + + if (IS_OPTION(message, COAP_OPTION_OBSERVE)) + { + lwm2m_data_t * dataP = NULL; + int size = 0; + + result = object_readData(contextP, uriP, &size, &dataP); + if (COAP_205_CONTENT == result) + { + result = observe_handleRequest(contextP, uriP, serverP, size, dataP, message, response); + if (COAP_205_CONTENT == result) + { + res = lwm2m_data_serialize(uriP, size, dataP, &format, &buffer); + if (res < 0) + { + result = COAP_500_INTERNAL_SERVER_ERROR; + } + else + { + length = (size_t)res; + LOG_ARG("Observe Request[/%d/%d/%d]: %.*s\n", uriP->objectId, uriP->instanceId, uriP->resourceId, length, buffer); + } + } + lwm2m_data_free(size, dataP); + } + } + else if (IS_OPTION(message, COAP_OPTION_ACCEPT) + && message->accept_num == 1 + && message->accept[0] == APPLICATION_LINK_FORMAT) + { + format = LWM2M_CONTENT_LINK; + result = object_discover(contextP, uriP, serverP, &buffer, &length); + } + else + { + if (IS_OPTION(message, COAP_OPTION_ACCEPT)) + { + format = utils_convertMediaType(message->accept[0]); + } + + result = object_read(contextP, uriP, &format, &buffer, &length); + } + if (COAP_205_CONTENT == result) + { + coap_set_header_content_type(response, format); + coap_set_payload(response, buffer, length); + // lwm2m_handle_packet will free buffer + } + else + { + lwm2m_free(buffer); + } + } + break; + + case COAP_POST: + { + if (!LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + result = object_create(contextP, uriP, format, message->payload, message->payload_len); + if (result == COAP_201_CREATED) + { + //longest uri is /65535/65535 = 12 + 1 (null) chars + char location_path[13] = ""; + //instanceId expected + if ((uriP->flag & LWM2M_URI_FLAG_INSTANCE_ID) == 0) + { + result = COAP_500_INTERNAL_SERVER_ERROR; + break; + } + + if (sprintf(location_path, "/%d/%d", uriP->objectId, uriP->instanceId) < 0) + { + result = COAP_500_INTERNAL_SERVER_ERROR; + break; + } + coap_set_header_location_path(response, location_path); + + lwm2m_update_registration(contextP, 0, true); + } + } + else if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + result = object_write(contextP, uriP, format, message->payload, message->payload_len); + } + else + { + result = object_execute(contextP, uriP, message->payload, message->payload_len); + } + } + break; + + case COAP_PUT: + { + if (IS_OPTION(message, COAP_OPTION_URI_QUERY)) + { + lwm2m_attributes_t attr; + + if (0 != prv_readAttributes(message->uri_query, &attr)) + { + result = COAP_400_BAD_REQUEST; + } + else + { + result = observe_setParameters(contextP, uriP, serverP, &attr); + } + } + else if (LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + result = object_write(contextP, uriP, format, message->payload, message->payload_len); + } + else + { + result = COAP_400_BAD_REQUEST; + } + } + break; + + case COAP_DELETE: + { + if (!LWM2M_URI_IS_SET_INSTANCE(uriP) || LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + result = COAP_400_BAD_REQUEST; + } + else + { + result = object_delete(contextP, uriP); + if (result == COAP_202_DELETED) + { + lwm2m_update_registration(contextP, 0, true); + } + } + } + break; + + default: + result = COAP_400_BAD_REQUEST; + break; + } + + return result; +} + +#endif + +#ifdef LWM2M_SERVER_MODE + +#define ID_AS_STRING_MAX_LEN 8 + +static void prv_resultCallback(lwm2m_transaction_t * transacP, + void * message) +{ + dm_data_t * dataP = (dm_data_t *)transacP->userData; + + if (message == NULL) + { + dataP->callback(dataP->clientID, + &dataP->uri, + COAP_503_SERVICE_UNAVAILABLE, + LWM2M_CONTENT_TEXT, NULL, 0, + dataP->userData); + } + else + { + coap_packet_t * packet = (coap_packet_t *)message; + + //if packet is a CREATE response and the instanceId was assigned by the client + if (packet->code == COAP_201_CREATED + && packet->location_path != NULL) + { + char * locationString = NULL; + int result = 0; + lwm2m_uri_t locationUri; + + locationString = coap_get_multi_option_as_string(packet->location_path); + if (locationString == NULL) + { + LOG("Error: coap_get_multi_option_as_string() failed for Location_path option in prv_resultCallback()"); + return; + } + + result = lwm2m_stringToUri(locationString, strlen(locationString), &locationUri); + if (result == 0) + { + LOG("Error: lwm2m_stringToUri() failed for Location_path option in prv_resultCallback()"); + lwm2m_free(locationString); + return; + } + + ((dm_data_t*)transacP->userData)->uri.instanceId = locationUri.instanceId; + ((dm_data_t*)transacP->userData)->uri.flag = locationUri.flag; + + lwm2m_free(locationString); + } + + dataP->callback(dataP->clientID, + &dataP->uri, + packet->code, + utils_convertMediaType(packet->content_type), + packet->payload, + packet->payload_len, + dataP->userData); + } + lwm2m_free(dataP); +} + +static int prv_makeOperation(lwm2m_context_t * contextP, + uint16_t clientID, + lwm2m_uri_t * uriP, + coap_method_t method, + lwm2m_media_type_t format, + uint8_t * buffer, + int length, + lwm2m_result_callback_t callback, + void * userData) +{ + lwm2m_client_t * clientP; + lwm2m_transaction_t * transaction; + dm_data_t * dataP; + + clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID); + if (clientP == NULL) return COAP_404_NOT_FOUND; + + transaction = transaction_new(clientP->sessionH, method, clientP->altPath, uriP, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + if (method == COAP_GET) + { + coap_set_header_accept(transaction->message, format); + } + else if (buffer != NULL) + { + coap_set_header_content_type(transaction->message, format); + // TODO: Take care of fragmentation + coap_set_payload(transaction->message, buffer, length); + } + + if (callback != NULL) + { + dataP = (dm_data_t *)lwm2m_malloc(sizeof(dm_data_t)); + if (dataP == NULL) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t)); + dataP->clientID = clientP->internalID; + dataP->callback = callback; + dataP->userData = userData; + + transaction->callback = prv_resultCallback; + transaction->userData = (void *)dataP; + } + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + + return transaction_send(contextP, transaction); +} + +int lwm2m_dm_read(lwm2m_context_t * contextP, + uint16_t clientID, + lwm2m_uri_t * uriP, + lwm2m_result_callback_t callback, + void * userData) +{ + lwm2m_client_t * clientP; + lwm2m_media_type_t format; + + LOG_ARG("clientID: %d", clientID); + LOG_URI(uriP); + + clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID); + if (clientP == NULL) return COAP_404_NOT_FOUND; + + if (clientP->supportJSON == true) + { + format = LWM2M_CONTENT_JSON; + } + else + { + format = LWM2M_CONTENT_TLV; + } + + return prv_makeOperation(contextP, clientID, uriP, + COAP_GET, + format, + NULL, 0, + callback, userData); +} + +int lwm2m_dm_write(lwm2m_context_t * contextP, + uint16_t clientID, + lwm2m_uri_t * uriP, + lwm2m_media_type_t format, + uint8_t * buffer, + int length, + lwm2m_result_callback_t callback, + void * userData) +{ + LOG_ARG("clientID: %d, format: %s, length: %d", clientID, STR_MEDIA_TYPE(format), length); + LOG_URI(uriP); + if (!LWM2M_URI_IS_SET_INSTANCE(uriP) + || length == 0) + { + return COAP_400_BAD_REQUEST; + } + + if (LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + return prv_makeOperation(contextP, clientID, uriP, + COAP_PUT, + format, buffer, length, + callback, userData); + } + else + { + return prv_makeOperation(contextP, clientID, uriP, + COAP_POST, + format, buffer, length, + callback, userData); + } +} + +int lwm2m_dm_execute(lwm2m_context_t * contextP, + uint16_t clientID, + lwm2m_uri_t * uriP, + lwm2m_media_type_t format, + uint8_t * buffer, + int length, + lwm2m_result_callback_t callback, + void * userData) +{ + LOG_ARG("clientID: %d, format: %s, length: %d", clientID, STR_MEDIA_TYPE(format), length); + LOG_URI(uriP); + if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + return COAP_400_BAD_REQUEST; + } + + return prv_makeOperation(contextP, clientID, uriP, + COAP_POST, + format, buffer, length, + callback, userData); +} + +int lwm2m_dm_create(lwm2m_context_t * contextP, + uint16_t clientID, + lwm2m_uri_t * uriP, + lwm2m_media_type_t format, + uint8_t * buffer, + int length, + lwm2m_result_callback_t callback, + void * userData) +{ + LOG_ARG("clientID: %d, format: %s, length: %d", clientID, STR_MEDIA_TYPE(format), length); + LOG_URI(uriP); + + if (LWM2M_URI_IS_SET_INSTANCE(uriP) + || length == 0) + { + return COAP_400_BAD_REQUEST; + } + + return prv_makeOperation(contextP, clientID, uriP, + COAP_POST, + format, buffer, length, + callback, userData); +} + +int lwm2m_dm_delete(lwm2m_context_t * contextP, + uint16_t clientID, + lwm2m_uri_t * uriP, + lwm2m_result_callback_t callback, + void * userData) +{ + LOG_ARG("clientID: %d", clientID); + LOG_URI(uriP); + if (!LWM2M_URI_IS_SET_INSTANCE(uriP) + || LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + return COAP_400_BAD_REQUEST; + } + + return prv_makeOperation(contextP, clientID, uriP, + COAP_DELETE, + LWM2M_CONTENT_TEXT, NULL, 0, + callback, userData); +} + +int lwm2m_dm_write_attributes(lwm2m_context_t * contextP, + uint16_t clientID, + lwm2m_uri_t * uriP, + lwm2m_attributes_t * attrP, + lwm2m_result_callback_t callback, + void * userData) +{ +#define _PRV_BUFFER_SIZE 32 + lwm2m_client_t * clientP; + lwm2m_transaction_t * transaction; + coap_packet_t * coap_pkt; + uint8_t buffer[_PRV_BUFFER_SIZE]; + size_t length; + + LOG_ARG("clientID: %d", clientID); + LOG_URI(uriP); + if (attrP == NULL) return COAP_400_BAD_REQUEST; + + if (0 != (attrP->toSet & attrP->toClear)) return COAP_400_BAD_REQUEST; + if (0 != (attrP->toSet & ATTR_FLAG_NUMERIC) && !LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST; + if (ATTR_FLAG_NUMERIC == (attrP->toSet & ATTR_FLAG_NUMERIC) + && (attrP->lessThan + 2 * attrP->step >= attrP->greaterThan)) return COAP_400_BAD_REQUEST; + + clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID); + if (clientP == NULL) return COAP_404_NOT_FOUND; + + transaction = transaction_new(clientP->sessionH, COAP_PUT, clientP->altPath, uriP, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + if (callback != NULL) + { + dm_data_t * dataP; + + dataP = (dm_data_t *)lwm2m_malloc(sizeof(dm_data_t)); + if (dataP == NULL) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t)); + dataP->clientID = clientP->internalID; + dataP->callback = callback; + dataP->userData = userData; + + transaction->callback = prv_resultCallback; + transaction->userData = (void *)dataP; + } + + coap_pkt = (coap_packet_t *)transaction->message; + free_multi_option(coap_pkt->uri_query); + if (attrP->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD) + { + memcpy(buffer, ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN); + length = utils_intToText(attrP->minPeriod, buffer + ATTR_MIN_PERIOD_LEN, _PRV_BUFFER_SIZE - ATTR_MIN_PERIOD_LEN); + if (length == 0) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_MIN_PERIOD_LEN + length, 0); + SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); + } + if (attrP->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD) + { + memcpy(buffer, ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN); + length = utils_intToText(attrP->maxPeriod, buffer + ATTR_MAX_PERIOD_LEN, _PRV_BUFFER_SIZE - ATTR_MAX_PERIOD_LEN); + if (length == 0) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_MAX_PERIOD_LEN + length, 0); + SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); + } + if (attrP->toSet & LWM2M_ATTR_FLAG_GREATER_THAN) + { + memcpy(buffer, ATTR_GREATER_THAN_STR, ATTR_GREATER_THAN_LEN); + length = utils_floatToText(attrP->greaterThan, buffer + ATTR_GREATER_THAN_LEN, _PRV_BUFFER_SIZE - ATTR_GREATER_THAN_LEN); + if (length == 0) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_GREATER_THAN_LEN + length, 0); + SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); + } + if (attrP->toSet & LWM2M_ATTR_FLAG_LESS_THAN) + { + memcpy(buffer, ATTR_LESS_THAN_STR, ATTR_LESS_THAN_LEN); + length = utils_floatToText(attrP->lessThan, buffer + ATTR_LESS_THAN_LEN, _PRV_BUFFER_SIZE - ATTR_LESS_THAN_LEN); + if (length == 0) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_LESS_THAN_LEN + length, 0); + SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); + } + if (attrP->toSet & LWM2M_ATTR_FLAG_STEP) + { + memcpy(buffer, ATTR_STEP_STR, ATTR_STEP_LEN); + length = utils_floatToText(attrP->step, buffer + ATTR_STEP_LEN, _PRV_BUFFER_SIZE - ATTR_STEP_LEN); + if (length == 0) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_STEP_LEN + length, 0); + SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); + } + if (attrP->toClear & LWM2M_ATTR_FLAG_MIN_PERIOD) + { + coap_add_multi_option(&(coap_pkt->uri_query), ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN -1, 0); + SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); + } + if (attrP->toClear & LWM2M_ATTR_FLAG_MAX_PERIOD) + { + coap_add_multi_option(&(coap_pkt->uri_query), ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN - 1, 0); + SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); + } + if (attrP->toClear & LWM2M_ATTR_FLAG_GREATER_THAN) + { + coap_add_multi_option(&(coap_pkt->uri_query), ATTR_GREATER_THAN_STR, ATTR_GREATER_THAN_LEN - 1, 0); + SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); + } + if (attrP->toClear & LWM2M_ATTR_FLAG_LESS_THAN) + { + coap_add_multi_option(&(coap_pkt->uri_query), ATTR_LESS_THAN_STR, ATTR_LESS_THAN_LEN - 1, 0); + SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); + } + if (attrP->toClear & LWM2M_ATTR_FLAG_STEP) + { + coap_add_multi_option(&(coap_pkt->uri_query), ATTR_STEP_STR, ATTR_STEP_LEN - 1, 0); + SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); + } + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + + return transaction_send(contextP, transaction); +} + +int lwm2m_dm_discover(lwm2m_context_t * contextP, + uint16_t clientID, + lwm2m_uri_t * uriP, + lwm2m_result_callback_t callback, + void * userData) +{ + lwm2m_client_t * clientP; + lwm2m_transaction_t * transaction; + dm_data_t * dataP; + + LOG_ARG("clientID: %d", clientID); + LOG_URI(uriP); + clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID); + if (clientP == NULL) return COAP_404_NOT_FOUND; + + transaction = transaction_new(clientP->sessionH, COAP_GET, clientP->altPath, uriP, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + coap_set_header_accept(transaction->message, LWM2M_CONTENT_LINK); + + if (callback != NULL) + { + dataP = (dm_data_t *)lwm2m_malloc(sizeof(dm_data_t)); + if (dataP == NULL) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t)); + dataP->clientID = clientP->internalID; + dataP->callback = callback; + dataP->userData = userData; + + transaction->callback = prv_resultCallback; + transaction->userData = (void *)dataP; + } + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + + return transaction_send(contextP, transaction); +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/objects.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,830 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * Fabien Fleutot - Please refer to git log + * Toby Jaffey - Please refer to git log + * Benjamin Cabé - Please refer to git log + * Bosch Software Innovations GmbH - Please refer to git log + * Pascal Rieux - Please refer to git log + * + *******************************************************************************/ + +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro <david.navarro@intel.com> + +*/ +#include "internals.h" + +#ifdef LWM2M_CLIENT_MODE + + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + + +uint8_t object_checkReadable(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP) +{ + coap_status_t result; + lwm2m_object_t * targetP; + lwm2m_data_t * dataP = NULL; + int size; + + LOG_URI(uriP); + targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId); + if (NULL == targetP) return COAP_404_NOT_FOUND; + if (NULL == targetP->readFunc) return COAP_405_METHOD_NOT_ALLOWED; + + if (!LWM2M_URI_IS_SET_INSTANCE(uriP)) return COAP_205_CONTENT; + + if (NULL == lwm2m_list_find(targetP->instanceList, uriP->instanceId)) return COAP_404_NOT_FOUND; + + if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_205_CONTENT; + + size = 1; + dataP = lwm2m_data_new(1); + if (dataP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + dataP->id = uriP->resourceId; + + result = targetP->readFunc(uriP->instanceId, &size, &dataP, targetP); + lwm2m_data_free(1, dataP); + + return result; +} + +uint8_t object_checkNumeric(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP) +{ + coap_status_t result; + lwm2m_object_t * targetP; + lwm2m_data_t * dataP = NULL; + int size; + + LOG_URI(uriP); + if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_405_METHOD_NOT_ALLOWED; + + targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId); + if (NULL == targetP) return COAP_404_NOT_FOUND; + if (NULL == targetP->readFunc) return COAP_405_METHOD_NOT_ALLOWED; + + size = 1; + dataP = lwm2m_data_new(1); + if (dataP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + dataP->id = uriP->resourceId; + + result = targetP->readFunc(uriP->instanceId, &size, &dataP, targetP); + if (result == COAP_205_CONTENT) + { + switch (dataP->type) + { + case LWM2M_TYPE_INTEGER: + case LWM2M_TYPE_FLOAT: + break; + default: + result = COAP_405_METHOD_NOT_ALLOWED; + } + } + + lwm2m_data_free(1, dataP); + + return result; +} + +coap_status_t object_readData(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + int * sizeP, + lwm2m_data_t ** dataP) +{ + coap_status_t result; + lwm2m_object_t * targetP; + + LOG_URI(uriP); + targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId); + if (NULL == targetP) return COAP_404_NOT_FOUND; + if (NULL == targetP->readFunc) return COAP_405_METHOD_NOT_ALLOWED; + + if (LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + if (NULL == lwm2m_list_find(targetP->instanceList, uriP->instanceId)) return COAP_404_NOT_FOUND; + + // single instance read + if (LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + *sizeP = 1; + *dataP = lwm2m_data_new(*sizeP); + if (*dataP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + (*dataP)->id = uriP->resourceId; + } + + result = targetP->readFunc(uriP->instanceId, sizeP, dataP, targetP); + } + else + { + // multiple object instances read + lwm2m_list_t * instanceP; + int i; + + result = COAP_205_CONTENT; + + *sizeP = 0; + for (instanceP = targetP->instanceList; instanceP != NULL ; instanceP = instanceP->next) + { + (*sizeP)++; + } + + if (*sizeP == 0) + { + *dataP = NULL; + } + else + { + *dataP = lwm2m_data_new(*sizeP); + if (*dataP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + instanceP = targetP->instanceList; + i = 0; + while (instanceP != NULL && result == COAP_205_CONTENT) + { + result = targetP->readFunc(instanceP->id, (int*)&((*dataP)[i].value.asChildren.count), &((*dataP)[i].value.asChildren.array), targetP); + (*dataP)[i].type = LWM2M_TYPE_OBJECT_INSTANCE; + (*dataP)[i].id = instanceP->id; + i++; + instanceP = instanceP->next; + } + } + } + + LOG_ARG("result: %u.%2u, size: %d", (result & 0xFF) >> 5, (result & 0x1F), *sizeP); + return result; +} + +coap_status_t object_read(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_media_type_t * formatP, + uint8_t ** bufferP, + size_t * lengthP) +{ + coap_status_t result; + lwm2m_data_t * dataP = NULL; + int size = 0; + int res; + + LOG_URI(uriP); + result = object_readData(contextP, uriP, &size, &dataP); + + if (result == COAP_205_CONTENT) + { + res = lwm2m_data_serialize(uriP, size, dataP, formatP, bufferP); + if (res < 0) + { + result = COAP_500_INTERNAL_SERVER_ERROR; + } + else + { + *lengthP = (size_t)res; + } + } + lwm2m_data_free(size, dataP); + + LOG_ARG("result: %u.%2u, length: %d", (result & 0xFF) >> 5, (result & 0x1F), *lengthP); + + return result; +} + +coap_status_t object_write(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_media_type_t format, + uint8_t * buffer, + size_t length) +{ + coap_status_t result = NO_ERROR; + lwm2m_object_t * targetP; + lwm2m_data_t * dataP = NULL; + int size = 0; + + LOG_URI(uriP); + targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId); + if (NULL == targetP) + { + result = COAP_404_NOT_FOUND; + } + else if (NULL == targetP->writeFunc) + { + result = COAP_405_METHOD_NOT_ALLOWED; + } + else + { + size = lwm2m_data_parse(uriP, buffer, length, format, &dataP); + if (size == 0) + { + result = COAP_406_NOT_ACCEPTABLE; + } + } + if (result == NO_ERROR) + { + result = targetP->writeFunc(uriP->instanceId, size, dataP, targetP); + lwm2m_data_free(size, dataP); + } + + LOG_ARG("result: %u.%2u", (result & 0xFF) >> 5, (result & 0x1F)); + + return result; +} + +coap_status_t object_execute(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + uint8_t * buffer, + size_t length) +{ + lwm2m_object_t * targetP; + + LOG_URI(uriP); + targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId); + if (NULL == targetP) return COAP_404_NOT_FOUND; + if (NULL == targetP->executeFunc) return COAP_405_METHOD_NOT_ALLOWED; + if (NULL == lwm2m_list_find(targetP->instanceList, uriP->instanceId)) return COAP_404_NOT_FOUND; + + return targetP->executeFunc(uriP->instanceId, uriP->resourceId, buffer, length, targetP); +} + +coap_status_t object_create(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_media_type_t format, + uint8_t * buffer, + size_t length) +{ + lwm2m_object_t * targetP; + lwm2m_data_t * dataP = NULL; + int size = 0; + uint8_t result; + + LOG_URI(uriP); + + if (length == 0 || buffer == 0) + { + return COAP_400_BAD_REQUEST; + } + + targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId); + if (NULL == targetP) return COAP_404_NOT_FOUND; + if (NULL == targetP->createFunc) return COAP_405_METHOD_NOT_ALLOWED; + + size = lwm2m_data_parse(uriP, buffer, length, format, &dataP); + if (size <= 0) return COAP_400_BAD_REQUEST; + + switch (dataP[0].type) + { + case LWM2M_TYPE_OBJECT: + result = COAP_400_BAD_REQUEST; + goto exit; + + case LWM2M_TYPE_OBJECT_INSTANCE: + if (size != 1) + { + result = COAP_400_BAD_REQUEST; + goto exit; + } + if (NULL != lwm2m_list_find(targetP->instanceList, dataP[0].id)) + { + // Instance already exists + result = COAP_406_NOT_ACCEPTABLE; + goto exit; + } + result = targetP->createFunc(dataP[0].id, dataP[0].value.asChildren.count, dataP[0].value.asChildren.array, targetP); + uriP->instanceId = dataP[0].id; + uriP->flag |= LWM2M_URI_FLAG_INSTANCE_ID; + break; + + default: + if (!LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + uriP->instanceId = lwm2m_list_newId(targetP->instanceList); + uriP->flag |= LWM2M_URI_FLAG_INSTANCE_ID; + } + result = targetP->createFunc(uriP->instanceId, size, dataP, targetP); + break; + } + +exit: + lwm2m_data_free(size, dataP); + + LOG_ARG("result: %u.%2u", (result & 0xFF) >> 5, (result & 0x1F)); + + return result; +} + +coap_status_t object_delete(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP) +{ + lwm2m_object_t * objectP; + coap_status_t result; + + LOG_URI(uriP); + objectP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId); + if (NULL == objectP) return COAP_404_NOT_FOUND; + if (NULL == objectP->deleteFunc) return COAP_405_METHOD_NOT_ALLOWED; + + LOG("Entering"); + + if (LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + result = objectP->deleteFunc(uriP->instanceId, objectP); + } + else + { + lwm2m_list_t * instanceP; + + result = COAP_202_DELETED; + instanceP = objectP->instanceList; + while (NULL != instanceP + && result == COAP_202_DELETED) + { + result = objectP->deleteFunc(instanceP->id, objectP); + instanceP = objectP->instanceList; + } + } + + LOG_ARG("result: %u.%2u", (result & 0xFF) >> 5, (result & 0x1F)); + + return result; +} + +coap_status_t object_discover(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_server_t * serverP, + uint8_t ** bufferP, + size_t * lengthP) +{ + coap_status_t result; + lwm2m_object_t * targetP; + lwm2m_data_t * dataP = NULL; + int size = 0; + + LOG_URI(uriP); + targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId); + if (NULL == targetP) return COAP_404_NOT_FOUND; + if (NULL == targetP->discoverFunc) return COAP_501_NOT_IMPLEMENTED; + + if (LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + if (NULL == lwm2m_list_find(targetP->instanceList, uriP->instanceId)) return COAP_404_NOT_FOUND; + + // single instance read + if (LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + size = 1; + dataP = lwm2m_data_new(size); + if (dataP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + dataP->id = uriP->resourceId; + } + + result = targetP->discoverFunc(uriP->instanceId, &size, &dataP, targetP); + } + else + { + // multiple object instances read + lwm2m_list_t * instanceP; + int i; + + result = COAP_205_CONTENT; + + size = 0; + for (instanceP = targetP->instanceList; instanceP != NULL ; instanceP = instanceP->next) + { + size++; + } + + if (size != 0) + { + dataP = lwm2m_data_new(size); + if (dataP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + instanceP = targetP->instanceList; + i = 0; + while (instanceP != NULL && result == COAP_205_CONTENT) + { + result = targetP->discoverFunc(instanceP->id, (int*)&(dataP[i].value.asChildren.count), &(dataP[i].value.asChildren.array), targetP); + dataP[i].type = LWM2M_TYPE_OBJECT_INSTANCE; + dataP[i].id = instanceP->id; + i++; + instanceP = instanceP->next; + } + } + } + + if (result == COAP_205_CONTENT) + { + int len; + + len = discover_serialize(contextP, uriP, serverP, size, dataP, bufferP); + if (len <= 0) result = COAP_500_INTERNAL_SERVER_ERROR; + else *lengthP = len; + } + lwm2m_data_free(size, dataP); + + LOG_ARG("result: %u.%2u", (result & 0xFF) >> 5, (result & 0x1F)); + + return result; +} + +bool object_isInstanceNew(lwm2m_context_t * contextP, + uint16_t objectId, + uint16_t instanceId) +{ + lwm2m_object_t * targetP; + + LOG("Entering"); + targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, objectId); + if (targetP != NULL) + { + if (NULL != lwm2m_list_find(targetP->instanceList, instanceId)) + { + return false; + } + } + + return true; +} + +static int prv_getObjectTemplate(uint8_t * buffer, + size_t length, + uint16_t id) +{ + int index; + int result; + + if (length < REG_OBJECT_MIN_LEN) return -1; + + buffer[0] = '<'; + buffer[1] = '/'; + index = 2; + + result = utils_intCopy((char *)buffer + index, length - index, id); + if (result < 0) return -1; + index += result; + + if (length - index < REG_OBJECT_MIN_LEN - 3) return -1; + buffer[index] = '/'; + index++; + + return index; +} + +int object_getRegisterPayload(lwm2m_context_t * contextP, + uint8_t * buffer, + size_t bufferLen) +{ + size_t index; + int result; + lwm2m_object_t * objectP; + + LOG("Entering"); + // index can not be greater than bufferLen + index = 0; + + result = utils_stringCopy((char *)buffer, bufferLen, REG_START); + if (result < 0) return 0; + index += result; + + if ((contextP->altPath != NULL) + && (contextP->altPath[0] != 0)) + { + result = utils_stringCopy((char *)buffer + index, bufferLen - index, contextP->altPath); + } + else + { + result = utils_stringCopy((char *)buffer + index, bufferLen - index, REG_DEFAULT_PATH); + } + if (result < 0) return 0; + index += result; + + result = utils_stringCopy((char *)buffer + index, bufferLen - index, REG_LWM2M_RESOURCE_TYPE); + if (result < 0) return 0; + index += result; + + for (objectP = contextP->objectList; objectP != NULL; objectP = objectP->next) + { + size_t start; + size_t length; + + if (objectP->objID == LWM2M_SECURITY_OBJECT_ID) continue; + + start = index; + result = prv_getObjectTemplate(buffer + index, bufferLen - index, objectP->objID); + if (result < 0) return 0; + length = result; + index += length; + + if (objectP->instanceList == NULL) + { + index--; + result = utils_stringCopy((char *)buffer + index, bufferLen - index, REG_PATH_END); + if (result < 0) return 0; + index += result; + } + else + { + lwm2m_list_t * targetP; + for (targetP = objectP->instanceList ; targetP != NULL ; targetP = targetP->next) + { + if (bufferLen - index <= length) return 0; + + if (index != start + length) + { + memcpy(buffer + index, buffer + start, length); + index += length; + } + + result = utils_intCopy((char *)buffer + index, bufferLen - index, targetP->id); + if (result < 0) return 0; + index += result; + + result = utils_stringCopy((char *)buffer + index, bufferLen - index, REG_PATH_END); + if (result < 0) return 0; + index += result; + } + } + } + + if (index > 0) + { + index = index - 1; // remove trailing ',' + } + + buffer[index] = 0; + + return index; +} + +static lwm2m_list_t * prv_findServerInstance(lwm2m_object_t * objectP, + uint16_t shortID) +{ + lwm2m_list_t * instanceP; + + instanceP = objectP->instanceList; + while (NULL != instanceP) + { + int64_t value; + lwm2m_data_t * dataP; + int size; + + size = 1; + dataP = lwm2m_data_new(size); + if (dataP == NULL) return NULL; + dataP->id = LWM2M_SERVER_SHORT_ID_ID; + + if (objectP->readFunc(instanceP->id, &size, &dataP, objectP) != COAP_205_CONTENT) + { + lwm2m_data_free(size, dataP); + return NULL; + } + + if (1 == lwm2m_data_decode_int(dataP, &value)) + { + if (value == shortID) + { + lwm2m_data_free(size, dataP); + break; + } + } + lwm2m_data_free(size, dataP); + instanceP = instanceP->next; + } + + return instanceP; +} + +static int prv_getMandatoryInfo(lwm2m_object_t * objectP, + uint16_t instanceID, + lwm2m_server_t * targetP) +{ + lwm2m_data_t * dataP; + int size; + int64_t value; + + size = 2; + dataP = lwm2m_data_new(size); + if (dataP == NULL) return -1; + dataP[0].id = LWM2M_SERVER_LIFETIME_ID; + dataP[1].id = LWM2M_SERVER_BINDING_ID; + + if (objectP->readFunc(instanceID, &size, &dataP, objectP) != COAP_205_CONTENT) + { + lwm2m_data_free(size, dataP); + return -1; + } + + if (0 == lwm2m_data_decode_int(dataP, &value) + || value < 0 || value >0xFFFFFFFF) // This is an implementation limit + { + lwm2m_data_free(size, dataP); + return -1; + } + targetP->lifetime = value; + + targetP->binding = utils_stringToBinding(dataP[1].value.asBuffer.buffer, dataP[1].value.asBuffer.length); + + lwm2m_data_free(size, dataP); + + if (targetP->binding == BINDING_UNKNOWN) + { + return -1; + } + + return 0; +} + +int object_getServers(lwm2m_context_t * contextP) +{ + lwm2m_object_t * objectP; + lwm2m_object_t * securityObjP = NULL; + lwm2m_object_t * serverObjP = NULL; + lwm2m_list_t * securityInstP; // instanceID of the server in the LWM2M Security Object + + LOG("Entering"); + + for (objectP = contextP->objectList; objectP != NULL; objectP = objectP->next) + { + if (objectP->objID == LWM2M_SECURITY_OBJECT_ID) + { + securityObjP = objectP; + } + else if (objectP->objID == LWM2M_SERVER_OBJECT_ID) + { + serverObjP = objectP; + } + } + + if (NULL == securityObjP) return -1; + + securityInstP = securityObjP->instanceList; + while (securityInstP != NULL) + { + if (LWM2M_LIST_FIND(contextP->bootstrapServerList, securityInstP->id) == NULL + && LWM2M_LIST_FIND(contextP->serverList, securityInstP->id) == NULL) + { + // This server is new. eg created by last bootstrap + + lwm2m_data_t * dataP; + int size; + lwm2m_server_t * targetP; + bool isBootstrap; + int64_t value = 0; + + size = 3; + dataP = lwm2m_data_new(size); + if (dataP == NULL) return -1; + dataP[0].id = LWM2M_SECURITY_BOOTSTRAP_ID; + dataP[1].id = LWM2M_SECURITY_SHORT_SERVER_ID; + dataP[2].id = LWM2M_SECURITY_HOLD_OFF_ID; + + if (securityObjP->readFunc(securityInstP->id, &size, &dataP, securityObjP) != COAP_205_CONTENT) + { + lwm2m_data_free(size, dataP); + return -1; + } + + targetP = (lwm2m_server_t *)lwm2m_malloc(sizeof(lwm2m_server_t)); + if (targetP == NULL) { + lwm2m_data_free(size, dataP); + return -1; + } + memset(targetP, 0, sizeof(lwm2m_server_t)); + targetP->secObjInstID = securityInstP->id; + + if (0 == lwm2m_data_decode_bool(dataP + 0, &isBootstrap)) + { + lwm2m_free(targetP); + lwm2m_data_free(size, dataP); + return -1; + } + + if (0 == lwm2m_data_decode_int(dataP + 1, &value) + || value < (isBootstrap ? 0 : 1) || value > 0xFFFF) // 0 is forbidden as a Short Server ID + { + lwm2m_free(targetP); + lwm2m_data_free(size, dataP); + return -1; + } + targetP->shortID = value; + + if (isBootstrap == true) + { + if (0 == lwm2m_data_decode_int(dataP + 2, &value) + || value < 0 || value > 0xFFFFFFFF) // This is an implementation limit + { + lwm2m_free(targetP); + lwm2m_data_free(size, dataP); + return -1; + } + // lifetime of a bootstrap server is set to ClientHoldOffTime + targetP->lifetime = value; + + contextP->bootstrapServerList = (lwm2m_server_t*)LWM2M_LIST_ADD(contextP->bootstrapServerList, targetP); + } + else + { + lwm2m_list_t * serverInstP; // instanceID of the server in the LWM2M Server Object + + serverInstP = prv_findServerInstance(serverObjP, targetP->shortID); + if (serverInstP == NULL) + { + lwm2m_free(targetP); + lwm2m_data_free(size, dataP); + return -1; + } + if (0 != prv_getMandatoryInfo(serverObjP, serverInstP->id, targetP)) + { + lwm2m_free(targetP); + lwm2m_data_free(size, dataP); + return -1; + } + targetP->status = STATE_DEREGISTERED; + contextP->serverList = (lwm2m_server_t*)LWM2M_LIST_ADD(contextP->serverList, targetP); + } + lwm2m_data_free(size, dataP); + } + securityInstP = securityInstP->next; + } + + return 0; +} + +coap_status_t object_createInstance(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_data_t * dataP) +{ + lwm2m_object_t * targetP; + + LOG_URI(uriP); + targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId); + if (NULL == targetP) return COAP_404_NOT_FOUND; + + if (NULL == targetP->createFunc) + { + return COAP_405_METHOD_NOT_ALLOWED; + } + + return targetP->createFunc(lwm2m_list_newId(targetP->instanceList), dataP->value.asChildren.count, dataP->value.asChildren.array, targetP); +} + +coap_status_t object_writeInstance(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_data_t * dataP) +{ + lwm2m_object_t * targetP; + + LOG_URI(uriP); + targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId); + if (NULL == targetP) return COAP_404_NOT_FOUND; + + if (NULL == targetP->writeFunc) + { + return COAP_405_METHOD_NOT_ALLOWED; + } + + return targetP->writeFunc(dataP->id, dataP->value.asChildren.count, dataP->value.asChildren.array, targetP); +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/observe.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,995 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * Toby Jaffey - Please refer to git log + * Bosch Software Innovations GmbH - Please refer to git log + * + *******************************************************************************/ + +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro <david.navarro@intel.com> + +*/ + +#include "internals.h" +#include <stdio.h> + + +#ifdef LWM2M_CLIENT_MODE +static lwm2m_observed_t * prv_findObserved(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP) +{ + lwm2m_observed_t * targetP; + + targetP = contextP->observedList; + while (targetP != NULL + && (targetP->uri.objectId != uriP->objectId + || targetP->uri.flag != uriP->flag + || (LWM2M_URI_IS_SET_INSTANCE(uriP) && targetP->uri.instanceId != uriP->instanceId) + || (LWM2M_URI_IS_SET_RESOURCE(uriP) && targetP->uri.resourceId != uriP->resourceId))) + { + targetP = targetP->next; + } + + return targetP; +} + +static void prv_unlinkObserved(lwm2m_context_t * contextP, + lwm2m_observed_t * observedP) +{ + if (contextP->observedList == observedP) + { + contextP->observedList = contextP->observedList->next; + } + else + { + lwm2m_observed_t * parentP; + + parentP = contextP->observedList; + while (parentP->next != NULL + && parentP->next != observedP) + { + parentP = parentP->next; + } + if (parentP->next != NULL) + { + parentP->next = parentP->next->next; + } + } +} + +static lwm2m_watcher_t * prv_findWatcher(lwm2m_observed_t * observedP, + lwm2m_server_t * serverP) +{ + lwm2m_watcher_t * targetP; + + targetP = observedP->watcherList; + while (targetP != NULL + && targetP->server != serverP) + { + targetP = targetP->next; + } + + return targetP; +} + +static lwm2m_watcher_t * prv_getWatcher(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_server_t * serverP) +{ + lwm2m_observed_t * observedP; + bool allocatedObserver; + lwm2m_watcher_t * watcherP; + + allocatedObserver = false; + + observedP = prv_findObserved(contextP, uriP); + if (observedP == NULL) + { + observedP = (lwm2m_observed_t *)lwm2m_malloc(sizeof(lwm2m_observed_t)); + if (observedP == NULL) return NULL; + allocatedObserver = true; + memset(observedP, 0, sizeof(lwm2m_observed_t)); + memcpy(&(observedP->uri), uriP, sizeof(lwm2m_uri_t)); + observedP->next = contextP->observedList; + contextP->observedList = observedP; + } + + watcherP = prv_findWatcher(observedP, serverP); + if (watcherP == NULL) + { + watcherP = (lwm2m_watcher_t *)lwm2m_malloc(sizeof(lwm2m_watcher_t)); + if (watcherP == NULL) + { + if (allocatedObserver == true) + { + lwm2m_free(observedP); + } + return NULL; + } + memset(watcherP, 0, sizeof(lwm2m_watcher_t)); + watcherP->active = false; + watcherP->server = serverP; + watcherP->next = observedP->watcherList; + observedP->watcherList = watcherP; + } + + return watcherP; +} + +coap_status_t observe_handleRequest(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_server_t * serverP, + int size, + lwm2m_data_t * dataP, + coap_packet_t * message, + coap_packet_t * response) +{ + lwm2m_watcher_t * watcherP; + uint32_t count; + + LOG_ARG("Code: %02X, server status: %s", message->code, STR_STATUS(serverP->status)); + LOG_URI(uriP); + + coap_get_header_observe(message, &count); + + switch (count) + { + case 0: + if (!LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST; + if (message->token_len == 0) return COAP_400_BAD_REQUEST; + + watcherP = prv_getWatcher(contextP, uriP, serverP); + if (watcherP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + watcherP->tokenLen = message->token_len; + memcpy(watcherP->token, message->token, message->token_len); + watcherP->active = true; + watcherP->lastTime = lwm2m_gettime(); + + if (LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + switch (dataP->type) + { + case LWM2M_TYPE_INTEGER: + if (1 != lwm2m_data_decode_int(dataP, &(watcherP->lastValue.asInteger))) return COAP_500_INTERNAL_SERVER_ERROR; + break; + case LWM2M_TYPE_FLOAT: + if (1 != lwm2m_data_decode_float(dataP, &(watcherP->lastValue.asFloat))) return COAP_500_INTERNAL_SERVER_ERROR; + break; + default: + break; + } + } + + coap_set_header_observe(response, watcherP->counter++); + + return COAP_205_CONTENT; + + case 1: + // cancellation + observe_cancel(contextP, LWM2M_MAX_ID, serverP->sessionH); + return COAP_205_CONTENT; + + default: + return COAP_400_BAD_REQUEST; + } +} + +void observe_cancel(lwm2m_context_t * contextP, + uint16_t mid, + void * fromSessionH) +{ + lwm2m_observed_t * observedP; + + LOG_ARG("mid: %d", mid); + + for (observedP = contextP->observedList; + observedP != NULL; + observedP = observedP->next) + { + lwm2m_watcher_t * targetP = NULL; + + if ((LWM2M_MAX_ID == mid || observedP->watcherList->lastMid == mid) + && lwm2m_session_is_equal(observedP->watcherList->server->sessionH, fromSessionH, contextP->userData)) + { + targetP = observedP->watcherList; + observedP->watcherList = observedP->watcherList->next; + } + else + { + lwm2m_watcher_t * parentP; + + parentP = observedP->watcherList; + while (parentP->next != NULL + && (parentP->next->lastMid != mid + || lwm2m_session_is_equal(parentP->next->server->sessionH, fromSessionH, contextP->userData))) + { + parentP = parentP->next; + } + if (parentP->next != NULL) + { + targetP = parentP->next; + parentP->next = parentP->next->next; + } + } + if (targetP != NULL) + { + lwm2m_free(targetP); + if (observedP->watcherList == NULL) + { + prv_unlinkObserved(contextP, observedP); + lwm2m_free(observedP); + } + return; + } + } +} + +coap_status_t observe_setParameters(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + lwm2m_server_t * serverP, + lwm2m_attributes_t * attrP) +{ + uint8_t result; + lwm2m_watcher_t * watcherP; + + LOG_URI(uriP); + LOG_ARG("toSet: %08X, toClear: %08X, minPeriod: %d, maxPeriod: %d, greaterThan: %f, lessThan: %f, step: %f", + attrP->toSet, attrP->toClear, attrP->minPeriod, attrP->maxPeriod, attrP->greaterThan, attrP->lessThan, attrP->step); + + if (!LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST; + + result = object_checkReadable(contextP, uriP); + if (COAP_205_CONTENT != result) return result; + + if (0 != (attrP->toSet & ATTR_FLAG_NUMERIC)) + { + result = object_checkNumeric(contextP, uriP); + if (COAP_205_CONTENT != result) return result; + } + + watcherP = prv_getWatcher(contextP, uriP, serverP); + if (watcherP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + // Check rule “lt” value + 2*”stp” values < “gt” value + if ((((attrP->toSet | (watcherP->parameters?watcherP->parameters->toSet:0)) & ~attrP->toClear) & ATTR_FLAG_NUMERIC) == ATTR_FLAG_NUMERIC) + { + float gt; + float lt; + float stp; + + if (0 != (attrP->toSet & LWM2M_ATTR_FLAG_GREATER_THAN)) + { + gt = attrP->greaterThan; + } + else + { + gt = watcherP->parameters->greaterThan; + } + if (0 != (attrP->toSet & LWM2M_ATTR_FLAG_LESS_THAN)) + { + lt = attrP->lessThan; + } + else + { + lt = watcherP->parameters->lessThan; + } + if (0 != (attrP->toSet & LWM2M_ATTR_FLAG_STEP)) + { + stp = attrP->step; + } + else + { + stp = watcherP->parameters->step; + } + + if (lt + (2 * stp) >= gt) return COAP_400_BAD_REQUEST; + } + + if (watcherP->parameters == NULL) + { + if (attrP->toSet != 0) + { + watcherP->parameters = (lwm2m_attributes_t *)lwm2m_malloc(sizeof(lwm2m_attributes_t)); + if (watcherP->parameters == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + memcpy(watcherP->parameters, attrP, sizeof(lwm2m_attributes_t)); + } + } + else + { + watcherP->parameters->toSet &= ~attrP->toClear; + if (attrP->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD) + { + watcherP->parameters->minPeriod = attrP->minPeriod; + } + if (attrP->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD) + { + watcherP->parameters->maxPeriod = attrP->maxPeriod; + } + if (attrP->toSet & LWM2M_ATTR_FLAG_GREATER_THAN) + { + watcherP->parameters->greaterThan = attrP->greaterThan; + } + if (attrP->toSet & LWM2M_ATTR_FLAG_LESS_THAN) + { + watcherP->parameters->lessThan = attrP->lessThan; + } + if (attrP->toSet & LWM2M_ATTR_FLAG_STEP) + { + watcherP->parameters->step = attrP->step; + } + } + + LOG_ARG("Final toSet: %08X, minPeriod: %d, maxPeriod: %d, greaterThan: %f, lessThan: %f, step: %f", + watcherP->parameters->toSet, watcherP->parameters->minPeriod, watcherP->parameters->maxPeriod, watcherP->parameters->greaterThan, watcherP->parameters->lessThan, watcherP->parameters->step); + + return COAP_204_CHANGED; +} + +lwm2m_observed_t * observe_findByUri(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP) +{ + lwm2m_observed_t * targetP; + + LOG_URI(uriP); + targetP = contextP->observedList; + while (targetP != NULL) + { + if (targetP->uri.objectId == uriP->objectId) + { + if ((!LWM2M_URI_IS_SET_INSTANCE(uriP) && !LWM2M_URI_IS_SET_INSTANCE(&(targetP->uri))) + || (LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_INSTANCE(&(targetP->uri)) && (uriP->instanceId == targetP->uri.instanceId))) + { + if ((!LWM2M_URI_IS_SET_RESOURCE(uriP) && !LWM2M_URI_IS_SET_RESOURCE(&(targetP->uri))) + || (LWM2M_URI_IS_SET_RESOURCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(&(targetP->uri)) && (uriP->resourceId == targetP->uri.resourceId))) + { + LOG_ARG("Found one with%s observers.", targetP->watcherList ? "" : " no"); + LOG_URI(&(targetP->uri)); + return targetP; + } + } + } + targetP = targetP->next; + } + + LOG("Found nothing"); + return NULL; +} + +void lwm2m_resource_value_changed(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP) +{ + lwm2m_observed_t * targetP; + + LOG_URI(uriP); + targetP = contextP->observedList; + while (targetP != NULL) + { + if (targetP->uri.objectId == uriP->objectId) + { + if (!LWM2M_URI_IS_SET_INSTANCE(uriP) + || (targetP->uri.flag & LWM2M_URI_FLAG_INSTANCE_ID) == 0 + || uriP->instanceId == targetP->uri.instanceId) + { + if (!LWM2M_URI_IS_SET_RESOURCE(uriP) + || (targetP->uri.flag & LWM2M_URI_FLAG_RESOURCE_ID) == 0 + || uriP->resourceId == targetP->uri.resourceId) + { + lwm2m_watcher_t * watcherP; + + LOG("Found an observation"); + LOG_URI(&(targetP->uri)); + + for (watcherP = targetP->watcherList ; watcherP != NULL ; watcherP = watcherP->next) + { + if (watcherP->active == true) + { + LOG("Tagging a watcher"); + watcherP->update = true; + } + } + } + } + } + targetP = targetP->next; + } +} + +void observe_step(lwm2m_context_t * contextP, + time_t currentTime, + time_t * timeoutP) +{ + lwm2m_observed_t * targetP; + + LOG("Entering"); + for (targetP = contextP->observedList ; targetP != NULL ; targetP = targetP->next) + { + lwm2m_watcher_t * watcherP; + uint8_t * buffer = NULL; + size_t length = 0; + lwm2m_data_t * dataP = NULL; + int size = 0; + double floatValue = 0; + int64_t integerValue = 0; + bool storeValue = false; + lwm2m_media_type_t format = LWM2M_CONTENT_TEXT; + coap_packet_t message[1]; + time_t interval; + + LOG_URI(&(targetP->uri)); + if (LWM2M_URI_IS_SET_RESOURCE(&targetP->uri)) + { + if (COAP_205_CONTENT != object_readData(contextP, &targetP->uri, &size, &dataP)) continue; + switch (dataP->type) + { + case LWM2M_TYPE_INTEGER: + if (1 != lwm2m_data_decode_int(dataP, &integerValue)) continue; + storeValue = true; + break; + case LWM2M_TYPE_FLOAT: + if (1 != lwm2m_data_decode_float(dataP, &floatValue)) continue; + storeValue = true; + break; + default: + break; + } + } + for (watcherP = targetP->watcherList ; watcherP != NULL ; watcherP = watcherP->next) + { + if (watcherP->active == true) + { + bool notify = false; + + if (watcherP->update == true) + { + // value changed, should we notify the server ? + + if (watcherP->parameters == NULL || watcherP->parameters->toSet == 0) + { + // no conditions + notify = true; + LOG("Notify with no conditions"); + LOG_URI(&(targetP->uri)); + } + + if (notify == false + && watcherP->parameters != NULL + && (watcherP->parameters->toSet & ATTR_FLAG_NUMERIC) != 0) + { + if ((watcherP->parameters->toSet & LWM2M_ATTR_FLAG_LESS_THAN) != 0) + { + LOG("Checking lower treshold"); + // Did we cross the lower treshold ? + switch (dataP->type) + { + case LWM2M_TYPE_INTEGER: + if ((integerValue <= watcherP->parameters->lessThan + && watcherP->lastValue.asInteger > watcherP->parameters->lessThan) + || (integerValue >= watcherP->parameters->lessThan + && watcherP->lastValue.asInteger < watcherP->parameters->lessThan)) + { + LOG("Notify on lower treshold crossing"); + notify = true; + } + break; + case LWM2M_TYPE_FLOAT: + if ((floatValue <= watcherP->parameters->lessThan + && watcherP->lastValue.asFloat > watcherP->parameters->lessThan) + || (floatValue >= watcherP->parameters->lessThan + && watcherP->lastValue.asFloat < watcherP->parameters->lessThan)) + { + LOG("Notify on lower treshold crossing"); + notify = true; + } + break; + default: + break; + } + } + if ((watcherP->parameters->toSet & LWM2M_ATTR_FLAG_GREATER_THAN) != 0) + { + LOG("Checking upper treshold"); + // Did we cross the upper treshold ? + switch (dataP->type) + { + case LWM2M_TYPE_INTEGER: + if ((integerValue <= watcherP->parameters->greaterThan + && watcherP->lastValue.asInteger > watcherP->parameters->greaterThan) + || (integerValue >= watcherP->parameters->greaterThan + && watcherP->lastValue.asInteger < watcherP->parameters->greaterThan)) + { + LOG("Notify on lower upper crossing"); + notify = true; + } + break; + case LWM2M_TYPE_FLOAT: + if ((floatValue <= watcherP->parameters->greaterThan + && watcherP->lastValue.asFloat > watcherP->parameters->greaterThan) + || (floatValue >= watcherP->parameters->greaterThan + && watcherP->lastValue.asFloat < watcherP->parameters->greaterThan)) + { + LOG("Notify on lower upper crossing"); + notify = true; + } + break; + default: + break; + } + } + if ((watcherP->parameters->toSet & LWM2M_ATTR_FLAG_STEP) != 0) + { + LOG("Checking step"); + + switch (dataP->type) + { + case LWM2M_TYPE_INTEGER: + { + int64_t diff; + + diff = integerValue - watcherP->lastValue.asInteger; + if ((diff < 0 && (0 - diff) >= watcherP->parameters->step) + || (diff >= 0 && diff >= watcherP->parameters->step)) + { + LOG("Notify on step condition"); + notify = true; + } + } + break; + case LWM2M_TYPE_FLOAT: + { + double diff; + + diff = floatValue - watcherP->lastValue.asFloat; + if ((diff < 0 && (0 - diff) >= watcherP->parameters->step) + || (diff >= 0 && diff >= watcherP->parameters->step)) + { + LOG("Notify on step condition"); + notify = true; + } + } + break; + default: + break; + } + } + } + + if (watcherP->parameters != NULL + && (watcherP->parameters->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD) != 0) + { + LOG_ARG("Checking minimal period (%d s)", watcherP->parameters->minPeriod); + + if (watcherP->lastTime + watcherP->parameters->minPeriod > currentTime) + { + // Minimum Period did not elapse yet + interval = watcherP->lastTime + watcherP->parameters->minPeriod - currentTime; + if (*timeoutP > interval) *timeoutP = interval; + notify = false; + } + else + { + LOG("Notify on minimal period"); + notify = true; + } + } + } + + // Is the Maximum Period reached ? + if (notify == false + && watcherP->parameters != NULL + && (watcherP->parameters->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD) != 0) + { + LOG_ARG("Checking maximal period (%d s)", watcherP->parameters->minPeriod); + + if (watcherP->lastTime + watcherP->parameters->maxPeriod <= currentTime) + { + LOG("Notify on maximal period"); + notify = true; + } + } + + if (notify == true) + { + if (buffer == NULL) + { + if (dataP != NULL) + { + int res; + + res = lwm2m_data_serialize(&targetP->uri, size, dataP, &format, &buffer); + if (res < 0) + { + break; + } + else + { + length = (size_t)res; + } + + } + else + { + if (COAP_205_CONTENT != object_read(contextP, &targetP->uri, &format, &buffer, &length)) + { + buffer = NULL; + break; + } + } + coap_init_message(message, COAP_TYPE_NON, COAP_205_CONTENT, 0); + coap_set_header_content_type(message, format); + coap_set_payload(message, buffer, length); + } + watcherP->lastTime = currentTime; + watcherP->lastMid = contextP->nextMID++; + message->mid = watcherP->lastMid; + coap_set_header_token(message, watcherP->token, watcherP->tokenLen); + coap_set_header_observe(message, watcherP->counter++); + (void)message_send(contextP, message, watcherP->server->sessionH); + watcherP->update = false; + } + + // Store this value + if (notify == true && storeValue == true) + { + switch (dataP->type) + { + case LWM2M_TYPE_INTEGER: + watcherP->lastValue.asInteger = integerValue; + break; + case LWM2M_TYPE_FLOAT: + watcherP->lastValue.asFloat = floatValue; + break; + default: + break; + } + } + + if (watcherP->parameters != NULL && (watcherP->parameters->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD) != 0) + { + // update timers + interval = watcherP->lastTime + watcherP->parameters->maxPeriod - currentTime; + if (*timeoutP > interval) *timeoutP = interval; + } + } + } + if (dataP != NULL) lwm2m_data_free(size, dataP); + if (buffer != NULL) lwm2m_free(buffer); + } +} + +#endif + +#ifdef LWM2M_SERVER_MODE + +typedef struct +{ + lwm2m_observation_t * observationP; + lwm2m_result_callback_t callbackP; + void * userDataP; +} cancellation_data_t; + +static lwm2m_observation_t * prv_findObservationByURI(lwm2m_client_t * clientP, + lwm2m_uri_t * uriP) +{ + lwm2m_observation_t * targetP; + + targetP = clientP->observationList; + while (targetP != NULL) + { + if (targetP->uri.objectId == uriP->objectId + && targetP->uri.flag == uriP->flag + && targetP->uri.instanceId == uriP->instanceId + && targetP->uri.resourceId == uriP->resourceId) + { + return targetP; + } + + targetP = targetP->next; + } + + return targetP; +} + +void observe_remove(lwm2m_observation_t * observationP) +{ + LOG("Entering"); + observationP->clientP->observationList = (lwm2m_observation_t *) LWM2M_LIST_RM(observationP->clientP->observationList, observationP->id, NULL); + lwm2m_free(observationP); +} + +static void prv_obsRequestCallback(lwm2m_transaction_t * transacP, + void * message) +{ + lwm2m_observation_t * observationP = (lwm2m_observation_t *)transacP->userData; + coap_packet_t * packet = (coap_packet_t *)message; + uint8_t code; + + switch (observationP->status) + { + case STATE_DEREG_PENDING: + // Observation was canceled by the user. + observe_remove(observationP); + return; + + case STATE_REG_PENDING: + observationP->status = STATE_REGISTERED; + break; + + default: + break; + } + + if (message == NULL) + { + code = COAP_503_SERVICE_UNAVAILABLE; + } + else if (packet->code == COAP_205_CONTENT + && !IS_OPTION(packet, COAP_OPTION_OBSERVE)) + { + code = COAP_405_METHOD_NOT_ALLOWED; + } + else + { + code = packet->code; + } + + if (code != COAP_205_CONTENT) + { + observationP->callback(observationP->clientP->internalID, + &observationP->uri, + code, + LWM2M_CONTENT_TEXT, NULL, 0, + observationP->userData); + observe_remove(observationP); + } + else + { + observationP->callback(observationP->clientP->internalID, + &observationP->uri, + 0, + packet->content_type, packet->payload, packet->payload_len, + observationP->userData); + } +} + + +static void prv_obsCancelRequestCallback(lwm2m_transaction_t * transacP, + void * message) +{ + cancellation_data_t * cancelP = (cancellation_data_t *)transacP->userData; + coap_packet_t * packet = (coap_packet_t *)message; + uint8_t code; + + if (message == NULL) + { + code = COAP_503_SERVICE_UNAVAILABLE; + } + else + { + code = packet->code; + } + + if (code != COAP_205_CONTENT) + { + cancelP->callbackP(cancelP->observationP->clientP->internalID, + &cancelP->observationP->uri, + code, + LWM2M_CONTENT_TEXT, NULL, 0, + cancelP->userDataP); + } + else + { + cancelP->callbackP(cancelP->observationP->clientP->internalID, + &cancelP->observationP->uri, + 0, + packet->content_type, packet->payload, packet->payload_len, + cancelP->userDataP); + } + + observe_remove(cancelP->observationP); + + lwm2m_free(cancelP); +} + + +int lwm2m_observe(lwm2m_context_t * contextP, + uint16_t clientID, + lwm2m_uri_t * uriP, + lwm2m_result_callback_t callback, + void * userData) +{ + lwm2m_client_t * clientP; + lwm2m_transaction_t * transactionP; + lwm2m_observation_t * observationP; + uint8_t token[4]; + + LOG_ARG("clientID: %d", clientID); + LOG_URI(uriP); + + if (!LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST; + + clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID); + if (clientP == NULL) return COAP_404_NOT_FOUND; + + observationP = (lwm2m_observation_t *)lwm2m_malloc(sizeof(lwm2m_observation_t)); + if (observationP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + memset(observationP, 0, sizeof(lwm2m_observation_t)); + + observationP->id = lwm2m_list_newId((lwm2m_list_t *)clientP->observationList); + memcpy(&observationP->uri, uriP, sizeof(lwm2m_uri_t)); + observationP->clientP = clientP; + observationP->status = STATE_REG_PENDING; + observationP->callback = callback; + observationP->userData = userData; + + token[0] = clientP->internalID >> 8; + token[1] = clientP->internalID & 0xFF; + token[2] = observationP->id >> 8; + token[3] = observationP->id & 0xFF; + + transactionP = transaction_new(clientP->sessionH, COAP_GET, clientP->altPath, uriP, contextP->nextMID++, 4, token); + if (transactionP == NULL) + { + lwm2m_free(observationP); + return COAP_500_INTERNAL_SERVER_ERROR; + } + + observationP->clientP->observationList = (lwm2m_observation_t *)LWM2M_LIST_ADD(observationP->clientP->observationList, observationP); + + coap_set_header_observe(transactionP->message, 0); + coap_set_header_token(transactionP->message, token, sizeof(token)); + + transactionP->callback = prv_obsRequestCallback; + transactionP->userData = (void *)observationP; + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transactionP); + + return transaction_send(contextP, transactionP); +} + +int lwm2m_observe_cancel(lwm2m_context_t * contextP, + uint16_t clientID, + lwm2m_uri_t * uriP, + lwm2m_result_callback_t callback, + void * userData) +{ + lwm2m_client_t * clientP; + lwm2m_observation_t * observationP; + + LOG_ARG("clientID: %d", clientID); + LOG_URI(uriP); + + clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID); + if (clientP == NULL) return COAP_404_NOT_FOUND; + + observationP = prv_findObservationByURI(clientP, uriP); + if (observationP == NULL) return COAP_404_NOT_FOUND; + + switch (observationP->status) + { + case STATE_REGISTERED: + { + lwm2m_transaction_t * transactionP; + cancellation_data_t * cancelP; + + transactionP = transaction_new(clientP->sessionH, COAP_GET, clientP->altPath, uriP, contextP->nextMID++, 0, NULL); + if (transactionP == NULL) + { + return COAP_500_INTERNAL_SERVER_ERROR; + } + cancelP = (cancellation_data_t *)lwm2m_malloc(sizeof(cancellation_data_t)); + if (cancelP == NULL) + { + lwm2m_free(transactionP); + return COAP_500_INTERNAL_SERVER_ERROR; + } + + coap_set_header_observe(transactionP->message, 1); + + cancelP->observationP = observationP; + cancelP->callbackP = callback; + cancelP->userDataP = userData; + + transactionP->callback = prv_obsCancelRequestCallback; + transactionP->userData = (void *)cancelP; + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transactionP); + + return transaction_send(contextP, transactionP); + } + + case STATE_REG_PENDING: + observationP->status = STATE_DEREG_PENDING; + break; + + default: + // Should not happen + break; + } + + return COAP_NO_ERROR; +} + +bool observe_handleNotify(lwm2m_context_t * contextP, + void * fromSessionH, + coap_packet_t * message, + coap_packet_t * response) +{ + uint8_t * tokenP; + int token_len; + uint16_t clientID; + uint16_t obsID; + lwm2m_client_t * clientP; + lwm2m_observation_t * observationP; + uint32_t count; + + LOG("Entering"); + token_len = coap_get_header_token(message, (const uint8_t **)&tokenP); + if (token_len != sizeof(uint32_t)) return false; + + if (1 != coap_get_header_observe(message, &count)) return false; + + clientID = (tokenP[0] << 8) | tokenP[1]; + obsID = (tokenP[2] << 8) | tokenP[3]; + + clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID); + if (clientP == NULL) return false; + + observationP = (lwm2m_observation_t *)lwm2m_list_find((lwm2m_list_t *)clientP->observationList, obsID); + if (observationP == NULL) + { + coap_init_message(response, COAP_TYPE_RST, 0, message->mid); + message_send(contextP, response, fromSessionH); + } + else + { + if (message->type == COAP_TYPE_CON ) { + coap_init_message(response, COAP_TYPE_ACK, 0, message->mid); + message_send(contextP, response, fromSessionH); + } + observationP->callback(clientID, + &observationP->uri, + (int)count, + message->content_type, message->payload, message->payload_len, + observationP->userData); + } + return true; +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/packet.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,443 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * domedambrosio - Please refer to git log + * Fabien Fleutot - Please refer to git log + * Fabien Fleutot - Please refer to git log + * Simon Bernard - Please refer to git log + * Toby Jaffey - Please refer to git log + * Pascal Rieux - Please refer to git log + * Bosch Software Innovations GmbH - Please refer to git log + * + *******************************************************************************/ + +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro <david.navarro@intel.com> + +*/ + +/* +Contains code snippets which are: + + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + +*/ + + +#include "internals.h" + +#include <stdlib.h> +#include <string.h> + +#include <stdio.h> + + +static void handle_reset(lwm2m_context_t * contextP, + void * fromSessionH, + coap_packet_t * message) +{ +#ifdef LWM2M_CLIENT_MODE + LOG("Entering"); + observe_cancel(contextP, message->mid, fromSessionH); +#endif +} + +static coap_status_t handle_request(lwm2m_context_t * contextP, + void * fromSessionH, + coap_packet_t * message, + coap_packet_t * response) +{ + lwm2m_uri_t * uriP; + coap_status_t result = COAP_IGNORE; + + LOG("Entering"); + +#ifdef LWM2M_CLIENT_MODE + uriP = uri_decode(contextP->altPath, message->uri_path); +#else + uriP = uri_decode(NULL, message->uri_path); +#endif + + if (uriP == NULL) return COAP_400_BAD_REQUEST; + + switch(uriP->flag & LWM2M_URI_MASK_TYPE) + { +#ifdef LWM2M_CLIENT_MODE + case LWM2M_URI_FLAG_DM: + { + lwm2m_server_t * serverP; + + serverP = utils_findServer(contextP, fromSessionH); + if (serverP != NULL) + { + result = dm_handleRequest(contextP, uriP, serverP, message, response); + } +#ifdef LWM2M_BOOTSTRAP + else + { + serverP = utils_findBootstrapServer(contextP, fromSessionH); + if (serverP != NULL) + { + result = bootstrap_handleCommand(contextP, uriP, serverP, message, response); + } + } +#endif + } + break; + +#ifdef LWM2M_BOOTSTRAP + case LWM2M_URI_FLAG_DELETE_ALL: + if (COAP_DELETE != message->code) + { + result = COAP_400_BAD_REQUEST; + } + else + { + result = bootstrap_handleDeleteAll(contextP, fromSessionH); + } + break; + + case LWM2M_URI_FLAG_BOOTSTRAP: + if (message->code == COAP_POST) + { + result = bootstrap_handleFinish(contextP, fromSessionH); + } + break; +#endif +#endif + +#ifdef LWM2M_SERVER_MODE + case LWM2M_URI_FLAG_REGISTRATION: + result = registration_handleRequest(contextP, uriP, fromSessionH, message, response); + break; +#endif +#ifdef LWM2M_BOOTSTRAP_SERVER_MODE + case LWM2M_URI_FLAG_BOOTSTRAP: + result = bootstrap_handleRequest(contextP, uriP, fromSessionH, message, response); + break; +#endif + default: + result = COAP_IGNORE; + break; + } + + coap_set_status_code(response, result); + + if (COAP_IGNORE < result && result < COAP_400_BAD_REQUEST) + { + result = NO_ERROR; + } + + lwm2m_free(uriP); + return result; +} + +/* This function is an adaptation of function coap_receive() from Erbium's er-coap-13-engine.c. + * Erbium is Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + */ +void lwm2m_handle_packet(lwm2m_context_t * contextP, + uint8_t * buffer, + int length, + void * fromSessionH) +{ + coap_status_t coap_error_code = NO_ERROR; + static coap_packet_t message[1]; + static coap_packet_t response[1]; + + LOG("Entering"); + coap_error_code = coap_parse_message(message, buffer, (uint16_t)length); + if (coap_error_code == NO_ERROR) + { + LOG_ARG("Parsed: ver %u, type %u, tkl %u, code %u.%.2u, mid %u, Content type: %d", + message->version, message->type, message->token_len, message->code >> 5, message->code & 0x1F, message->mid, message->content_type); + LOG_ARG("Payload: %.*s", message->payload_len, message->payload); + if (message->code >= COAP_GET && message->code <= COAP_DELETE) + { + uint32_t block_num = 0; + uint16_t block_size = REST_MAX_CHUNK_SIZE; + uint32_t block_offset = 0; + int64_t new_offset = 0; + + /* prepare response */ + if (message->type == COAP_TYPE_CON) + { + /* Reliable CON requests are answered with an ACK. */ + coap_init_message(response, COAP_TYPE_ACK, COAP_205_CONTENT, message->mid); + } + else + { + /* Unreliable NON requests are answered with a NON as well. */ + coap_init_message(response, COAP_TYPE_NON, COAP_205_CONTENT, contextP->nextMID++); + } + + /* mirror token */ + if (message->token_len) + { + coap_set_header_token(response, message->token, message->token_len); + } + + /* get offset for blockwise transfers */ + if (coap_get_header_block2(message, &block_num, NULL, &block_size, &block_offset)) + { + LOG_ARG("Blockwise: block request %u (%u/%u) @ %u bytes", block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset); + block_size = MIN(block_size, REST_MAX_CHUNK_SIZE); + new_offset = block_offset; + } + + /* handle block1 option */ + if (IS_OPTION(message, COAP_OPTION_BLOCK1)) + { +#ifdef LWM2M_CLIENT_MODE + // get server + lwm2m_server_t * serverP; + serverP = utils_findServer(contextP, fromSessionH); +#ifdef LWM2M_BOOTSTRAP + if (serverP == NULL) + { + serverP = utils_findBootstrapServer(contextP, fromSessionH); + } +#endif + if (serverP == NULL) + { + coap_error_code = COAP_500_INTERNAL_SERVER_ERROR; + } + else + { + uint32_t block1_num; + uint8_t block1_more; + uint16_t block1_size; + uint8_t * complete_buffer = NULL; + size_t complete_buffer_size; + + // parse block1 header + coap_get_header_block1(message, &block1_num, &block1_more, &block1_size, NULL); + LOG_ARG("Blockwise: block1 request NUM %u (SZX %u/ SZX Max%u) MORE %u", block1_num, block1_size, REST_MAX_CHUNK_SIZE, block1_more); + + // handle block 1 + coap_error_code = coap_block1_handler(&serverP->block1Data, message->mid, message->payload, message->payload_len, block1_size, block1_num, block1_more, &complete_buffer, &complete_buffer_size); + + // if payload is complete, replace it in the coap message. + if (coap_error_code == NO_ERROR) + { + message->payload = complete_buffer; + message->payload_len = complete_buffer_size; + } + else if (coap_error_code == COAP_231_CONTINUE) + { + block1_size = MIN(block1_size, REST_MAX_CHUNK_SIZE); + coap_set_header_block1(response,block1_num, block1_more,block1_size); + } + } +#else + coap_error_code = COAP_501_NOT_IMPLEMENTED; +#endif + } + if (coap_error_code == NO_ERROR) + { + coap_error_code = handle_request(contextP, fromSessionH, message, response); + } + if (coap_error_code==NO_ERROR) + { + if ( IS_OPTION(message, COAP_OPTION_BLOCK2) ) + { + /* unchanged new_offset indicates that resource is unaware of blockwise transfer */ + if (new_offset==block_offset) + { + LOG_ARG("Blockwise: unaware resource with payload length %u/%u", response->payload_len, block_size); + if (block_offset >= response->payload_len) + { + LOG("handle_incoming_data(): block_offset >= response->payload_len"); + + response->code = COAP_402_BAD_OPTION; + coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */ + } + else + { + coap_set_header_block2(response, block_num, response->payload_len - block_offset > block_size, block_size); + coap_set_payload(response, response->payload+block_offset, MIN(response->payload_len - block_offset, block_size)); + } /* if (valid offset) */ + } + else + { + /* resource provides chunk-wise data */ + LOG_ARG("Blockwise: blockwise resource, new offset %d", (int) new_offset); + coap_set_header_block2(response, block_num, new_offset!=-1 || response->payload_len > block_size, block_size); + if (response->payload_len > block_size) coap_set_payload(response, response->payload, block_size); + } /* if (resource aware of blockwise) */ + } + else if (new_offset!=0) + { + LOG_ARG("Blockwise: no block option for blockwise resource, using block size %u", REST_MAX_CHUNK_SIZE); + + coap_set_header_block2(response, 0, new_offset!=-1, REST_MAX_CHUNK_SIZE); + coap_set_payload(response, response->payload, MIN(response->payload_len, REST_MAX_CHUNK_SIZE)); + } /* if (blockwise request) */ + + coap_error_code = message_send(contextP, response, fromSessionH); + + lwm2m_free(response->payload); + response->payload = NULL; + response->payload_len = 0; + } + else if (coap_error_code != COAP_IGNORE) + { + if (1 == coap_set_status_code(response, coap_error_code)) + { + coap_error_code = message_send(contextP, response, fromSessionH); + } + } + } + else + { + /* Responses */ + switch (message->type) + { + case COAP_TYPE_NON: + case COAP_TYPE_CON: + { + bool done = transaction_handleResponse(contextP, fromSessionH, message, response); + +#ifdef LWM2M_SERVER_MODE + if (!done && IS_OPTION(message, COAP_OPTION_OBSERVE) && + ((message->code == COAP_204_CHANGED) || (message->code == COAP_205_CONTENT))) + { + done = observe_handleNotify(contextP, fromSessionH, message, response); + } +#endif + if (!done && message->type == COAP_TYPE_CON ) + { + coap_init_message(response, COAP_TYPE_ACK, 0, message->mid); + coap_error_code = message_send(contextP, response, fromSessionH); + } + } + break; + + case COAP_TYPE_RST: + /* Cancel possible subscriptions. */ + handle_reset(contextP, fromSessionH, message); + transaction_handleResponse(contextP, fromSessionH, message, NULL); + break; + + case COAP_TYPE_ACK: + transaction_handleResponse(contextP, fromSessionH, message, NULL); + break; + + default: + break; + } + } /* Request or Response */ + coap_free_header(message); + } /* if (parsed correctly) */ + else + { + LOG_ARG("Message parsing failed %u.%2u", coap_error_code >> 5, coap_error_code & 0x1F); + } + + if (coap_error_code != NO_ERROR && coap_error_code != COAP_IGNORE) + { + LOG_ARG("ERROR %u: %s", coap_error_code, coap_error_message); + + /* Set to sendable error code. */ + if (coap_error_code >= 192) + { + coap_error_code = COAP_500_INTERNAL_SERVER_ERROR; + } + /* Reuse input buffer for error message. */ + coap_init_message(message, COAP_TYPE_ACK, coap_error_code, message->mid); + coap_set_payload(message, coap_error_message, strlen(coap_error_message)); + message_send(contextP, message, fromSessionH); + } +} + + +coap_status_t message_send(lwm2m_context_t * contextP, + coap_packet_t * message, + void * sessionH) +{ + coap_status_t result = COAP_500_INTERNAL_SERVER_ERROR; + uint8_t * pktBuffer; + size_t pktBufferLen = 0; + size_t allocLen; + + LOG("Entering"); + allocLen = coap_serialize_get_size(message); + LOG_ARG("Size to allocate: %d", allocLen); + if (allocLen == 0) return COAP_500_INTERNAL_SERVER_ERROR; + + pktBuffer = (uint8_t *)lwm2m_malloc(allocLen); + if (pktBuffer != NULL) + { + pktBufferLen = coap_serialize_message(message, pktBuffer); + LOG_ARG("coap_serialize_message() returned %d", pktBufferLen); + if (0 != pktBufferLen) + { + result = lwm2m_buffer_send(sessionH, pktBuffer, pktBufferLen, contextP->userData); + } + lwm2m_free(pktBuffer); + } + + return result; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/registration.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,1275 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * domedambrosio - Please refer to git log + * Fabien Fleutot - Please refer to git log + * Simon Bernard - Please refer to git log + * Toby Jaffey - Please refer to git log + * Manuel Sangoi - Please refer to git log + * Julien Vermillard - Please refer to git log + * Bosch Software Innovations GmbH - Please refer to git log + * Pascal Rieux - Please refer to git log + * + *******************************************************************************/ + +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro <david.navarro@intel.com> + +*/ + +#include "internals.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#define MAX_LOCATION_LENGTH 10 // strlen("/rd/65534") + 1 + +#ifdef LWM2M_CLIENT_MODE + +static int prv_getRegistrationQuery(lwm2m_context_t * contextP, + lwm2m_server_t * server, + char * buffer, + size_t length) +{ + int index; + int res; + + index = utils_stringCopy(buffer, length, QUERY_STARTER QUERY_VERSION_FULL QUERY_DELIMITER QUERY_NAME); + if (index < 0) return 0; + res = utils_stringCopy(buffer + index, length - index, contextP->endpointName); + if (res < 0) return 0; + index += res; + + if (NULL != contextP->msisdn) + { + res = utils_stringCopy(buffer + index, length - index, QUERY_DELIMITER QUERY_SMS); + if (res < 0) return 0; + index += res; + res = utils_stringCopy(buffer + index, length - index, contextP->msisdn); + if (res < 0) return 0; + index += res; + } + + switch (server->binding) + { + case BINDING_U: + res = utils_stringCopy(buffer + index, length - index, "&b=U"); + break; + case BINDING_UQ: + res = utils_stringCopy(buffer + index, length - index, "&b=UQ"); + break; + case BINDING_S: + res = utils_stringCopy(buffer + index, length - index, "&b=S"); + break; + case BINDING_SQ: + res = utils_stringCopy(buffer + index, length - index, "&b=SQ"); + break; + case BINDING_US: + res = utils_stringCopy(buffer + index, length - index, "&b=US"); + break; + case BINDING_UQS: + res = utils_stringCopy(buffer + index, length - index, "&b=UQS"); + break; + default: + res = -1; + } + if (res < 0) return 0; + + return index + res; +} + +static void prv_handleRegistrationReply(lwm2m_transaction_t * transacP, + void * message) +{ + coap_packet_t * packet = (coap_packet_t *)message; + lwm2m_server_t * targetP = (lwm2m_server_t *)(transacP->userData); + + if (targetP->status == STATE_REG_PENDING) + { + time_t tv_sec = lwm2m_gettime(); + if (tv_sec >= 0) + { + targetP->registration = tv_sec; + } + if (packet != NULL && packet->code == COAP_201_CREATED) + { + targetP->status = STATE_REGISTERED; + if (NULL != targetP->location) + { + lwm2m_free(targetP->location); + } + targetP->location = coap_get_multi_option_as_string(packet->location_path); + + LOG("Registration successful"); + } + else + { + targetP->status = STATE_REG_FAILED; + LOG("Registration failed"); + } + } +} + +#define PRV_QUERY_BUFFER_LENGTH 200 + +// send the registration for a single server +static uint8_t prv_register(lwm2m_context_t * contextP, + lwm2m_server_t * server) +{ + char query[200]; + int query_length; + uint8_t payload[512]; + int payload_length; + lwm2m_transaction_t * transaction; + + payload_length = object_getRegisterPayload(contextP, payload, sizeof(payload)); + if (payload_length == 0) return COAP_500_INTERNAL_SERVER_ERROR; + + query_length = prv_getRegistrationQuery(contextP, server, query, sizeof(query)); + + if (query_length == 0) return COAP_500_INTERNAL_SERVER_ERROR; + + if (0 != server->lifetime) + { + int res; + + res = utils_stringCopy(query + query_length, PRV_QUERY_BUFFER_LENGTH - query_length, QUERY_DELIMITER QUERY_LIFETIME); + if (res < 0) return COAP_500_INTERNAL_SERVER_ERROR; + query_length += res; + res = utils_intCopy(query + query_length, PRV_QUERY_BUFFER_LENGTH - query_length, server->lifetime); + if (res < 0) return COAP_500_INTERNAL_SERVER_ERROR; + query_length += res; + } + + if (server->sessionH == NULL) + { + server->sessionH = lwm2m_connect_server(server->secObjInstID, contextP->userData); + } + + if (NULL == server->sessionH) return COAP_503_SERVICE_UNAVAILABLE; + + transaction = transaction_new(server->sessionH, COAP_POST, NULL, NULL, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + coap_set_header_uri_path(transaction->message, "/"URI_REGISTRATION_SEGMENT); + coap_set_header_uri_query(transaction->message, query); + coap_set_header_content_type(transaction->message, LWM2M_CONTENT_LINK); + coap_set_payload(transaction->message, payload, payload_length); + + transaction->callback = prv_handleRegistrationReply; + transaction->userData = (void *) server; + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + if (transaction_send(contextP, transaction) != 0) return COAP_500_INTERNAL_SERVER_ERROR; + + server->status = STATE_REG_PENDING; + + return COAP_NO_ERROR; +} + +static void prv_handleRegistrationUpdateReply(lwm2m_transaction_t * transacP, + void * message) +{ + coap_packet_t * packet = (coap_packet_t *)message; + lwm2m_server_t * targetP = (lwm2m_server_t *)(transacP->userData); + + if (targetP->status == STATE_REG_UPDATE_PENDING) + { + time_t tv_sec = lwm2m_gettime(); + if (tv_sec >= 0) + { + targetP->registration = tv_sec; + } + if (packet != NULL && packet->code == COAP_204_CHANGED) + { + targetP->status = STATE_REGISTERED; + LOG("Registration update successful"); + } + else + { + targetP->status = STATE_REG_FAILED; + LOG("Registration update failed"); + } + } +} + +static int prv_updateRegistration(lwm2m_context_t * contextP, + lwm2m_server_t * server, + bool withObjects) +{ + lwm2m_transaction_t * transaction; + uint8_t payload[512]; + int payload_length; + + transaction = transaction_new(server->sessionH, COAP_POST, NULL, NULL, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + coap_set_header_uri_path(transaction->message, server->location); + + if (withObjects == true) + { + payload_length = object_getRegisterPayload(contextP, payload, sizeof(payload)); + if (payload_length == 0) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + coap_set_payload(transaction->message, payload, payload_length); + } + + transaction->callback = prv_handleRegistrationUpdateReply; + transaction->userData = (void *) server; + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + + if (transaction_send(contextP, transaction) == 0) + { + server->status = STATE_REG_UPDATE_PENDING; + } + + return COAP_NO_ERROR; +} + +// update the registration of a given server +int lwm2m_update_registration(lwm2m_context_t * contextP, + uint16_t shortServerID, + bool withObjects) +{ + lwm2m_server_t * targetP; + uint8_t result; + + LOG_ARG("State: %s, shortServerID: %d", STR_STATE(contextP->state), shortServerID); + + result = COAP_NO_ERROR; + + targetP = contextP->serverList; + if (targetP == NULL) + { + if (object_getServers(contextP) == -1) + { + LOG("No server found"); + return COAP_404_NOT_FOUND; + } + } + while (targetP != NULL && result == COAP_NO_ERROR) + { + if (shortServerID != 0) + { + if (targetP->shortID == shortServerID) + { + // found the server, trigger the update transaction + if (targetP->status == STATE_REGISTERED + || targetP->status == STATE_REG_UPDATE_PENDING) + { + if (withObjects == true) + { + targetP->status = STATE_REG_FULL_UPDATE_NEEDED; + } + else + { + targetP->status = STATE_REG_UPDATE_NEEDED; + } + return COAP_NO_ERROR; + } + else if ((targetP->status == STATE_REG_FULL_UPDATE_NEEDED) + || (targetP->status == STATE_REG_UPDATE_NEEDED)) + { + // if REG (FULL) UPDATE is already set, returns COAP_NO_ERROR + if (withObjects == true) + { + targetP->status = STATE_REG_FULL_UPDATE_NEEDED; + } + return COAP_NO_ERROR; + } + else + { + return COAP_400_BAD_REQUEST; + } + } + } + else + { + if (targetP->status == STATE_REGISTERED + || targetP->status == STATE_REG_UPDATE_PENDING) + { + if (withObjects == true) + { + targetP->status = STATE_REG_FULL_UPDATE_NEEDED; + } + else + { + targetP->status = STATE_REG_UPDATE_NEEDED; + } + } + } + targetP = targetP->next; + } + + if (shortServerID != 0 + && targetP == NULL) + { + // no server found + result = COAP_404_NOT_FOUND; + } + + return result; +} + +uint8_t registration_start(lwm2m_context_t * contextP) +{ + lwm2m_server_t * targetP; + uint8_t result; + + LOG_ARG("State: %s", STR_STATE(contextP->state)); + + result = COAP_NO_ERROR; + + targetP = contextP->serverList; + while (targetP != NULL && result == COAP_NO_ERROR) + { + if (targetP->status == STATE_DEREGISTERED + || targetP->status == STATE_REG_FAILED) + { + result = prv_register(contextP, targetP); + } + targetP = targetP->next; + } + + return result; +} + + +/* + * Returns STATE_REG_PENDING if at least one registration is still pending + * Returns STATE_REGISTERED if no registration is pending and there is at least one server the client is registered to + * Returns STATE_REG_FAILED if all registration failed. + */ +lwm2m_status_t registration_getStatus(lwm2m_context_t * contextP) +{ + lwm2m_server_t * targetP; + lwm2m_status_t reg_status; + + LOG_ARG("State: %s", STR_STATE(contextP->state)); + + targetP = contextP->serverList; + reg_status = STATE_REG_FAILED; + + while (targetP != NULL) + { + LOG_ARG("targetP->status: %s", STR_STATUS(targetP->status)); + switch (targetP->status) + { + case STATE_REGISTERED: + case STATE_REG_UPDATE_NEEDED: + case STATE_REG_FULL_UPDATE_NEEDED: + case STATE_REG_UPDATE_PENDING: + if (reg_status == STATE_REG_FAILED) + { + reg_status = STATE_REGISTERED; + } + break; + + case STATE_REG_PENDING: + reg_status = STATE_REG_PENDING; + break; + + case STATE_REG_FAILED: + case STATE_DEREG_PENDING: + case STATE_DEREGISTERED: + default: + break; + } + LOG_ARG("reg_status: %s", STR_STATUS(reg_status)); + + targetP = targetP->next; + } + + return reg_status; +} + +static void prv_handleDeregistrationReply(lwm2m_transaction_t * transacP, + void * message) +{ + lwm2m_server_t * targetP; + + targetP = (lwm2m_server_t *)(transacP->userData); + if (NULL != targetP) + { + if (targetP->status == STATE_DEREG_PENDING) + { + targetP->status = STATE_DEREGISTERED; + } + } +} + +void registration_deregister(lwm2m_context_t * contextP, + lwm2m_server_t * serverP) +{ + lwm2m_transaction_t * transaction; + + LOG_ARG("State: %s, serverP->status: %s", STR_STATE(contextP->state), STR_STATUS(serverP->status)); + + if (serverP->status == STATE_DEREGISTERED + || serverP->status == STATE_REG_PENDING + || serverP->status == STATE_DEREG_PENDING + || serverP->status == STATE_REG_FAILED + || serverP->location == NULL) + { + return; + } + + transaction = transaction_new(serverP->sessionH, COAP_DELETE, NULL, NULL, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return; + + coap_set_header_uri_path(transaction->message, serverP->location); + + transaction->callback = prv_handleDeregistrationReply; + transaction->userData = (void *) contextP; + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + if (transaction_send(contextP, transaction) == 0) + { + serverP->status = STATE_DEREG_PENDING; + } +} +#endif + +#ifdef LWM2M_SERVER_MODE +static void prv_freeClientObjectList(lwm2m_client_object_t * objects) +{ + while (objects != NULL) + { + lwm2m_client_object_t * objP; + + while (objects->instanceList != NULL) + { + lwm2m_list_t * target; + + target = objects->instanceList; + objects->instanceList = objects->instanceList->next; + lwm2m_free(target); + } + + objP = objects; + objects = objects->next; + lwm2m_free(objP); + } +} + +static int prv_getParameters(multi_option_t * query, + char ** nameP, + uint32_t * lifetimeP, + char ** msisdnP, + lwm2m_binding_t * bindingP, + char ** versionP) +{ + *nameP = NULL; + *lifetimeP = 0; + *msisdnP = NULL; + *bindingP = BINDING_UNKNOWN; + *versionP = NULL; + + while (query != NULL) + { + if (lwm2m_strncmp((char *)query->data, QUERY_NAME, QUERY_NAME_LEN) == 0) + { + if (*nameP != NULL) goto error; + if (query->len == QUERY_NAME_LEN) goto error; + + *nameP = (char *)lwm2m_malloc(query->len - QUERY_NAME_LEN + 1); + if (*nameP != NULL) + { + memcpy(*nameP, query->data + QUERY_NAME_LEN, query->len - QUERY_NAME_LEN); + (*nameP)[query->len - QUERY_NAME_LEN] = 0; + } + } + else if (lwm2m_strncmp((char *)query->data, QUERY_SMS, QUERY_SMS_LEN) == 0) + { + if (*msisdnP != NULL) goto error; + if (query->len == QUERY_SMS_LEN) goto error; + + *msisdnP = (char *)lwm2m_malloc(query->len - QUERY_SMS_LEN + 1); + if (*msisdnP != NULL) + { + memcpy(*msisdnP, query->data + QUERY_SMS_LEN, query->len - QUERY_SMS_LEN); + (*msisdnP)[query->len - QUERY_SMS_LEN] = 0; + } + } + else if (lwm2m_strncmp((char *)query->data, QUERY_LIFETIME, QUERY_LIFETIME_LEN) == 0) + { + int i; + + if (*lifetimeP != 0) goto error; + if (query->len == QUERY_LIFETIME_LEN) goto error; + + for (i = QUERY_LIFETIME_LEN ; i < query->len ; i++) + { + if (query->data[i] < '0' || query->data[i] > '9') goto error; + *lifetimeP = (*lifetimeP * 10) + (query->data[i] - '0'); + } + } + else if (lwm2m_strncmp((char *)query->data, QUERY_VERSION, QUERY_VERSION_LEN) == 0) + { + if (*versionP != NULL) goto error; + if (query->len == QUERY_VERSION_LEN) goto error; + + *versionP = (char *)lwm2m_malloc(query->len - QUERY_VERSION_LEN + 1); + if (*versionP != NULL) + { + memcpy(*versionP, query->data + QUERY_VERSION_LEN, query->len - QUERY_VERSION_LEN); + (*versionP)[query->len - QUERY_VERSION_LEN] = 0; + } + } + else if (lwm2m_strncmp((char *)query->data, QUERY_BINDING, QUERY_BINDING_LEN) == 0) + { + if (*bindingP != BINDING_UNKNOWN) goto error; + if (query->len == QUERY_BINDING_LEN) goto error; + + *bindingP = utils_stringToBinding(query->data + QUERY_BINDING_LEN, query->len - QUERY_BINDING_LEN); + } + query = query->next; + } + + return 0; + +error: + if (*nameP != NULL) lwm2m_free(*nameP); + if (*msisdnP != NULL) lwm2m_free(*msisdnP); + if (*versionP != NULL) lwm2m_free(*versionP); + + return -1; +} + +static uint16_t prv_splitLinkAttribute(uint8_t * data, + uint16_t length, + uint16_t * keyStart, + uint16_t * keyLength, + uint16_t * valueStart, + uint16_t * valueLength) +{ + uint16_t index; + uint16_t end; + + index = 0; + while (index < length && data[index] == ' ') index++; + if (index == length) return 0; + + if (data[index] == REG_ATTR_SEPARATOR) + { + index++; + } + if (index == length) return 0; + + *keyStart = index; + + while (index < length && data[index] != REG_ATTR_EQUALS) index++; + if (index == *keyStart || index == length) return 0; + + *keyLength = index - *keyStart; + + index++; + while (index < length && data[index] == ' ') index++; + if (index == length) return 0; + + *valueStart = index; + + while (index < length && data[index] != REG_ATTR_SEPARATOR) index++; + end = index; + + index--; + while (index > *valueStart && data[index] == ' ') index--; + if (index == *valueStart) return 0; + + *valueLength = index - *valueStart + 1; + + return end; +} + +static int prv_parseLinkAttributes(uint8_t * data, + uint16_t length, + bool * supportJSON, + char ** altPath) +{ + uint16_t index; + uint16_t pathStart; + uint16_t pathLength; + bool isValid; + + isValid = false; + + // Expecting application/link-format (RFC6690) + // leading space were removed before. Remove trailing spaces. + while (length > 0 && data[length-1] == ' ') length--; + + // strip open tag + if (length >= 2 && data[0] == REG_URI_START) + { + data += 1; + length -= 1; + } + else + { + return 0; + } + + pathStart = 0; + index = length - 1; + while (index > 0 && data[index] != REG_URI_END) index--; + // link attributes are required + if (index == 0 || index == length - 1) return 0; + + // If there is a preceding /, remove it + if (data[pathStart] == '/') + { + pathStart += 1; + } + pathLength = index - pathStart; + + index++; + if (index >= length || data[index] != REG_ATTR_SEPARATOR) return 0; + index++; + + while (index < length) + { + uint16_t result; + uint16_t keyStart; + uint16_t keyLength; + uint16_t valueStart; + uint16_t valueLength; + + result = prv_splitLinkAttribute(data + index, length - index, &keyStart, &keyLength, &valueStart, &valueLength); + if (result == 0) return 0; + + if (keyLength == REG_ATTR_TYPE_KEY_LEN + && 0 == lwm2m_strncmp(REG_ATTR_TYPE_KEY, data + index + keyStart, keyLength)) + { + if (isValid == true) return 0; // declared twice + if (valueLength != REG_ATTR_TYPE_VALUE_LEN + || 0 != lwm2m_strncmp(REG_ATTR_TYPE_VALUE, data + index + valueStart, valueLength)) + { + return 0; + } + isValid = true; + } + else if (keyLength == REG_ATTR_CONTENT_KEY_LEN + && 0 == lwm2m_strncmp(REG_ATTR_CONTENT_KEY, data + index + keyStart, keyLength)) + { + if (*supportJSON == true) return 0; // declared twice + if (valueLength == REG_ATTR_CONTENT_JSON_LEN + && 0 == lwm2m_strncmp(REG_ATTR_CONTENT_JSON, data + index + valueStart, valueLength)) + { + *supportJSON = true; + } + else + { + return 0; + } + } + // else ignore this one + + index += result; + } + + if (isValid == false) return 0; + + if (pathLength != 0) + { + *altPath = (char *)lwm2m_malloc(pathLength + 1); + if (*altPath == NULL) return 0; + memcpy(*altPath, data + pathStart, pathLength); + (*altPath)[pathLength] = 0; + } + + return 1; +} + +static int prv_getId(uint8_t * data, + uint16_t length, + uint16_t * objId, + uint16_t * instanceId) +{ + int value; + uint16_t limit; + + // Expecting application/link-format (RFC6690) + // leading space were removed before. Remove trailing spaces. + while (length > 0 && data[length-1] == ' ') length--; + + // strip open and close tags + if (length >= 1 && data[0] == REG_URI_START && data[length-1] == REG_URI_END) + { + data += 1; + length -= 2; + } + else + { + return 0; + } + + // If there is a preceding /, remove it + if (length >= 1 && data[0] == '/') + { + data += 1; + length -= 1; + } + + limit = 0; + while (limit < length && data[limit] != '/') limit++; + value = uri_getNumber(data, limit); + if (value < 0 || value >= LWM2M_MAX_ID) return 0; + *objId = value; + + if (limit < length) + { + limit += 1; + data += limit; + length -= limit; + + if (length > 0) + { + value = uri_getNumber(data, length); + if (value >= 0 && value < LWM2M_MAX_ID) + { + *instanceId = value; + return 2; + } + else + { + return 0; + } + } + } + + return 1; +} + +static lwm2m_client_object_t * prv_decodeRegisterPayload(uint8_t * payload, + uint16_t payloadLength, + bool * supportJSON, + char ** altPath) +{ + uint16_t index; + lwm2m_client_object_t * objList; + bool linkAttrFound; + + *altPath = NULL; + *supportJSON = false; + objList = NULL; + linkAttrFound = false; + index = 0; + + while (index <= payloadLength) + { + uint16_t start; + uint16_t length; + int result; + uint16_t id; + uint16_t instance; + + while (index < payloadLength && payload[index] == ' ') index++; + if (index == payloadLength) break; + + start = index; + while (index < payloadLength && payload[index] != REG_DELIMITER) index++; + length = index - start; + + result = prv_getId(payload + start, length, &id, &instance); + if (result != 0) + { + lwm2m_client_object_t * objectP; + + objectP = (lwm2m_client_object_t *)lwm2m_list_find((lwm2m_list_t *)objList, id); + if (objectP == NULL) + { + objectP = (lwm2m_client_object_t *)lwm2m_malloc(sizeof(lwm2m_client_object_t)); + memset(objectP, 0, sizeof(lwm2m_client_object_t)); + if (objectP == NULL) goto error; + objectP->id = id; + objList = (lwm2m_client_object_t *)LWM2M_LIST_ADD(objList, objectP); + } + if (result == 2) + { + lwm2m_list_t * instanceP; + + instanceP = lwm2m_list_find(objectP->instanceList, instance); + if (instanceP == NULL) + { + instanceP = (lwm2m_list_t *)lwm2m_malloc(sizeof(lwm2m_list_t)); + memset(instanceP, 0, sizeof(lwm2m_list_t)); + instanceP->id = instance; + objectP->instanceList = LWM2M_LIST_ADD(objectP->instanceList, instanceP); + } + } + } + else if (linkAttrFound == false) + { + result = prv_parseLinkAttributes(payload + start, length, supportJSON, altPath); + if (result == 0) goto error; + + linkAttrFound = true; + } + else goto error; + + index++; + } + + return objList; + +error: + if (*altPath != NULL) + { + lwm2m_free(*altPath); + *altPath = NULL; + } + prv_freeClientObjectList(objList); + + return NULL; +} + +static lwm2m_client_t * prv_getClientByName(lwm2m_context_t * contextP, + char * name) +{ + lwm2m_client_t * targetP; + + targetP = contextP->clientList; + while (targetP != NULL && strcmp(name, targetP->name) != 0) + { + targetP = targetP->next; + } + + return targetP; +} + +void registration_freeClient(lwm2m_client_t * clientP) +{ + LOG("Entering"); + if (clientP->name != NULL) lwm2m_free(clientP->name); + if (clientP->msisdn != NULL) lwm2m_free(clientP->msisdn); + if (clientP->altPath != NULL) lwm2m_free(clientP->altPath); + prv_freeClientObjectList(clientP->objectList); + while(clientP->observationList != NULL) + { + lwm2m_observation_t * targetP; + + targetP = clientP->observationList; + clientP->observationList = clientP->observationList->next; + lwm2m_free(targetP); + } + lwm2m_free(clientP); +} + +static int prv_getLocationString(uint16_t id, + char location[MAX_LOCATION_LENGTH]) +{ + int index; + int result; + + memset(location, 0, MAX_LOCATION_LENGTH); + + result = utils_stringCopy(location, MAX_LOCATION_LENGTH, "/"URI_REGISTRATION_SEGMENT"/"); + if (result < 0) return 0; + index = result; + + result = utils_intCopy(location + index, MAX_LOCATION_LENGTH - index, id); + if (result < 0) return 0; + + return index + result; +} + +coap_status_t registration_handleRequest(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + void * fromSessionH, + coap_packet_t * message, + coap_packet_t * response) +{ + coap_status_t result; + time_t tv_sec; + + LOG_URI(uriP); + tv_sec = lwm2m_gettime(); + if (tv_sec < 0) return COAP_500_INTERNAL_SERVER_ERROR; + + switch(message->code) + { + case COAP_POST: + { + char * name = NULL; + uint32_t lifetime; + char * msisdn; + char * altPath; + char * version; + lwm2m_binding_t binding; + lwm2m_client_object_t * objects; + bool supportJSON; + lwm2m_client_t * clientP; + char location[MAX_LOCATION_LENGTH]; + + if (0 != prv_getParameters(message->uri_query, &name, &lifetime, &msisdn, &binding, &version)) + { + return COAP_400_BAD_REQUEST; + } + if (message->content_type != LWM2M_CONTENT_LINK + && message->content_type != LWM2M_CONTENT_TEXT) + { + return COAP_400_BAD_REQUEST; + } + + objects = prv_decodeRegisterPayload(message->payload, message->payload_len, &supportJSON, &altPath); + + switch (uriP->flag & LWM2M_URI_MASK_ID) + { + case 0: + // Register operation + // Version is mandatory + if (version == NULL) + { + if (name != NULL) lwm2m_free(name); + if (msisdn != NULL) lwm2m_free(msisdn); + return COAP_400_BAD_REQUEST; + } + // Endpoint client name is mandatory + if (name == NULL) + { + lwm2m_free(version); + if (msisdn != NULL) lwm2m_free(msisdn); + return COAP_400_BAD_REQUEST; + } + // Object list is mandatory + if (objects == NULL) + { + lwm2m_free(version); + lwm2m_free(name); + if (msisdn != NULL) lwm2m_free(msisdn); + return COAP_400_BAD_REQUEST; + } + // version must be 1.0 + if (strlen(version) != LWM2M_VERSION_LEN + || lwm2m_strncmp(version, LWM2M_VERSION, LWM2M_VERSION_LEN)) + { + lwm2m_free(version); + lwm2m_free(name); + if (msisdn != NULL) lwm2m_free(msisdn); + return COAP_412_PRECONDITION_FAILED; + } + + if (lifetime == 0) + { + lifetime = LWM2M_DEFAULT_LIFETIME; + } + + clientP = prv_getClientByName(contextP, name); + if (clientP != NULL) + { + // we reset this registration + lwm2m_free(clientP->name); + if (clientP->msisdn != NULL) lwm2m_free(clientP->msisdn); + if (clientP->altPath != NULL) lwm2m_free(clientP->altPath); + prv_freeClientObjectList(clientP->objectList); + clientP->objectList = NULL; + } + else + { + clientP = (lwm2m_client_t *)lwm2m_malloc(sizeof(lwm2m_client_t)); + if (clientP == NULL) + { + lwm2m_free(name); + lwm2m_free(altPath); + if (msisdn != NULL) lwm2m_free(msisdn); + prv_freeClientObjectList(objects); + return COAP_500_INTERNAL_SERVER_ERROR; + } + memset(clientP, 0, sizeof(lwm2m_client_t)); + clientP->internalID = lwm2m_list_newId((lwm2m_list_t *)contextP->clientList); + contextP->clientList = (lwm2m_client_t *)LWM2M_LIST_ADD(contextP->clientList, clientP); + } + clientP->name = name; + clientP->binding = binding; + clientP->msisdn = msisdn; + clientP->altPath = altPath; + clientP->supportJSON = supportJSON; + clientP->lifetime = lifetime; + clientP->endOfLife = tv_sec + lifetime; + clientP->objectList = objects; + clientP->sessionH = fromSessionH; + + if (prv_getLocationString(clientP->internalID, location) == 0) + { + registration_freeClient(clientP); + return COAP_500_INTERNAL_SERVER_ERROR; + } + if (coap_set_header_location_path(response, location) == 0) + { + registration_freeClient(clientP); + return COAP_500_INTERNAL_SERVER_ERROR; + } + + if (contextP->monitorCallback != NULL) + { + contextP->monitorCallback(clientP->internalID, NULL, COAP_201_CREATED, LWM2M_CONTENT_TEXT, NULL, 0, contextP->monitorUserData); + } + result = COAP_201_CREATED; + break; + + case LWM2M_URI_FLAG_OBJECT_ID: + clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, uriP->objectId); + if (clientP == NULL) return COAP_404_NOT_FOUND; + + // Endpoint client name MUST NOT be present + if (name != NULL) + { + lwm2m_free(name); + if (msisdn != NULL) lwm2m_free(msisdn); + return COAP_400_BAD_REQUEST; + } + + if (binding != BINDING_UNKNOWN) + { + clientP->binding = binding; + } + if (msisdn != NULL) + { + if (clientP->msisdn != NULL) lwm2m_free(clientP->msisdn); + clientP->msisdn = msisdn; + } + if (lifetime != 0) + { + clientP->lifetime = lifetime; + } + // client IP address, port or MSISDN may have changed + clientP->sessionH = fromSessionH; + + if (objects != NULL) + { + lwm2m_observation_t * observationP; + + // remove observations on object/instance no longer existing + observationP = clientP->observationList; + while (observationP != NULL) + { + lwm2m_client_object_t * objP; + lwm2m_observation_t * nextP; + + nextP = observationP->next; + + objP = (lwm2m_client_object_t *)lwm2m_list_find((lwm2m_list_t *)objects, observationP->uri.objectId); + if (objP == NULL) + { + observationP->callback(clientP->internalID, + &observationP->uri, + COAP_202_DELETED, + LWM2M_CONTENT_TEXT, NULL, 0, + observationP->userData); + observe_remove(observationP); + } + else + { + if ((observationP->uri.flag & LWM2M_URI_FLAG_INSTANCE_ID) != 0) + { + if (lwm2m_list_find((lwm2m_list_t *)objP->instanceList, observationP->uri.instanceId) == NULL) + { + observationP->callback(clientP->internalID, + &observationP->uri, + COAP_202_DELETED, + LWM2M_CONTENT_TEXT, NULL, 0, + observationP->userData); + observe_remove(observationP); + } + } + } + + observationP = nextP; + } + + prv_freeClientObjectList(clientP->objectList); + clientP->objectList = objects; + } + + clientP->endOfLife = tv_sec + clientP->lifetime; + + if (contextP->monitorCallback != NULL) + { + contextP->monitorCallback(clientP->internalID, NULL, COAP_204_CHANGED, LWM2M_CONTENT_TEXT, NULL, 0, contextP->monitorUserData); + } + result = COAP_204_CHANGED; + break; + + default: + return COAP_400_BAD_REQUEST; + } + } + break; + + case COAP_DELETE: + { + lwm2m_client_t * clientP; + + if ((uriP->flag & LWM2M_URI_MASK_ID) != LWM2M_URI_FLAG_OBJECT_ID) return COAP_400_BAD_REQUEST; + + contextP->clientList = (lwm2m_client_t *)LWM2M_LIST_RM(contextP->clientList, uriP->objectId, &clientP); + if (clientP == NULL) return COAP_400_BAD_REQUEST; + if (contextP->monitorCallback != NULL) + { + contextP->monitorCallback(clientP->internalID, NULL, COAP_202_DELETED, LWM2M_CONTENT_TEXT, NULL, 0, contextP->monitorUserData); + } + registration_freeClient(clientP); + result = COAP_202_DELETED; + } + break; + + default: + return COAP_400_BAD_REQUEST; + } + + return result; +} + +void lwm2m_set_monitoring_callback(lwm2m_context_t * contextP, + lwm2m_result_callback_t callback, + void * userData) +{ + LOG("Entering"); + contextP->monitorCallback = callback; + contextP->monitorUserData = userData; +} +#endif + +// for each server update the registration if needed +// for each client check if the registration expired +void registration_step(lwm2m_context_t * contextP, + time_t currentTime, + time_t * timeoutP) +{ +#ifdef LWM2M_CLIENT_MODE + lwm2m_server_t * targetP = contextP->serverList; + + LOG_ARG("State: %s", STR_STATE(contextP->state)); + + targetP = contextP->serverList; + while (targetP != NULL) + { + switch (targetP->status) + { + case STATE_REGISTERED: + { + time_t nextUpdate; + time_t interval; + + nextUpdate = targetP->lifetime; + if (COAP_MAX_TRANSMIT_WAIT < nextUpdate) + { + nextUpdate -= COAP_MAX_TRANSMIT_WAIT; + } + else + { + nextUpdate = nextUpdate >> 1; + } + + interval = targetP->registration + nextUpdate - currentTime; + if (0 >= interval) + { + LOG("Updating registration"); + prv_updateRegistration(contextP, targetP, false); + } + else if (interval < *timeoutP) + { + *timeoutP = interval; + } + } + break; + + case STATE_REG_UPDATE_NEEDED: + prv_updateRegistration(contextP, targetP, false); + break; + + case STATE_REG_FULL_UPDATE_NEEDED: + prv_updateRegistration(contextP, targetP, true); + break; + + case STATE_REG_FAILED: + if (targetP->sessionH != NULL) + { + lwm2m_close_connection(targetP->sessionH, contextP->userData); + targetP->sessionH = NULL; + } + break; + + default: + break; + } + targetP = targetP->next; + } + +#endif +#ifdef LWM2M_SERVER_MODE + lwm2m_client_t * clientP; + + LOG("Entering"); + // monitor clients lifetime + clientP = contextP->clientList; + while (clientP != NULL) + { + lwm2m_client_t * nextP = clientP->next; + + if (clientP->endOfLife <= currentTime) + { + contextP->clientList = (lwm2m_client_t *)LWM2M_LIST_RM(contextP->clientList, clientP->internalID, NULL); + if (contextP->monitorCallback != NULL) + { + contextP->monitorCallback(clientP->internalID, NULL, COAP_202_DELETED, LWM2M_CONTENT_TEXT, NULL, 0, contextP->monitorUserData); + } + registration_freeClient(clientP); + } + else + { + time_t interval; + + interval = clientP->endOfLife - currentTime; + + if (*timeoutP > interval) + { + *timeoutP = interval; + } + } + clientP = nextP; + } +#endif + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/tlv.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,523 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * Fabien Fleutot - Please refer to git log + * Bosch Software Innovations GmbH - Please refer to git log + * + *******************************************************************************/ + +#include "internals.h" +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <inttypes.h> +#include <float.h> + +#ifndef LWM2M_BIG_ENDIAN +#ifndef LWM2M_LITTLE_ENDIAN +#error Please define LWM2M_BIG_ENDIAN or LWM2M_LITTLE_ENDIAN +#endif +#endif + +#define _PRV_TLV_TYPE_MASK 0xC0 +#define _PRV_TLV_HEADER_MAX_LENGTH 6 + +#define _PRV_TLV_TYPE_UNKNOWN (uint8_t)0xFF +#define _PRV_TLV_TYPE_OBJECT (uint8_t)0x10 +#define _PRV_TLV_TYPE_OBJECT_INSTANCE (uint8_t)0x00 +#define _PRV_TLV_TYPE_RESOURCE (uint8_t)0xC0 +#define _PRV_TLV_TYPE_MULTIPLE_RESOURCE (uint8_t)0x80 +#define _PRV_TLV_TYPE_RESOURCE_INSTANCE (uint8_t)0x40 + + +static uint8_t prv_getHeaderType(lwm2m_data_type_t type) +{ + switch (type) + { + case LWM2M_TYPE_OBJECT: + return _PRV_TLV_TYPE_OBJECT; + + case LWM2M_TYPE_OBJECT_INSTANCE: + return _PRV_TLV_TYPE_OBJECT_INSTANCE; + + case LWM2M_TYPE_MULTIPLE_RESOURCE: + return _PRV_TLV_TYPE_MULTIPLE_RESOURCE; + + + case LWM2M_TYPE_STRING: + case LWM2M_TYPE_INTEGER: + case LWM2M_TYPE_FLOAT: + case LWM2M_TYPE_BOOLEAN: + case LWM2M_TYPE_OPAQUE: + case LWM2M_TYPE_OBJECT_LINK: + return _PRV_TLV_TYPE_RESOURCE; + + case LWM2M_TYPE_UNDEFINED: + default: + return _PRV_TLV_TYPE_UNKNOWN; + } +} + +static lwm2m_data_type_t prv_getDataType(uint8_t type) +{ + switch (type) + { + case _PRV_TLV_TYPE_OBJECT: + return LWM2M_TYPE_OBJECT; + + case _PRV_TLV_TYPE_OBJECT_INSTANCE: + return LWM2M_TYPE_OBJECT_INSTANCE; + + case _PRV_TLV_TYPE_MULTIPLE_RESOURCE: + return LWM2M_TYPE_MULTIPLE_RESOURCE; + + case _PRV_TLV_TYPE_RESOURCE: + case _PRV_TLV_TYPE_RESOURCE_INSTANCE: + return LWM2M_TYPE_OPAQUE; + + default: + return LWM2M_TYPE_UNDEFINED; + } +} + +static int prv_getHeaderLength(uint16_t id, + size_t dataLen) +{ + int length; + + length = 2; + + if (id > 0xFF) + { + length += 1; + } + + if (dataLen > 0xFFFF) + { + length += 3; + } + else if (dataLen > 0xFF) + { + length += 2; + } + else if (dataLen > 7) + { + length += 1; + } + + return length; +} + +static int prv_createHeader(uint8_t * header, + bool isInstance, + lwm2m_data_type_t type, + uint16_t id, + size_t data_len) +{ + int header_len; + int offset; + uint8_t hdrType; + + header_len = prv_getHeaderLength(id, data_len); + if (isInstance == true) + { + hdrType = _PRV_TLV_TYPE_RESOURCE_INSTANCE; + } + else + { + hdrType = prv_getHeaderType(type); + } + + header[0] = 0; + header[0] |= hdrType&_PRV_TLV_TYPE_MASK; + + if (id > 0xFF) + { + header[0] |= 0x20; + header[1] = (id >> 8) & 0XFF; + header[2] = id & 0XFF; + offset = 3; + } + else + { + header[1] = id; + offset = 2; + } + if (data_len <= 7) + { + header[0] += data_len; + } + else if (data_len <= 0xFF) + { + header[0] |= 0x08; + header[offset] = data_len; + } + else if (data_len <= 0xFFFF) + { + header[0] |= 0x10; + header[offset] = (data_len >> 8) & 0XFF; + header[offset + 1] = data_len & 0XFF; + } + else if (data_len <= 0xFFFFFF) + { + header[0] |= 0x18; + header[offset] = (data_len >> 16) & 0XFF; + header[offset + 1] = (data_len >> 8) & 0XFF; + header[offset + 2] = data_len & 0XFF; + } + + return header_len; +} + +int lwm2m_decode_TLV(const uint8_t * buffer, + size_t buffer_len, + lwm2m_data_type_t * oType, + uint16_t * oID, + size_t * oDataIndex, + size_t * oDataLen) +{ + + LOG_ARG("buffer_len: %d", buffer_len); + ; + if (buffer_len < 2) return 0; + + *oDataIndex = 2; + + *oType = prv_getDataType(buffer[0]&_PRV_TLV_TYPE_MASK); + + if ((buffer[0]&0x20) == 0x20) + { + // id is 16 bits long + if (buffer_len < 3) return 0; + *oDataIndex += 1; + *oID = (buffer[1]<<8) + buffer[2]; + } + else + { + // id is 8 bits long + *oID = buffer[1]; + } + + switch (buffer[0]&0x18) + { + case 0x00: + // no length field + *oDataLen = buffer[0]&0x07; + break; + case 0x08: + // length field is 8 bits long + if (buffer_len < *oDataIndex + 1) return 0; + *oDataLen = buffer[*oDataIndex]; + *oDataIndex += 1; + break; + case 0x10: + // length field is 16 bits long + if (buffer_len < *oDataIndex + 2) return 0; + *oDataLen = (buffer[*oDataIndex]<<8) + buffer[*oDataIndex+1]; + *oDataIndex += 2; + break; + case 0x18: + // length field is 24 bits long + if (buffer_len < *oDataIndex + 3) return 0; + *oDataLen = (buffer[*oDataIndex]<<16) + (buffer[*oDataIndex+1]<<8) + buffer[*oDataIndex+2]; + *oDataIndex += 3; + break; + default: + // can't happen + return 0; + } + + if (*oDataIndex + *oDataLen > buffer_len) return 0; + + return *oDataIndex + *oDataLen; +} + + +int tlv_parse(uint8_t * buffer, + size_t bufferLen, + lwm2m_data_t ** dataP) +{ + lwm2m_data_type_t type; + uint16_t id; + size_t dataIndex; + size_t dataLen; + int index = 0; + int result; + int size = 0; + + LOG_ARG("bufferLen: %d", bufferLen); + + *dataP = NULL; + + while (0 != (result = lwm2m_decode_TLV((uint8_t*)buffer + index, bufferLen - index, &type, &id, &dataIndex, &dataLen))) + { + lwm2m_data_t * newTlvP; + + newTlvP = lwm2m_data_new(size + 1); + if (size >= 1) + { + if (newTlvP == NULL) + { + lwm2m_data_free(size, *dataP); + return 0; + } + else + { + memcpy(newTlvP, *dataP, size * sizeof(lwm2m_data_t)); + lwm2m_free(*dataP); + } + } + *dataP = newTlvP; + + (*dataP)[size].type = type; + (*dataP)[size].id = id; + if (type == LWM2M_TYPE_OBJECT_INSTANCE || type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + (*dataP)[size].value.asChildren.count = tlv_parse(buffer + index + dataIndex, + dataLen, + &((*dataP)[size].value.asChildren.array)); + if ((*dataP)[size].value.asChildren.count == 0) + { + lwm2m_data_free(size + 1, *dataP); + return 0; + } + } + else + { + lwm2m_data_encode_opaque(buffer + index + dataIndex, dataLen, (*dataP) + size); + } + size++; + index += result; + } + + return size; +} + + +static int prv_getLength(int size, + lwm2m_data_t * dataP) +{ + int length; + int i; + + length = 0; + + for (i = 0 ; i < size && length != -1 ; i++) + { + switch (dataP[i].type) + { + case LWM2M_TYPE_OBJECT_INSTANCE: + case LWM2M_TYPE_MULTIPLE_RESOURCE: + { + int subLength; + + subLength = prv_getLength(dataP[i].value.asChildren.count, dataP[i].value.asChildren.array); + if (subLength == -1) + { + length = -1; + } + else + { + length += prv_getHeaderLength(dataP[i].id, subLength) + subLength; + } + } + break; + + case LWM2M_TYPE_STRING: + case LWM2M_TYPE_OPAQUE: + length += prv_getHeaderLength(dataP[i].id, dataP[i].value.asBuffer.length) + dataP[i].value.asBuffer.length; + break; + + case LWM2M_TYPE_INTEGER: + { + size_t data_len; + uint8_t unused_buffer[_PRV_64BIT_BUFFER_SIZE]; + + data_len = utils_encodeInt(dataP[i].value.asInteger, unused_buffer); + length += prv_getHeaderLength(dataP[i].id, data_len) + data_len; + } + break; + + case LWM2M_TYPE_FLOAT: + { + size_t data_len; + + if ((dataP[i].value.asFloat < 0.0 - (double)FLT_MAX) + || (dataP[i].value.asFloat >(double)FLT_MAX)) + { + data_len = 8; + } + else + { + data_len = 4; + } + + length += prv_getHeaderLength(dataP[i].id, data_len) + data_len; + } + break; + + case LWM2M_TYPE_BOOLEAN: + // Booleans are always encoded on one byte + length += prv_getHeaderLength(dataP[i].id, 1) + 1; + break; + + case LWM2M_TYPE_OBJECT_LINK: + // Object Link are always encoded on four bytes + length += prv_getHeaderLength(dataP[i].id, 4) + 4; + break; + + default: + length = -1; + break; + } + } + + return length; +} + + +int tlv_serialize(bool isResourceInstance, + int size, + lwm2m_data_t * dataP, + uint8_t ** bufferP) +{ + int length; + int index; + int i; + + LOG_ARG("isResourceInstance: %s, size: %d", isResourceInstance?"true":"false", size); + + *bufferP = NULL; + length = prv_getLength(size, dataP); + if (length <= 0) return length; + + *bufferP = (uint8_t *)lwm2m_malloc(length); + if (*bufferP == NULL) return 0; + + index = 0; + for (i = 0 ; i < size && length != 0 ; i++) + { + int headerLen; + bool isInstance; + + isInstance = isResourceInstance; + switch (dataP[i].type) + { + case LWM2M_TYPE_MULTIPLE_RESOURCE: + isInstance = true; + // fall through + case LWM2M_TYPE_OBJECT_INSTANCE: + { + uint8_t * tmpBuffer; + int res; + + res = tlv_serialize(isInstance, dataP[i].value.asChildren.count, dataP[i].value.asChildren.array, &tmpBuffer); + if (res < 0) + { + length = -1; + } + else + { + size_t tmpLength; + + tmpLength = (size_t)res; + headerLen = prv_createHeader(*bufferP + index, false, dataP[i].type, dataP[i].id, tmpLength); + index += headerLen; + if (tmpLength > 0) + { + memcpy(*bufferP + index, tmpBuffer, tmpLength); + index += tmpLength; + lwm2m_free(tmpBuffer); + } + } + } + break; + + case LWM2M_TYPE_OBJECT_LINK: + { + int k; + uint8_t buf[4]; + uint32_t v = dataP[i].value.asObjLink.objectId; + v <<= 16; + v |= dataP[i].value.asObjLink.objectInstanceId; + for (k = 3; k >= 0; --k) { + buf[k] = (uint8_t)(v & 0xFF); + v >>= 8; + } + // keep encoding as buffer + headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, 4); + index += headerLen; + memcpy(*bufferP + index, buf, 4); + index += 4; + } + break; + + case LWM2M_TYPE_STRING: + case LWM2M_TYPE_OPAQUE: + headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, dataP[i].value.asBuffer.length); + index += headerLen; + memcpy(*bufferP + index, dataP[i].value.asBuffer.buffer, dataP[i].value.asBuffer.length); + index += dataP[i].value.asBuffer.length; + break; + + case LWM2M_TYPE_INTEGER: + { + size_t data_len; + uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE]; + + data_len = utils_encodeInt(dataP[i].value.asInteger, data_buffer); + headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, data_len); + index += headerLen; + memcpy(*bufferP + index, data_buffer, data_len); + index += data_len; + } + break; + + case LWM2M_TYPE_FLOAT: + { + size_t data_len; + uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE]; + + data_len = utils_encodeFloat(dataP[i].value.asFloat, data_buffer); + headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, data_len); + index += headerLen; + memcpy(*bufferP + index, data_buffer, data_len); + index += data_len; + } + break; + + case LWM2M_TYPE_BOOLEAN: + headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, 1); + index += headerLen; + (*bufferP)[index] = dataP[i].value.asBoolean ? 1 : 0; + index += 1; + break; + + default: + length = -1; + break; + } + } + + if (length < 0) + { + lwm2m_free(*bufferP); + *bufferP = NULL; + } + + LOG_ARG("returning %u", length); + + return length; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/transaction.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,454 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * Simon Bernard - Please refer to git log + * Toby Jaffey - Please refer to git log + * Pascal Rieux - Please refer to git log + * Bosch Software Innovations GmbH - Please refer to git log + * + *******************************************************************************/ + +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro <david.navarro@intel.com> + +*/ + +/* +Contains code snippets which are: + + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + +*/ + +/************************************************************************ + * Function for communications transactions. + * + * Basic specification: rfc7252 + * + * Transaction implements processing of piggybacked and separate response communication + * dialogs specified in section 2.2 of the above specification. + * The caller registers a callback function, which is called, when either the result is + * received or a timeout occurs. + * + * Supported dialogs: + * Requests (GET - DELETE): + * - CON with mid, without token => regular finished with corresponding ACK.MID + * - CON with mid, with token => regular finished with corresponding ACK.MID and response containing + * the token. Supports both versions, with piggybacked ACK and separate ACK/response. + * Though the ACK.MID may be lost in the separate version, a matching response may + * finish the transaction even without the ACK.MID. + * - NON without token => no transaction, no result expected! + * - NON with token => regular finished with response containing the token. + * Responses (COAP_201_CREATED - ?): + * - CON with mid => regular finished with corresponding ACK.MID + */ + +#include "internals.h" + + +/* + * Modulo mask (+1 and +0.5 for rounding) for a random number to get the tick number for the random + * retransmission time between COAP_RESPONSE_TIMEOUT and COAP_RESPONSE_TIMEOUT*COAP_RESPONSE_RANDOM_FACTOR. + */ +#define COAP_RESPONSE_TIMEOUT_TICKS (CLOCK_SECOND * COAP_RESPONSE_TIMEOUT) +#define COAP_RESPONSE_TIMEOUT_BACKOFF_MASK ((CLOCK_SECOND * COAP_RESPONSE_TIMEOUT * (COAP_RESPONSE_RANDOM_FACTOR - 1)) + 1.5) + +static int prv_checkFinished(lwm2m_transaction_t * transacP, + coap_packet_t * receivedMessage) +{ + int len; + const uint8_t* token; + coap_packet_t * transactionMessage = transacP->message; + + if (COAP_DELETE < transactionMessage->code) + { + // response + return transacP->ack_received ? 1 : 0; + } + if (!IS_OPTION(transactionMessage, COAP_OPTION_TOKEN)) + { + // request without token + return transacP->ack_received ? 1 : 0; + } + + len = coap_get_header_token(receivedMessage, &token); + if (transactionMessage->token_len == len) + { + if (memcmp(transactionMessage->token, token, len)==0) return 1; + } + + return 0; +} + +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) +{ + lwm2m_transaction_t * transacP; + int result; + + LOG_ARG("method: %d, altPath: \"%s\", mID: %d, token_len: %d", + method, altPath, mID, token_len); + LOG_URI(uriP); + + // no transactions without peer + if (NULL == sessionH) return NULL; + + transacP = (lwm2m_transaction_t *)lwm2m_malloc(sizeof(lwm2m_transaction_t)); + + if (NULL == transacP) return NULL; + memset(transacP, 0, sizeof(lwm2m_transaction_t)); + + transacP->message = lwm2m_malloc(sizeof(coap_packet_t)); + if (NULL == transacP->message) goto error; + + coap_init_message(transacP->message, COAP_TYPE_CON, method, mID); + + transacP->peerH = sessionH; + + transacP->mID = mID; + + if (altPath != NULL) + { + // TODO: Support multi-segment alternative path + coap_set_header_uri_path_segment(transacP->message, altPath + 1); + } + if (NULL != uriP) + { + result = utils_intCopy(transacP->objStringID, LWM2M_STRING_ID_MAX_LEN, uriP->objectId); + if (result < 0) goto error; + + coap_set_header_uri_path_segment(transacP->message, transacP->objStringID); + + if (LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + result = utils_intCopy(transacP->instanceStringID, LWM2M_STRING_ID_MAX_LEN, uriP->instanceId); + if (result < 0) goto error; + coap_set_header_uri_path_segment(transacP->message, transacP->instanceStringID); + } + else + { + if (LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + coap_set_header_uri_path_segment(transacP->message, NULL); + } + } + if (LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + result = utils_intCopy(transacP->resourceStringID, LWM2M_STRING_ID_MAX_LEN, uriP->resourceId); + if (result < 0) goto error; + coap_set_header_uri_path_segment(transacP->message, transacP->resourceStringID); + } + } + if (0 < token_len) + { + if (NULL != token) + { + coap_set_header_token(transacP->message, token, token_len); + } + else { + // generate a token + uint8_t temp_token[COAP_TOKEN_LEN]; + time_t tv_sec = lwm2m_gettime(); + + // initialize first 6 bytes, leave the last 2 random + temp_token[0] = mID; + temp_token[1] = mID >> 8; + temp_token[2] = tv_sec; + temp_token[3] = tv_sec >> 8; + temp_token[4] = tv_sec >> 16; + temp_token[5] = tv_sec >> 24; + // use just the provided amount of bytes + coap_set_header_token(transacP->message, temp_token, token_len); + } + } + + LOG("Exiting on success"); + return transacP; + +error: + LOG("Exiting on failure"); + lwm2m_free(transacP); + return NULL; +} + +void transaction_free(lwm2m_transaction_t * transacP) +{ + LOG("Entering"); + if (transacP->message) lwm2m_free(transacP->message); + if (transacP->buffer) lwm2m_free(transacP->buffer); + lwm2m_free(transacP); +} + +void transaction_remove(lwm2m_context_t * contextP, + lwm2m_transaction_t * transacP) +{ + LOG("Entering"); + contextP->transactionList = (lwm2m_transaction_t *) LWM2M_LIST_RM(contextP->transactionList, transacP->mID, NULL); + transaction_free(transacP); +} + +bool transaction_handleResponse(lwm2m_context_t * contextP, + void * fromSessionH, + coap_packet_t * message, + coap_packet_t * response) +{ + bool found = false; + bool reset = false; + lwm2m_transaction_t * transacP; + + LOG("Entering"); + transacP = contextP->transactionList; + + while (NULL != transacP) + { + if (lwm2m_session_is_equal(fromSessionH, transacP->peerH, contextP->userData) == true) + { + if (!transacP->ack_received) + { + if ((COAP_TYPE_ACK == message->type) || (COAP_TYPE_RST == message->type)) + { + if (transacP->mID == message->mid) + { + found = true; + transacP->ack_received = true; + reset = COAP_TYPE_RST == message->type; + } + } + } + + if (reset || prv_checkFinished(transacP, message)) + { + // HACK: If a message is sent from the monitor callback, + // it will arrive before the registration ACK. + // So we resend transaction that were denied for authentication reason. + if (!reset) + { + if (COAP_TYPE_CON == message->type && NULL != response) + { + coap_init_message(response, COAP_TYPE_ACK, 0, message->mid); + message_send(contextP, response, fromSessionH); + } + + if ((COAP_401_UNAUTHORIZED == message->code) && (COAP_MAX_RETRANSMIT > transacP->retrans_counter)) + { + transacP->ack_received = false; + transacP->retrans_time += COAP_RESPONSE_TIMEOUT; + return true; + } + } + if (transacP->callback != NULL) + { + transacP->callback(transacP, message); + } + transaction_remove(contextP, transacP); + return true; + } + // if we found our guy, exit + if (found) + { + time_t tv_sec = lwm2m_gettime(); + if (0 <= tv_sec) + { + transacP->retrans_time = tv_sec; + } + if (transacP->response_timeout) + { + transacP->retrans_time += transacP->response_timeout; + } + else + { + transacP->retrans_time += COAP_RESPONSE_TIMEOUT * transacP->retrans_counter; + } + return true; + } + } + + transacP = transacP->next; + } + return false; +} + +int transaction_send(lwm2m_context_t * contextP, + lwm2m_transaction_t * transacP) +{ + bool maxRetriesReached = false; + + LOG("Entering"); + if (transacP->buffer == NULL) + { + transacP->buffer_len = coap_serialize_get_size(transacP->message); + if (transacP->buffer_len == 0) return COAP_500_INTERNAL_SERVER_ERROR; + + transacP->buffer = (uint8_t*)lwm2m_malloc(transacP->buffer_len); + if (transacP->buffer == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + transacP->buffer_len = coap_serialize_message(transacP->message, transacP->buffer); + if (transacP->buffer_len == 0) + { + lwm2m_free(transacP->buffer); + transacP->buffer = NULL; + transaction_remove(contextP, transacP); + return COAP_500_INTERNAL_SERVER_ERROR; + } + } + + if (!transacP->ack_received) + { + long unsigned timeout; + + if (0 == transacP->retrans_counter) + { + time_t tv_sec = lwm2m_gettime(); + if (0 <= tv_sec) + { + transacP->retrans_time = tv_sec + COAP_RESPONSE_TIMEOUT; + transacP->retrans_counter = 1; + timeout = 0; + } + else + { + maxRetriesReached = true; + } + } + else + { + timeout = COAP_RESPONSE_TIMEOUT << (transacP->retrans_counter - 1); + } + + if (COAP_MAX_RETRANSMIT + 1 >= transacP->retrans_counter) + { + (void)lwm2m_buffer_send(transacP->peerH, transacP->buffer, transacP->buffer_len, contextP->userData); + + transacP->retrans_time += timeout; + transacP->retrans_counter += 1; + } + else + { + maxRetriesReached = true; + } + } + + if (transacP->ack_received || maxRetriesReached) + { + if (transacP->callback) + { + transacP->callback(transacP, NULL); + } + transaction_remove(contextP, transacP); + return -1; + } + + return 0; +} + +void transaction_step(lwm2m_context_t * contextP, + time_t currentTime, + time_t * timeoutP) +{ + lwm2m_transaction_t * transacP; + + LOG("Entering"); + transacP = contextP->transactionList; + while (transacP != NULL) + { + // transaction_send() may remove transaction from the linked list + lwm2m_transaction_t * nextP = transacP->next; + int removed = 0; + + if (transacP->retrans_time <= currentTime) + { + removed = transaction_send(contextP, transacP); + } + + if (0 == removed) + { + time_t interval; + + if (transacP->retrans_time > currentTime) + { + interval = transacP->retrans_time - currentTime; + } + else + { + interval = 1; + } + + if (*timeoutP > interval) + { + *timeoutP = interval; + } + } + else + { + *timeoutP = 1; + } + + transacP = nextP; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/uri.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,329 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * Fabien Fleutot - Please refer to git log + * Toby Jaffey - Please refer to git log + * Bosch Software Innovations GmbH - Please refer to git log + * Pascal Rieux - Please refer to git log + * + *******************************************************************************/ + +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro <david.navarro@intel.com> + +*/ + +#include "internals.h" +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + + +static int prv_parseNumber(uint8_t * uriString, + size_t uriLength, + size_t * headP) +{ + int result = 0; + + if (uriString[*headP] == '/') + { + // empty Object Instance ID with resource ID is not allowed + return -1; + } + while (*headP < uriLength && uriString[*headP] != '/') + { + if ('0' <= uriString[*headP] && uriString[*headP] <= '9') + { + result += uriString[*headP] - '0'; + result *= 10; + } + else + { + return -1; + } + *headP += 1; + } + + result /= 10; + return result; +} + + +int uri_getNumber(uint8_t * uriString, + size_t uriLength) +{ + size_t index = 0; + + return prv_parseNumber(uriString, uriLength, &index); +} + + +lwm2m_uri_t * uri_decode(char * altPath, + multi_option_t *uriPath) +{ + lwm2m_uri_t * uriP; + int readNum; + + LOG_ARG("altPath: \"%s\"", altPath); + + uriP = (lwm2m_uri_t *)lwm2m_malloc(sizeof(lwm2m_uri_t)); + if (NULL == uriP) return NULL; + + memset(uriP, 0, sizeof(lwm2m_uri_t)); + + // Read object ID + if (NULL != uriPath + && URI_REGISTRATION_SEGMENT_LEN == uriPath->len + && 0 == strncmp(URI_REGISTRATION_SEGMENT, (char *)uriPath->data, uriPath->len)) + { + uriP->flag |= LWM2M_URI_FLAG_REGISTRATION; + uriPath = uriPath->next; + if (uriPath == NULL) return uriP; + } + else if (NULL != uriPath + && URI_BOOTSTRAP_SEGMENT_LEN == uriPath->len + && 0 == strncmp(URI_BOOTSTRAP_SEGMENT, (char *)uriPath->data, uriPath->len)) + { + uriP->flag |= LWM2M_URI_FLAG_BOOTSTRAP; + uriPath = uriPath->next; + if (uriPath != NULL) goto error; + return uriP; + } + + if ((uriP->flag & LWM2M_URI_MASK_TYPE) != LWM2M_URI_FLAG_REGISTRATION) + { + // Read altPath if any + if (altPath != NULL) + { + int i; + if (NULL == uriPath) + { + lwm2m_free(uriP); + return NULL; + } + for (i = 0 ; i < uriPath->len ; i++) + { + if (uriPath->data[i] != altPath[i+1]) + { + lwm2m_free(uriP); + return NULL; + } + } + uriPath = uriPath->next; + } + if (NULL == uriPath || uriPath->len == 0) + { + uriP->flag |= LWM2M_URI_FLAG_DELETE_ALL; + return uriP; + } + } + + readNum = uri_getNumber(uriPath->data, uriPath->len); + if (readNum < 0 || readNum > LWM2M_MAX_ID) goto error; + uriP->objectId = (uint16_t)readNum; + uriP->flag |= LWM2M_URI_FLAG_OBJECT_ID; + uriPath = uriPath->next; + + if ((uriP->flag & LWM2M_URI_MASK_TYPE) == LWM2M_URI_FLAG_REGISTRATION) + { + if (uriPath != NULL) goto error; + return uriP; + } + uriP->flag |= LWM2M_URI_FLAG_DM; + + if (uriPath == NULL) return uriP; + + // Read object instance + if (uriPath->len != 0) + { + readNum = uri_getNumber(uriPath->data, uriPath->len); + if (readNum < 0 || readNum >= LWM2M_MAX_ID) goto error; + uriP->instanceId = (uint16_t)readNum; + uriP->flag |= LWM2M_URI_FLAG_INSTANCE_ID; + } + uriPath = uriPath->next; + + if (uriPath == NULL) return uriP; + + // Read resource ID + if (uriPath->len != 0) + { + // resource ID without an instance ID is not allowed + if ((uriP->flag & LWM2M_URI_FLAG_INSTANCE_ID) == 0) goto error; + + readNum = uri_getNumber(uriPath->data, uriPath->len); + if (readNum < 0 || readNum > LWM2M_MAX_ID) goto error; + uriP->resourceId = (uint16_t)readNum; + uriP->flag |= LWM2M_URI_FLAG_RESOURCE_ID; + } + + // must be the last segment + if (NULL == uriPath->next) + { + LOG_URI(uriP); + return uriP; + } + +error: + LOG("Exiting on error"); + lwm2m_free(uriP); + return NULL; +} + +int lwm2m_stringToUri(const char * buffer, + size_t buffer_len, + lwm2m_uri_t * uriP) +{ + size_t head; + int readNum; + + LOG_ARG("buffer_len: %u, buffer: \"%.*s\"", buffer_len, buffer_len, buffer); + + if (buffer == NULL || buffer_len == 0 || uriP == NULL) return 0; + + memset(uriP, 0, sizeof(lwm2m_uri_t)); + + // Skip any white space + head = 0; + while (head < buffer_len && isspace(buffer[head]&0xFF)) + { + head++; + } + if (head == buffer_len) return 0; + + // Check the URI start with a '/' + if (buffer[head] != '/') return 0; + head++; + if (head == buffer_len) return 0; + + // Read object ID + readNum = prv_parseNumber((uint8_t *)buffer, buffer_len, &head); + if (readNum < 0 || readNum > LWM2M_MAX_ID) return 0; + uriP->objectId = (uint16_t)readNum; + uriP->flag |= LWM2M_URI_FLAG_OBJECT_ID; + + if (buffer[head] == '/') head += 1; + if (head >= buffer_len) + { + LOG_ARG("Parsed characters: %u", head); + LOG_URI(uriP); + return head; + } + + readNum = prv_parseNumber((uint8_t *)buffer, buffer_len, &head); + if (readNum < 0 || readNum >= LWM2M_MAX_ID) return 0; + uriP->instanceId = (uint16_t)readNum; + uriP->flag |= LWM2M_URI_FLAG_INSTANCE_ID; + + if (buffer[head] == '/') head += 1; + if (head >= buffer_len) + { + LOG_ARG("Parsed characters: %u", head); + LOG_URI(uriP); + return head; + } + + readNum = prv_parseNumber((uint8_t *)buffer, buffer_len, &head); + if (readNum < 0 || readNum >= LWM2M_MAX_ID) return 0; + uriP->resourceId = (uint16_t)readNum; + uriP->flag |= LWM2M_URI_FLAG_RESOURCE_ID; + + if (head != buffer_len) return 0; + + LOG_ARG("Parsed characters: %u", head); + LOG_URI(uriP); + + return head; +} + +int uri_toString(lwm2m_uri_t * uriP, + uint8_t * buffer, + size_t bufferLen, + uri_depth_t * depthP) +{ + size_t head; + int res; + + LOG_ARG("bufferLen: %u", bufferLen); + LOG_URI(uriP); + + buffer[0] = '/'; + + if (uriP == NULL) + { + if (depthP) *depthP = URI_DEPTH_OBJECT; + return 1; + } + + head = 1; + + res = utils_intToText(uriP->objectId, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + if (head >= bufferLen - 1) return -1; + if (depthP) *depthP = URI_DEPTH_OBJECT_INSTANCE; + + if (LWM2M_URI_IS_SET_INSTANCE(uriP)) + { + buffer[head] = '/'; + head++; + res = utils_intToText(uriP->instanceId, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + if (head >= bufferLen - 1) return -1; + if (depthP) *depthP = URI_DEPTH_RESOURCE; + if (LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + buffer[head] = '/'; + head++; + res = utils_intToText(uriP->resourceId, buffer + head, bufferLen - head); + if (res <= 0) return -1; + head += res; + if (head >= bufferLen - 1) return -1; + if (depthP) *depthP = URI_DEPTH_RESOURCE_INSTANCE; + } + } + + buffer[head] = '/'; + head++; + + LOG_ARG("length: %u, buffer: \"%.*s\"", head, head, buffer); + + return head; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/utils.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,893 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * Toby Jaffey - Please refer to git log + * + *******************************************************************************/ + +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro <david.navarro@intel.com> + +*/ + +#include "internals.h" +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <float.h> + + +int utils_plainTextToInt64(uint8_t * buffer, + int length, + int64_t * dataP) +{ + uint64_t result = 0; + int sign = 1; + int i = 0; + + if (0 == length) return 0; + + if (buffer[0] == '-') + { + sign = -1; + i = 1; + } + + while (i < length) + { + if ('0' <= buffer[i] && buffer[i] <= '9') + { + if (result > (UINT64_MAX / 10)) return 0; + result *= 10; + result += buffer[i] - '0'; + } + else + { + return 0; + } + i++; + } + + if (result > INT64_MAX) return 0; + + if (sign == -1) + { + *dataP = 0 - result; + } + else + { + *dataP = result; + } + + return 1; +} + +int utils_plainTextToFloat64(uint8_t * buffer, + int length, + double * dataP) +{ + double result; + int sign; + int i; + + if (0 == length) return 0; + + if (buffer[0] == '-') + { + sign = -1; + i = 1; + } + else + { + sign = 1; + i = 0; + } + + result = 0; + while (i < length && buffer[i] != '.') + { + if ('0' <= buffer[i] && buffer[i] <= '9') + { + if (result > (DBL_MAX / 10)) return 0; + result *= 10; + result += (buffer[i] - '0'); + } + else + { + return 0; + } + i++; + } + if (buffer[i] == '.') + { + double dec; + + i++; + if (i == length) return 0; + + dec = 0.1; + while (i < length) + { + if ('0' <= buffer[i] && buffer[i] <= '9') + { + if (result > (DBL_MAX - 1)) return 0; + result += (buffer[i] - '0') * dec; + dec /= 10; + } + else + { + return 0; + } + i++; + } + } + + *dataP = result * sign; + return 1; +} + +size_t utils_intToText(int64_t data, + uint8_t * string, + size_t length) +{ + int index; + bool minus; + size_t result; + + if (data < 0) + { + minus = true; + data = 0 - data; + } + else + { + minus = false; + } + + index = length - 1; + do + { + string[index] = '0' + data%10; + data /= 10; + index --; + } while (index >= 0 && data > 0); + + if (data > 0) return 0; + + if (minus == true) + { + if (index == 0) return 0; + string[index] = '-'; + } + else + { + index++; + } + + result = length - index; + + if (result < length) + { + memmove(string, string + index, result); + } + + return result; +} + +size_t utils_floatToText(double data, + uint8_t * string, + size_t length) +{ + size_t intLength; + size_t decLength; + int64_t intPart; + double decPart; + + if (data <= (double)INT64_MIN || data >= (double)INT64_MAX) return 0; + + intPart = (int64_t)data; + decPart = data - intPart; + if (decPart < 0) + { + decPart = 1 - decPart; + } + else + { + decPart = 1 + decPart; + } + + if (decPart <= 1 + FLT_EPSILON) + { + decPart = 0; + } + + if (intPart == 0 && data < 0) + { + // deal with numbers between -1 and 0 + if (length < 4) return 0; // "-0.n" + string[0] = '-'; + string[1] = '0'; + intLength = 2; + } + else + { + intLength = utils_intToText(intPart, string, length); + if (intLength == 0) return 0; + } + decLength = 0; + if (decPart >= FLT_EPSILON) + { + double noiseFloor; + + if (intLength >= length - 1) return 0; + + noiseFloor = FLT_EPSILON; + do + { + decPart *= 10; + noiseFloor *= 10; + } while (decPart - (int64_t)decPart > noiseFloor); + + decLength = utils_intToText(decPart, string + intLength, length - intLength); + if (decLength <= 1) return 0; + + // replace the leading 1 with a dot + string[intLength] = '.'; + } + + return intLength + decLength; +} + +size_t utils_int64ToPlainText(int64_t data, + uint8_t ** bufferP) +{ +#define _PRV_STR_LENGTH 32 + uint8_t string[_PRV_STR_LENGTH]; + size_t length; + + length = utils_intToText(data, string, _PRV_STR_LENGTH); + if (length == 0) return 0; + + *bufferP = (uint8_t *)lwm2m_malloc(length); + if (NULL == *bufferP) return 0; + + memcpy(*bufferP, string, length); + + return length; +} + + +size_t utils_float64ToPlainText(double data, + uint8_t ** bufferP) +{ + uint8_t string[_PRV_STR_LENGTH * 2]; + size_t length; + + length = utils_floatToText(data, string, _PRV_STR_LENGTH * 2); + if (length == 0) return 0; + + *bufferP = (uint8_t *)lwm2m_malloc(length); + if (NULL == *bufferP) return 0; + + memcpy(*bufferP, string, length); + + return length; +} + + +size_t utils_boolToPlainText(bool data, + uint8_t ** bufferP) +{ + return utils_int64ToPlainText((int64_t)(data?1:0), bufferP); +} + +lwm2m_binding_t utils_stringToBinding(uint8_t * buffer, + size_t length) +{ + if (length == 0) return BINDING_UNKNOWN; + + switch (buffer[0]) + { + case 'U': + switch (length) + { + case 1: + return BINDING_U; + case 2: + switch (buffer[1]) + { + case 'Q': + return BINDING_UQ; + case 'S': + return BINDING_US; + default: + break; + } + break; + case 3: + if (buffer[1] == 'Q' && buffer[2] == 'S') + { + return BINDING_UQS; + } + break; + default: + break; + } + break; + + case 'S': + switch (length) + { + case 1: + return BINDING_S; + case 2: + if (buffer[1] == 'Q') + { + return BINDING_SQ; + } + break; + default: + break; + } + break; + + default: + break; + } + + return BINDING_UNKNOWN; +} + +lwm2m_media_type_t utils_convertMediaType(coap_content_type_t type) +{ + // Here we just check the content type is a valid value for LWM2M + switch((uint16_t)type) + { + case TEXT_PLAIN: + return LWM2M_CONTENT_TEXT; + case APPLICATION_OCTET_STREAM: + return LWM2M_CONTENT_OPAQUE; + case LWM2M_CONTENT_TLV_OLD: + return LWM2M_CONTENT_TLV_OLD; + case LWM2M_CONTENT_TLV: + return LWM2M_CONTENT_TLV; + case LWM2M_CONTENT_JSON_OLD: + return LWM2M_CONTENT_JSON_OLD; + case LWM2M_CONTENT_JSON: + return LWM2M_CONTENT_JSON; + case APPLICATION_LINK_FORMAT: + return LWM2M_CONTENT_LINK; + + default: + return LWM2M_CONTENT_TEXT; + } +} + +#ifdef LWM2M_CLIENT_MODE +lwm2m_server_t * utils_findServer(lwm2m_context_t * contextP, + void * fromSessionH) +{ + lwm2m_server_t * targetP; + + targetP = contextP->serverList; + while (targetP != NULL + && false == lwm2m_session_is_equal(targetP->sessionH, fromSessionH, contextP->userData)) + { + targetP = targetP->next; + } + + return targetP; +} +#endif + +lwm2m_server_t * utils_findBootstrapServer(lwm2m_context_t * contextP, + void * fromSessionH) +{ +#ifdef LWM2M_CLIENT_MODE + + lwm2m_server_t * targetP; + + targetP = contextP->bootstrapServerList; + while (targetP != NULL + && false == lwm2m_session_is_equal(targetP->sessionH, fromSessionH, contextP->userData)) + { + targetP = targetP->next; + } + + return targetP; + +#else + + return NULL; + +#endif +} + +int utils_isAltPathValid(const char * altPath) +{ + int i; + + if (altPath == NULL) return 0; + + if (altPath[0] != '/') return 0; + + for (i = 1 ; altPath[i] != 0 ; i++) + { + // TODO: Support multi-segment alternative path + if (altPath[i] == '/') return 0; + // TODO: Check needs for sub-delims, ':' and '@' + if ((altPath[i] < 'A' || altPath[i] > 'Z') // ALPHA + && (altPath[i] < 'a' || altPath[i] > 'z') + && (altPath[i] < '0' || altPath[i] > '9') // DIGIT + && (altPath[i] != '-') // Other unreserved + && (altPath[i] != '.') + && (altPath[i] != '_') + && (altPath[i] != '~') + && (altPath[i] != '%')) // pct_encoded + { + return 0; + } + + } + return 1; +} + +// copy a string in a buffer. +// return the number of copied bytes or -1 if the buffer is not large enough +int utils_stringCopy(char * buffer, + size_t length, + const char * str) +{ + size_t i; + + for (i = 0 ; i < length && str[i] != 0 ; i++) + { + buffer[i] = str[i]; + } + + if (i == length) return -1; + + buffer[i] = 0; + + return (int)i; +} + +int utils_intCopy(char * buffer, + size_t length, + int32_t value) +{ +#define _PRV_INT32_MAX_STR_LEN 11 + uint8_t str[_PRV_INT32_MAX_STR_LEN]; + size_t len; + + len = utils_intToText(value, str, _PRV_INT32_MAX_STR_LEN); + if (len == 0) return -1; + if (len > length + 1) return -1; + + memcpy(buffer, str, len); + buffer[len] = 0; + + return len; +} + +void utils_copyValue(void * dst, + const void * src, + size_t len) +{ +#ifdef LWM2M_BIG_ENDIAN + memcpy(dst, src, len); +#else +#ifdef LWM2M_LITTLE_ENDIAN + size_t i; + + for (i = 0; i < len; i++) + { + ((uint8_t *)dst)[i] = ((uint8_t *)src)[len - 1 - i]; + } +#endif +#endif +} + +int utils_opaqueToInt(const uint8_t * buffer, + size_t buffer_len, + int64_t * dataP) +{ + *dataP = 0; + + switch (buffer_len) + { + case 1: + { + *dataP = (int8_t)buffer[0]; + + break; + } + + case 2: + { + int16_t value; + + utils_copyValue(&value, buffer, buffer_len); + + *dataP = value; + break; + } + + case 4: + { + int32_t value; + + utils_copyValue(&value, buffer, buffer_len); + + *dataP = value; + break; + } + + case 8: + utils_copyValue(dataP, buffer, buffer_len); + return buffer_len; + + default: + return 0; + } + + return buffer_len; +} + +int utils_opaqueToFloat(const uint8_t * buffer, + size_t buffer_len, + double * dataP) +{ + switch (buffer_len) + { + case 4: + { + float temp; + + utils_copyValue(&temp, buffer, buffer_len); + + *dataP = temp; + } + return 4; + + case 8: + utils_copyValue(dataP, buffer, buffer_len); + return 8; + + default: + return 0; + } +} + +/** +* Encode an integer value to a byte representation. +* Returns the length of the result. For values < 0xff length is 1, +* for values < 0xffff length is 2 and so on. +* @param data Input value +* @param data_buffer Result in data_buffer is in big endian encoding +* Negative values are represented in two's complement as of +* OMA-TS-LightweightM2M-V1_0-20160308-D, Appendix C +*/ +size_t utils_encodeInt(int64_t data, + uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE]) +{ + size_t length = 0; + + memset(data_buffer, 0, _PRV_64BIT_BUFFER_SIZE); + + if (data >= INT8_MIN && data <= INT8_MAX) + { + length = 1; + data_buffer[0] = data; + } + else if (data >= INT16_MIN && data <= INT16_MAX) + { + int16_t value; + + value = data; + length = 2; + data_buffer[0] = (value >> 8) & 0xFF; + data_buffer[1] = value & 0xFF; + } + else if (data >= INT32_MIN && data <= INT32_MAX) + { + int32_t value; + + value = data; + length = 4; + utils_copyValue(data_buffer, &value, length); + } + else if (data >= INT64_MIN && data <= INT64_MAX) + { + length = 8; + utils_copyValue(data_buffer, &data, length); + } + + return length; +} + +size_t utils_encodeFloat(double data, + uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE]) +{ + size_t length = 0; + + memset(data_buffer, 0, _PRV_64BIT_BUFFER_SIZE); + + if ((data < 0.0 - (double)FLT_MAX) || (data >(double)FLT_MAX)) + { + length = 8; + utils_copyValue(data_buffer, &data, 8); + } + else + { + float value; + + length = 4; + value = (float)data; + utils_copyValue(data_buffer, &value, 4); + } + + return length; +} + +#define PRV_B64_PADDING '=' + +static char b64Alphabet[64] = +{ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +static void prv_encodeBlock(uint8_t input[3], + uint8_t output[4]) +{ + output[0] = b64Alphabet[input[0] >> 2]; + output[1] = b64Alphabet[((input[0] & 0x03) << 4) | (input[1] >> 4)]; + output[2] = b64Alphabet[((input[1] & 0x0F) << 2) | (input[2] >> 6)]; + output[3] = b64Alphabet[input[2] & 0x3F]; +} + +static uint8_t prv_b64Revert(uint8_t value) +{ + if (value >= 'A' && value <= 'Z') + { + return (value - 'A'); + } + if (value >= 'a' && value <= 'z') + { + return (26 + value - 'a'); + } + if (value >= '0' && value <= '9') + { + return (52 + value - '0'); + } + switch (value) + { + case '+': + return 62; + case '/': + return 63; + default: + return 0; + } +} + +static void prv_decodeBlock(uint8_t input[4], + uint8_t output[3]) +{ + uint8_t tmp[4]; + int i; + + memset(output, 0, 3); + + for (i = 0; i < 4; i++) + { + tmp[i] = prv_b64Revert(input[i]); + } + + output[0] = (tmp[0] << 2) | (tmp[1] >> 4); + output[1] = (tmp[1] << 4) | (tmp[2] >> 2); + output[2] = (tmp[2] << 6) | tmp[3]; +} + +static size_t prv_getBase64Size(size_t dataLen) +{ + size_t result_len; + + result_len = 4 * (dataLen / 3); + if (dataLen % 3) result_len += 4; + + return result_len; +} + +size_t utils_base64Encode(uint8_t * dataP, + size_t dataLen, + uint8_t * bufferP, + size_t bufferLen) +{ + unsigned int data_index; + unsigned int result_index; + size_t result_len; + + result_len = prv_getBase64Size(dataLen); + + if (result_len > bufferLen) return 0; + + data_index = 0; + result_index = 0; + while (data_index < dataLen) + { + switch (dataLen - data_index) + { + case 0: + // should never happen + break; + case 1: + bufferP[result_index] = b64Alphabet[dataP[data_index] >> 2]; + bufferP[result_index + 1] = b64Alphabet[(dataP[data_index] & 0x03) << 4]; + bufferP[result_index + 2] = PRV_B64_PADDING; + bufferP[result_index + 3] = PRV_B64_PADDING; + break; + case 2: + bufferP[result_index] = b64Alphabet[dataP[data_index] >> 2]; + bufferP[result_index + 1] = b64Alphabet[(dataP[data_index] & 0x03) << 4 | (dataP[data_index + 1] >> 4)]; + bufferP[result_index + 2] = b64Alphabet[(dataP[data_index + 1] & 0x0F) << 2]; + bufferP[result_index + 3] = PRV_B64_PADDING; + break; + default: + prv_encodeBlock(dataP + data_index, bufferP + result_index); + break; + } + data_index += 3; + result_index += 4; + } + + return result_len; +} + +size_t utils_opaqueToBase64(uint8_t * dataP, + size_t dataLen, + uint8_t ** bufferP) +{ + size_t buffer_len; + size_t result_len; + + buffer_len = prv_getBase64Size(dataLen); + + *bufferP = (uint8_t *)lwm2m_malloc(buffer_len); + if (!*bufferP) return 0; + memset(*bufferP, 0, buffer_len); + + result_len = utils_base64Encode(dataP, dataLen, *bufferP, buffer_len); + + if (result_len == 0) + { + lwm2m_free(*bufferP); + *bufferP = NULL; + } + + return result_len; +} + +size_t utils_base64ToOpaque(uint8_t * dataP, + size_t dataLen, + uint8_t ** bufferP) +{ + size_t data_index; + size_t result_index; + size_t result_len; + + if (dataLen % 4) return 0; + + result_len = (dataLen >> 2) * 3; + *bufferP = (uint8_t *)lwm2m_malloc(result_len); + if (NULL == *bufferP) return 0; + memset(*bufferP, 0, result_len); + + // remove padding + while (dataP[dataLen - 1] == PRV_B64_PADDING) + { + dataLen--; + } + + data_index = 0; + result_index = 0; + while (data_index < dataLen) + { + prv_decodeBlock(dataP + data_index, *bufferP + result_index); + data_index += 4; + result_index += 3; + } + switch (data_index - dataLen) + { + case 0: + break; + case 2: + { + uint8_t tmp[2]; + + tmp[0] = prv_b64Revert(dataP[dataLen - 2]); + tmp[1] = prv_b64Revert(dataP[dataLen - 1]); + + *bufferP[result_index - 3] = (tmp[0] << 2) | (tmp[1] >> 4); + *bufferP[result_index - 2] = (tmp[1] << 4); + result_len -= 2; + } + break; + case 3: + { + uint8_t tmp[3]; + + tmp[0] = prv_b64Revert(dataP[dataLen - 3]); + tmp[1] = prv_b64Revert(dataP[dataLen - 2]); + tmp[2] = prv_b64Revert(dataP[dataLen - 1]); + + *bufferP[result_index - 3] = (tmp[0] << 2) | (tmp[1] >> 4); + *bufferP[result_index - 2] = (tmp[1] << 4) | (tmp[2] >> 2); + *bufferP[result_index - 1] = (tmp[2] << 6); + result_len -= 1; + } + break; + default: + // error + lwm2m_free(*bufferP); + *bufferP = NULL; + result_len = 0; + break; + } + + return result_len; +} + +lwm2m_data_type_t utils_depthToDatatype(uri_depth_t depth) +{ + switch (depth) + { + case URI_DEPTH_OBJECT: + return LWM2M_TYPE_OBJECT; + case URI_DEPTH_OBJECT_INSTANCE: + return LWM2M_TYPE_OBJECT_INSTANCE; + default: + break; + } + + return LWM2M_TYPE_UNDEFINED; +}