* initial commit

This commit is contained in:
Danyi Dávid 2017-10-17 22:07:27 +02:00
commit ac2baaf33d
23 changed files with 13590 additions and 0 deletions

10
library.properties Normal file
View File

@ -0,0 +1,10 @@
name=Wakaama Core
version=0.1.1
author=Eclipse
maintainer=
sentence=Eclipse Wakaama lwm2m protocoll implementation
paragraph=
category=Communication
url=
architectures=samd
includes=

153
src/block1.c Normal file
View File

@ -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
uint8_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);
}
}

768
src/bootstrap.c Normal file
View File

@ -0,0 +1,768 @@
/*******************************************************************************
*
* 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;
bootstrapServer->registration = lwm2m_gettime() + COAP_EXCHANGE_LIFETIME;
}
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:
if (targetP->registration <= currentTime)
{
targetP->status = STATE_BS_FAILING;
*timeoutP = 0;
}
else if (*timeoutP > targetP->registration - currentTime)
{
*timeoutP = targetP->registration - currentTime;
}
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;
}
}
uint8_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)
{
if (object_getServers(context, true) == 0)
{
LOG("Bootstrap server status changed to STATE_BS_FINISHING");
bootstrapServer->status = STATE_BS_FINISHING;
return COAP_204_CHANGED;
}
else
{
return COAP_406_NOT_ACCEPTABLE;
}
}
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:
case STATE_BS_FAILING:
bs_status = STATE_BS_PENDING;
break;
default:
break;
}
targetP = targetP->next;
}
LOG_ARG("Returned status: %s", STR_STATUS(bs_status));
return bs_status;
}
static uint8_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:
serverP->registration = lwm2m_gettime() + COAP_EXCHANGE_LIFETIME;
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;
}
}
uint8_t bootstrap_handleCommand(lwm2m_context_t * contextP,
lwm2m_uri_t * uriP,
lwm2m_server_t * serverP,
coap_packet_t * message,
coap_packet_t * response)
{
uint8_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;
}
uint8_t bootstrap_handleDeleteAll(lwm2m_context_t * contextP,
void * fromSessionH)
{
lwm2m_server_t * serverP;
uint8_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

656
src/data.c Normal file
View File

@ -0,0 +1,656 @@
/*******************************************************************************
*
* 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>
#define _PRV_STR_LENGTH 32
// 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:
{
uint8_t intString[_PRV_STR_LENGTH];
res = utils_intToText(dataP->value.asInteger, intString, _PRV_STR_LENGTH);
if (res == 0) return -1;
*bufferP = (uint8_t *)lwm2m_malloc(res);
if (NULL == *bufferP) return -1;
memcpy(*bufferP, intString, res);
return (int)res;
}
case LWM2M_TYPE_FLOAT:
{
uint8_t floatString[_PRV_STR_LENGTH * 2];
res = utils_floatToText(dataP->value.asFloat, floatString, _PRV_STR_LENGTH * 2);
if (res == 0) return -1;
*bufferP = (uint8_t *)lwm2m_malloc(res);
if (NULL == *bufferP) return -1;
memcpy(*bufferP, floatString, res);
return (int)res;
}
case LWM2M_TYPE_BOOLEAN:
*bufferP = (uint8_t *)lwm2m_malloc(1);
if (NULL == *bufferP) return -1;
*bufferP[0] = dataP->value.asBoolean ? '1' : '0';
return 1;
case LWM2M_TYPE_OBJECT_LINK:
{
char stringBuffer[11];
size_t length;
length = utils_intToText(dataP->value.asObjLink.objectId, (uint8_t*)stringBuffer, 5);
if (length == 0) return -1;
stringBuffer[5] = ':';
res = length + 1;
length = utils_intToText(dataP->value.asObjLink.objectInstanceId, (uint8_t*)stringBuffer + res, 5);
if (length == 0) return -1;
res += length;
*bufferP = (uint8_t *)lwm2m_malloc(res);
if (*bufferP == NULL) return -1;
memcpy(*bufferP, stringBuffer, res);
return res;
}
case LWM2M_TYPE_OPAQUE:
{
size_t length;
length = utils_base64GetSize(dataP->value.asBuffer.length);
*bufferP = (uint8_t *)lwm2m_malloc(length);
if (*bufferP == NULL) return 0;
length = utils_base64Encode(dataP->value.asBuffer.buffer, dataP->value.asBuffer.length, *bufferP, length);
if (length == 0)
{
lwm2m_free(*bufferP);
*bufferP = NULL;
return 0;
}
return (int)length;
}
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)
{
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_textToInt(dataP->value.asBuffer.buffer, dataP->value.asBuffer.length, valueP);
break;
case LWM2M_TYPE_OPAQUE:
switch (dataP->value.asBuffer.length)
{
case 1:
*valueP = (int8_t)dataP->value.asBuffer.buffer[0];
result = 1;
break;
case 2:
{
int16_t value;
utils_copyValue(&value, dataP->value.asBuffer.buffer, dataP->value.asBuffer.length);
*valueP = value;
result = 1;
break;
}
case 4:
{
int32_t value;
utils_copyValue(&value, dataP->value.asBuffer.buffer, dataP->value.asBuffer.length);
*valueP = value;
result = 1;
break;
}
case 8:
utils_copyValue(valueP, dataP->value.asBuffer.buffer, dataP->value.asBuffer.length);
result = 1;
break;
default:
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_textToFloat(dataP->value.asBuffer.buffer, dataP->value.asBuffer.length, valueP);
break;
case LWM2M_TYPE_OPAQUE:
switch (dataP->value.asBuffer.length)
{
case 4:
{
float temp;
utils_copyValue(&temp, dataP->value.asBuffer.buffer, dataP->value.asBuffer.length);
*valueP = temp;
result = 1;
}
break;
case 8:
utils_copyValue(valueP, dataP->value.asBuffer.buffer, dataP->value.asBuffer.length);
result = 1;
break;
default:
result = 0;
}
break;
default:
result = 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)
{
int res;
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;
res = prv_setBuffer(*dataP, buffer, bufferLen);
if (res == 0)
{
lwm2m_data_free(1, *dataP);
*dataP = NULL;
}
return res;
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;
res = prv_setBuffer(*dataP, buffer, bufferLen);
if (res == 0)
{
lwm2m_data_free(1, *dataP);
*dataP = NULL;
}
return res;
#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_OPAQUE
&& dataP->type != LWM2M_TYPE_OPAQUE)
{
LOG("Opaque format is reserved to opaque resources.");
return -1;
}
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:
case LWM2M_CONTENT_TLV_OLD:
{
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:
case LWM2M_CONTENT_JSON_OLD:
return json_serialize(uriP, size, dataP, bufferP);
#endif
default:
return -1;
}
}

442
src/discover.c Normal file
View File

@ -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

30
src/er-coap-13/LICENSE Normal file
View File

@ -0,0 +1,30 @@
/*
* 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.
*/

