* initial commit
This commit is contained in:
commit
a10a8ac253
8
arduino-base.cmake
Normal file
8
arduino-base.cmake
Normal file
@ -0,0 +1,8 @@
|
||||
set(ARDUINO_BASE_LIBDIR /opt/arduino-1.8.5)
|
||||
set(ARDUINO_SAMD_DIR /home/yvan/.arduino15/packages/arduino/hardware/samd/1.6.16)
|
||||
|
||||
set(ARDUINO_INCLUDE_DIRS
|
||||
${ARDUINO_SAMD_DIR}/cores/arduino
|
||||
${ARDUINO_SAMD_DIR}/libraries/SPI
|
||||
${ARDUINO_BASE_LIBDIR}/libraries/Ethernet/src
|
||||
)
|
||||
2
wakaama-client/.gitignore
vendored
Normal file
2
wakaama-client/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.idea
|
||||
cmake-build-debug
|
||||
31
wakaama-client/CMakeLists.txt
Normal file
31
wakaama-client/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
cmake_minimum_required (VERSION 3.0)
|
||||
|
||||
project (lightclient)
|
||||
|
||||
if(DTLS)
|
||||
message(FATAL_ERROR "DTLS option is not supported." )
|
||||
endif()
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../arduino-base.cmake)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../wakaama-core/wakaama.cmake)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/shared.cmake)
|
||||
|
||||
add_definitions(-DLWM2M_CLIENT_MODE)
|
||||
add_definitions(${SHARED_DEFINITIONS} ${WAKAAMA_DEFINITIONS})
|
||||
|
||||
include_directories (${WAKAAMA_SOURCES_DIR} ${SHARED_INCLUDE_DIRS} ${ARDUINO_INCLUDE_DIRS})
|
||||
|
||||
SET(SOURCES
|
||||
${CMAKE_CURRENT_LIST_DIR}/wakaama-client.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/object_security.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/object_server.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/object_device.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/test_object.c
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${SOURCES} ${WAKAAMA_SOURCES} ${SHARED_SOURCES})
|
||||
|
||||
# Add WITH_LOGS to debug variant
|
||||
set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY COMPILE_DEFINITIONS $<$<CONFIG:Debug>:WITH_LOGS>)
|
||||
|
||||
SOURCE_GROUP(wakaama FILES ${WAKAAMA_SOURCES})
|
||||
146
wakaama-client/connection.cpp
Normal file
146
wakaama-client/connection.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* 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
|
||||
* Pascal Rieux - Please refer to git log
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <Dns.h>
|
||||
#include <Ethernet.h>
|
||||
#include <IPAddress.h>
|
||||
#include "connection.h"
|
||||
|
||||
|
||||
connection_t * connection_find(connection_t * connList,
|
||||
EthernetUDP * udpConnection)
|
||||
{
|
||||
connection_t * connP;
|
||||
|
||||
connP = connList;
|
||||
while (connP != nullptr)
|
||||
{
|
||||
if (connP->udpConnection == udpConnection)
|
||||
{
|
||||
return connP;
|
||||
}
|
||||
connP = connP->next;
|
||||
}
|
||||
|
||||
return connP;
|
||||
}
|
||||
|
||||
|
||||
connection_t * connection_new_incoming(connection_t * connList,
|
||||
EthernetUDP * udpConnection,
|
||||
IPAddress * remoteIp,
|
||||
uint16_t port)
|
||||
{
|
||||
connection_t * connP;
|
||||
|
||||
connP = (connection_t *)malloc(sizeof(connection_t));
|
||||
if (connP != nullptr)
|
||||
{
|
||||
connP->udpConnection = udpConnection;
|
||||
connP->remoteIp = remoteIp;
|
||||
connP->port = port;
|
||||
connP->next = connList;
|
||||
SerialUSB.println((uint32_t)udpConnection);
|
||||
}
|
||||
|
||||
return connP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo add some sort of sanity checking to see if its possible to connect
|
||||
* @param connList
|
||||
* @param udpConnection
|
||||
* @param remoteIp
|
||||
* @param port
|
||||
* @return
|
||||
*/
|
||||
connection_t * connection_create(connection_t * connList,
|
||||
EthernetUDP * udpConnection,
|
||||
IPAddress * remoteIp,
|
||||
uint16_t port)
|
||||
{
|
||||
connection_t * connP = nullptr;
|
||||
|
||||
if(*remoteIp != 0) {
|
||||
connP = connection_new_incoming(connList, udpConnection, remoteIp, port);
|
||||
}
|
||||
return connP;
|
||||
}
|
||||
|
||||
|
||||
void connection_free(connection_t * connList)
|
||||
{
|
||||
while (connList != nullptr)
|
||||
{
|
||||
connection_t * nextP;
|
||||
|
||||
nextP = connList->next;
|
||||
free(connList);
|
||||
|
||||
connList = nextP;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int connection_send(connection_t *connP,
|
||||
uint8_t * buffer,
|
||||
size_t length)
|
||||
{
|
||||
SerialUSB.println("connection_send() start");
|
||||
// connP->remoteIp->printTo(SerialUSB);
|
||||
IPAddress srv(192,168,14,14);
|
||||
// connP->udpConnection->beginPacket(*(connP->remoteIp), connP->port);
|
||||
connP->udpConnection->beginPacket(srv, connP->port);
|
||||
connP->udpConnection->write(buffer, length);
|
||||
connP->udpConnection->endPacket();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint8_t lwm2m_buffer_send(void * sessionH,
|
||||
uint8_t * buffer,
|
||||
size_t length,
|
||||
void * userdata)
|
||||
{
|
||||
connection_t * connP = (connection_t*) sessionH;
|
||||
|
||||
if (connP == nullptr)
|
||||
{
|
||||
// fprintf(stderr, "#> failed sending %lu bytes, missing connection\r\n", length);
|
||||
return COAP_500_INTERNAL_SERVER_ERROR ;
|
||||
}
|
||||
|
||||
SerialUSB.println("before send");
|
||||
SerialUSB.println((uint32_t)connP->remoteIp);
|
||||
if (-1 == connection_send(connP, buffer, length))
|
||||
{
|
||||
// fprintf(stderr, "#> failed sending %lu bytes\r\n", length);
|
||||
return COAP_500_INTERNAL_SERVER_ERROR ;
|
||||
}
|
||||
|
||||
return COAP_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
bool lwm2m_session_is_equal(void * session1,
|
||||
void * session2,
|
||||
void * userData)
|
||||
{
|
||||
return (session1 == session2);
|
||||
}
|
||||
60
wakaama-client/connection.h
Normal file
60
wakaama-client/connection.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef CONNECTION_H_
|
||||
#define CONNECTION_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <liblwm2m.h>
|
||||
|
||||
#include <EthernetUdp.h>
|
||||
|
||||
#define LWM2M_STANDARD_PORT_STR "5683"
|
||||
#define LWM2M_STANDARD_PORT 5683
|
||||
#define LWM2M_DTLS_PORT_STR "5684"
|
||||
#define LWM2M_DTLS_PORT 5684
|
||||
#define LWM2M_BSSERVER_PORT_STR "5685"
|
||||
#define LWM2M_BSSERVER_PORT 5685
|
||||
|
||||
typedef struct _connection_t
|
||||
{
|
||||
struct _connection_t * next;
|
||||
EthernetUDP * udpConnection;
|
||||
IPAddress * remoteIp;
|
||||
uint16_t port;
|
||||
} connection_t;
|
||||
|
||||
//int create_socket(const char * portStr, int ai_family);
|
||||
|
||||
connection_t * connection_find(connection_t * connList,
|
||||
EthernetUDP * udpConnection);
|
||||
connection_t * connection_new_incoming(connection_t * connList,
|
||||
EthernetUDP * udpConnection,
|
||||
IPAddress * remoteIp,
|
||||
uint16_t port);
|
||||
connection_t * connection_create(connection_t * connList,
|
||||
EthernetUDP * udpConnection,
|
||||
IPAddress * remoteIp,
|
||||
uint16_t port);
|
||||
|
||||
void connection_free(connection_t * connList);
|
||||
|
||||
int connection_send(connection_t *connP, uint8_t * buffer, size_t length);
|
||||
|
||||
#endif
|
||||
307
wakaama-client/object_device.c
Normal file
307
wakaama-client/object_device.c
Normal file
@ -0,0 +1,307 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2013, 2014, 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
|
||||
* domedambrosio - Please refer to git log
|
||||
* Fabien Fleutot - Please refer to git log
|
||||
* Axel Lorente - 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>
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* This object is single instance only, and is mandatory to all LWM2M device as it describe the object such as its
|
||||
* manufacturer, model, etc...
|
||||
*
|
||||
* Here we implement only some of the optional resources.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "liblwm2m.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
#define PRV_MANUFACTURER "Open Mobile Alliance"
|
||||
#define PRV_MODEL_NUMBER "Lightweight M2M Client"
|
||||
#define PRV_BINDING_MODE "U"
|
||||
|
||||
// Resource Id's:
|
||||
#define RES_O_MANUFACTURER 0
|
||||
#define RES_O_MODEL_NUMBER 1
|
||||
#define RES_O_SERIAL_NUMBER 2
|
||||
#define RES_O_FIRMWARE_VERSION 3
|
||||
#define RES_M_REBOOT 4
|
||||
#define RES_O_FACTORY_RESET 5
|
||||
#define RES_O_AVL_POWER_SOURCES 6
|
||||
#define RES_O_POWER_SOURCE_VOLTAGE 7
|
||||
#define RES_O_POWER_SOURCE_CURRENT 8
|
||||
#define RES_O_BATTERY_LEVEL 9
|
||||
#define RES_O_MEMORY_FREE 10
|
||||
#define RES_M_ERROR_CODE 11
|
||||
#define RES_O_RESET_ERROR_CODE 12
|
||||
#define RES_O_CURRENT_TIME 13
|
||||
#define RES_O_UTC_OFFSET 14
|
||||
#define RES_O_TIMEZONE 15
|
||||
#define RES_M_BINDING_MODES 16
|
||||
#define RES_O_DEVICE_TYPE 17
|
||||
#define RES_O_HARDWARE_VERSION 18
|
||||
#define RES_O_SOFTWARE_VERSION 19
|
||||
#define RES_O_BATTERY_STATUS 20
|
||||
#define RES_O_MEMORY_TOTAL 21
|
||||
|
||||
|
||||
static uint8_t prv_set_value(lwm2m_data_t * dataP)
|
||||
{
|
||||
// a simple switch structure is used to respond at the specified resource asked
|
||||
switch (dataP->id)
|
||||
{
|
||||
case RES_O_MANUFACTURER:
|
||||
lwm2m_data_encode_string(PRV_MANUFACTURER, dataP);
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case RES_O_MODEL_NUMBER:
|
||||
lwm2m_data_encode_string(PRV_MODEL_NUMBER, dataP);
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case RES_M_REBOOT:
|
||||
return COAP_405_METHOD_NOT_ALLOWED;
|
||||
|
||||
case RES_M_BINDING_MODES:
|
||||
lwm2m_data_encode_string(PRV_BINDING_MODE, dataP);
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
|
||||
default:
|
||||
return COAP_404_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t prv_device_read(uint16_t instanceId,
|
||||
int * numDataP,
|
||||
lwm2m_data_t ** dataArrayP,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
uint8_t result;
|
||||
int i;
|
||||
|
||||
// this is a single instance object
|
||||
if (instanceId != 0)
|
||||
{
|
||||
return COAP_404_NOT_FOUND;
|
||||
}
|
||||
|
||||
// is the server asking for the full object ?
|
||||
if (*numDataP == 0)
|
||||
{
|
||||
uint16_t resList[] = {
|
||||
RES_O_MANUFACTURER,
|
||||
RES_O_MODEL_NUMBER,
|
||||
RES_M_BINDING_MODES
|
||||
};
|
||||
int nbRes = sizeof(resList)/sizeof(uint16_t);
|
||||
|
||||
*dataArrayP = lwm2m_data_new(nbRes);
|
||||
if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
|
||||
*numDataP = nbRes;
|
||||
for (i = 0 ; i < nbRes ; i++)
|
||||
{
|
||||
(*dataArrayP)[i].id = resList[i];
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
do
|
||||
{
|
||||
result = prv_set_value((*dataArrayP) + i);
|
||||
i++;
|
||||
} while (i < *numDataP && result == COAP_205_CONTENT);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint8_t prv_device_discover(uint16_t instanceId,
|
||||
int * numDataP,
|
||||
lwm2m_data_t ** dataArrayP,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
uint8_t result;
|
||||
int i;
|
||||
|
||||
// this is a single instance object
|
||||
if (instanceId != 0)
|
||||
{
|
||||
return COAP_404_NOT_FOUND;
|
||||
}
|
||||
|
||||
result = COAP_205_CONTENT;
|
||||
|
||||
// is the server asking for the full object ?
|
||||
if (*numDataP == 0)
|
||||
{
|
||||
uint16_t resList[] = {
|
||||
RES_O_MANUFACTURER,
|
||||
RES_O_MODEL_NUMBER,
|
||||
RES_M_BINDING_MODES,
|
||||
RES_M_REBOOT
|
||||
};
|
||||
int nbRes = sizeof(resList)/sizeof(uint16_t);
|
||||
|
||||
*dataArrayP = lwm2m_data_new(nbRes);
|
||||
if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
|
||||
*numDataP = nbRes;
|
||||
for (i = 0 ; i < nbRes ; i++)
|
||||
{
|
||||
(*dataArrayP)[i].id = resList[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < *numDataP && result == COAP_205_CONTENT; i++)
|
||||
{
|
||||
switch ((*dataArrayP)[i].id)
|
||||
{
|
||||
case RES_O_MANUFACTURER:
|
||||
case RES_O_MODEL_NUMBER:
|
||||
case RES_M_BINDING_MODES:
|
||||
case RES_M_REBOOT:
|
||||
break;
|
||||
default:
|
||||
result = COAP_404_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint8_t prv_device_execute(uint16_t instanceId,
|
||||
uint16_t resourceId,
|
||||
uint8_t * buffer,
|
||||
int length,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
// this is a single instance object
|
||||
if (instanceId != 0)
|
||||
{
|
||||
return COAP_404_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (length != 0) return COAP_400_BAD_REQUEST;
|
||||
|
||||
if (resourceId == RES_M_REBOOT)
|
||||
{
|
||||
// fprintf(stdout, "\n\t REBOOT\r\n\n");
|
||||
return COAP_204_CHANGED;
|
||||
}
|
||||
|
||||
return COAP_405_METHOD_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
lwm2m_object_t * get_object_device()
|
||||
{
|
||||
/*
|
||||
* The get_object_device function create the object itself and return a pointer to the structure that represent it.
|
||||
*/
|
||||
lwm2m_object_t * deviceObj;
|
||||
|
||||
deviceObj = (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t));
|
||||
|
||||
if (NULL != deviceObj)
|
||||
{
|
||||
memset(deviceObj, 0, sizeof(lwm2m_object_t));
|
||||
|
||||
/*
|
||||
* It assigns his unique ID
|
||||
* The 3 is the standard ID for the mandatory object "Object device".
|
||||
*/
|
||||
deviceObj->objID = LWM2M_DEVICE_OBJECT_ID;
|
||||
|
||||
/*
|
||||
* and its unique instance
|
||||
*
|
||||
*/
|
||||
deviceObj->instanceList = (lwm2m_list_t *)lwm2m_malloc(sizeof(lwm2m_list_t));
|
||||
if (NULL != deviceObj->instanceList)
|
||||
{
|
||||
memset(deviceObj->instanceList, 0, sizeof(lwm2m_list_t));
|
||||
}
|
||||
else
|
||||
{
|
||||
lwm2m_free(deviceObj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* And the private function that will access the object.
|
||||
* Those function will be called when a read/write/execute query is made by the server. In fact the library don't need to
|
||||
* know the resources of the object, only the server does.
|
||||
*/
|
||||
deviceObj->readFunc = prv_device_read;
|
||||
deviceObj->executeFunc = prv_device_execute;
|
||||
deviceObj->discoverFunc = prv_device_discover;
|
||||
|
||||
}
|
||||
|
||||
return deviceObj;
|
||||
}
|
||||
|
||||
void free_object_device(lwm2m_object_t * objectP)
|
||||
{
|
||||
if (NULL != objectP->userData)
|
||||
{
|
||||
lwm2m_free(objectP->userData);
|
||||
objectP->userData = NULL;
|
||||
}
|
||||
if (NULL != objectP->instanceList)
|
||||
{
|
||||
lwm2m_free(objectP->instanceList);
|
||||
objectP->instanceList = NULL;
|
||||
}
|
||||
|
||||
lwm2m_free(objectP);
|
||||
}
|
||||
253
wakaama-client/object_security.c
Normal file
253
wakaama-client/object_security.c
Normal file
@ -0,0 +1,253 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2013, 2014, 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
|
||||
* Bosch Software Innovations GmbH - Please refer to git log
|
||||
* Pascal Rieux - Please refer to git log
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
* Resources:
|
||||
*
|
||||
* Name | ID | Operations | Instances | Mandatory | Type | Range | Units |
|
||||
* Server URI | 0 | | Single | Yes | String | | |
|
||||
* Bootstrap Server | 1 | | Single | Yes | Boolean | | |
|
||||
* Security Mode | 2 | | Single | Yes | Integer | 0-3 | |
|
||||
* Public Key or ID | 3 | | Single | Yes | Opaque | | |
|
||||
* Server Public Key or ID | 4 | | Single | Yes | Opaque | | |
|
||||
* Secret Key | 5 | | Single | Yes | Opaque | | |
|
||||
* SMS Security Mode | 6 | | Single | Yes | Integer | 0-255 | |
|
||||
* SMS Binding Key Param. | 7 | | Single | Yes | Opaque | 6 B | |
|
||||
* SMS Binding Secret Keys | 8 | | Single | Yes | Opaque | 32-48 B | |
|
||||
* Server SMS Number | 9 | | Single | Yes | Integer | | |
|
||||
* Short Server ID | 10 | | Single | No | Integer | 1-65535 | |
|
||||
* Client Hold Off Time | 11 | | Single | Yes | Integer | | s |
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Here we implement a very basic LWM2M Security Object which only knows NoSec security mode.
|
||||
*/
|
||||
|
||||
#include "liblwm2m.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
typedef struct _security_instance_
|
||||
{
|
||||
struct _security_instance_ * next; // matches lwm2m_list_t::next
|
||||
uint16_t instanceId; // matches lwm2m_list_t::id
|
||||
char * uri;
|
||||
bool isBootstrap;
|
||||
uint16_t shortID;
|
||||
uint32_t clientHoldOffTime;
|
||||
} security_instance_t;
|
||||
|
||||
static uint8_t prv_get_value(lwm2m_data_t * dataP,
|
||||
security_instance_t * targetP)
|
||||
{
|
||||
|
||||
switch (dataP->id)
|
||||
{
|
||||
case LWM2M_SECURITY_URI_ID:
|
||||
lwm2m_data_encode_string(targetP->uri, dataP);
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SECURITY_BOOTSTRAP_ID:
|
||||
lwm2m_data_encode_bool(targetP->isBootstrap, dataP);
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SECURITY_SECURITY_ID:
|
||||
lwm2m_data_encode_int(LWM2M_SECURITY_MODE_NONE, dataP);
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SECURITY_PUBLIC_KEY_ID:
|
||||
// Here we return an opaque of 1 byte containing 0
|
||||
{
|
||||
uint8_t value = 0;
|
||||
|
||||
lwm2m_data_encode_opaque(&value, 1, dataP);
|
||||
}
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SECURITY_SERVER_PUBLIC_KEY_ID:
|
||||
// Here we return an opaque of 1 byte containing 0
|
||||
{
|
||||
uint8_t value = 0;
|
||||
|
||||
lwm2m_data_encode_opaque(&value, 1, dataP);
|
||||
}
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SECURITY_SECRET_KEY_ID:
|
||||
// Here we return an opaque of 1 byte containing 0
|
||||
{
|
||||
uint8_t value = 0;
|
||||
|
||||
lwm2m_data_encode_opaque(&value, 1, dataP);
|
||||
}
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SECURITY_SMS_SECURITY_ID:
|
||||
lwm2m_data_encode_int(LWM2M_SECURITY_MODE_NONE, dataP);
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SECURITY_SMS_KEY_PARAM_ID:
|
||||
// Here we return an opaque of 6 bytes containing a buggy value
|
||||
{
|
||||
char * value = "12345";
|
||||
lwm2m_data_encode_opaque((uint8_t *)value, 6, dataP);
|
||||
}
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SECURITY_SMS_SECRET_KEY_ID:
|
||||
// Here we return an opaque of 32 bytes containing a buggy value
|
||||
{
|
||||
char * value = "1234567890abcdefghijklmnopqrstu";
|
||||
lwm2m_data_encode_opaque((uint8_t *)value, 32, dataP);
|
||||
}
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SECURITY_SMS_SERVER_NUMBER_ID:
|
||||
lwm2m_data_encode_int(0, dataP);
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SECURITY_SHORT_SERVER_ID:
|
||||
lwm2m_data_encode_int(targetP->shortID, dataP);
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SECURITY_HOLD_OFF_ID:
|
||||
lwm2m_data_encode_int(targetP->clientHoldOffTime, dataP);
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
default:
|
||||
return COAP_404_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t prv_security_read(uint16_t instanceId,
|
||||
int * numDataP,
|
||||
lwm2m_data_t ** dataArrayP,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
security_instance_t * targetP;
|
||||
uint8_t result;
|
||||
int i;
|
||||
|
||||
targetP = (security_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId);
|
||||
if (NULL == targetP) return COAP_404_NOT_FOUND;
|
||||
|
||||
// is the server asking for the full instance ?
|
||||
if (*numDataP == 0)
|
||||
{
|
||||
uint16_t resList[] = {LWM2M_SECURITY_URI_ID,
|
||||
LWM2M_SECURITY_BOOTSTRAP_ID,
|
||||
LWM2M_SECURITY_SECURITY_ID,
|
||||
LWM2M_SECURITY_PUBLIC_KEY_ID,
|
||||
LWM2M_SECURITY_SERVER_PUBLIC_KEY_ID,
|
||||
LWM2M_SECURITY_SECRET_KEY_ID,
|
||||
LWM2M_SECURITY_SMS_SECURITY_ID,
|
||||
LWM2M_SECURITY_SMS_KEY_PARAM_ID,
|
||||
LWM2M_SECURITY_SMS_SECRET_KEY_ID,
|
||||
LWM2M_SECURITY_SMS_SERVER_NUMBER_ID,
|
||||
LWM2M_SECURITY_SHORT_SERVER_ID,
|
||||
LWM2M_SECURITY_HOLD_OFF_ID};
|
||||
int nbRes = sizeof(resList)/sizeof(uint16_t);
|
||||
|
||||
*dataArrayP = lwm2m_data_new(nbRes);
|
||||
if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
|
||||
*numDataP = nbRes;
|
||||
for (i = 0 ; i < nbRes ; i++)
|
||||
{
|
||||
(*dataArrayP)[i].id = resList[i];
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
do
|
||||
{
|
||||
result = prv_get_value((*dataArrayP) + i, targetP);
|
||||
i++;
|
||||
} while (i < *numDataP && result == COAP_205_CONTENT);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
lwm2m_object_t * get_security_object(const char * uri)
|
||||
{
|
||||
lwm2m_object_t * securityObj;
|
||||
|
||||
securityObj = (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t));
|
||||
|
||||
if (NULL != securityObj)
|
||||
{
|
||||
security_instance_t * targetP;
|
||||
|
||||
memset(securityObj, 0, sizeof(lwm2m_object_t));
|
||||
|
||||
securityObj->objID = 0;
|
||||
|
||||
// Manually create an hardcoded instance
|
||||
targetP = (security_instance_t *)lwm2m_malloc(sizeof(security_instance_t));
|
||||
if (NULL == targetP)
|
||||
{
|
||||
lwm2m_free(securityObj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(targetP, 0, sizeof(security_instance_t));
|
||||
targetP->instanceId = 0;
|
||||
targetP->uri = strdup(uri);
|
||||
targetP->isBootstrap = false;
|
||||
targetP->shortID = 123;
|
||||
targetP->clientHoldOffTime = 10;
|
||||
|
||||
securityObj->instanceList = LWM2M_LIST_ADD(securityObj->instanceList, targetP);
|
||||
|
||||
securityObj->readFunc = prv_security_read;
|
||||
}
|
||||
|
||||
return securityObj;
|
||||
}
|
||||
|
||||
void free_security_object(lwm2m_object_t * objectP)
|
||||
{
|
||||
while (objectP->instanceList != NULL)
|
||||
{
|
||||
security_instance_t * securityInstance = (security_instance_t *)objectP->instanceList;
|
||||
objectP->instanceList = objectP->instanceList->next;
|
||||
if (NULL != securityInstance->uri)
|
||||
{
|
||||
lwm2m_free(securityInstance->uri);
|
||||
}
|
||||
lwm2m_free(securityInstance);
|
||||
}
|
||||
lwm2m_free(objectP);
|
||||
}
|
||||
|
||||
char * get_server_uri(lwm2m_object_t * objectP,
|
||||
uint16_t secObjInstID)
|
||||
{
|
||||
security_instance_t * targetP = (security_instance_t *)LWM2M_LIST_FIND(objectP->instanceList, secObjInstID);
|
||||
|
||||
if (NULL != targetP)
|
||||
{
|
||||
return lwm2m_strdup(targetP->uri);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
425
wakaama-client/object_server.c
Normal file
425
wakaama-client/object_server.c
Normal file
@ -0,0 +1,425 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* 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
|
||||
* Julien Vermillard, Sierra Wireless
|
||||
* Bosch Software Innovations GmbH - Please refer to git log
|
||||
* Pascal Rieux - Please refer to git log
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
* Resources:
|
||||
*
|
||||
* Name | ID | Operations | Instances | Mandatory | Type | Range | Units |
|
||||
* Short ID | 0 | R | Single | Yes | Integer | 1-65535 | |
|
||||
* Lifetime | 1 | R/W | Single | Yes | Integer | | s |
|
||||
* Default Min Period | 2 | R/W | Single | No | Integer | | s |
|
||||
* Default Max Period | 3 | R/W | Single | No | Integer | | s |
|
||||
* Disable | 4 | E | Single | No | | | |
|
||||
* Disable Timeout | 5 | R/W | Single | No | Integer | | s |
|
||||
* Notification Storing | 6 | R/W | Single | Yes | Boolean | | |
|
||||
* Binding | 7 | R/W | Single | Yes | String | | |
|
||||
* Registration Update | 8 | E | Single | Yes | | | |
|
||||
*
|
||||
*/
|
||||
|
||||
#include "liblwm2m.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct _server_instance_
|
||||
{
|
||||
struct _server_instance_ * next; // matches lwm2m_list_t::next
|
||||
uint16_t instanceId; // matches lwm2m_list_t::id
|
||||
uint16_t shortServerId;
|
||||
uint32_t lifetime;
|
||||
bool storing;
|
||||
char binding[4];
|
||||
} server_instance_t;
|
||||
|
||||
static uint8_t prv_get_value(lwm2m_data_t * dataP,
|
||||
server_instance_t * targetP)
|
||||
{
|
||||
switch (dataP->id)
|
||||
{
|
||||
case LWM2M_SERVER_SHORT_ID_ID:
|
||||
lwm2m_data_encode_int(targetP->shortServerId, dataP);
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SERVER_LIFETIME_ID:
|
||||
lwm2m_data_encode_int(targetP->lifetime, dataP);
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SERVER_DISABLE_ID:
|
||||
return COAP_405_METHOD_NOT_ALLOWED;
|
||||
|
||||
case LWM2M_SERVER_STORING_ID:
|
||||
lwm2m_data_encode_bool(targetP->storing, dataP);
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SERVER_BINDING_ID:
|
||||
lwm2m_data_encode_string(targetP->binding, dataP);
|
||||
return COAP_205_CONTENT;
|
||||
|
||||
case LWM2M_SERVER_UPDATE_ID:
|
||||
return COAP_405_METHOD_NOT_ALLOWED;
|
||||
|
||||
default:
|
||||
return COAP_404_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t prv_server_read(uint16_t instanceId,
|
||||
int * numDataP,
|
||||
lwm2m_data_t ** dataArrayP,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
server_instance_t * targetP;
|
||||
uint8_t result;
|
||||
int i;
|
||||
|
||||
targetP = (server_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId);
|
||||
if (NULL == targetP) return COAP_404_NOT_FOUND;
|
||||
|
||||
// is the server asking for the full instance ?
|
||||
if (*numDataP == 0)
|
||||
{
|
||||
uint16_t resList[] = {
|
||||
LWM2M_SERVER_SHORT_ID_ID,
|
||||
LWM2M_SERVER_LIFETIME_ID,
|
||||
LWM2M_SERVER_STORING_ID,
|
||||
LWM2M_SERVER_BINDING_ID
|
||||
};
|
||||
int nbRes = sizeof(resList)/sizeof(uint16_t);
|
||||
|
||||
*dataArrayP = lwm2m_data_new(nbRes);
|
||||
if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
|
||||
*numDataP = nbRes;
|
||||
for (i = 0 ; i < nbRes ; i++)
|
||||
{
|
||||
(*dataArrayP)[i].id = resList[i];
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
do
|
||||
{
|
||||
result = prv_get_value((*dataArrayP) + i, targetP);
|
||||
i++;
|
||||
} while (i < *numDataP && result == COAP_205_CONTENT);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint8_t prv_server_discover(uint16_t instanceId,
|
||||
int * numDataP,
|
||||
lwm2m_data_t ** dataArrayP,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
uint8_t result;
|
||||
int i;
|
||||
|
||||
result = COAP_205_CONTENT;
|
||||
|
||||
// is the server asking for the full object ?
|
||||
if (*numDataP == 0)
|
||||
{
|
||||
uint16_t resList[] = {
|
||||
LWM2M_SERVER_SHORT_ID_ID,
|
||||
LWM2M_SERVER_LIFETIME_ID,
|
||||
LWM2M_SERVER_MIN_PERIOD_ID,
|
||||
LWM2M_SERVER_MAX_PERIOD_ID,
|
||||
LWM2M_SERVER_DISABLE_ID,
|
||||
LWM2M_SERVER_TIMEOUT_ID,
|
||||
LWM2M_SERVER_STORING_ID,
|
||||
LWM2M_SERVER_BINDING_ID,
|
||||
LWM2M_SERVER_UPDATE_ID
|
||||
};
|
||||
int nbRes = sizeof(resList)/sizeof(uint16_t);
|
||||
|
||||
*dataArrayP = lwm2m_data_new(nbRes);
|
||||
if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
|
||||
*numDataP = nbRes;
|
||||
for (i = 0 ; i < nbRes ; i++)
|
||||
{
|
||||
(*dataArrayP)[i].id = resList[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < *numDataP && result == COAP_205_CONTENT; i++)
|
||||
{
|
||||
switch ((*dataArrayP)[i].id)
|
||||
{
|
||||
case LWM2M_SERVER_SHORT_ID_ID:
|
||||
case LWM2M_SERVER_LIFETIME_ID:
|
||||
case LWM2M_SERVER_MIN_PERIOD_ID:
|
||||
case LWM2M_SERVER_MAX_PERIOD_ID:
|
||||
case LWM2M_SERVER_DISABLE_ID:
|
||||
case LWM2M_SERVER_TIMEOUT_ID:
|
||||
case LWM2M_SERVER_STORING_ID:
|
||||
case LWM2M_SERVER_BINDING_ID:
|
||||
case LWM2M_SERVER_UPDATE_ID:
|
||||
break;
|
||||
default:
|
||||
result = COAP_404_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint8_t prv_set_int_value(lwm2m_data_t * dataArray,
|
||||
uint32_t * data)
|
||||
{
|
||||
uint8_t result;
|
||||
int64_t value;
|
||||
|
||||
if (1 == lwm2m_data_decode_int(dataArray, &value))
|
||||
{
|
||||
if (value >= 0 && value <= 0xFFFFFFFF)
|
||||
{
|
||||
*data = value;
|
||||
result = COAP_204_CHANGED;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = COAP_406_NOT_ACCEPTABLE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = COAP_400_BAD_REQUEST;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint8_t prv_server_write(uint16_t instanceId,
|
||||
int numData,
|
||||
lwm2m_data_t * dataArray,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
server_instance_t * targetP;
|
||||
int i;
|
||||
uint8_t result;
|
||||
|
||||
targetP = (server_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId);
|
||||
if (NULL == targetP)
|
||||
{
|
||||
return COAP_404_NOT_FOUND;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
do
|
||||
{
|
||||
switch (dataArray[i].id)
|
||||
{
|
||||
case LWM2M_SERVER_SHORT_ID_ID:
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
result = prv_set_int_value(dataArray + i, &value);
|
||||
if (COAP_204_CHANGED == result)
|
||||
{
|
||||
if (0 < value && value <= 0xFFFF)
|
||||
{
|
||||
targetP->shortServerId = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = COAP_406_NOT_ACCEPTABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LWM2M_SERVER_LIFETIME_ID:
|
||||
result = prv_set_int_value(dataArray + i, (uint32_t *)&(targetP->lifetime));
|
||||
break;
|
||||
|
||||
case LWM2M_SERVER_DISABLE_ID:
|
||||
result = COAP_405_METHOD_NOT_ALLOWED;
|
||||
break;
|
||||
|
||||
case LWM2M_SERVER_STORING_ID:
|
||||
{
|
||||
bool value;
|
||||
|
||||
if (1 == lwm2m_data_decode_bool(dataArray + i, &value))
|
||||
{
|
||||
targetP->storing = value;
|
||||
result = COAP_204_CHANGED;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = COAP_400_BAD_REQUEST;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LWM2M_SERVER_BINDING_ID:
|
||||
if ((dataArray[i].type == LWM2M_TYPE_STRING || dataArray[i].type == LWM2M_TYPE_OPAQUE)
|
||||
&& dataArray[i].value.asBuffer.length > 0 && dataArray[i].value.asBuffer.length <= 3
|
||||
&& (strncmp((char*)dataArray[i].value.asBuffer.buffer, "U", dataArray[i].value.asBuffer.length) == 0
|
||||
|| strncmp((char*)dataArray[i].value.asBuffer.buffer, "UQ", dataArray[i].value.asBuffer.length) == 0
|
||||
|| strncmp((char*)dataArray[i].value.asBuffer.buffer, "S", dataArray[i].value.asBuffer.length) == 0
|
||||
|| strncmp((char*)dataArray[i].value.asBuffer.buffer, "SQ", dataArray[i].value.asBuffer.length) == 0
|
||||
|| strncmp((char*)dataArray[i].value.asBuffer.buffer, "US", dataArray[i].value.asBuffer.length) == 0
|
||||
|| strncmp((char*)dataArray[i].value.asBuffer.buffer, "UQS", dataArray[i].value.asBuffer.length) == 0))
|
||||
{
|
||||
strncpy(targetP->binding, (char*)dataArray[i].value.asBuffer.buffer, dataArray[i].value.asBuffer.length);
|
||||
result = COAP_204_CHANGED;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = COAP_400_BAD_REQUEST;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWM2M_SERVER_UPDATE_ID:
|
||||
result = COAP_405_METHOD_NOT_ALLOWED;
|
||||
break;
|
||||
|
||||
default:
|
||||
return COAP_404_NOT_FOUND;
|
||||
}
|
||||
i++;
|
||||
} while (i < numData && result == COAP_204_CHANGED);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint8_t prv_server_execute(uint16_t instanceId,
|
||||
uint16_t resourceId,
|
||||
uint8_t * buffer,
|
||||
int length,
|
||||
lwm2m_object_t * objectP)
|
||||
|
||||
{
|
||||
server_instance_t * targetP;
|
||||
|
||||
targetP = (server_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId);
|
||||
if (NULL == targetP) return COAP_404_NOT_FOUND;
|
||||
|
||||
switch (resourceId)
|
||||
{
|
||||
case LWM2M_SERVER_DISABLE_ID:
|
||||
// executed in core, if COAP_204_CHANGED is returned
|
||||
return COAP_204_CHANGED;
|
||||
|
||||
case LWM2M_SERVER_UPDATE_ID:
|
||||
// executed in core, if COAP_204_CHANGED is returned
|
||||
return COAP_204_CHANGED;
|
||||
|
||||
default:
|
||||
return COAP_405_METHOD_NOT_ALLOWED;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t prv_server_delete(uint16_t id,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
server_instance_t * serverInstance;
|
||||
|
||||
objectP->instanceList = lwm2m_list_remove(objectP->instanceList, id, (lwm2m_list_t **)&serverInstance);
|
||||
if (NULL == serverInstance) return COAP_404_NOT_FOUND;
|
||||
|
||||
lwm2m_free(serverInstance);
|
||||
|
||||
return COAP_202_DELETED;
|
||||
}
|
||||
|
||||
static uint8_t prv_server_create(uint16_t instanceId,
|
||||
int numData,
|
||||
lwm2m_data_t * dataArray,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
server_instance_t * serverInstance;
|
||||
uint8_t result;
|
||||
|
||||
serverInstance = (server_instance_t *)lwm2m_malloc(sizeof(server_instance_t));
|
||||
if (NULL == serverInstance) return COAP_500_INTERNAL_SERVER_ERROR;
|
||||
memset(serverInstance, 0, sizeof(server_instance_t));
|
||||
|
||||
serverInstance->instanceId = instanceId;
|
||||
objectP->instanceList = LWM2M_LIST_ADD(objectP->instanceList, serverInstance);
|
||||
|
||||
result = prv_server_write(instanceId, numData, dataArray, objectP);
|
||||
|
||||
if (result != COAP_204_CHANGED)
|
||||
{
|
||||
(void)prv_server_delete(instanceId, objectP);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = COAP_201_CREATED;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
lwm2m_object_t * get_server_object()
|
||||
{
|
||||
lwm2m_object_t * serverObj;
|
||||
|
||||
serverObj = (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t));
|
||||
|
||||
if (NULL != serverObj)
|
||||
{
|
||||
server_instance_t * serverInstance;
|
||||
|
||||
memset(serverObj, 0, sizeof(lwm2m_object_t));
|
||||
|
||||
serverObj->objID = 1;
|
||||
|
||||
// Manually create an hardcoded server
|
||||
serverInstance = (server_instance_t *)lwm2m_malloc(sizeof(server_instance_t));
|
||||
if (NULL == serverInstance)
|
||||
{
|
||||
lwm2m_free(serverObj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(serverInstance, 0, sizeof(server_instance_t));
|
||||
serverInstance->instanceId = 0;
|
||||
serverInstance->shortServerId = 123;
|
||||
serverInstance->lifetime = 300;
|
||||
serverInstance->storing = false;
|
||||
serverInstance->binding[0] = 'U';
|
||||
serverObj->instanceList = LWM2M_LIST_ADD(serverObj->instanceList, serverInstance);
|
||||
|
||||
serverObj->readFunc = prv_server_read;
|
||||
serverObj->writeFunc = prv_server_write;
|
||||
serverObj->createFunc = prv_server_create;
|
||||
serverObj->deleteFunc = prv_server_delete;
|
||||
serverObj->executeFunc = prv_server_execute;
|
||||
serverObj->discoverFunc = prv_server_discover;
|
||||
}
|
||||
|
||||
return serverObj;
|
||||
}
|
||||
|
||||
void free_server_object(lwm2m_object_t * object)
|
||||
{
|
||||
while (object->instanceList != NULL)
|
||||
{
|
||||
server_instance_t * serverInstance = (server_instance_t *)object->instanceList;
|
||||
object->instanceList = object->instanceList->next;
|
||||
lwm2m_free(serverInstance);
|
||||
}
|
||||
lwm2m_free(object);
|
||||
}
|
||||
67
wakaama-client/platform.c
Normal file
67
wakaama-client/platform.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2013, 2014, 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 <liblwm2m.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/time.h>
|
||||
#include <delay.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef LWM2M_MEMORY_TRACE
|
||||
|
||||
void * lwm2m_malloc(size_t s)
|
||||
{
|
||||
return malloc(s);
|
||||
}
|
||||
|
||||
void lwm2m_free(void * p)
|
||||
{
|
||||
return free(p);
|
||||
}
|
||||
|
||||
char * lwm2m_strdup(const char * str)
|
||||
{
|
||||
return strdup(str);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int lwm2m_strncmp(const char * s1,
|
||||
const char * s2,
|
||||
size_t n)
|
||||
{
|
||||
return strncmp(s1, s2, n);
|
||||
}
|
||||
|
||||
time_t lwm2m_gettime(void)
|
||||
{
|
||||
time_t seconds = floor(millis() / 1000);
|
||||
return seconds;
|
||||
}
|
||||
|
||||
//void lwm2m_printf(const char * format, ...)
|
||||
//{
|
||||
// va_list ap;
|
||||
//
|
||||
// va_start(ap, format);
|
||||
//
|
||||
// SerialUSB.println(format, ap);
|
||||
//
|
||||
// va_end(ap);
|
||||
//}
|
||||
29
wakaama-client/shared.cmake
Normal file
29
wakaama-client/shared.cmake
Normal file
@ -0,0 +1,29 @@
|
||||
# Provides SHARED_SOURCES_DIR, SHARED_SOURCES, SHARED_INCLUDE_DIRS and SHARED_DEFINITIONS variables
|
||||
|
||||
set(SHARED_SOURCES_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
set(SHARED_SOURCES
|
||||
${SHARED_SOURCES_DIR}/platform.c)
|
||||
|
||||
if(DTLS)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/tinydtls.cmake)
|
||||
|
||||
set(SHARED_SOURCES
|
||||
${SHARED_SOURCES}
|
||||
${TINYDTLS_SOURCES}
|
||||
${SHARED_SOURCES_DIR}/dtlsconnection.c)
|
||||
|
||||
set(SHARED_INCLUDE_DIRS
|
||||
${SHARED_SOURCES_DIR}
|
||||
${TINYDTLS_SOURCES_DIR})
|
||||
|
||||
set(SHARED_DEFINITIONS -DWITH_TINYDTLS)
|
||||
else()
|
||||
set(SHARED_SOURCES
|
||||
${SHARED_SOURCES}
|
||||
${SHARED_SOURCES_DIR}/connection.cpp)
|
||||
|
||||
set(SHARED_INCLUDE_DIRS ${SHARED_SOURCES_DIR})
|
||||
endif()
|
||||
|
||||
|
||||
391
wakaama-client/test_object.c
Normal file
391
wakaama-client/test_object.c
Normal file
@ -0,0 +1,391 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* 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
|
||||
* Axel Lorente - Please refer to git log
|
||||
* Achim Kraus, 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>
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* Implements an object for testing purpose
|
||||
*
|
||||
* Multiple
|
||||
* Object | ID | Instances | Mandatoty |
|
||||
* Test | 31024 | Yes | No |
|
||||
*
|
||||
* Resources:
|
||||
* Supported Multiple
|
||||
* Name | ID | Operations | Instances | Mandatory | Type | Range | Units | Description |
|
||||
* test | 1 | R/W | No | Yes | Integer | 0-255 | | |
|
||||
* exec | 2 | E | No | Yes | | | | |
|
||||
* dec | 3 | R/W | No | Yes | Float | | | |
|
||||
* sig | 4 | R/W | No | Yes | Integer | | | 16-bit signed integer |
|
||||
*
|
||||
*/
|
||||
|
||||
#include "liblwm2m.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
|
||||
static void prv_output_buffer(uint8_t * buffer,
|
||||
int length)
|
||||
{
|
||||
int i;
|
||||
uint8_t array[16];
|
||||
|
||||
i = 0;
|
||||
while (i < length)
|
||||
{
|
||||
int j;
|
||||
fprintf(stderr, " ");
|
||||
|
||||
memcpy(array, buffer+i, 16);
|
||||
|
||||
for (j = 0 ; j < 16 && i+j < length; j++)
|
||||
{
|
||||
fprintf(stderr, "%02X ", array[j]);
|
||||
}
|
||||
while (j < 16)
|
||||
{
|
||||
fprintf(stderr, " ");
|
||||
j++;
|
||||
}
|
||||
fprintf(stderr, " ");
|
||||
for (j = 0 ; j < 16 && i+j < length; j++)
|
||||
{
|
||||
if (isprint(array[j]))
|
||||
fprintf(stderr, "%c ", array[j]);
|
||||
else
|
||||
fprintf(stderr, ". ");
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
i += 16;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Multiple instance objects can use userdata to store data that will be shared between the different instances.
|
||||
* The lwm2m_object_t object structure - which represent every object of the liblwm2m as seen in the single instance
|
||||
* object - contain a chained list called instanceList with the object specific structure prv_instance_t:
|
||||
*/
|
||||
typedef struct _prv_instance_
|
||||
{
|
||||
/*
|
||||
* The first two are mandatories and represent the pointer to the next instance and the ID of this one. The rest
|
||||
* is the instance scope user data (uint8_t test in this case)
|
||||
*/
|
||||
struct _prv_instance_ * next; // matches lwm2m_list_t::next
|
||||
uint16_t shortID; // matches lwm2m_list_t::id
|
||||
uint8_t test;
|
||||
double dec;
|
||||
int16_t sig;
|
||||
} prv_instance_t;
|
||||
|
||||
static uint8_t prv_read(uint16_t instanceId,
|
||||
int * numDataP,
|
||||
lwm2m_data_t ** dataArrayP,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
prv_instance_t * targetP;
|
||||
int i;
|
||||
|
||||
targetP = (prv_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId);
|
||||
if (NULL == targetP) return COAP_404_NOT_FOUND;
|
||||
|
||||
if (*numDataP == 0)
|
||||
{
|
||||
*dataArrayP = lwm2m_data_new(3);
|
||||
if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
|
||||
*numDataP = 3;
|
||||
(*dataArrayP)[0].id = 1;
|
||||
(*dataArrayP)[1].id = 3;
|
||||
(*dataArrayP)[2].id = 4;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < *numDataP ; i++)
|
||||
{
|
||||
switch ((*dataArrayP)[i].id)
|
||||
{
|
||||
case 1:
|
||||
lwm2m_data_encode_int(targetP->test, *dataArrayP + i);
|
||||
break;
|
||||
case 2:
|
||||
return COAP_405_METHOD_NOT_ALLOWED;
|
||||
case 3:
|
||||
lwm2m_data_encode_float(targetP->dec, *dataArrayP + i);
|
||||
break;
|
||||
case 4:
|
||||
lwm2m_data_encode_int(targetP->sig, *dataArrayP + i);
|
||||
break;
|
||||
default:
|
||||
return COAP_404_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
return COAP_205_CONTENT;
|
||||
}
|
||||
|
||||
static uint8_t prv_discover(uint16_t instanceId,
|
||||
int * numDataP,
|
||||
lwm2m_data_t ** dataArrayP,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
int i;
|
||||
|
||||
// is the server asking for the full object ?
|
||||
if (*numDataP == 0)
|
||||
{
|
||||
*dataArrayP = lwm2m_data_new(4);
|
||||
if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
|
||||
*numDataP = 4;
|
||||
(*dataArrayP)[0].id = 1;
|
||||
(*dataArrayP)[1].id = 2;
|
||||
(*dataArrayP)[2].id = 3;
|
||||
(*dataArrayP)[3].id = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < *numDataP; i++)
|
||||
{
|
||||
switch ((*dataArrayP)[i].id)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
break;
|
||||
default:
|
||||
return COAP_404_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
}
|
||||
return COAP_205_CONTENT;
|
||||
}
|
||||
|
||||
static uint8_t prv_write(uint16_t instanceId,
|
||||
int numData,
|
||||
lwm2m_data_t * dataArray,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
prv_instance_t * targetP;
|
||||
int i;
|
||||
|
||||
targetP = (prv_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId);
|
||||
if (NULL == targetP) return COAP_404_NOT_FOUND;
|
||||
|
||||
for (i = 0 ; i < numData ; i++)
|
||||
{
|
||||
switch (dataArray[i].id)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
int64_t value;
|
||||
|
||||
if (1 != lwm2m_data_decode_int(dataArray + i, &value) || value < 0 || value > 0xFF)
|
||||
{
|
||||
return COAP_400_BAD_REQUEST;
|
||||
}
|
||||
targetP->test = (uint8_t)value;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
return COAP_405_METHOD_NOT_ALLOWED;
|
||||
case 3:
|
||||
if (1 != lwm2m_data_decode_float(dataArray + i, &(targetP->dec)))
|
||||
{
|
||||
return COAP_400_BAD_REQUEST;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
int64_t value;
|
||||
|
||||
if (1 != lwm2m_data_decode_int(dataArray + i, &value) || value < INT16_MIN || value > INT16_MAX)
|
||||
{
|
||||
return COAP_400_BAD_REQUEST;
|
||||
}
|
||||
targetP->sig = (int16_t)value;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return COAP_404_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
return COAP_204_CHANGED;
|
||||
}
|
||||
|
||||
static uint8_t prv_delete(uint16_t id,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
prv_instance_t * targetP;
|
||||
|
||||
objectP->instanceList = lwm2m_list_remove(objectP->instanceList, id, (lwm2m_list_t **)&targetP);
|
||||
if (NULL == targetP) return COAP_404_NOT_FOUND;
|
||||
|
||||
lwm2m_free(targetP);
|
||||
|
||||
return COAP_202_DELETED;
|
||||
}
|
||||
|
||||
static uint8_t prv_create(uint16_t instanceId,
|
||||
int numData,
|
||||
lwm2m_data_t * dataArray,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
prv_instance_t * targetP;
|
||||
uint8_t result;
|
||||
|
||||
|
||||
targetP = (prv_instance_t *)lwm2m_malloc(sizeof(prv_instance_t));
|
||||
if (NULL == targetP) return COAP_500_INTERNAL_SERVER_ERROR;
|
||||
memset(targetP, 0, sizeof(prv_instance_t));
|
||||
|
||||
targetP->shortID = instanceId;
|
||||
objectP->instanceList = LWM2M_LIST_ADD(objectP->instanceList, targetP);
|
||||
|
||||
result = prv_write(instanceId, numData, dataArray, objectP);
|
||||
|
||||
if (result != COAP_204_CHANGED)
|
||||
{
|
||||
(void)prv_delete(instanceId, objectP);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = COAP_201_CREATED;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint8_t prv_exec(uint16_t instanceId,
|
||||
uint16_t resourceId,
|
||||
uint8_t * buffer,
|
||||
int length,
|
||||
lwm2m_object_t * objectP)
|
||||
{
|
||||
|
||||
if (NULL == lwm2m_list_find(objectP->instanceList, instanceId)) return COAP_404_NOT_FOUND;
|
||||
|
||||
switch (resourceId)
|
||||
{
|
||||
case 1:
|
||||
return COAP_405_METHOD_NOT_ALLOWED;
|
||||
case 2:
|
||||
// fprintf(stdout, "\r\n-----------------\r\n"
|
||||
// "Execute on %hu/%d/%d\r\n"
|
||||
// " Parameter (%d bytes):\r\n",
|
||||
// objectP->objID, instanceId, resourceId, length);
|
||||
prv_output_buffer((uint8_t*)buffer, length);
|
||||
// fprintf(stdout, "-----------------\r\n\r\n");
|
||||
return COAP_204_CHANGED;
|
||||
case 3:
|
||||
return COAP_405_METHOD_NOT_ALLOWED;
|
||||
default:
|
||||
return COAP_404_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
lwm2m_object_t * get_test_object(void)
|
||||
{
|
||||
lwm2m_object_t * testObj;
|
||||
|
||||
testObj = (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t));
|
||||
|
||||
if (NULL != testObj)
|
||||
{
|
||||
int i;
|
||||
prv_instance_t * targetP;
|
||||
|
||||
memset(testObj, 0, sizeof(lwm2m_object_t));
|
||||
|
||||
testObj->objID = 31024;
|
||||
for (i=0 ; i < 3 ; i++)
|
||||
{
|
||||
targetP = (prv_instance_t *)lwm2m_malloc(sizeof(prv_instance_t));
|
||||
if (NULL == targetP) return NULL;
|
||||
memset(targetP, 0, sizeof(prv_instance_t));
|
||||
targetP->shortID = 10 + i;
|
||||
targetP->test = 20 + i;
|
||||
targetP->dec = -30 + i + (double)i/100.0;
|
||||
targetP->sig = 0 - i;
|
||||
testObj->instanceList = LWM2M_LIST_ADD(testObj->instanceList, targetP);
|
||||
}
|
||||
/*
|
||||
* From a single instance object, two more functions are available.
|
||||
* - The first one (createFunc) create a new instance and filled it with the provided informations. If an ID is
|
||||
* provided a check is done for verifying his disponibility, or a new one is generated.
|
||||
* - The other one (deleteFunc) delete an instance by removing it from the instance list (and freeing the memory
|
||||
* allocated to it)
|
||||
*/
|
||||
testObj->readFunc = prv_read;
|
||||
testObj->writeFunc = prv_write;
|
||||
testObj->executeFunc = prv_exec;
|
||||
testObj->createFunc = prv_create;
|
||||
testObj->deleteFunc = prv_delete;
|
||||
testObj->discoverFunc = prv_discover;
|
||||
}
|
||||
|
||||
return testObj;
|
||||
}
|
||||
|
||||
void free_test_object(lwm2m_object_t * object)
|
||||
{
|
||||
LWM2M_LIST_FREE(object->instanceList);
|
||||
if (object->userData != NULL)
|
||||
{
|
||||
lwm2m_free(object->userData);
|
||||
object->userData = NULL;
|
||||
}
|
||||
lwm2m_free(object);
|
||||
}
|
||||
255
wakaama-client/wakaama-client.cpp
Normal file
255
wakaama-client/wakaama-client.cpp
Normal file
@ -0,0 +1,255 @@
|
||||
#include "wakaama-client.h"
|
||||
#include <liblwm2m.h>
|
||||
#include <USB/USBAPI.h>
|
||||
#include <Dns.h>
|
||||
#include <Ethernet.h>
|
||||
|
||||
|
||||
#ifndef LWM2M_WITH_LOGS
|
||||
// Same usage as C89 printf()
|
||||
extern void lwm2m_printf(const char * format, ...);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* "coap://localhost:5683"
|
||||
* @param uri
|
||||
* @todo init and use uri in security object
|
||||
*/
|
||||
void ArduinoClient::init() {
|
||||
SerialUSB.println(F("init():start"));
|
||||
memset(&data, 0, sizeof(client_data_t));
|
||||
|
||||
const __FlashStringHelper * objFail = F("Failed to create object");
|
||||
|
||||
// create udp listener
|
||||
data.udp = new EthernetUDP();
|
||||
data.udp->begin(localPort);
|
||||
|
||||
// debug phase
|
||||
IPAddress srv(192,168,14,14);
|
||||
data.udp->beginPacket(srv, 5683);
|
||||
data.udp->write("HereBeDragons");
|
||||
data.udp->endPacket();
|
||||
|
||||
// init objects
|
||||
SerialUSB.println(F("*object:security"));
|
||||
objArray[0] = get_security_object(uri);
|
||||
if (nullptr == objArray[0])
|
||||
{
|
||||
SerialUSB.println(objFail);
|
||||
exit(0);
|
||||
}
|
||||
data.securityObjP = objArray[0];
|
||||
|
||||
SerialUSB.println(F("*object:server"));
|
||||
objArray[1] = get_server_object();
|
||||
if (nullptr == objArray[1])
|
||||
{
|
||||
SerialUSB.println(objFail);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
SerialUSB.println(F("*object:device"));
|
||||
objArray[2] = get_object_device();
|
||||
if (nullptr == objArray[2])
|
||||
{
|
||||
SerialUSB.println(objFail);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
SerialUSB.println(F("*object:test"));
|
||||
objArray[3] = get_test_object();
|
||||
if (nullptr == objArray[3])
|
||||
{
|
||||
SerialUSB.println(objFail);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The liblwm2m library is now initialized with the functions that will be in
|
||||
* charge of communication
|
||||
*/
|
||||
SerialUSB.println(F("*lwm2m_init()"));
|
||||
lwm2mH = lwm2m_init(&data);
|
||||
if (NULL == lwm2mH)
|
||||
{
|
||||
SerialUSB.println(F("lwm2m_init() failed"));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* We configure the liblwm2m library with the name of the client - which shall be unique for each client -
|
||||
* the number of objects we will be passing through and the objects array
|
||||
*/
|
||||
SerialUSB.println(F("*lwm2m_configure()"));
|
||||
result = lwm2m_configure(lwm2mH, name, NULL, NULL, OBJ_COUNT, objArray);
|
||||
if (result != 0)
|
||||
{
|
||||
SerialUSB.println(F("lwm2m_configure() failed"));
|
||||
exit(0);
|
||||
}
|
||||
SerialUSB.println(F("init():done"));
|
||||
}
|
||||
|
||||
|
||||
void ArduinoClient::doWorkStep() {
|
||||
SerialUSB.println(F("doWorkStep():start"));
|
||||
|
||||
/*
|
||||
* This function does two things:
|
||||
* - first it does the work needed by liblwm2m (eg. (re)sending some packets).
|
||||
* - Secondly it adjusts the timeout value (default 60s) depending on the state of the transaction
|
||||
* (eg. retransmission) and the time before the next operation
|
||||
*/
|
||||
SerialUSB.println(F("lwm2m_step()"));
|
||||
result = lwm2m_step(lwm2mH, &step_delay);
|
||||
SerialUSB.println(result, DEC);
|
||||
if (result != 0)
|
||||
{
|
||||
SerialUSB.print(F("lwm2m_step() failed"));
|
||||
SerialUSB.println(result, HEX);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// wait for socket event
|
||||
SerialUSB.println(F("parsePacket()"));
|
||||
int packetSize = data.udp->parsePacket();
|
||||
if (packetSize) {
|
||||
int numBytes = data.udp->read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
|
||||
|
||||
connection_t * connP;
|
||||
connP = connection_find(data.connList, data.udp);
|
||||
if (connP != nullptr)
|
||||
{
|
||||
/*
|
||||
* Let liblwm2m respond to the query depending on the context
|
||||
*/
|
||||
lwm2m_handle_packet(lwm2mH, (uint8_t*)packetBuffer, numBytes, connP);
|
||||
}
|
||||
}
|
||||
SerialUSB.println(F("doWorkStep():done"));
|
||||
}
|
||||
|
||||
|
||||
ArduinoClient::~ArduinoClient() {
|
||||
SerialUSB.println("teardown():start");
|
||||
lwm2m_close(lwm2mH);
|
||||
data.udp->stop();
|
||||
connection_free(data.connList);
|
||||
|
||||
free_security_object(objArray[0]);
|
||||
free_server_object(objArray[1]);
|
||||
free_object_device(objArray[2]);
|
||||
free_test_object(objArray[3]);
|
||||
SerialUSB.println("teardown():done");
|
||||
}
|
||||
|
||||
|
||||
void * lwm2m_connect_server(uint16_t secObjInstID, void *userData) {
|
||||
client_data_t * dataP;
|
||||
char * uri;
|
||||
char * host;
|
||||
char * port;
|
||||
connection_t * newConnP = nullptr;
|
||||
|
||||
dataP = (client_data_t *)userData;
|
||||
|
||||
uri = get_server_uri(dataP->securityObjP, secObjInstID);
|
||||
|
||||
if (uri == nullptr) return nullptr;
|
||||
|
||||
SerialUSB.print(F("Connecting to "));
|
||||
SerialUSB.println(uri);
|
||||
|
||||
// parse uri in the form "coaps://[host]:[port]"
|
||||
if (0 == strncmp(uri, "coaps://", strlen("coaps://")))
|
||||
{
|
||||
host = uri+strlen("coaps://");
|
||||
}
|
||||
else if (0 == strncmp(uri, "coap://", strlen("coap://")))
|
||||
{
|
||||
host = uri+strlen("coap://");
|
||||
}
|
||||
else
|
||||
{
|
||||
lwm2m_free(uri);
|
||||
return (void *)newConnP;
|
||||
}
|
||||
port = strrchr(host, ':');
|
||||
if (port == nullptr) {
|
||||
lwm2m_free(uri);
|
||||
return (void *)newConnP;
|
||||
}
|
||||
// remove brackets
|
||||
if (host[0] == '[')
|
||||
{
|
||||
host++;
|
||||
if (*(port - 1) == ']')
|
||||
{
|
||||
*(port - 1) = 0;
|
||||
}
|
||||
else {
|
||||
lwm2m_free(uri);
|
||||
return (void *)newConnP;
|
||||
}
|
||||
}
|
||||
// split strings
|
||||
*port = 0;
|
||||
port++;
|
||||
|
||||
IPAddress remoteIp;
|
||||
if(!remoteIp.fromString(host)) {
|
||||
DNSClient dns;
|
||||
dns.begin(Ethernet.dnsServerIP());
|
||||
dns.getHostByName(host, remoteIp);
|
||||
}
|
||||
|
||||
String portStr = port;
|
||||
|
||||
newConnP = connection_create(dataP->connList, dataP->udp, &remoteIp, portStr.toInt());
|
||||
if (newConnP == nullptr) {
|
||||
SerialUSB.println(F("Connection creation failed"));
|
||||
}
|
||||
else {
|
||||
dataP->connList = newConnP;
|
||||
}
|
||||
SerialUSB.println(F("Connection created"));
|
||||
|
||||
lwm2m_free(uri);
|
||||
return (void *)newConnP;
|
||||
};
|
||||
|
||||
|
||||
void lwm2m_close_connection(void *sessionH, void *userData) {
|
||||
client_data_t * app_data;
|
||||
connection_t * targetP;
|
||||
|
||||
app_data = (client_data_t *)userData;
|
||||
targetP = (connection_t *)sessionH;
|
||||
|
||||
if (targetP == app_data->connList)
|
||||
{
|
||||
app_data->connList = targetP->next;
|
||||
lwm2m_free(targetP);
|
||||
}
|
||||
else
|
||||
{
|
||||
connection_t * parentP;
|
||||
|
||||
parentP = app_data->connList;
|
||||
while (parentP != nullptr && parentP->next != targetP)
|
||||
{
|
||||
parentP = parentP->next;
|
||||
}
|
||||
if (parentP != nullptr)
|
||||
{
|
||||
parentP->next = targetP->next;
|
||||
lwm2m_free(targetP);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void lwm2m_printf(const char * format, ...)
|
||||
{
|
||||
SerialUSB.println(format);
|
||||
}
|
||||
53
wakaama-client/wakaama-client.h
Normal file
53
wakaama-client/wakaama-client.h
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef WAKAAMA_CLIENT_H_
|
||||
#define WAKAAMA_CLIENT_H_
|
||||
|
||||
|
||||
#include <Udp.h>
|
||||
#include <EthernetUdp.h>
|
||||
#include "connection.h"
|
||||
|
||||
#define OBJ_COUNT 4
|
||||
|
||||
extern "C" {
|
||||
extern lwm2m_object_t * get_object_device(void);
|
||||
extern void free_object_device(lwm2m_object_t * objectP);
|
||||
extern lwm2m_object_t * get_server_object(void);
|
||||
extern void free_server_object(lwm2m_object_t * object);
|
||||
extern lwm2m_object_t * get_security_object(const char * uri);
|
||||
extern void free_security_object(lwm2m_object_t * objectP);
|
||||
extern char * get_server_uri(lwm2m_object_t * objectP, uint16_t secObjInstID);
|
||||
extern lwm2m_object_t * get_test_object(void);
|
||||
extern void free_test_object(lwm2m_object_t * object);
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
lwm2m_object_t * securityObjP;
|
||||
connection_t * connList;
|
||||
EthernetUDP * udp;
|
||||
} client_data_t;
|
||||
|
||||
|
||||
class ArduinoClient {
|
||||
|
||||
public:
|
||||
ArduinoClient(const char *uri) : uri(uri) {};
|
||||
void init();
|
||||
~ArduinoClient();
|
||||
void doWorkStep();
|
||||
private:
|
||||
const char * uri;
|
||||
const uint16_t localPort = 56830;
|
||||
const char * name = "arduino_lwm2m_client";
|
||||
time_t step_delay = 60;
|
||||
int result;
|
||||
|
||||
char packetBuffer[UDP_TX_PACKET_MAX_SIZE];
|
||||
|
||||
client_data_t data;
|
||||
lwm2m_context_t * lwm2mH = nullptr;
|
||||
lwm2m_object_t * objArray[OBJ_COUNT];
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
2
wakaama-core/.gitignore
vendored
Normal file
2
wakaama-core/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.idea
|
||||
cmake-build-debug
|
||||
28
wakaama-core/CMakeLists.txt
Normal file
28
wakaama-core/CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(wakaama_core)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
set(SOURCE_FILES
|
||||
er-coap-13/er-coap-13.c
|
||||
er-coap-13/er-coap-13.h
|
||||
block1.c
|
||||
bootstrap.c
|
||||
data.c
|
||||
discover.c
|
||||
internals.h
|
||||
json.c
|
||||
liblwm2m.c
|
||||
liblwm2m.h
|
||||
list.c
|
||||
management.c
|
||||
objects.c
|
||||
observe.c
|
||||
packet.c
|
||||
registration.c
|
||||
tlv.c
|
||||
transaction.c
|
||||
uri.c
|
||||
utils.c)
|
||||
|
||||
add_executable(wakaama_core ${SOURCE_FILES})
|
||||
153
wakaama-core/block1.c
Normal file
153
wakaama-core/block1.c
Normal 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
wakaama-core/bootstrap.c
Normal file
768
wakaama-core/bootstrap.c
Normal 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
wakaama-core/data.c
Normal file
656
wakaama-core/data.c
Normal 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
wakaama-core/discover.c
Normal file
442
wakaama-core/discover.c
Normal 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
|
||||
1385
wakaama-core/er-coap-13.c
Normal file
1385
wakaama-core/er-coap-13.c
Normal file
File diff suppressed because it is too large
Load Diff
407
wakaama-core/er-coap-13.h
Normal file
407
wakaama-core/er-coap-13.h
Normal 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
wakaama-core/internals.h
Normal file
349
wakaama-core/internals.h
Normal 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.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
wakaama-core/json.c
Normal file
1436
wakaama-core/json.c
Normal file
File diff suppressed because it is too large
Load Diff
484
wakaama-core/liblwm2m.c
Normal file
484
wakaama-core/liblwm2m.c
Normal 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
wakaama-core/liblwm2m.h
Normal file
743
wakaama-core/liblwm2m.h
Normal 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
wakaama-core/list.c
Normal file
125
wakaama-core/list.c
Normal 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
wakaama-core/management.c
Normal file
786
wakaama-core/management.c
Normal 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
wakaama-core/objects.c
Normal file
926
wakaama-core/objects.c
Normal 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
wakaama-core/observe.c
Normal file
1076
wakaama-core/observe.c
Normal file
File diff suppressed because it is too large
Load Diff
443
wakaama-core/packet.c
Normal file
443
wakaama-core/packet.c
Normal 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;
|
||||
}
|
||||
|
||||
1383
wakaama-core/registration.c
Normal file
1383
wakaama-core/registration.c
Normal file
File diff suppressed because it is too large
Load Diff
580
wakaama-core/tlv.c
Normal file
580
wakaama-core/tlv.c
Normal 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;
|
||||
}
|
||||
|
||||
473
wakaama-core/transaction.c
Normal file
473
wakaama-core/transaction.c
Normal file
@ -0,0 +1,473 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
lwm2m_printf("max");
|
||||
|
||||
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
wakaama-core/uri.c
Normal file
328
wakaama-core/uri.c
Normal 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
wakaama-core/utils.c
Normal file
554
wakaama-core/utils.c
Normal 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
wakaama-core/wakaama.cmake
Normal file
55
wakaama-core/wakaama.cmake
Normal 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()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user