1385
src/er-coap-13/er-coap-13.c Normal file

File diff suppressed because it is too large Load Diff

407
src/er-coap-13/er-coap-13.h Normal file
View File

@ -0,0 +1,407 @@
/*
* 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_
#ifdef __cplusplus
extern "C" {
#endif
#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_LATENCY 100
#define COAP_PROCESSING_DELAY COAP_RESPONSE_TIMEOUT
#define COAP_MAX_TRANSMIT_WAIT ((COAP_RESPONSE_TIMEOUT * ( (1 << (COAP_MAX_RETRANSMIT + 1) ) - 1) * COAP_ACK_RANDOM_FACTOR))
#define COAP_MAX_TRANSMIT_SPAN ((COAP_RESPONSE_TIMEOUT * ( (1 << COAP_MAX_RETRANSMIT) - 1) * COAP_ACK_RANDOM_FACTOR))
#define COAP_EXCHANGE_LIFETIME (COAP_MAX_TRANSMIT_SPAN + (2 * COAP_MAX_LATENCY) + COAP_PROCESSING_DELAY)
#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) {if (opt <= sizeof((packet)->options) * OPTION_MAP_SIZE) {(packet)->options[opt / OPTION_MAP_SIZE] |= 1 << (opt % OPTION_MAP_SIZE);}}
#define IS_OPTION(packet, opt) ((opt <= sizeof((packet)->options) * OPTION_MAP_SIZE)?(packet)->options[opt / OPTION_MAP_SIZE] & (1 << (opt % OPTION_MAP_SIZE)):0)
#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);
#ifdef __cplusplus
}
#endif
#endif /* COAP_13_H_ */

349
src/internals.h Normal file
View File

@ -0,0 +1,349 @@
/*******************************************************************************
*
* 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
* Scott Bertin - 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_
#ifdef __cplusplus
extern "C" {
#endif
#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
uint8_t object_readData(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, int * sizeP, lwm2m_data_t ** dataP);
uint8_t object_read(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_media_type_t * formatP, uint8_t ** bufferP, size_t * lengthP);
uint8_t object_write(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, size_t length);
uint8_t object_create(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, size_t length);
uint8_t object_execute(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, uint8_t * buffer, size_t length);
uint8_t object_delete(lwm2m_context_t * contextP, lwm2m_uri_t * uriP);
uint8_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_getRegisterPayloadBufferLength(lwm2m_context_t * contextP);
int object_getRegisterPayload(lwm2m_context_t * contextP, uint8_t * buffer, size_t length);
int object_getServers(lwm2m_context_t * contextP, bool checkOnly);
uint8_t object_createInstance(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_data_t * dataP);
uint8_t object_writeInstance(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_data_t * dataP);
// 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
uint8_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
uint8_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);
uint8_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);
void observe_clear(lwm2m_context_t * contextP, lwm2m_uri_t * uriP);
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
uint8_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
uint8_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);
uint8_t bootstrap_handleCommand(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_server_t * serverP, coap_packet_t * message, coap_packet_t * response);
uint8_t bootstrap_handleDeleteAll(lwm2m_context_t * context, void * fromSessionH);
uint8_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
uint8_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);
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_textToInt(uint8_t * buffer, int length, int64_t * dataP);
int utils_textToFloat(uint8_t * buffer, int length, double * dataP);
void utils_copyValue(void * dst, const void * src, size_t len);
size_t utils_base64GetSize(size_t dataLen);
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
#ifdef __cplusplus
}
#endif
#endif

1436
src/json.c Normal file

File diff suppressed because it is too large Load Diff

484
src/liblwm2m.c Normal file
View File

@ -0,0 +1,484 @@
/*******************************************************************************
*
* 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 "liblwm2m.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, void *userData)
{
// TODO parse transaction and observation to remove the ones related to this server
if (serverP->sessionH != NULL)
{
lwm2m_close_connection(serverP->sessionH, userData);
}
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, context->userData);
}
}
static void prv_deleteBootstrapServer(lwm2m_server_t * serverP, void *userData)
{
// TODO should we free location as in prv_deleteServer ?
// TODO should we parse transaction and observation to remove the ones related to this server ?
if (serverP->sessionH != NULL)
{
lwm2m_close_connection(serverP->sessionH, userData);
}
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, context->userData);
}
}
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, contextP->userData);
}
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, contextP->userData);
}
targetP = nextP;
}
return object_getServers(contextP, false);
}
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;
}

743
src/liblwm2m.h Normal file
View File

@ -0,0 +1,743 @@
/*******************************************************************************
*
* 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
* Ville Skyttä - 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_
#define LWM2M_LITTLE_ENDIAN
#define LWM2M_CLIENT_MODE
#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
/*
* Resource 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
/*
* Resource 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;
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;
lwm2m_media_type_t format;
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

125
src/list.c Normal file
View File

@ -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);
}
}

786
src/management.c Normal file
View File

@ -0,0 +1,786 @@
/*******************************************************************************
*
* 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_textToInt(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_textToInt(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_textToFloat(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_textToFloat(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_textToFloat(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;
}
uint8_t dm_handleRequest(lwm2m_context_t * contextP,
lwm2m_uri_t * uriP,
lwm2m_server_t * serverP,
coap_packet_t * message,
coap_packet_t * response)
{
uint8_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)
{
if (IS_OPTION(message, COAP_OPTION_ACCEPT))
{
format = utils_convertMediaType(message->accept[0]);
}
else
{
format = LWM2M_CONTENT_TLV;
}
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), (uint8_t*)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), (uint8_t*)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), (uint8_t*)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), (uint8_t*)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), (uint8_t*)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

926
src/objects.c Normal file
View File

@ -0,0 +1,926 @@
/*******************************************************************************
*
* 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
* Scott Bertin - 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)
{
uint8_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)
{
uint8_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;
}
uint8_t object_readData(lwm2m_context_t * contextP,
lwm2m_uri_t * uriP,
int * sizeP,
lwm2m_data_t ** dataP)
{
uint8_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;
}
uint8_t object_read(lwm2m_context_t * contextP,
lwm2m_uri_t * uriP,
lwm2m_media_type_t * formatP,
uint8_t ** bufferP,
size_t * lengthP)
{
uint8_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;
}
uint8_t object_write(lwm2m_context_t * contextP,
lwm2m_uri_t * uriP,
lwm2m_media_type_t format,
uint8_t * buffer,
size_t length)
{
uint8_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;
}
uint8_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);
}
uint8_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;
}
uint8_t object_delete(lwm2m_context_t * contextP,
lwm2m_uri_t * uriP)
{
lwm2m_object_t * objectP;
uint8_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);
if (result == COAP_202_DELETED)
{
observe_clear(contextP, uriP);
}
}
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);
if (result == COAP_202_DELETED)
{
uriP->flag |= LWM2M_URI_FLAG_INSTANCE_ID;
uriP->instanceId = instanceP->id;
observe_clear(contextP, uriP);
uriP->flag &= ~LWM2M_URI_FLAG_INSTANCE_ID;
}
instanceP = objectP->instanceList;
}
}
LOG_ARG("result: %u.%2u", (result & 0xFF) >> 5, (result & 0x1F));
return result;
}
uint8_t object_discover(lwm2m_context_t * contextP,
lwm2m_uri_t * uriP,
lwm2m_server_t * serverP,
uint8_t ** bufferP,
size_t * lengthP)
{
uint8_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_intToText(id, buffer + index, length - index);
if (result == 0) return -1;
index += result;
if (length - index < REG_OBJECT_MIN_LEN - 3) return -1;
buffer[index] = '/';
index++;
return index;
}
int object_getRegisterPayloadBufferLength(lwm2m_context_t * contextP)
{
size_t index;
int result;
lwm2m_object_t * objectP;
char buffer[REG_OBJECT_MIN_LEN + 5];
LOG("Entering");
index = strlen(REG_START);
if ((contextP->altPath != NULL)
&& (contextP->altPath[0] != 0))
{
index += strlen(contextP->altPath);
}
else
{
index += strlen(REG_DEFAULT_PATH);
}
index += strlen(REG_LWM2M_RESOURCE_TYPE);
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, sizeof(buffer), objectP->objID);
if (result < 0) return 0;
length = result;
index += length;
if (objectP->instanceList == NULL)
{
index -= 1;
index += strlen(REG_PATH_END);
}
else
{
lwm2m_list_t * targetP;
for (targetP = objectP->instanceList ; targetP != NULL ; targetP = targetP->next)
{
if (index != start + length)
{
index += length;
}
result = utils_intToText(targetP->id, buffer, sizeof(buffer));
if (result == 0) return 0;
index += result;
index += strlen(REG_PATH_END);
}
}
}
index += 1; // account for trailing null
// Note that object_getRegisterPayload() has REG_PATH_END added after each
// object or instance, and then the trailing comma is replaced by null. The
// trailing nulls are not counted as part of the payload length, so this
// will return a size two bytes greater than what
// object_getRegisterPayload() returns.
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 (index != start + length)
{
if (bufferLen - index <= length) return 0;
memcpy(buffer + index, buffer + start, length);
index += length;
}
result = utils_intToText(targetP->id, buffer + index, bufferLen - index);
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, bool checkOnly)
{
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;
if (checkOnly)
{
lwm2m_free(targetP);
}
else
{
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);
}
else
{
if (0 != prv_getMandatoryInfo(serverObjP, serverInstP->id, targetP))
{
lwm2m_free(targetP);
lwm2m_data_free(size, dataP);
return -1;
}
targetP->status = STATE_DEREGISTERED;
if (checkOnly)
{
lwm2m_free(targetP);
}
else
{
contextP->serverList = (lwm2m_server_t*)LWM2M_LIST_ADD(contextP->serverList, targetP);
}
}
}
lwm2m_data_free(size, dataP);
}
securityInstP = securityInstP->next;
}
return 0;
}
uint8_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);
}
uint8_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

1076
src/observe.c Normal file

File diff suppressed because it is too large Load Diff

443
src/packet.c Normal file
View File

@ -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 uint8_t handle_request(lwm2m_context_t * contextP,
void * fromSessionH,
coap_packet_t * message,
coap_packet_t * response)
{
lwm2m_uri_t * uriP;
uint8_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)
{
uint8_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);
}
}
uint8_t message_send(lwm2m_context_t * contextP,
coap_packet_t * message,
void * sessionH)
{
uint8_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;
}

1382
src/registration.c Normal file

File diff suppressed because it is too large Load Diff

580
src/tlv.c Normal file
View File

@ -0,0 +1,580 @@
/*******************************************************************************
*
* 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 size_t prv_encodeFloat(double data,
uint8_t * data_buffer)
{
size_t length = 0;
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;
}
static size_t prv_encodeInt(int64_t data,
uint8_t * data_buffer)
{
size_t length = 0;
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;
}
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 = prv_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 = prv_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 = prv_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;
}

472
src/transaction.c Normal file
View File

@ -0,0 +1,472 @@
/*******************************************************************************
*
* 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"
#include "liblwm2m.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)
{
char stringID[LWM2M_STRING_ID_MAX_LEN];
result = utils_intToText(uriP->objectId, (uint8_t*)stringID, LWM2M_STRING_ID_MAX_LEN);
if (result == 0) goto error;
stringID[result] = 0;
coap_set_header_uri_path_segment(transacP->message, stringID);
if (LWM2M_URI_IS_SET_INSTANCE(uriP))
{
result = utils_intToText(uriP->instanceId, (uint8_t*)stringID, LWM2M_STRING_ID_MAX_LEN);
if (result == 0) goto error;
stringID[result] = 0;
coap_set_header_uri_path_segment(transacP->message, stringID);
}
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_intToText(uriP->resourceId, (uint8_t*)stringID, LWM2M_STRING_ID_MAX_LEN);
if (result == 0) goto error;
stringID[result] = 0;
coap_set_header_uri_path_segment(transacP->message, stringID);
}
}
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)
{
coap_free_header(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)
{
transaction_remove(contextP, transacP);
return COAP_500_INTERNAL_SERVER_ERROR;
}
transacP->buffer = (uint8_t*)lwm2m_malloc(transacP->buffer_len);
if (transacP->buffer == NULL)
{
transaction_remove(contextP, transacP);
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;
}
}

328
src/uri.c Normal file
View File

@ -0,0 +1,328 @@
/*******************************************************************************
*
* 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;
}

554
src/utils.c Normal file
View File

@ -0,0 +1,554 @@
/*******************************************************************************
*
* 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_textToInt(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_textToFloat(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;
}
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;
}
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
}
#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];
}
size_t utils_base64GetSize(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 = utils_base64GetSize(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;
}
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;
}

55
src/wakaama.cmake Normal file
View File

@ -0,0 +1,55 @@
# Provides WAKAAMA_SOURCES_DIR and WAKAAMA_SOURCES and WAKAAMA_DEFINITIONS variables.
# Add LWM2M_WITH_LOGS to compile definitions to enable logging.
# Set LWM2M_LITTLE_ENDIAN to FALSE or TRUE according to your destination platform or leave
# it unset to determine endianess automatically.
set(WAKAAMA_SOURCES_DIR ${CMAKE_CURRENT_LIST_DIR})
set(EXT_SOURCES
${WAKAAMA_SOURCES_DIR}/er-coap-13/er-coap-13.c
${WAKAAMA_SOURCES_DIR}/er-coap-13/er-coap-13.h)
set(CORE_HEADERS
${WAKAAMA_SOURCES_DIR}/liblwm2m.h)
set(WAKAAMA_SOURCES
${WAKAAMA_SOURCES_DIR}/liblwm2m.c
${WAKAAMA_SOURCES_DIR}/uri.c
${WAKAAMA_SOURCES_DIR}/utils.c
${WAKAAMA_SOURCES_DIR}/objects.c
${WAKAAMA_SOURCES_DIR}/tlv.c
${WAKAAMA_SOURCES_DIR}/data.c
${WAKAAMA_SOURCES_DIR}/list.c
${WAKAAMA_SOURCES_DIR}/packet.c
${WAKAAMA_SOURCES_DIR}/transaction.c
${WAKAAMA_SOURCES_DIR}/registration.c
${WAKAAMA_SOURCES_DIR}/bootstrap.c
${WAKAAMA_SOURCES_DIR}/management.c
${WAKAAMA_SOURCES_DIR}/observe.c
${WAKAAMA_SOURCES_DIR}/json.c
${WAKAAMA_SOURCES_DIR}/discover.c
${WAKAAMA_SOURCES_DIR}/block1.c
${WAKAAMA_SOURCES_DIR}/internals.h
${CORE_HEADERS}
${EXT_SOURCES})
# This will not work for multi project cmake generators like the Visual Studio Generator
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(WAKAAMA_DEFINITIONS ${WAKAAMA_DEFINITIONS} -DLWM2M_WITH_LOGS)
endif()
# Automatically determine endianess. This can be overwritten by setting LWM2M_LITTLE_ENDIAN
# accordingly in a cross compile toolchain file.
if(NOT DEFINED LWM2M_LITTLE_ENDIAN)
include(TestBigEndian)
TEST_BIG_ENDIAN(LWM2M_BIG_ENDIAN)
if (LWM2M_BIG_ENDIAN)
set(LWM2M_LITTLE_ENDIAN FALSE)
else()
set(LWM2M_LITTLE_ENDIAN TRUE)
endif()
endif()
if (LWM2M_LITTLE_ENDIAN)
set(WAKAAMA_DEFINITIONS ${WAKAAMA_DEFINITIONS} -DLWM2M_LITTLE_ENDIAN)
endif()