diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..cf8286ca9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,26 @@
+*.o
+*.lo
+*~
+*.la
+*.pc
+*.tgz
+*.gz
+.dirstamp
+
+Makefile
+
+.cproject
+.project
+
+CMakeCache.txt
+CMakeFiles
+Debug
+Release
+cmake_install.cmake
+core/CMakeCache.txt
+core/CMakeFiles
+core/Makefile
+core/cmake_install.cmake
+lwm2mclient
+lwm2mserver
+tlvdecode
diff --git a/EDL-v1.0 b/EDL-v1.0
new file mode 100644
index 000000000..265744759
--- /dev/null
+++ b/EDL-v1.0
@@ -0,0 +1,30 @@
+*Eclipse Distribution License - v 1.0*
+
+Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
+
+All rights reserved.
+
+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 the Eclipse Foundation, Inc. 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.
+
diff --git a/EPL-v1.0 b/EPL-v1.0
new file mode 100644
index 000000000..0d347ab80
--- /dev/null
+++ b/EPL-v1.0
@@ -0,0 +1,224 @@
+
+ Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF
+THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+*1. DEFINITIONS*
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and
+documentation distributed under this Agreement, and
+
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and
+are distributed by that particular Contributor. A Contribution
+'originates' from a Contributor if it was added to the Program by such
+Contributor itself or anyone acting on such Contributor's behalf.
+Contributions do not include additions to the Program which: (i) are
+separate modules of software distributed in conjunction with the Program
+under their own license agreement, and (ii) are not derivative works of
+the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which
+are necessarily infringed by the use or sale of its Contribution alone
+or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement,
+including all Contributors.
+
+*2. GRANT OF RIGHTS*
+
+a) Subject to the terms of this Agreement, each Contributor hereby
+grants Recipient a non-exclusive, worldwide, royalty-free copyright
+license to reproduce, prepare derivative works of, publicly display,
+publicly perform, distribute and sublicense the Contribution of such
+Contributor, if any, and such derivative works, in source code and
+object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby
+grants Recipient a non-exclusive, worldwide, royalty-free patent license
+under Licensed Patents to make, use, sell, offer to sell, import and
+otherwise transfer the Contribution of such Contributor, if any, in
+source code and object code form. This patent license shall apply to the
+combination of the Contribution and the Program if, at the time the
+Contribution is added by the Contributor, such addition of the
+Contribution causes such combination to be covered by the Licensed
+Patents. The patent license shall not apply to any other combinations
+which include the Contribution. No hardware per se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the
+licenses to its Contributions set forth herein, no assurances are
+provided by any Contributor that the Program does not infringe the
+patent or other intellectual property rights of any other entity. Each
+Contributor disclaims any liability to Recipient for claims brought by
+any other entity based on infringement of intellectual property rights
+or otherwise. As a condition to exercising the rights and licenses
+granted hereunder, each Recipient hereby assumes sole responsibility to
+secure any other intellectual property rights needed, if any. For
+example, if a third party patent license is required to allow Recipient
+to distribute the Program, it is Recipient's responsibility to acquire
+that license before distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has sufficient
+copyright rights in its Contribution, if any, to grant the copyright
+license set forth in this Agreement.
+
+*3. REQUIREMENTS*
+
+A Contributor may choose to distribute the Program in object code form
+under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties
+and conditions, express and implied, including warranties or conditions
+of title and non-infringement, and implied warranties or conditions of
+merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability for
+damages, including direct, indirect, special, incidental and
+consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement are
+offered by that Contributor alone and not by any other party; and
+
+iv) states that source code for the Program is available from such
+Contributor, and informs licensees how to obtain it in a reasonable
+manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained
+within the Program.
+
+Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.
+
+*4. COMMERCIAL DISTRIBUTION*
+
+Commercial distributors of software may accept certain responsibilities
+with respect to end users, business partners and the like. While this
+license is intended to facilitate the commercial use of the Program, the
+Contributor who includes the Program in a commercial product offering
+should do so in a manner which does not create potential liability for
+other Contributors. Therefore, if a Contributor includes the Program in
+a commercial product offering, such Contributor ("Commercial
+Contributor") hereby agrees to defend and indemnify every other
+Contributor ("Indemnified Contributor") against any losses, damages and
+costs (collectively "Losses") arising from claims, lawsuits and other
+legal actions brought by a third party against the Indemnified
+Contributor to the extent caused by the acts or omissions of such
+Commercial Contributor in connection with its distribution of the
+Program in a commercial product offering. The obligations in this
+section do not apply to any claims or Losses relating to any actual or
+alleged intellectual property infringement. In order to qualify, an
+Indemnified Contributor must: a) promptly notify the Commercial
+Contributor in writing of such claim, and b) allow the Commercial
+Contributor to control, and cooperate with the Commercial Contributor
+in, the defense and any related settlement negotiations. The Indemnified
+Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those
+performance claims and warranties, and if a court requires any other
+Contributor to pay any damages as a result, the Commercial Contributor
+must pay those damages.
+
+*5. NO WARRANTY*
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED
+ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES
+OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR
+A PARTICULAR PURPOSE. Each Recipient is solely responsible for
+determining the appropriateness of using and distributing the Program
+and assumes all risks associated with its exercise of rights under this
+Agreement , including but not limited to the risks and costs of program
+errors, compliance with applicable laws, damage to or loss of data,
+programs or equipment, and unavailability or interruption of operations.
+
+*6. DISCLAIMER OF LIABILITY*
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), 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 OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+*7. GENERAL*
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further action
+by the parties hereto, such provision shall be reformed to the minimum
+extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including
+a cross-claim or counterclaim in a lawsuit) alleging that the Program
+itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails
+to comply with any of the material terms or conditions of this Agreement
+and does not cure such failure in a reasonable period of time after
+becoming aware of such noncompliance. If all Recipient's rights under
+this Agreement terminate, Recipient agrees to cease use and distribution
+of the Program as soon as reasonably practicable. However, Recipient's
+obligations under this Agreement and any licenses granted by Recipient
+relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement,
+but in order to avoid inconsistency the Agreement is copyrighted and may
+only be modified in the following manner. The Agreement Steward reserves
+the right to publish new versions (including revisions) of this
+Agreement from time to time. No one other than the Agreement Steward has
+the right to modify this Agreement. The Eclipse Foundation is the
+initial Agreement Steward. The Eclipse Foundation may assign the
+responsibility to serve as the Agreement Steward to a suitable separate
+entity. Each new version of the Agreement will be given a distinguishing
+version number. The Program (including Contributions) may always be
+distributed subject to the version of the Agreement under which it was
+received. In addition, after a new version of the Agreement is
+published, Contributor may elect to distribute the Program (including
+its Contributions) under the new version. Except as expressly stated in
+Sections 2(a) and 2(b) above, Recipient receives no rights or licenses
+to the intellectual property of any Contributor under this Agreement,
+whether expressly, by implication, estoppel or otherwise. All rights in
+the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the
+intellectual property laws of the United States of America. No party to
+this Agreement will bring a legal action under this Agreement more than
+one year after the cause of action arose. Each party waives its rights
+to a jury trial in any resulting litigation.
+
diff --git a/README b/README
new file mode 100644
index 000000000..fd54cb8bb
--- /dev/null
+++ b/README
@@ -0,0 +1,80 @@
+Wakaama (formerly liblwm2m) is an implementation of the Open Mobile Alliance's LightWeight M2M
+protocol (LWM2M).
+
+
+Source Layout
+-------------
+
+-+- core (the LWM2M engine)
+ | |
+ | +- er-coap-13 (Erbium's CoAP engine from
+ | http://people.inf.ethz.ch/mkovatsc/erbium.php, modified
+ | to run on linux)
+ |
+ +- tests (example and test application)
+ |
+ +- client (a command-line LWM2M client with two test objects)
+ |
+ +- server (a command-line LWM2M server)
+ |
+ +- TLV (application decoding two hard-coded TLV buffers)
+ |
+ +- utils (utility functions for connection handling and command-
+ line interface)
+
+
+Compiling
+---------
+
+Despite its name, liblwm2m is not a library but files to be built with an
+application. liblwm2m uses CMake. Look at tests/server/CMakeLists.txt for an
+example of how to include it.
+Two compilation switches are used: LWM2M_CLIENT_MODE and LWM2M_SERVER_MODE.
+Define LWM2M_CLIENT_MODE to enable LWM2M Client interfaces. Define
+LWM2M_SERVER_MODE to enable LWM2M Server interfaces. Both can be defined at the
+same time.
+
+
+Testing
+-------
+
+To compile the test server
+ - - - - - - - - - - - - -
+
+In the any directory, run the following commands:
+ cmake [liblwm2m directory]/tests/server
+ make
+ ./lwm2mserver
+
+The lwm2mserver listens on UDP port 5684. It features a basic command line
+interface. Type 'help' for a list of supported commands.
+
+
+To compile the test client
+ - - - - - - - - - - - - -
+
+In the any directory, run the following commands:
+ cmake [liblwm2m directory]/tests/client
+ make
+ ./lwm2mclient
+
+The lwm2mclient features four LWM2M objects:
+ - Server Object (id: 1) provided by the core.
+ - Device Object (id: 3) containing hard-coded values from the Example LWM2M
+ Client of Appendix E of the LWM2M Technical Specification.
+ - FirmwareUpdate Object (id: 5) as a skeleton.
+ - a test object (id: 1024) with the following description:
+
+ Multiple
+ Object | ID | Instances | Mandatoty |
+ Test | 1024 | Yes | No |
+
+ Ressources:
+ Supported Multiple
+ Name | ID | Operations | Instances | Mandatory | Type | Range |
+ test | 1 | R/W | No | Yes | Integer | 0-255 |
+ exec | 2 | E | No | Yes | | |
+
+The lwm2mclient opens udp port 5683 and tries to register to a LWM2M Server at
+127.0.0.1:5684. It features a basic command line interface. Type 'help' for a
+list of supported commands.
diff --git a/TODO b/TODO
new file mode 100644
index 000000000..4ba2536e8
--- /dev/null
+++ b/TODO
@@ -0,0 +1,54 @@
+LWM2M Features
+--------------
+
+ - LWM2M Security and LWM2M Server Objects:
+ Implement these two objects in core as interfaces to lwm2m_server_t instances.
+ Forbid clients to add objects with ID 0 or 1.
+ * dnav WORKING ON IT *
+
+ - Bootstrap interface:
+ Implements LWM2M Bootstap Server as a special instance of lwm2m_server_t.
+
+ - Access Control List
+
+ - JSON support
+
+ - Add token in every message
+
+ - Handle Observe parameters
+
+ - Keep-alive mechanism
+
+
+ Implementation Improvments
+ --------------------------
+
+ - Store lwm2m_transaction_t per peer
+
+ - bufferize all CoaP messages until all callbacks returned
+ Currently if a server sends a request from its monitoring callback upon client
+ registration, the client will receive the request before the ACK to its register
+ and it will discard it.
+
+ - Easy access to the lwm2m_context_t from objects callbacks
+
+ - Allow object callbacks to return static buffers
+ This can be done by a flag stating if the retruned buffer must be freed or not by the core.
+
+ - Simplify lwm2m_uri_t handling
+ Basically hide the flag member.
+
+ - Use an unified result callback
+ Add some parameter so that the same user callback can be used for all DM operations.
+
+ - Use lwm2m-*-t types in er-coap-13
+ To avoid multiple conversions.
+
+ - Switch to void* parameters in lwm2m_list_* APIs
+
+ - Change object interface to hide the whole TLV mechanism
+ Useful when the core will support JSON.
+
+ - Utility functions to easily implements objects
+ The utility will just use read and write individual resources. Either statically or
+ throught callbacks. See [https://github.com/01org/libdmclient]/tests/mgtobj/utils/static_mo_util.h
\ No newline at end of file
diff --git a/about.html b/about.html
new file mode 100644
index 000000000..e95fef7f2
--- /dev/null
+++ b/about.html
@@ -0,0 +1,46 @@
+
+
+
+
+About
+
+
+About This Content
+
+April 24, 2014
+License
+
+The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL") and the Eclipse Distribution License Version 1.0 ("EDL").
+A copy of the EPL is available at
+http://www.eclipse.org/legal/epl-v10.html.
+A copy of the EDL is available at
+http://www.eclipse.org/org/documents/edl-v10.php.
+For purposes of the EPL, "Program" will mean the Content.
+
+If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at http://www.eclipse.org.
+
+
+ Third Party Content
+ The Content includes items that have been sourced from third parties as set out below. If you
+ did not receive this Content directly from the Eclipse Foundation, the following is provided
+ for informational purposes only, and you should look to the Redistributor's license for
+ terms and conditions of use.
+
+ er-coap-13
+ Erbium's CoAP engine from
+ http://people.inf.ethz.ch/mkovatsc/erbium.php
+ Your use of er-coap-13 is subject to the terms and conditions of the BSD license contained in the file LICENSE
+ in core/er-coap-13
+
+
+
+
+
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
new file mode 100644
index 000000000..b697c1c49
--- /dev/null
+++ b/core/CMakeLists.txt
@@ -0,0 +1,16 @@
+SET(EXT_SOURCES ${CMAKE_CURRENT_LIST_DIR}/er-coap-13/er-coap-13.c)
+SET(CORE_SOURCES
+ ${CMAKE_CURRENT_LIST_DIR}/liblwm2m.c
+ ${CMAKE_CURRENT_LIST_DIR}/uri.c
+ ${CMAKE_CURRENT_LIST_DIR}/utils.c
+ ${CMAKE_CURRENT_LIST_DIR}/objects.c
+ ${CMAKE_CURRENT_LIST_DIR}/object_server.c
+ ${CMAKE_CURRENT_LIST_DIR}/tlv.c
+ ${CMAKE_CURRENT_LIST_DIR}/list.c
+ ${CMAKE_CURRENT_LIST_DIR}/packet.c
+ ${CMAKE_CURRENT_LIST_DIR}/transaction.c
+ ${CMAKE_CURRENT_LIST_DIR}/registration.c
+ ${CMAKE_CURRENT_LIST_DIR}/management.c
+ ${CMAKE_CURRENT_LIST_DIR}/observe.c
+ ${EXT_SOURCES}
+ PARENT_SCOPE)
diff --git a/core/er-coap-13/LICENSE b/core/er-coap-13/LICENSE
new file mode 100644
index 000000000..0cf394b29
--- /dev/null
+++ b/core/er-coap-13/LICENSE
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is part of the Contiki operating system.
+ */
diff --git a/core/er-coap-13/er-coap-13.c b/core/er-coap-13/er-coap-13.c
new file mode 100644
index 000000000..351c1ef28
--- /dev/null
+++ b/core/er-coap-13/er-coap-13.c
@@ -0,0 +1,1294 @@
+/*
+ * 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
+ */
+
+/*******************************************************************************
+ *
+ * This file is 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 - Adapt to usage in liblwm2m
+ *
+ *******************************************************************************/
+
+#include
+
+#include
+#include
+
+#include "er-coap-13.h"
+
+#include "liblwm2m.h" /* for lwm2m_malloc() and lwm2m_free() */
+
+#define DEBUG 0
+#if DEBUG
+#include
+#define PRINTF(...) printf(__VA_ARGS__)
+#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
+#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
+#else
+#define PRINTF(...)
+#define PRINT6ADDR(addr)
+#define PRINTLLADDR(addr)
+#endif
+
+/*-----------------------------------------------------------------------------------*/
+/*- Variables -----------------------------------------------------------------------*/
+/*-----------------------------------------------------------------------------------*/
+static uint16_t current_mid = 0;
+
+coap_status_t coap_error_code = NO_ERROR;
+char *coap_error_message = "";
+/*-----------------------------------------------------------------------------------*/
+/*- LOCAL HELP FUNCTIONS ------------------------------------------------------------*/
+/*-----------------------------------------------------------------------------------*/
+static
+uint16_t
+coap_log_2(uint16_t value)
+{
+ uint16_t result = 0;
+ do {
+ value = value >> 1;
+ result++;
+ } while (value);
+
+ return result ? result - 1 : result;
+}
+/*-----------------------------------------------------------------------------------*/
+static
+uint32_t
+coap_parse_int_option(uint8_t *bytes, size_t length)
+{
+ uint32_t var = 0;
+ int i = 0;
+ while (i268)
+ {
+ buffer[++written] = (*x-269)>>8;
+ buffer[++written] = (*x-269);
+ }
+ else if (*x>12)
+ {
+ buffer[++written] = (*x-13);
+ }
+ }
+ while (x!=(unsigned int *)&length && (x=(unsigned int *)&length));
+
+ PRINTF("WRITTEN %u B opt header\n", written);
+
+ return ++written;
+}
+/*-----------------------------------------------------------------------------------*/
+static
+size_t
+coap_serialize_int_option(unsigned int number, unsigned int current_number, uint8_t *buffer, uint32_t value)
+{
+ size_t i = 0;
+
+ if (0xFF000000 & value) ++i;
+ if (0xFFFF0000 & value) ++i;
+ if (0xFFFFFF00 & value) ++i;
+ if (0xFFFFFFFF & value) ++i;
+
+ PRINTF("OPTION %u (delta %u, len %u)\n", number, number - current_number, i);
+
+ i = coap_set_option_header(number - current_number, i, buffer);
+
+ if (0xFF000000 & value) buffer[i++] = (uint8_t) (value>>24);
+ if (0xFFFF0000 & value) buffer[i++] = (uint8_t) (value>>16);
+ if (0xFFFFFF00 & value) buffer[i++] = (uint8_t) (value>>8);
+ if (0xFFFFFFFF & value) buffer[i++] = (uint8_t) (value);
+
+ return i;
+}
+/*-----------------------------------------------------------------------------------*/
+static
+size_t
+coap_serialize_array_option(unsigned int number, unsigned int current_number, uint8_t *buffer, uint8_t *array, size_t length, char split_char)
+{
+ size_t i = 0;
+
+ if (split_char!='\0')
+ {
+ int j;
+ uint8_t *part_start = array;
+ uint8_t *part_end = NULL;
+ size_t temp_length;
+
+ for (j = 0; j<=length; ++j)
+ {
+ if (array[j]==split_char || j==length)
+ {
+ part_end = array + j;
+ temp_length = part_end-part_start;
+
+ i += coap_set_option_header(number - current_number, temp_length, &buffer[i]);
+ memcpy(&buffer[i], part_start, temp_length);
+ i += temp_length;
+
+ PRINTF("OPTION type %u, delta %u, len %u, part [%.*s]\n", number, number - current_number, i, temp_length, part_start);
+
+ ++j; /* skip the splitter */
+ current_number = number;
+ part_start = array + j;
+ }
+ } /* for */
+ }
+ else
+ {
+ i += coap_set_option_header(number - current_number, length, &buffer[i]);
+ memcpy(&buffer[i], array, length);
+ i += length;
+
+ PRINTF("OPTION type %u, delta %u, len %u\n", number, number - current_number, length);
+ }
+
+ return i;
+}
+/*-----------------------------------------------------------------------------------*/
+static
+size_t
+coap_serialize_multi_option(unsigned int number, unsigned int current_number, uint8_t *buffer, multi_option_t *array)
+{
+ size_t i = 0;
+ multi_option_t * j;
+
+ for (j = array; j != NULL; j= j->next)
+ {
+ i += coap_set_option_header(number - current_number, j->len, &buffer[i]);
+ current_number = number;
+ memcpy(&buffer[i], j->data, j->len);
+ i += j->len;
+ } /* for */
+
+ return i;
+}
+/*-----------------------------------------------------------------------------------*/
+static
+void
+coap_merge_multi_option(char **dst, size_t *dst_len, uint8_t *option, size_t option_len, char separator)
+{
+ /* Merge multiple options. */
+ if (*dst_len > 0)
+ {
+ /* dst already contains an option: concatenate */
+ (*dst)[*dst_len] = separator;
+ *dst_len += 1;
+
+ /* memmove handles 2-byte option headers */
+ memmove((*dst)+(*dst_len), option, option_len);
+
+ *dst_len += option_len;
+ }
+ else
+ {
+ /* dst is empty: set to option */
+ *dst = (char *) option;
+ *dst_len = option_len;
+ }
+}
+
+static
+void
+coap_add_multi_option(multi_option_t **dst, uint8_t *option, size_t option_len, uint8_t is_static)
+{
+ multi_option_t *opt = (multi_option_t *)lwm2m_malloc(sizeof(multi_option_t));
+
+ if (opt)
+ {
+ opt->next = NULL;
+ opt->len = option_len;
+ if (is_static)
+ {
+ opt->data = option;
+ opt->is_static = 1;
+ }
+ else
+ {
+ opt->data = (char *)lwm2m_malloc(option_len);
+ if (opt->data == NULL)
+ {
+ lwm2m_free(opt);
+ return;
+ }
+ memcpy(opt->data, option, option_len);
+ }
+
+ if (*dst)
+ {
+ multi_option_t * i = *dst;
+ while (i->next)
+ {
+ i = i->next;
+ }
+ i->next = opt;
+ }
+ else
+ {
+ *dst = opt;
+ }
+ }
+}
+
+static
+void
+free_multi_option(multi_option_t *dst)
+{
+ if (dst)
+ {
+ multi_option_t *n = dst->next;
+ if (dst->is_static == 0)
+ {
+ lwm2m_free(dst->data);
+ }
+ lwm2m_free(dst);
+ free_multi_option(n);
+ }
+}
+
+char * coap_get_multi_option_as_string(multi_option_t * option)
+{
+ size_t len = 0;
+ multi_option_t * opt;
+ char * output;
+
+ for (opt = option; opt != NULL; opt = opt->next)
+ {
+ len += opt->len + 1; // for separator
+ }
+
+ output = lwm2m_malloc(len + 1);
+ if (output != NULL)
+ {
+ size_t i = 0;
+
+ for (opt = option; opt != NULL; opt = opt->next)
+ {
+ output[i] = '/';
+ i += 1;
+
+ memmove(output + i, opt->data, opt->len);
+ i += opt->len;
+ }
+ output[i] = 0;
+ }
+
+ return output;
+}
+
+/*-----------------------------------------------------------------------------------*/
+static
+int
+coap_get_variable(const char *buffer, size_t length, const char *name, const char **output)
+{
+ const char *start = NULL;
+ const char *end = NULL;
+ const char *value_end = NULL;
+ size_t name_len = 0;
+
+ /*initialize the output buffer first*/
+ *output = 0;
+
+ name_len = strlen(name);
+ end = buffer + length;
+
+ for (start = buffer; start + name_len < end; ++start){
+ if ((start == buffer || start[-1] == '&') && start[name_len] == '=' &&
+ strncmp(name, start, name_len)==0) {
+
+ /* Point start to variable value */
+ start += name_len + 1;
+
+ /* Point end to the end of the value */
+ value_end = (const char *) memchr(start, '&', end - start);
+ if (value_end == NULL) {
+ value_end = end;
+ }
+
+ *output = start;
+
+ return (value_end - start);
+ }
+ }
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------------*/
+uint16_t
+coap_get_mid()
+{
+ return ++current_mid;
+}
+/*-----------------------------------------------------------------------------------*/
+/*- MEASSAGE PROCESSING -------------------------------------------------------------*/
+/*-----------------------------------------------------------------------------------*/
+void
+coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t mid)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ /* Important thing */
+ memset(coap_pkt, 0, sizeof(coap_packet_t));
+
+ coap_pkt->type = type;
+ coap_pkt->code = code;
+ coap_pkt->mid = mid;
+}
+
+void
+coap_free_header(void *packet)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ free_multi_option(coap_pkt->uri_path);
+ free_multi_option(coap_pkt->uri_query);
+ free_multi_option(coap_pkt->location_path);
+}
+
+/*-----------------------------------------------------------------------------------*/
+size_t
+coap_serialize_message(void *packet, uint8_t *buffer)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+ uint8_t *option;
+ unsigned int current_number = 0;
+
+ /* Initialize */
+ coap_pkt->buffer = buffer;
+ coap_pkt->version = 1;
+
+ PRINTF("-Serializing MID %u to %p, ", coap_pkt->mid, coap_pkt->buffer);
+
+ /* set header fields */
+ coap_pkt->buffer[0] = 0x00;
+ coap_pkt->buffer[0] |= COAP_HEADER_VERSION_MASK & (coap_pkt->version)<buffer[0] |= COAP_HEADER_TYPE_MASK & (coap_pkt->type)<buffer[0] |= COAP_HEADER_TOKEN_LEN_MASK & (coap_pkt->token_len)<buffer[1] = coap_pkt->code;
+ coap_pkt->buffer[2] = (uint8_t) ((coap_pkt->mid)>>8);
+ coap_pkt->buffer[3] = (uint8_t) (coap_pkt->mid);
+
+ /* set Token */
+ PRINTF("Token (len %u)", coap_pkt->token_len);
+ option = coap_pkt->buffer + COAP_HEADER_LEN;
+ for (current_number=0; current_numbertoken_len; ++current_number)
+ {
+ PRINTF(" %02X", coap_pkt->token[current_number]);
+ *option = coap_pkt->token[current_number];
+ ++option;
+ }
+ PRINTF("-\n");
+
+ /* Serialize options */
+ current_number = 0;
+
+ PRINTF("-Serializing options at %p-\n", option);
+
+ /* The options must be serialized in the order of their number */
+ COAP_SERIALIZE_BYTE_OPTION( COAP_OPTION_IF_MATCH, if_match, "If-Match")
+ COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_URI_HOST, uri_host, '\0', "Uri-Host")
+ COAP_SERIALIZE_BYTE_OPTION( COAP_OPTION_ETAG, etag, "ETag")
+ COAP_SERIALIZE_INT_OPTION( COAP_OPTION_IF_NONE_MATCH, content_type-coap_pkt->content_type, "If-None-Match") /* hack to get a zero field */
+ COAP_SERIALIZE_INT_OPTION( COAP_OPTION_OBSERVE, observe, "Observe")
+ COAP_SERIALIZE_INT_OPTION( COAP_OPTION_URI_PORT, uri_port, "Uri-Port")
+ COAP_SERIALIZE_MULTI_OPTION( COAP_OPTION_LOCATION_PATH, location_path, "Location-Path")
+ COAP_SERIALIZE_MULTI_OPTION( COAP_OPTION_URI_PATH, uri_path, "Uri-Path")
+ COAP_SERIALIZE_INT_OPTION( COAP_OPTION_CONTENT_TYPE, content_type, "Content-Format")
+ COAP_SERIALIZE_INT_OPTION( COAP_OPTION_MAX_AGE, max_age, "Max-Age")
+ COAP_SERIALIZE_MULTI_OPTION( COAP_OPTION_URI_QUERY, uri_query, "Uri-Query")
+ COAP_SERIALIZE_ACCEPT_OPTION( COAP_OPTION_ACCEPT, accept, "Accept")
+ COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_LOCATION_QUERY, location_query, '&', "Location-Query")
+ COAP_SERIALIZE_BLOCK_OPTION( COAP_OPTION_BLOCK2, block2, "Block2")
+ COAP_SERIALIZE_BLOCK_OPTION( COAP_OPTION_BLOCK1, block1, "Block1")
+ COAP_SERIALIZE_INT_OPTION( COAP_OPTION_SIZE, size, "Size")
+ COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_PROXY_URI, proxy_uri, '\0', "Proxy-Uri")
+
+ PRINTF("-Done serializing at %p----\n", option);
+
+ /* Free allocated header fields */
+ coap_free_header(packet);
+
+ /* Pack payload */
+ if ((option - coap_pkt->buffer)<=COAP_MAX_HEADER_SIZE)
+ {
+ /* Payload marker */
+ if (coap_pkt->payload_len)
+ {
+ *option = 0xFF;
+ ++option;
+ }
+
+ memmove(option, coap_pkt->payload, coap_pkt->payload_len);
+ }
+ else
+ {
+ /* An error occured. Caller must check for !=0. */
+ coap_pkt->buffer = NULL;
+ coap_error_message = "Serialized header exceeds COAP_MAX_HEADER_SIZE";
+ return 0;
+ }
+
+ PRINTF("-Done %u B (header len %u, payload len %u)-\n", coap_pkt->payload_len + option - buffer, option - buffer, coap_pkt->payload_len);
+
+ PRINTF("Dump [0x%02X %02X %02X %02X %02X %02X %02X %02X]\n",
+ coap_pkt->buffer[0],
+ coap_pkt->buffer[1],
+ coap_pkt->buffer[2],
+ coap_pkt->buffer[3],
+ coap_pkt->buffer[4],
+ coap_pkt->buffer[5],
+ coap_pkt->buffer[6],
+ coap_pkt->buffer[7]
+ );
+
+ return (option - buffer) + coap_pkt->payload_len; /* packet length */
+}
+/*-----------------------------------------------------------------------------------*/
+coap_status_t
+coap_parse_message(void *packet, uint8_t *data, uint16_t data_len)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ /* Initialize packet */
+ memset(coap_pkt, 0, sizeof(coap_packet_t));
+
+ /* pointer to packet bytes */
+ coap_pkt->buffer = data;
+
+ /* parse header fields */
+ coap_pkt->version = (COAP_HEADER_VERSION_MASK & coap_pkt->buffer[0])>>COAP_HEADER_VERSION_POSITION;
+ coap_pkt->type = (COAP_HEADER_TYPE_MASK & coap_pkt->buffer[0])>>COAP_HEADER_TYPE_POSITION;
+ coap_pkt->token_len = MIN(COAP_TOKEN_LEN, (COAP_HEADER_TOKEN_LEN_MASK & coap_pkt->buffer[0])>>COAP_HEADER_TOKEN_LEN_POSITION);
+ coap_pkt->code = coap_pkt->buffer[1];
+ coap_pkt->mid = coap_pkt->buffer[2]<<8 | coap_pkt->buffer[3];
+
+ if (coap_pkt->version != 1)
+ {
+ coap_error_message = "CoAP version must be 1";
+ return BAD_REQUEST_4_00;
+ }
+
+ uint8_t *current_option = data + COAP_HEADER_LEN;
+
+ if (coap_pkt->token_len != 0)
+ {
+ memcpy(coap_pkt->token, current_option, coap_pkt->token_len);
+ SET_OPTION(coap_pkt, COAP_OPTION_TOKEN);
+
+ PRINTF("Token (len %u) [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->token_len,
+ coap_pkt->token[0],
+ coap_pkt->token[1],
+ coap_pkt->token[2],
+ coap_pkt->token[3],
+ coap_pkt->token[4],
+ coap_pkt->token[5],
+ coap_pkt->token[6],
+ coap_pkt->token[7]
+ ); /*FIXME always prints 8 bytes */
+ }
+
+ /* parse options */
+ current_option += coap_pkt->token_len;
+
+ unsigned int option_number = 0;
+ unsigned int option_delta = 0;
+ size_t option_length = 0;
+
+ while (current_option < data+data_len)
+ {
+ /* Payload marker 0xFF, currently only checking for 0xF* because rest is reserved */
+ if ((current_option[0] & 0xF0)==0xF0)
+ {
+ coap_pkt->payload = ++current_option;
+ coap_pkt->payload_len = data_len - (coap_pkt->payload - data);
+
+ /* also for receiving, the Erbium upper bound is REST_MAX_CHUNK_SIZE */
+ if (coap_pkt->payload_len > REST_MAX_CHUNK_SIZE)
+ {
+ coap_pkt->payload_len = REST_MAX_CHUNK_SIZE;
+ }
+
+ break;
+ }
+
+ option_delta = current_option[0]>>4;
+ option_length = current_option[0] & 0x0F;
+ ++current_option;
+
+ /* avoids code duplication without function overhead */
+ unsigned int *x = &option_delta;
+ do
+ {
+ if (*x==13)
+ {
+ *x += current_option[0];
+ ++current_option;
+ }
+ else if (*x==14)
+ {
+ *x += 255;
+ *x += current_option[0]<<8;
+ ++current_option;
+ *x += current_option[0];
+ ++current_option;
+ }
+ }
+ while (x!=(unsigned int *)&option_length && (x=(unsigned int *)&option_length));
+
+ option_number += option_delta;
+
+ PRINTF("OPTION %u (delta %u, len %u): ", option_number, option_delta, option_length);
+
+ SET_OPTION(coap_pkt, option_number);
+
+ switch (option_number)
+ {
+ case COAP_OPTION_CONTENT_TYPE:
+ coap_pkt->content_type = coap_parse_int_option(current_option, option_length);
+ PRINTF("Content-Format [%u]\n", coap_pkt->content_type);
+ break;
+ case COAP_OPTION_MAX_AGE:
+ coap_pkt->max_age = coap_parse_int_option(current_option, option_length);
+ PRINTF("Max-Age [%lu]\n", coap_pkt->max_age);
+ break;
+ case COAP_OPTION_ETAG:
+ coap_pkt->etag_len = MIN(COAP_ETAG_LEN, option_length);
+ memcpy(coap_pkt->etag, current_option, coap_pkt->etag_len);
+ PRINTF("ETag %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->etag_len,
+ coap_pkt->etag[0],
+ coap_pkt->etag[1],
+ coap_pkt->etag[2],
+ coap_pkt->etag[3],
+ coap_pkt->etag[4],
+ coap_pkt->etag[5],
+ coap_pkt->etag[6],
+ coap_pkt->etag[7]
+ ); /*FIXME always prints 8 bytes */
+ break;
+ case COAP_OPTION_ACCEPT:
+ if (coap_pkt->accept_num < COAP_MAX_ACCEPT_NUM)
+ {
+ coap_pkt->accept[coap_pkt->accept_num] = coap_parse_int_option(current_option, option_length);
+ coap_pkt->accept_num += 1;
+ PRINTF("Accept [%u]\n", coap_pkt->content_type);
+ }
+ break;
+ case COAP_OPTION_IF_MATCH:
+ /*FIXME support multiple ETags */
+ coap_pkt->if_match_len = MIN(COAP_ETAG_LEN, option_length);
+ memcpy(coap_pkt->if_match, current_option, coap_pkt->if_match_len);
+ PRINTF("If-Match %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->if_match_len,
+ coap_pkt->if_match[0],
+ coap_pkt->if_match[1],
+ coap_pkt->if_match[2],
+ coap_pkt->if_match[3],
+ coap_pkt->if_match[4],
+ coap_pkt->if_match[5],
+ coap_pkt->if_match[6],
+ coap_pkt->if_match[7]
+ ); /*FIXME always prints 8 bytes */
+ break;
+ case COAP_OPTION_IF_NONE_MATCH:
+ coap_pkt->if_none_match = 1;
+ PRINTF("If-None-Match\n");
+ break;
+
+ case COAP_OPTION_URI_HOST:
+ coap_pkt->uri_host = (char *) current_option;
+ coap_pkt->uri_host_len = option_length;
+ PRINTF("Uri-Host [%.*s]\n", coap_pkt->uri_host_len, coap_pkt->uri_host);
+ break;
+ case COAP_OPTION_URI_PORT:
+ coap_pkt->uri_port = coap_parse_int_option(current_option, option_length);
+ PRINTF("Uri-Port [%u]\n", coap_pkt->uri_port);
+ break;
+ case COAP_OPTION_URI_PATH:
+ /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */
+ // coap_merge_multi_option( (char **) &(coap_pkt->uri_path), &(coap_pkt->uri_path_len), current_option, option_length, 0);
+ coap_add_multi_option( &(coap_pkt->uri_path), current_option, option_length, 1);
+ PRINTF("Uri-Path [%.*s]\n", sizeof(multi_option_t), coap_pkt->uri_path);
+ break;
+ case COAP_OPTION_URI_QUERY:
+ /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */
+ // coap_merge_multi_option( (char **) &(coap_pkt->uri_query), &(coap_pkt->uri_query_len), current_option, option_length, '&');
+ coap_add_multi_option( &(coap_pkt->uri_query), current_option, option_length, 1);
+ PRINTF("Uri-Query [%.*s]\n", sizeof(multi_option_t), coap_pkt->uri_query);
+ break;
+
+ case COAP_OPTION_LOCATION_PATH:
+ coap_add_multi_option( &(coap_pkt->location_path), current_option, option_length, 1);
+ break;
+ case COAP_OPTION_LOCATION_QUERY:
+ /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */
+ coap_merge_multi_option( (char **) &(coap_pkt->location_query), &(coap_pkt->location_query_len), current_option, option_length, '&');
+ PRINTF("Location-Query [%.*s]\n", coap_pkt->location_query_len, coap_pkt->location_query);
+ break;
+
+ case COAP_OPTION_PROXY_URI:
+ /*FIXME check for own end-point */
+ coap_pkt->proxy_uri = (char *) current_option;
+ coap_pkt->proxy_uri_len = option_length;
+ /*TODO length > 270 not implemented (actually not required) */
+ PRINTF("Proxy-Uri NOT IMPLEMENTED [%.*s]\n", coap_pkt->proxy_uri_len, coap_pkt->proxy_uri);
+ coap_error_message = "This is a constrained server (Contiki)";
+ return PROXYING_NOT_SUPPORTED_5_05;
+ break;
+
+ case COAP_OPTION_OBSERVE:
+ coap_pkt->observe = coap_parse_int_option(current_option, option_length);
+ PRINTF("Observe [%lu]\n", coap_pkt->observe);
+ break;
+ case COAP_OPTION_BLOCK2:
+ coap_pkt->block2_num = coap_parse_int_option(current_option, option_length);
+ coap_pkt->block2_more = (coap_pkt->block2_num & 0x08)>>3;
+ coap_pkt->block2_size = 16 << (coap_pkt->block2_num & 0x07);
+ coap_pkt->block2_offset = (coap_pkt->block2_num & ~0x0000000F)<<(coap_pkt->block2_num & 0x07);
+ coap_pkt->block2_num >>= 4;
+ PRINTF("Block2 [%lu%s (%u B/blk)]\n", coap_pkt->block2_num, coap_pkt->block2_more ? "+" : "", coap_pkt->block2_size);
+ break;
+ case COAP_OPTION_BLOCK1:
+ coap_pkt->block1_num = coap_parse_int_option(current_option, option_length);
+ coap_pkt->block1_more = (coap_pkt->block1_num & 0x08)>>3;
+ coap_pkt->block1_size = 16 << (coap_pkt->block1_num & 0x07);
+ coap_pkt->block1_offset = (coap_pkt->block1_num & ~0x0000000F)<<(coap_pkt->block1_num & 0x07);
+ coap_pkt->block1_num >>= 4;
+ PRINTF("Block1 [%lu%s (%u B/blk)]\n", coap_pkt->block1_num, coap_pkt->block1_more ? "+" : "", coap_pkt->block1_size);
+ break;
+ case COAP_OPTION_SIZE:
+ coap_pkt->size = coap_parse_int_option(current_option, option_length);
+ PRINTF("Size [%lu]\n", coap_pkt->size);
+ break;
+ default:
+ PRINTF("unknown (%u)\n", option_number);
+ /* Check if critical (odd) */
+ if (option_number & 1)
+ {
+ coap_error_message = "Unsupported critical option";
+ return BAD_OPTION_4_02;
+ }
+ }
+
+ current_option += option_length;
+ } /* for */
+ PRINTF("-Done parsing-------\n");
+
+
+
+ return NO_ERROR;
+}
+/*-----------------------------------------------------------------------------------*/
+/*- REST FRAMEWORK FUNCTIONS --------------------------------------------------------*/
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_query_variable(void *packet, const char *name, const char **output)
+{
+/*
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) {
+ return coap_get_variable(coap_pkt->uri_query, coap_pkt->uri_query_len, name, output);
+ }
+*/
+ return 0;
+}
+
+int
+coap_get_post_variable(void *packet, const char *name, const char **output)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (coap_pkt->payload_len) {
+ return coap_get_variable((const char *)coap_pkt->payload, coap_pkt->payload_len, name, output);
+ }
+ return 0;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_set_status_code(void *packet, unsigned int code)
+{
+ if (code <= 0xFF)
+ {
+ ((coap_packet_t *)packet)->code = (uint8_t) code;
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+/*-----------------------------------------------------------------------------------*/
+/*- HEADER OPTION GETTERS AND SETTERS -----------------------------------------------*/
+/*-----------------------------------------------------------------------------------*/
+unsigned int
+coap_get_header_content_type(void *packet)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE)) return -1;
+
+ return coap_pkt->content_type;
+}
+
+int
+coap_set_header_content_type(void *packet, unsigned int content_type)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ coap_pkt->content_type = (coap_content_type_t) content_type;
+ SET_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE);
+ return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_accept(void *packet, const uint16_t **accept)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_ACCEPT)) return 0;
+
+ *accept = coap_pkt->accept;
+ return coap_pkt->accept_num;
+}
+
+int
+coap_set_header_accept(void *packet, uint16_t accept)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (coap_pkt->accept_num < COAP_MAX_ACCEPT_NUM)
+ {
+ coap_pkt->accept[coap_pkt->accept_num] = accept;
+ coap_pkt->accept_num += 1;
+
+ SET_OPTION(coap_pkt, COAP_OPTION_ACCEPT);
+ }
+ return coap_pkt->accept_num;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_max_age(void *packet, uint32_t *age)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_MAX_AGE)) {
+ *age = COAP_DEFAULT_MAX_AGE;
+ } else {
+ *age = coap_pkt->max_age;
+ }
+ return 1;
+}
+
+int
+coap_set_header_max_age(void *packet, uint32_t age)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ coap_pkt->max_age = age;
+ SET_OPTION(coap_pkt, COAP_OPTION_MAX_AGE);
+ return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_etag(void *packet, const uint8_t **etag)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_ETAG)) return 0;
+
+ *etag = coap_pkt->etag;
+ return coap_pkt->etag_len;
+}
+
+int
+coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ coap_pkt->etag_len = MIN(COAP_ETAG_LEN, etag_len);
+ memcpy(coap_pkt->etag, etag, coap_pkt->etag_len);
+
+ SET_OPTION(coap_pkt, COAP_OPTION_ETAG);
+ return coap_pkt->etag_len;
+}
+/*-----------------------------------------------------------------------------------*/
+/*FIXME support multiple ETags */
+int
+coap_get_header_if_match(void *packet, const uint8_t **etag)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_IF_MATCH)) return 0;
+
+ *etag = coap_pkt->if_match;
+ return coap_pkt->if_match_len;
+}
+
+int
+coap_set_header_if_match(void *packet, const uint8_t *etag, size_t etag_len)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ coap_pkt->if_match_len = MIN(COAP_ETAG_LEN, etag_len);
+ memcpy(coap_pkt->if_match, etag, coap_pkt->if_match_len);
+
+ SET_OPTION(coap_pkt, COAP_OPTION_IF_MATCH);
+ return coap_pkt->if_match_len;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_if_none_match(void *packet)
+{
+ return IS_OPTION((coap_packet_t *)packet, COAP_OPTION_IF_NONE_MATCH) ? 1 : 0;
+}
+
+int
+coap_set_header_if_none_match(void *packet)
+{
+ SET_OPTION((coap_packet_t *)packet, COAP_OPTION_IF_NONE_MATCH);
+ return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_token(void *packet, const uint8_t **token)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_TOKEN)) return 0;
+
+ *token = coap_pkt->token;
+ return coap_pkt->token_len;
+}
+
+int
+coap_set_header_token(void *packet, const uint8_t *token, size_t token_len)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ coap_pkt->token_len = MIN(COAP_TOKEN_LEN, token_len);
+ memcpy(coap_pkt->token, token, coap_pkt->token_len);
+
+ SET_OPTION(coap_pkt, COAP_OPTION_TOKEN);
+ return coap_pkt->token_len;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_proxy_uri(void *packet, const char **uri)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_PROXY_URI)) return 0;
+
+ *uri = coap_pkt->proxy_uri;
+ return coap_pkt->proxy_uri_len;
+}
+
+int
+coap_set_header_proxy_uri(void *packet, const char *uri)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ coap_pkt->proxy_uri = uri;
+ coap_pkt->proxy_uri_len = strlen(uri);
+
+ SET_OPTION(coap_pkt, COAP_OPTION_PROXY_URI);
+ return coap_pkt->proxy_uri_len;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_uri_host(void *packet, const char **host)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_HOST)) return 0;
+
+ *host = coap_pkt->uri_host;
+ return coap_pkt->uri_host_len;
+}
+
+int
+coap_set_header_uri_host(void *packet, const char *host)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ coap_pkt->uri_host = host;
+ coap_pkt->uri_host_len = strlen(host);
+
+ SET_OPTION(coap_pkt, COAP_OPTION_URI_HOST);
+ return coap_pkt->uri_host_len;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_uri_path(void *packet, const char **path)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_PATH)) return 0;
+
+ *path = NULL; //coap_pkt->uri_path;
+ return 0; //coap_pkt->uri_path_len;
+}
+
+int
+coap_set_header_uri_path(void *packet, const char *path)
+{
+ coap_packet_t *coap_pkt = (coap_packet_t *) packet;
+ int length = 0;
+
+ free_multi_option(coap_pkt->uri_path);
+ coap_pkt->uri_path = NULL;
+
+ if (path[0]=='/') ++path;
+
+ do
+ {
+ int i = 0;
+
+ while (path[i] != 0 && path[i] != '/') i++;
+ coap_add_multi_option(&(coap_pkt->uri_path), (uint8_t *)path, i, 0);
+
+ if (path[i] == '/') i++;
+ path += i;
+ length += i;
+ } while (path[0] != 0);
+
+ SET_OPTION(coap_pkt, COAP_OPTION_URI_PATH);
+ return length;
+}
+
+int
+coap_set_header_uri_path_segment(void *packet, const char *segment)
+{
+ coap_packet_t *coap_pkt = (coap_packet_t *) packet;
+ int length;
+
+ if (segment == NULL || segment[0] == 0)
+ {
+ coap_add_multi_option(&(coap_pkt->uri_path), NULL, 0, 1);
+ length = 0;
+ }
+ else
+ {
+ length = strlen(segment);
+ coap_add_multi_option(&(coap_pkt->uri_path), (uint8_t *)segment, length, 0);
+ }
+
+ SET_OPTION(coap_pkt, COAP_OPTION_URI_PATH);
+ return length;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_uri_query(void *packet, const char **query)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) return 0;
+
+ *query = NULL; //coap_pkt->uri_query;
+ return 0; //coap_pkt->uri_query_len;
+}
+
+int
+coap_set_header_uri_query(void *packet, const char *query)
+{
+ int length = 0;
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ free_multi_option(coap_pkt->uri_query);
+ coap_pkt->uri_query = NULL;
+
+ if (query[0]=='?') ++query;
+
+ do
+ {
+ int i = 0;
+
+ while (query[i] != 0 && query[i] != '&') i++;
+ coap_add_multi_option(&(coap_pkt->uri_query), (uint8_t *)query, i, 0);
+
+ if (query[i] == '&') i++;
+ query += i;
+ length += i;
+ } while (query[0] != 0);
+
+ SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY);
+ return length;
+ }
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_location_path(void *packet, const char **path)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH)) return 0;
+
+ *path = NULL; //coap_pkt->location_path;
+ return 0; //coap_pkt->location_path_len;
+}
+
+int
+coap_set_header_location_path(void *packet, const char *path)
+{
+ coap_packet_t *coap_pkt = (coap_packet_t *) packet;
+ int length = 0;
+
+ free_multi_option(coap_pkt->location_path);
+ coap_pkt->location_path = NULL;
+
+ if (path[0]=='/') ++path;
+
+ do
+ {
+ int i = 0;
+
+ while (path[i] != 0 && path[i] != '/') i++;
+ coap_add_multi_option(&(coap_pkt->location_path), (uint8_t *)path, i, 0);
+
+ if (path[i] == '/') i++;
+ path += i;
+ length += i;
+ } while (path[0] != 0);
+
+ SET_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH);
+ return length;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_location_query(void *packet, const char **query)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY)) return 0;
+
+ *query = coap_pkt->location_query;
+ return coap_pkt->location_query_len;
+}
+
+int
+coap_set_header_location_query(void *packet, const char *query)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ while (query[0]=='?') ++query;
+
+ coap_pkt->location_query = query;
+ coap_pkt->location_query_len = strlen(query);
+
+ SET_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY);
+ return coap_pkt->location_query_len;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_observe(void *packet, uint32_t *observe)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_OBSERVE)) return 0;
+
+ *observe = coap_pkt->observe;
+ return 1;
+}
+
+int
+coap_set_header_observe(void *packet, uint32_t observe)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ coap_pkt->observe = 0x00FFFFFF & observe;
+ SET_OPTION(coap_pkt, COAP_OPTION_OBSERVE);
+ return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_BLOCK2)) return 0;
+
+ /* pointers may be NULL to get only specific block parameters */
+ if (num!=NULL) *num = coap_pkt->block2_num;
+ if (more!=NULL) *more = coap_pkt->block2_more;
+ if (size!=NULL) *size = coap_pkt->block2_size;
+ if (offset!=NULL) *offset = coap_pkt->block2_offset;
+
+ return 1;
+}
+
+int
+coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t size)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (size<16) return 0;
+ if (size>2048) return 0;
+ if (num>0x0FFFFF) return 0;
+
+ coap_pkt->block2_num = num;
+ coap_pkt->block2_more = more ? 1 : 0;
+ coap_pkt->block2_size = size;
+
+ SET_OPTION(coap_pkt, COAP_OPTION_BLOCK2);
+ return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_BLOCK1)) return 0;
+
+ /* pointers may be NULL to get only specific block parameters */
+ if (num!=NULL) *num = coap_pkt->block1_num;
+ if (more!=NULL) *more = coap_pkt->block1_more;
+ if (size!=NULL) *size = coap_pkt->block1_size;
+ if (offset!=NULL) *offset = coap_pkt->block1_offset;
+
+ return 1;
+}
+
+int
+coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (size<16) return 0;
+ if (size>2048) return 0;
+ if (num>0x0FFFFF) return 0;
+
+ coap_pkt->block1_num = num;
+ coap_pkt->block1_more = more;
+ coap_pkt->block1_size = size;
+
+ SET_OPTION(coap_pkt, COAP_OPTION_BLOCK1);
+ return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_size(void *packet, uint32_t *size)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (!IS_OPTION(coap_pkt, COAP_OPTION_SIZE)) return 0;
+
+ *size = coap_pkt->size;
+ return 1;
+}
+
+int
+coap_set_header_size(void *packet, uint32_t size)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ coap_pkt->size = size;
+ SET_OPTION(coap_pkt, COAP_OPTION_SIZE);
+ return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+/*- PAYLOAD -------------------------------------------------------------------------*/
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_payload(void *packet, const uint8_t **payload)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ if (coap_pkt->payload) {
+ *payload = coap_pkt->payload;
+ return coap_pkt->payload_len;
+ } else {
+ *payload = NULL;
+ return 0;
+ }
+}
+
+int
+coap_set_payload(void *packet, const void *payload, size_t length)
+{
+ coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+ //PRINTF("setting payload (%u/%u)\n", length, REST_MAX_CHUNK_SIZE);
+
+ coap_pkt->payload = (uint8_t *) payload;
+ coap_pkt->payload_len = MIN(REST_MAX_CHUNK_SIZE, length);
+
+ return coap_pkt->payload_len;
+}
+/*-----------------------------------------------------------------------------------*/
diff --git a/core/er-coap-13/er-coap-13.h b/core/er-coap-13/er-coap-13.h
new file mode 100644
index 000000000..b786eb2a3
--- /dev/null
+++ b/core/er-coap-13/er-coap-13.h
@@ -0,0 +1,401 @@
+/*
+ * 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
+ */
+
+/*******************************************************************************
+ *
+ * This file is 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 - Adapt to usage in liblwm2m
+ *
+ *******************************************************************************/
+
+#ifndef COAP_13_H_
+#define COAP_13_H_
+
+#include
+#include /* for size_t */
+
+#include
+#include
+
+/*
+ * 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_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_HEADER_VERSION_MASK 0xC0
+#define COAP_HEADER_VERSION_POSITION 6
+#define COAP_HEADER_TYPE_MASK 0x30
+#define COAP_HEADER_TYPE_POSITION 4
+#define COAP_HEADER_TOKEN_LEN_MASK 0x0F
+#define COAP_HEADER_TOKEN_LEN_POSITION 0
+
+#define COAP_HEADER_OPTION_DELTA_MASK 0xF0
+#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F
+
+/*
+ * Conservative size limit, as not all options have to be set at the same time.
+ */
+#ifndef COAP_MAX_HEADER_SIZE
+/* Hdr CoT Age Tag Obs Tok Blo strings */
+#define COAP_MAX_HEADER_SIZE (4 + 3 + 5 + 1+COAP_ETAG_LEN + 3 + 1+COAP_TOKEN_LEN + 4 + 30) /* 70 */
+#endif /* COAP_MAX_HEADER_SIZE */
+
+#define COAP_MAX_PACKET_SIZE (COAP_MAX_HEADER_SIZE + REST_MAX_CHUNK_SIZE)
+/* 0/14 48 for IPv6 (28 for IPv4) */
+#if COAP_MAX_PACKET_SIZE > (UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN)
+//#error "UIP_CONF_BUFFER_SIZE too small for REST_MAX_CHUNK_SIZE"
+#endif
+
+
+/* Bitmap for set options */
+enum { OPTION_MAP_SIZE = sizeof(uint8_t) * 8 };
+#define SET_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] |= 1 << (opt % OPTION_MAP_SIZE))
+#define IS_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] & (1 << (opt % OPTION_MAP_SIZE)))
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b)? (a) : (b))
+#endif /* MIN */
+
+/* CoAP message types */
+typedef enum {
+ COAP_TYPE_CON, /* confirmables */
+ COAP_TYPE_NON, /* non-confirmables */
+ COAP_TYPE_ACK, /* acknowledgements */
+ COAP_TYPE_RST /* reset */
+} coap_message_type_t;
+
+/* CoAP request method codes */
+typedef enum {
+ COAP_GET = 1,
+ COAP_POST,
+ COAP_PUT,
+ COAP_DELETE
+} coap_method_t;
+
+/* CoAP response codes */
+typedef enum {
+ NO_ERROR = 0,
+
+ CREATED_2_01 = 65, /* CREATED */
+ DELETED_2_02 = 66, /* DELETED */
+ VALID_2_03 = 67, /* NOT_MODIFIED */
+ CHANGED_2_04 = 68, /* CHANGED */
+ CONTENT_2_05 = 69, /* OK */
+
+ BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */
+ UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */
+ BAD_OPTION_4_02 = 130, /* BAD_OPTION */
+ FORBIDDEN_4_03 = 131, /* FORBIDDEN */
+ NOT_FOUND_4_04 = 132, /* NOT_FOUND */
+ METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */
+ NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */
+ PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */
+ REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */
+ UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */
+
+ INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */
+ NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */
+ BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */
+ SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */
+ GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */
+ PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */
+
+ /* Erbium errors */
+ MEMORY_ALLOCATION_ERROR = 192,
+ PACKET_SERIALIZATION_ERROR,
+
+ /* Erbium hooks */
+ MANUAL_RESPONSE
+
+} coap_status_t;
+
+/* CoAP header options */
+typedef enum {
+ COAP_OPTION_IF_MATCH = 1, /* 0-8 B */
+ COAP_OPTION_URI_HOST = 3, /* 1-255 B */
+ COAP_OPTION_ETAG = 4, /* 1-8 B */
+ COAP_OPTION_IF_NONE_MATCH = 5, /* 0 B */
+ COAP_OPTION_OBSERVE = 6, /* 0-3 B */
+ COAP_OPTION_URI_PORT = 7, /* 0-2 B */
+ COAP_OPTION_LOCATION_PATH = 8, /* 0-255 B */
+ COAP_OPTION_URI_PATH = 11, /* 0-255 B */
+ COAP_OPTION_CONTENT_TYPE = 12, /* 0-2 B */
+ COAP_OPTION_MAX_AGE = 14, /* 0-4 B */
+ COAP_OPTION_URI_QUERY = 15, /* 0-270 B */
+ COAP_OPTION_ACCEPT = 16, /* 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 */
+} 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
+} coap_content_type_t;
+
+typedef struct _multi_option_t {
+ struct _multi_option_t *next;
+ uint8_t is_static;
+ uint8_t len;
+ char *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 char *proxy_uri;
+ uint8_t etag_len;
+ uint8_t etag[COAP_ETAG_LEN];
+ size_t uri_host_len;
+ const char *uri_host;
+ multi_option_t *location_path;
+ uint16_t uri_port;
+ size_t location_query_len;
+ const char *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; ifield##_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)) \
+ { \
+ PRINTF(text" [%lu%s (%u B/blk)]\n", coap_pkt->field##_num, coap_pkt->field##_more ? "+" : "", coap_pkt->field##_size); \
+ uint32_t block = coap_pkt->field##_num << 4; \
+ 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 coap_status_t coap_error_code;
+extern 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_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);
+
+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, const char *query);
+
+int coap_get_header_observe(void *packet, uint32_t *observe);
+int coap_set_header_observe(void *packet, uint32_t observe);
+
+int coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset);
+int coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t size);
+
+int coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset);
+int coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size);
+
+int coap_get_header_size(void *packet, uint32_t *size);
+int coap_set_header_size(void *packet, uint32_t size);
+
+int coap_get_payload(void *packet, const uint8_t **payload);
+int coap_set_payload(void *packet, const void *payload, size_t length);
+
+#endif /* COAP_13_H_ */
diff --git a/core/internals.h b/core/internals.h
new file mode 100644
index 000000000..dd2e06926
--- /dev/null
+++ b/core/internals.h
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+/*
+ 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
+
+*/
+
+#ifndef _LWM2M_INTERNALS_H_
+#define _LWM2M_INTERNALS_H_
+
+#include "liblwm2m.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "er-coap-13/er-coap-13.h"
+
+#ifdef WITH_LOGS
+#define LOG(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define LOG(...)
+#endif
+
+#define LWM2M_MAX_PACKET_SIZE 198
+
+#define LWM2M_SECURITY_OBJECT_ID 0
+#define LWM2M_SERVER_OBJECT_ID 1
+#define LWM2M_ACL_OBJECT_ID 2
+
+#define URI_REGISTRATION_SEGMENT "rd"
+#define URI_REGISTRATION_SEGMENT_LEN 2
+#define URI_BOOTSTRAP_SEGMENT "bs"
+#define URI_BOOTSTRAP_SEGMENT_LEN 2
+
+#define LWM2M_URI_FLAG_DM (uint8_t)0x00
+#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
+{
+ lwm2m_uri_t uri;
+ lwm2m_result_callback_t callback;
+ void * userData;
+} dm_data_t;
+
+typedef struct _obs_list_
+{
+ struct _obs_list_ * next;
+ lwm2m_observed_t * item;
+} obs_list_t;
+
+// defined in uri.c
+int prv_get_number(const char * uriString, size_t uriLength);
+lwm2m_uri_t * lwm2m_decode_uri(multi_option_t *uriPath);
+
+// defined in objects.c
+coap_status_t object_read(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, char ** bufferP, int * lengthP);
+coap_status_t object_write(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, char * buffer, int length);
+coap_status_t object_create(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, char * buffer, int length);
+coap_status_t object_execute(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, char * buffer, int length);
+coap_status_t object_delete(lwm2m_context_t * contextP, lwm2m_uri_t * uriP);
+bool object_isInstanceNew(lwm2m_context_t * contextP, uint16_t objectId, uint16_t instanceId);
+int prv_getRegisterPayload(lwm2m_context_t * contextP, char * buffer, size_t length);
+
+// defined in object_server.c
+coap_status_t object_server_read(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, char ** bufferP, int * lengthP);
+coap_status_t object_server_write(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, char * buffer, int length);
+coap_status_t object_server_execute(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, char * buffer, int length);
+coap_status_t object_server_create(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, char * buffer, int length);
+coap_status_t object_server_delete(lwm2m_context_t * contextP, lwm2m_uri_t * uriP);
+coap_status_t object_security_create(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, char * buffer, int length);
+coap_status_t object_security_delete(lwm2m_context_t * contextP, lwm2m_uri_t * uriP);
+
+// defined in transaction.c
+lwm2m_transaction_t * transaction_new(coap_method_t method, lwm2m_uri_t * uriP, uint16_t mID, lwm2m_endpoint_type_t peerType, void * peerP);
+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);
+void transaction_handle_response(lwm2m_context_t * contextP, void * fromSessionH, coap_packet_t * message);
+
+// defined in management.c
+coap_status_t handle_dm_request(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, void * fromSessionH, coap_packet_t * message, coap_packet_t * response);
+
+// defined in observe.c
+coap_status_t handle_observe_request(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, void * fromSessionH, coap_packet_t * message, coap_packet_t * response);
+void cancel_observe(lwm2m_context_t * contextP, uint16_t mid, void * fromSessionH);
+
+// defined in registration.c
+coap_status_t handle_registration_request(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 prv_freeClient(lwm2m_client_t * clientP);
+
+// defined in packet.c
+coap_status_t message_send(lwm2m_context_t * contextP, coap_packet_t * message, void * sessionH);
+
+// defined in observe.c
+void handle_observe_notify(lwm2m_context_t * contextP, void * fromSessionH, coap_packet_t * message);
+
+#endif
diff --git a/core/liblwm2m.c b/core/liblwm2m.c
new file mode 100644
index 000000000..ee661a06f
--- /dev/null
+++ b/core/liblwm2m.c
@@ -0,0 +1,290 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+
+/*
+ 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
+
+*/
+
+#include "internals.h"
+
+#include
+#include
+
+#include
+
+
+lwm2m_context_t * lwm2m_init(char * endpointName,
+ uint16_t numObject,
+ lwm2m_object_t * objectList[],
+ lwm2m_buffer_send_callback_t bufferSendCallback,
+ void * bufferSendUserData)
+{
+ lwm2m_context_t * contextP;
+
+ if (NULL == bufferSendCallback)
+ return NULL;
+
+#ifdef LWM2M_CLIENT_MODE
+ if (numObject != 0)
+ {
+ int i;
+
+ for (i = 0 ; i < numObject ; i++)
+ {
+ if (objectList[i]->objID <= LWM2M_ACL_OBJECT_ID)
+ {
+ // Use of a reserved object ID
+ return NULL;
+ }
+ }
+ }
+#endif
+
+ contextP = (lwm2m_context_t *)lwm2m_malloc(sizeof(lwm2m_context_t));
+ if (NULL != contextP)
+ {
+ memset(contextP, 0, sizeof(lwm2m_context_t));
+ contextP->bufferSendCallback = bufferSendCallback;
+ contextP->bufferSendUserData = bufferSendUserData;
+#ifdef LWM2M_CLIENT_MODE
+ contextP->endpointName = strdup(endpointName);
+ if (contextP->endpointName == NULL)
+ {
+ lwm2m_free(contextP);
+ return NULL;
+ }
+ if (numObject != 0)
+ {
+ contextP->objectList = (lwm2m_object_t **)lwm2m_malloc(numObject * sizeof(lwm2m_object_t *));
+ if (NULL != contextP->objectList)
+ {
+ memcpy(contextP->objectList, objectList, numObject * sizeof(lwm2m_object_t *));
+ contextP->numObject = numObject;
+ }
+ else
+ {
+ lwm2m_free(contextP->endpointName);
+ lwm2m_free(contextP);
+ return NULL;
+ }
+ }
+#endif
+ }
+
+ return contextP;
+}
+
+void lwm2m_close(lwm2m_context_t * contextP)
+{
+ int i;
+
+#ifdef LWM2M_CLIENT_MODE
+ for (i = 0 ; i < contextP->numObject ; i++)
+ {
+ if (NULL != contextP->objectList[i]->closeFunc)
+ {
+ contextP->objectList[i]->closeFunc(contextP->objectList[i]);
+ }
+ lwm2m_free(contextP->objectList[i]);
+ }
+
+ if (NULL != contextP->bootstrapServer)
+ {
+ if (NULL != contextP->bootstrapServer->uri) lwm2m_free (contextP->bootstrapServer->uri);
+ if (NULL != contextP->bootstrapServer->security.privateKey) lwm2m_free (contextP->bootstrapServer->security.privateKey);
+ if (NULL != contextP->bootstrapServer->security.publicKey) lwm2m_free (contextP->bootstrapServer->security.publicKey);
+ lwm2m_free(contextP->bootstrapServer);
+ }
+
+ while (NULL != contextP->serverList)
+ {
+ lwm2m_server_t * targetP;
+
+ targetP = contextP->serverList;
+ contextP->serverList = contextP->serverList->next;
+
+ registration_deregister(contextP, targetP);
+
+ if (NULL != targetP->location) lwm2m_free(targetP->location);
+ if (NULL != targetP->security.privateKey) lwm2m_free (targetP->security.privateKey);
+ if (NULL != targetP->security.publicKey) lwm2m_free (targetP->security.publicKey);
+ lwm2m_free(targetP);
+ }
+
+ while (NULL != contextP->observedList)
+ {
+ lwm2m_observed_t * targetP;
+
+ targetP = contextP->observedList;
+ contextP->observedList = contextP->observedList->next;
+
+ while (NULL != targetP->watcherList)
+ {
+ lwm2m_watcher_t * watcherP;
+
+ watcherP = targetP->watcherList;
+ targetP->watcherList = targetP->watcherList->next;
+ lwm2m_free(watcherP);
+ }
+ lwm2m_free(targetP);
+ }
+
+ if (NULL != contextP->objectList)
+ {
+ lwm2m_free(contextP->objectList);
+ }
+
+ lwm2m_free(contextP->endpointName);
+#endif
+
+#ifdef LWM2M_SERVER_MODE
+ while (NULL != contextP->clientList)
+ {
+ lwm2m_client_t * clientP;
+
+ clientP = contextP->clientList;
+ contextP->clientList = contextP->clientList->next;
+
+ prv_freeClient(clientP);
+ }
+#endif
+
+ while (NULL != contextP->transactionList)
+ {
+ lwm2m_transaction_t * transacP;
+
+ transacP = contextP->transactionList;
+ contextP->transactionList = contextP->transactionList->next;
+
+ transaction_free(transacP);
+ }
+
+ lwm2m_free(contextP);
+}
+
+#ifdef LWM2M_CLIENT_MODE
+void lwm2m_set_bootstrap_server(lwm2m_context_t * contextP,
+ lwm2m_bootstrap_server_t * serverP)
+{
+ if (NULL != contextP->bootstrapServer)
+ {
+ if (NULL != contextP->bootstrapServer->uri) lwm2m_free (contextP->bootstrapServer->uri);
+ if (NULL != contextP->bootstrapServer->security.privateKey) lwm2m_free (contextP->bootstrapServer->security.privateKey);
+ if (NULL != contextP->bootstrapServer->security.publicKey) lwm2m_free (contextP->bootstrapServer->security.publicKey);
+ lwm2m_free(contextP->bootstrapServer);
+ }
+ contextP->bootstrapServer = serverP;
+}
+
+int lwm2m_add_server(lwm2m_context_t * contextP,
+ uint16_t shortID,
+ void * sessionH,
+ lwm2m_security_t * securityP)
+{
+ lwm2m_server_t * serverP;
+ int status = COAP_500_INTERNAL_SERVER_ERROR;
+
+ serverP = (lwm2m_server_t *)lwm2m_malloc(sizeof(lwm2m_server_t));
+ if (serverP != NULL)
+ {
+ memset(serverP, 0, sizeof(lwm2m_server_t));
+ memcpy(&(serverP->security), securityP, sizeof(lwm2m_security_t));
+ serverP->shortID = shortID;
+ serverP->sessionH = sessionH;
+
+ contextP->serverList = (lwm2m_server_t*)LWM2M_LIST_ADD(contextP->serverList, serverP);
+
+ status = COAP_NO_ERROR;
+ }
+
+ return status;
+}
+#endif
+
+int lwm2m_step(lwm2m_context_t * contextP,
+ struct timeval * timeoutP)
+{
+ lwm2m_transaction_t * transacP;
+ struct timeval tv;
+
+ if (0 != lwm2m_gettimeofday(&tv, NULL)) return COAP_500_INTERNAL_SERVER_ERROR;
+
+ 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 <= tv.tv_sec)
+ {
+ removed = transaction_send(contextP, transacP);
+ }
+
+ if (0 == removed)
+ {
+ time_t interval;
+
+ if (transacP->retrans_time > tv.tv_sec)
+ {
+ interval = transacP->retrans_time - tv.tv_sec;
+ }
+ else
+ {
+ interval = 1;
+ }
+
+ if (timeoutP->tv_sec > interval)
+ {
+ timeoutP->tv_sec = interval;
+ }
+ }
+
+ transacP = nextP;
+ }
+
+ return 0;
+}
diff --git a/core/liblwm2m.h b/core/liblwm2m.h
new file mode 100644
index 000000000..d20d070b1
--- /dev/null
+++ b/core/liblwm2m.h
@@ -0,0 +1,490 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+
+/*
+ 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
+
+*/
+
+#ifndef _LWM2M_CLIENT_H_
+#define _LWM2M_CLIENT_H_
+
+#include
+#include
+#include
+#include
+
+#ifndef LWM2M_EMBEDDED_MODE
+#define lwm2m_gettimeofday gettimeofday
+#define lwm2m_malloc malloc
+#define lwm2m_free free
+#else
+int lwm2m_gettimeofday(struct timeval *tv, void *p);
+void *lwm2m_malloc(size_t s);
+void lwm2m_free(void *p);
+#endif
+
+/*
+ * Error code
+ */
+
+#define COAP_NO_ERROR (uint8_t)0x00
+
+#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_400_BAD_REQUEST (uint8_t)0x80
+#define COAP_401_UNAUTHORIZED (uint8_t)0x81
+#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_500_INTERNAL_SERVER_ERROR (uint8_t)0xA0
+#define COAP_501_NOT_IMPLEMENTED (uint8_t)0xA1
+#define COAP_503_SERVICE_UNAVAILABLE (uint8_t)0xA3
+
+
+/*
+ * 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);
+
+#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);
+
+
+/*
+ * Resource values
+ */
+
+// defined in utils.c
+int lwm2m_PlainTextToInt64(char * buffer, int length, int64_t * dataP);
+
+/*
+ * These utility functions allocate a new buffer storing the plain text
+ * representation of data. They return the size in bytes of the buffer
+ * or 0 in case of error.
+ * There is no trailing '\0' character in the buffer.
+ */
+int lwm2m_int8ToPlainText(int8_t data, char ** bufferP);
+int lwm2m_int16ToPlainText(int16_t data, char ** bufferP);
+int lwm2m_int32ToPlainText(int32_t data, char ** bufferP);
+int lwm2m_int64ToPlainText(int64_t data, char ** bufferP);
+int lwm2m_float32ToPlainText(float data, char ** bufferP);
+int lwm2m_float64ToPlainText(double data, char ** bufferP);
+int lwm2m_boolToPlainText(bool data, char ** bufferP);
+
+
+/*
+ * TLV
+ */
+
+#define LWM2M_TLV_HEADER_MAX_LENGTH 6
+
+#define LWM2M_TYPE_RESSOURCE 0x00
+#define LWM2M_TYPE_MULTIPLE_RESSOURCE 0x01
+#define LWM2M_TYPE_RESSOURCE_INSTANCE 0x02
+#define LWM2M_TYPE_OBJECT_INSTANCE 0x03
+
+/*
+ * Bitmask for the lwm2m_tlv_t::flag
+ * LWM2M_TLV_FLAG_STATIC_DATA specifies that lwm2m_tlv_t::value
+ * points to static memory and must no be freeed by the caller.
+ * LWM2M_TLV_FLAG_TEXT_FORMAT specifies that lwm2m_tlv_t::value
+ * is expressed or requested in plain text format.
+ */
+#define LWM2M_TLV_FLAG_STATIC_DATA 0x01
+#define LWM2M_TLV_FLAG_TEXT_FORMAT 0x02
+
+typedef enum
+{
+ TLV_OBJECT_INSTANCE = LWM2M_TYPE_OBJECT_INSTANCE,
+ TLV_RESSOURCE_INSTANCE = LWM2M_TYPE_RESSOURCE_INSTANCE,
+ TLV_MULTIPLE_INSTANCE = LWM2M_TYPE_MULTIPLE_RESSOURCE,
+ TLV_RESSOURCE = LWM2M_TYPE_RESSOURCE
+} lwm2m_tlv_type_t;
+
+typedef struct
+{
+ uint8_t flags;
+ uint8_t type;
+ uint16_t id;
+ size_t length;
+ uint8_t * value;
+} lwm2m_tlv_t;
+
+lwm2m_tlv_t * lwm2m_tlv_new(int size);
+int lwm2m_tlv_parse(char * buffer, size_t bufferLen, lwm2m_tlv_t ** dataP);
+int lwm2m_tlv_serialize(int size, lwm2m_tlv_t * tlvP, char ** bufferP);
+void lwm2m_tlv_free(int size, lwm2m_tlv_t * tlvP);
+
+void lwm2m_tlv_encode_int(int64_t data, lwm2m_tlv_t * tlvP);
+int lwm2m_tlv_decode_int(lwm2m_tlv_t * tlvP, int64_t * dataP);
+
+/*
+ * These utility functions fill the buffer with a TLV record containing
+ * the data. They return the size in bytes of the TLV record, 0 in case
+ * of error.
+ */
+int lwm2m_intToTLV(lwm2m_tlv_type_t type, int64_t data, uint16_t id, char * buffer, size_t buffer_len);
+int lwm2m_boolToTLV(lwm2m_tlv_type_t type, bool value, uint16_t id, char * buffer, size_t buffer_len);
+int lwm2m_opaqueToTLV(lwm2m_tlv_type_t type, uint8_t * dataP, size_t data_len, uint16_t id, char * buffer, size_t buffer_len);
+int lwm2m_decodeTLV(char * buffer, size_t buffer_len, lwm2m_tlv_type_t * oType, uint16_t * oID, size_t * oDataIndex, size_t * oDataLen);
+int lwm2m_opaqueToInt(char * buffer, size_t buffer_len, int64_t * dataP);
+
+/*
+ * URI
+ *
+ * objectId is always set
+ * if instanceId or resourceId is greater than LWM2M_URI_MAX_ID, it means it is not specified
+ *
+ */
+
+#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(char * buffer, size_t buffer_len, lwm2m_uri_t * uriP);
+
+
+/*
+ * 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_tlv_t ** dataArrayP, lwm2m_object_t * objectP);
+typedef uint8_t (*lwm2m_write_callback_t) (uint16_t instanceId, int numData, lwm2m_tlv_t * dataArray, lwm2m_object_t * objectP);
+typedef uint8_t (*lwm2m_execute_callback_t) (uint16_t instanceId, uint16_t resourceId, char * buffer, int length, lwm2m_object_t * objectP);
+typedef uint8_t (*lwm2m_create_callback_t) (uint16_t instanceId, int numData, lwm2m_tlv_t * dataArray, lwm2m_object_t * objectP);
+typedef uint8_t (*lwm2m_delete_callback_t) (uint16_t instanceId, lwm2m_object_t * objectP);
+typedef void (*lwm2m_close_callback_t) (lwm2m_object_t * objectP);
+
+
+struct _lwm2m_object_t
+{
+ 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_close_callback_t closeFunc;
+ 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
+{
+ SEC_NONE = 0,
+ SEC_PRE_SHARED_KEY,
+ SEC_RAW_PUBLIC_KEY,
+ SEC_CERTIFICATE
+} lwm2m_security_mode_t;
+
+typedef struct
+{
+ lwm2m_security_mode_t mode;
+ size_t publicKeyLength;
+ uint8_t * publicKey;
+ size_t privateKeyLength;
+ uint8_t * privateKey;
+} lwm2m_security_t;
+
+typedef enum
+{
+ STATE_UNKNOWN = 0,
+ STATE_REG_PENDING,
+ STATE_REGISTERED
+} lwm2m_status_t;
+
+typedef struct _lwm2m_server_
+{
+ struct _lwm2m_server_ * next; // matches lwm2m_list_t::next
+ uint16_t shortID; // matches lwm2m_list_t::id
+ lwm2m_security_t security;
+ void * sessionH;
+ lwm2m_status_t status;
+ char * location;
+ uint16_t mid;
+} lwm2m_server_t;
+
+typedef struct
+{
+ char * uri;
+ lwm2m_security_t security;
+ uint32_t holdOffTime;
+} lwm2m_bootstrap_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, 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.
+ */
+
+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_result_callback_t callback;
+ void * userData;
+} lwm2m_observation_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;
+ void * sessionH;
+ lwm2m_client_object_t * objectList;
+ lwm2m_observation_t * observationList;
+} lwm2m_client_t;
+
+
+/*
+ * LWM2M transaction
+ *
+ * Adaptation of Erbium's coap_transaction_t
+ */
+
+typedef enum
+{
+ ENDPOINT_UNKNOWN = 0,
+ ENDPOINT_CLIENT,
+ ENDPOINT_SERVER,
+ ENDPOINT_BOOTSTRAP
+} lwm2m_endpoint_type_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
+ lwm2m_endpoint_type_t peerType;
+ void * peerP;
+ uint8_t retrans_counter;
+ time_t retrans_time;
+ char objStringID[LWM2M_STRING_ID_MAX_LEN];
+ char instanceStringID[LWM2M_STRING_ID_MAX_LEN];
+ char resourceStringID[LWM2M_STRING_ID_MAX_LEN];
+ void * message;
+ uint16_t buffer_len;
+ uint8_t * buffer;
+ lwm2m_transaction_callback_t callback;
+ void * userData;
+};
+
+/*
+ * LWM2M observed resources
+ */
+typedef struct _lwm2m_watcher_
+{
+ struct _lwm2m_watcher_ * next;
+
+ lwm2m_server_t * server;
+ uint8_t token[8];
+ size_t tokenLen;
+ uint32_t counter;
+ uint16_t lastMid;
+} lwm2m_watcher_t;
+
+typedef struct _lwm2m_observed_
+{
+ struct _lwm2m_observed_ * next;
+
+ lwm2m_uri_t uri;
+ lwm2m_watcher_t * watcherList;
+} lwm2m_observed_t;
+
+
+/*
+ * LWM2M Context
+ */
+
+// The session handle MUST uniquely identify a peer.
+typedef uint8_t (*lwm2m_buffer_send_callback_t)(void * sessionH, uint8_t * buffer, size_t length, void * userData);
+
+
+typedef struct
+{
+ int socket;
+#ifdef LWM2M_CLIENT_MODE
+ char * endpointName;
+ lwm2m_bootstrap_server_t * bootstrapServer;
+ lwm2m_server_t * serverList;
+ lwm2m_object_t ** objectList;
+ uint16_t numObject;
+ lwm2m_observed_t * observedList;
+#endif
+#ifdef LWM2M_SERVER_MODE
+ lwm2m_client_t * clientList;
+ lwm2m_result_callback_t monitorCallback;
+ void * monitorUserData;
+#endif
+ uint16_t nextMID;
+ lwm2m_transaction_t * transactionList;
+ // buffer send callback
+ lwm2m_buffer_send_callback_t bufferSendCallback;
+ void * bufferSendUserData;
+} lwm2m_context_t;
+
+
+// initialize a liblwm2m context. endpointName, numObject and objectList are ignored for pure servers.
+lwm2m_context_t * lwm2m_init(char * endpointName, uint16_t numObject, lwm2m_object_t * objectList[], lwm2m_buffer_send_callback_t bufferSendCallback, void * bufferSendUserData);
+// 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.
+int lwm2m_step(lwm2m_context_t * contextP, struct timeval * 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
+void lwm2m_set_bootstrap_server(lwm2m_context_t * contextP, lwm2m_bootstrap_server_t * serverP);
+int lwm2m_add_server(lwm2m_context_t * contextP, uint16_t shortID, void * sessionH, lwm2m_security_t * securityP);
+
+// send registration message to all known LWM2M Servers.
+int lwm2m_register(lwm2m_context_t * contextP);
+// inform liblwm2m that a resource value has changed.
+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 CREATED_2_01.
+// When a LWM2M client deregisters, the callback is called with status DELETED_2_02.
+// 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_write(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, char * buffer, int length, lwm2m_result_callback_t callback, void * userData);
+int lwm2m_dm_execute(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, char * 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, char * 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
+
+#endif
diff --git a/core/list.c b/core/list.c
new file mode 100644
index 000000000..6ba487b41
--- /dev/null
+++ b/core/list.c
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ *
+ * 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)
+ {
+ *nodeP = NULL;
+ return NULL;
+ }
+
+ if (head->id == id)
+ {
+ *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)
+ {
+ *nodeP = target->next;
+ target->next = target->next->next;
+ }
+ else
+ {
+ *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;
+}
diff --git a/core/management.c b/core/management.c
new file mode 100644
index 000000000..8fe734f00
--- /dev/null
+++ b/core/management.c
@@ -0,0 +1,356 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+/*
+ 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
+
+*/
+
+#include "internals.h"
+#include
+
+
+#ifdef LWM2M_CLIENT_MODE
+coap_status_t handle_dm_request(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP,
+ void * fromSessionH,
+ coap_packet_t * message,
+ coap_packet_t * response)
+{
+ coap_status_t result;
+
+ switch (message->code)
+ {
+ case COAP_GET:
+ {
+ char * buffer = NULL;
+ int length = 0;
+
+ result = object_read(contextP, uriP, &buffer, &length);
+ if (result == COAP_205_CONTENT)
+ {
+ if (IS_OPTION(message, COAP_OPTION_OBSERVE))
+ {
+ result = handle_observe_request(contextP, uriP, fromSessionH, message, response);
+ }
+ if (result == COAP_205_CONTENT)
+ {
+ coap_set_payload(response, buffer, length);
+ // lwm2m_handle_packet will free buffer
+ }
+ }
+ }
+ break;
+ case COAP_POST:
+ {
+ if (!LWM2M_URI_IS_SET_INSTANCE(uriP))
+ {
+ result = object_create(contextP, uriP, 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);
+ }
+ }
+ else if (!LWM2M_URI_IS_SET_RESOURCE(uriP))
+ {
+ if (object_isInstanceNew(contextP, uriP->objectId, uriP->instanceId))
+ {
+ result = object_create(contextP, uriP, message->payload, message->payload_len);
+ }
+ else
+ {
+ result = object_write(contextP, uriP, message->payload, message->payload_len);
+ }
+ }
+ else
+ {
+ result = object_execute(contextP, uriP, message->payload, message->payload_len);
+ }
+ }
+ break;
+ case COAP_PUT:
+ {
+ if (LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(uriP))
+ {
+ result = object_write(contextP, uriP, message->payload, message->payload_len);
+ }
+ else
+ {
+ result = BAD_REQUEST_4_00;
+ }
+ }
+ break;
+ case COAP_DELETE:
+ {
+ if (LWM2M_URI_IS_SET_INSTANCE(uriP) && !LWM2M_URI_IS_SET_RESOURCE(uriP))
+ {
+ result = object_delete(contextP, uriP);
+ }
+ else
+ {
+ result = BAD_REQUEST_4_00;
+ }
+ }
+ break;
+ default:
+ result = BAD_REQUEST_4_00;
+ break;
+ }
+
+ return result;
+}
+#endif
+
+#ifdef LWM2M_SERVER_MODE
+
+#define ID_AS_STRING_MAX_LEN 8
+
+static void dm_result_callback(lwm2m_transaction_t * transacP,
+ void * message)
+{
+ dm_data_t * dataP = (dm_data_t *)transacP->userData;
+
+ if (message == NULL)
+ {
+ dataP->callback(((lwm2m_client_t*)transacP->peerP)->internalID,
+ &dataP->uri,
+ COAP_503_SERVICE_UNAVAILABLE,
+ 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)
+ {
+ fprintf(stderr, "Error: coap_get_multi_option_as_string() failed for Location_path option in dm_result_callback()\n");
+ return;
+ }
+
+ result = lwm2m_stringToUri(locationString, strlen(locationString), &locationUri);
+ if (result == 0)
+ {
+ fprintf(stderr, "Error: lwm2m_stringToUri() failed for Location_path option in dm_result_callback()\n");
+ 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(((lwm2m_client_t*)transacP->peerP)->internalID,
+ &dataP->uri,
+ packet->code,
+ packet->payload,
+ packet->payload_len,
+ dataP->userData);
+ }
+ lwm2m_free(dataP);
+}
+
+static int prv_make_operation(lwm2m_context_t * contextP,
+ uint16_t clientID,
+ lwm2m_uri_t * uriP,
+ coap_method_t method,
+ char * 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(method, uriP, contextP->nextMID++, ENDPOINT_CLIENT, (void *)clientP);
+ if (transaction == NULL) return INTERNAL_SERVER_ERROR_5_00;
+
+ if (buffer != NULL)
+ {
+ // 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->callback = callback;
+ dataP->userData = userData;
+
+ transaction->callback = dm_result_callback;
+ 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)
+{
+ return prv_make_operation(contextP, clientID, uriP,
+ COAP_GET, NULL, 0,
+ callback, userData);
+}
+
+int lwm2m_dm_write(lwm2m_context_t * contextP,
+ uint16_t clientID,
+ lwm2m_uri_t * uriP,
+ char * buffer,
+ int length,
+ lwm2m_result_callback_t callback,
+ void * userData)
+{
+ if (!LWM2M_URI_IS_SET_INSTANCE(uriP)
+ || length == 0)
+ {
+ return COAP_400_BAD_REQUEST;
+ }
+
+ if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+ {
+ return prv_make_operation(contextP, clientID, uriP,
+ COAP_PUT, buffer, length,
+ callback, userData);
+ }
+ else
+ {
+ return prv_make_operation(contextP, clientID, uriP,
+ COAP_POST, buffer, length,
+ callback, userData);
+ }
+}
+
+int lwm2m_dm_execute(lwm2m_context_t * contextP,
+ uint16_t clientID,
+ lwm2m_uri_t * uriP,
+ char * buffer,
+ int length,
+ lwm2m_result_callback_t callback,
+ void * userData)
+{
+ if (!LWM2M_URI_IS_SET_RESOURCE(uriP))
+ {
+ return COAP_400_BAD_REQUEST;
+ }
+
+ return prv_make_operation(contextP, clientID, uriP,
+ COAP_POST, buffer, length,
+ callback, userData);
+}
+
+int lwm2m_dm_create(lwm2m_context_t * contextP,
+ uint16_t clientID,
+ lwm2m_uri_t * uriP,
+ char * buffer,
+ int length,
+ lwm2m_result_callback_t callback,
+ void * userData)
+{
+ if (LWM2M_URI_IS_SET_RESOURCE(uriP)
+ || length == 0)
+ {
+ return COAP_400_BAD_REQUEST;
+ }
+
+ return prv_make_operation(contextP, clientID, uriP,
+ COAP_POST, 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)
+{
+ if (!LWM2M_URI_IS_SET_INSTANCE(uriP)
+ || LWM2M_URI_IS_SET_RESOURCE(uriP))
+ {
+ return COAP_400_BAD_REQUEST;
+ }
+
+ return prv_make_operation(contextP, clientID, uriP,
+ COAP_DELETE, NULL, 0,
+ callback, userData);
+}
+#endif
diff --git a/core/object_server.c b/core/object_server.c
new file mode 100644
index 000000000..29a99d176
--- /dev/null
+++ b/core/object_server.c
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+
+/*
+ * 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 | String | | |
+ * Binding | 7 | R/W | Single | Yes | | | |
+ * Registration Update | 8 | E | Single | Yes | | | |
+ *
+ */
+
+#ifdef LWM2M_CLIENT_MODE
+
+#include "internals.h"
+
+#define RESOURCE_SHORTID_ID 0
+#define RESOURCE_LIFETIME_ID 1
+#define RESOURCE_MINPERIOD_ID 2
+#define RESOURCE_MAXPERIOD_ID 3
+#define RESOURCE_DISABLE_ID 4
+#define RESOURCE_TIMEOUT_ID 5
+#define RESOURCE_STORING_ID 6
+#define RESOURCE_BINDING_ID 7
+#define RESOURCE_UPDATE_ID 8
+
+
+coap_status_t object_server_read(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP,
+ char ** bufferP,
+ int * lengthP)
+{
+ if (!LWM2M_URI_IS_SET_INSTANCE(uriP))
+ {
+ return COAP_501_NOT_IMPLEMENTED;
+ }
+ else
+ {
+ lwm2m_server_t * serverP;
+
+ serverP = (lwm2m_server_t *)lwm2m_list_find((lwm2m_list_t *)contextP->serverList, uriP->instanceId);
+ if (serverP == NULL) return COAP_404_NOT_FOUND;
+
+ if (!LWM2M_URI_IS_SET_RESOURCE(uriP))
+ {
+ return COAP_501_NOT_IMPLEMENTED;
+ }
+ else
+ {
+ switch (uriP->resourceId)
+ {
+ case RESOURCE_SHORTID_ID:
+ *lengthP = lwm2m_int32ToPlainText(serverP->shortID, bufferP);
+ if (0 != *lengthP)
+ {
+ return COAP_205_CONTENT;
+ }
+ else
+ {
+ return COAP_500_INTERNAL_SERVER_ERROR;
+ }
+ break;
+
+ case RESOURCE_LIFETIME_ID:
+ return COAP_501_NOT_IMPLEMENTED;
+ case RESOURCE_MINPERIOD_ID:
+ return COAP_404_NOT_FOUND;
+ case RESOURCE_MAXPERIOD_ID:
+ return COAP_404_NOT_FOUND;
+ case RESOURCE_TIMEOUT_ID:
+ return COAP_404_NOT_FOUND;
+ case RESOURCE_STORING_ID:
+ return COAP_501_NOT_IMPLEMENTED;
+ case RESOURCE_BINDING_ID:
+ return COAP_501_NOT_IMPLEMENTED;
+ default:
+ return COAP_405_METHOD_NOT_ALLOWED;
+ }
+ }
+ }
+}
+
+coap_status_t object_server_write(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP,
+ char * buffer,
+ int length)
+{
+ if (!LWM2M_URI_IS_SET_INSTANCE(uriP))
+ {
+ return COAP_501_NOT_IMPLEMENTED;
+ }
+ else
+ {
+ lwm2m_server_t * serverP;
+
+ serverP = (lwm2m_server_t *)lwm2m_list_find((lwm2m_list_t *)contextP->serverList, uriP->instanceId);
+ if (serverP == NULL) return COAP_404_NOT_FOUND;
+
+ if (!LWM2M_URI_IS_SET_RESOURCE(uriP))
+ {
+ return COAP_501_NOT_IMPLEMENTED;
+ }
+ else
+ {
+ switch (uriP->resourceId)
+ {
+ case RESOURCE_LIFETIME_ID:
+ return COAP_501_NOT_IMPLEMENTED;
+ case RESOURCE_MINPERIOD_ID:
+ return COAP_404_NOT_FOUND;
+ case RESOURCE_MAXPERIOD_ID:
+ return COAP_404_NOT_FOUND;
+ case RESOURCE_TIMEOUT_ID:
+ return COAP_404_NOT_FOUND;
+ case RESOURCE_STORING_ID:
+ return COAP_501_NOT_IMPLEMENTED;
+ case RESOURCE_BINDING_ID:
+ return COAP_501_NOT_IMPLEMENTED;
+ default:
+ return COAP_405_METHOD_NOT_ALLOWED;
+ }
+ }
+ }
+}
+
+coap_status_t object_server_execute(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP,
+ char * buffer,
+ int length)
+{
+ lwm2m_server_t * serverP;
+
+ serverP = (lwm2m_server_t *)lwm2m_list_find((lwm2m_list_t *)contextP->serverList, uriP->instanceId);
+ if (serverP == NULL) return COAP_404_NOT_FOUND;
+
+ switch (uriP->resourceId)
+ {
+ case RESOURCE_DISABLE_ID:
+ return COAP_404_NOT_FOUND;
+ case RESOURCE_UPDATE_ID:
+ return COAP_501_NOT_IMPLEMENTED;
+ default:
+ return COAP_405_METHOD_NOT_ALLOWED;
+ }
+}
+
+coap_status_t object_server_create(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP,
+ char * buffer,
+ int length)
+{
+ lwm2m_server_t * serverP;
+
+ serverP = (lwm2m_server_t *)lwm2m_list_find((lwm2m_list_t *)contextP->serverList, uriP->instanceId);
+ if (serverP == NULL) return COAP_404_NOT_FOUND;
+
+ return COAP_501_NOT_IMPLEMENTED;
+}
+
+coap_status_t object_server_delete(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP)
+{
+ lwm2m_server_t * serverP;
+
+ serverP = (lwm2m_server_t *)lwm2m_list_find((lwm2m_list_t *)contextP->serverList, uriP->instanceId);
+ if (serverP == NULL) return COAP_404_NOT_FOUND;
+
+ return COAP_501_NOT_IMPLEMENTED;
+}
+
+coap_status_t object_security_create(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP,
+ char * buffer,
+ int length)
+{
+ lwm2m_server_t * serverP;
+
+ serverP = (lwm2m_server_t *)lwm2m_list_find((lwm2m_list_t *)contextP->serverList, uriP->instanceId);
+ if (serverP == NULL) return COAP_404_NOT_FOUND;
+
+ return COAP_501_NOT_IMPLEMENTED;
+}
+
+coap_status_t object_security_delete(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP)
+{
+ lwm2m_server_t * serverP;
+
+ serverP = (lwm2m_server_t *)lwm2m_list_find((lwm2m_list_t *)contextP->serverList, uriP->instanceId);
+ if (serverP == NULL) return COAP_404_NOT_FOUND;
+
+ return COAP_501_NOT_IMPLEMENTED;
+}
+
+#endif
diff --git a/core/objects.c b/core/objects.c
new file mode 100644
index 000000000..c73a30b7d
--- /dev/null
+++ b/core/objects.c
@@ -0,0 +1,462 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+
+/*
+ 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
+
+*/
+
+#ifdef LWM2M_CLIENT_MODE
+
+#include "internals.h"
+
+#include
+#include
+#include
+
+
+static lwm2m_object_t * prv_find_object(lwm2m_context_t * contextP,
+ uint16_t Id)
+{
+ int i;
+
+ for (i = 0 ; i < contextP->numObject ; i++)
+ {
+ if (contextP->objectList[i]->objID == Id)
+ {
+ return contextP->objectList[i];
+ }
+ }
+
+ return NULL;
+}
+
+coap_status_t object_read(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP,
+ char ** bufferP,
+ int * lengthP)
+{
+ coap_status_t result;
+
+ switch (uriP->objectId)
+ {
+ case LWM2M_SECURITY_OBJECT_ID:
+ result = NOT_FOUND_4_04;
+ break;
+
+ case LWM2M_SERVER_OBJECT_ID:
+ result = object_server_read(contextP, uriP, bufferP, lengthP);
+ break;
+
+ default:
+ {
+ lwm2m_object_t * targetP;
+ lwm2m_tlv_t * tlvP = NULL;
+ int size = 0;
+
+ targetP = prv_find_object(contextP, uriP->objectId);
+ if (NULL == targetP) return NOT_FOUND_4_04;
+ if (NULL == targetP->readFunc) return METHOD_NOT_ALLOWED_4_05;
+ if (targetP->instanceList == NULL)
+ {
+ // this is a single instance object
+ if (LWM2M_URI_IS_SET_INSTANCE(uriP) && (uriP->instanceId != 0))
+ {
+ return COAP_404_NOT_FOUND;
+ }
+ }
+ else
+ {
+ if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+ {
+ if (NULL == lwm2m_list_find(targetP->instanceList, uriP->instanceId))
+ {
+ return COAP_404_NOT_FOUND;
+ }
+ }
+ else
+ {
+ // multiple object instances read
+ lwm2m_list_t * instanceP;
+ int i;
+
+ size = 0;
+ for (instanceP = targetP->instanceList; instanceP != NULL ; instanceP = instanceP->next)
+ {
+ size++;
+ }
+
+ tlvP = lwm2m_tlv_new(size);
+ if (tlvP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+ result = COAP_205_CONTENT;
+ instanceP = targetP->instanceList;
+ i = 0;
+ while (instanceP != NULL && result == COAP_205_CONTENT)
+ {
+ result = targetP->readFunc(instanceP->id, (int*)&(tlvP[i].length), (lwm2m_tlv_t **)&(tlvP[i].value), targetP);
+ tlvP[i].type = LWM2M_TYPE_OBJECT_INSTANCE;
+ tlvP[i].id = instanceP->id;
+ i++;
+ instanceP = instanceP->next;
+ }
+
+ if (result == COAP_205_CONTENT)
+ {
+ *lengthP = lwm2m_tlv_serialize(size, tlvP, bufferP);
+ if (*lengthP == 0) result = COAP_500_INTERNAL_SERVER_ERROR;
+ }
+ lwm2m_tlv_free(size, tlvP);
+
+ return result;
+ }
+ }
+
+ // single instance read
+ if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+ {
+ size = 1;
+ tlvP = lwm2m_tlv_new(size);
+ if (tlvP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+ tlvP->type = LWM2M_TYPE_RESSOURCE;
+ tlvP->flags = LWM2M_TLV_FLAG_TEXT_FORMAT;
+ tlvP->id = uriP->resourceId;
+ }
+ result = targetP->readFunc(uriP->instanceId, &size, &tlvP, targetP);
+ if (result == COAP_205_CONTENT)
+ {
+ if (size == 1
+ && tlvP->type == LWM2M_TYPE_RESSOURCE
+ && (tlvP->flags && LWM2M_TLV_FLAG_TEXT_FORMAT) != 0 )
+ {
+ *bufferP = (char *)malloc(tlvP->length);
+ if (*bufferP == NULL)
+ {
+ result = COAP_500_INTERNAL_SERVER_ERROR;
+ }
+ else
+ {
+ memcpy(*bufferP, tlvP->value, tlvP->length);
+ *lengthP = tlvP->length;
+ }
+ }
+ else
+ {
+ *lengthP = lwm2m_tlv_serialize(size, tlvP, bufferP);
+ if (*lengthP == 0) result = COAP_500_INTERNAL_SERVER_ERROR;
+ }
+ }
+ lwm2m_tlv_free(size, tlvP);
+ }
+ }
+
+ return result;
+}
+
+coap_status_t object_write(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP,
+ char * buffer,
+ int length)
+{
+ coap_status_t result;
+
+ switch (uriP->objectId)
+ {
+ case LWM2M_SECURITY_OBJECT_ID:
+ result = NOT_FOUND_4_04;
+ break;
+
+ case LWM2M_SERVER_OBJECT_ID:
+ result = object_server_write(contextP, uriP, buffer, length);
+ break;
+
+ default:
+ {
+ lwm2m_object_t * targetP;
+ lwm2m_tlv_t * tlvP = NULL;
+ int size = 0;
+
+ targetP = prv_find_object(contextP, uriP->objectId);
+ if (NULL == targetP) return NOT_FOUND_4_04;
+ if (NULL == targetP->writeFunc) return METHOD_NOT_ALLOWED_4_05;
+
+ if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+ {
+ size = 1;
+ tlvP = lwm2m_tlv_new(size);
+ if (tlvP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+ tlvP->flags = LWM2M_TLV_FLAG_TEXT_FORMAT | LWM2M_TLV_FLAG_STATIC_DATA;
+ tlvP->type = LWM2M_TYPE_RESSOURCE;
+ tlvP->id = uriP->resourceId;
+ tlvP->length = length;
+ tlvP->value = buffer;
+ }
+ else
+ {
+ size = lwm2m_tlv_parse(buffer, length, &tlvP);
+ if (size = 0) return COAP_500_INTERNAL_SERVER_ERROR;
+ }
+ result = targetP->writeFunc(uriP->instanceId, size, tlvP, targetP);
+ lwm2m_tlv_free(size, tlvP);
+ }
+ }
+
+ return result;
+}
+
+coap_status_t object_execute(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP,
+ char * buffer,
+ int length)
+{
+ switch (uriP->objectId)
+ {
+ case LWM2M_SECURITY_OBJECT_ID:
+ return NOT_FOUND_4_04;
+
+ case LWM2M_SERVER_OBJECT_ID:
+ return object_server_execute(contextP, uriP, buffer, length);
+
+ default:
+ {
+ lwm2m_object_t * targetP;
+
+ targetP = prv_find_object(contextP, uriP->objectId);
+ if (NULL == targetP) return NOT_FOUND_4_04;
+ if (NULL == targetP->executeFunc) return METHOD_NOT_ALLOWED_4_05;
+
+ return targetP->executeFunc(uriP->instanceId, uriP->resourceId, buffer, length, targetP);
+ }
+ }
+}
+
+coap_status_t object_create(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP,
+ char * buffer,
+ int length)
+{
+ if (length == 0 || buffer == 0)
+ {
+ return BAD_REQUEST_4_00;
+ }
+
+ switch (uriP->objectId)
+ {
+ case LWM2M_SECURITY_OBJECT_ID:
+ return object_security_create(contextP, uriP, buffer, length);
+
+ case LWM2M_SERVER_OBJECT_ID:
+ return object_server_create(contextP, uriP, buffer, length);
+
+ default:
+ {
+ lwm2m_object_t * targetP;
+ lwm2m_tlv_t * tlvP = NULL;
+ int size = 0;
+ uint8_t result;
+
+ targetP = prv_find_object(contextP, uriP->objectId);
+ if (NULL == targetP) return NOT_FOUND_4_04;
+ if (NULL == targetP->createFunc) return METHOD_NOT_ALLOWED_4_05;
+
+ if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+ {
+ if (NULL != lwm2m_list_find(targetP->instanceList, uriP->instanceId))
+ {
+ // Instance already exists
+ return COAP_406_NOT_ACCEPTABLE;
+ }
+ }
+ else
+ {
+ uriP->instanceId = lwm2m_list_newId(targetP->instanceList);
+ uriP->flag |= LWM2M_URI_FLAG_INSTANCE_ID;
+ }
+
+ targetP = prv_find_object(contextP, uriP->objectId);
+ if (NULL == targetP) return NOT_FOUND_4_04;
+ if (NULL == targetP->writeFunc) return METHOD_NOT_ALLOWED_4_05;
+
+ size = lwm2m_tlv_parse(buffer, length, &tlvP);
+ if (size == 0) return COAP_500_INTERNAL_SERVER_ERROR;
+
+ result = targetP->createFunc(uriP->instanceId, size, tlvP, targetP);
+ lwm2m_tlv_free(size, tlvP);
+
+ return result;
+ }
+ }
+}
+
+coap_status_t object_delete(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP)
+{
+ switch (uriP->objectId)
+ {
+ case LWM2M_SECURITY_OBJECT_ID:
+ return object_security_delete(contextP, uriP);
+
+ case LWM2M_SERVER_OBJECT_ID:
+ return object_server_delete(contextP, uriP);
+
+ default:
+ {
+ lwm2m_object_t * targetP;
+
+ targetP = prv_find_object(contextP, uriP->objectId);
+ if (NULL == targetP) return NOT_FOUND_4_04;
+ if (NULL == targetP->deleteFunc) return METHOD_NOT_ALLOWED_4_05;
+
+ return targetP->deleteFunc(uriP->instanceId, targetP);
+ }
+ }
+}
+
+bool object_isInstanceNew(lwm2m_context_t * contextP,
+ uint16_t objectId,
+ uint16_t instanceId)
+{
+ switch (objectId)
+ {
+ case LWM2M_SECURITY_OBJECT_ID:
+ case LWM2M_SERVER_OBJECT_ID:
+ if (NULL != lwm2m_list_find((lwm2m_list_t *)contextP->serverList, instanceId))
+ {
+ return false;
+ }
+ break;
+
+ default:
+ {
+ lwm2m_object_t * targetP;
+
+ targetP = prv_find_object(contextP, objectId);
+ if (targetP != NULL)
+ {
+ if (NULL != lwm2m_list_find(targetP->instanceList, instanceId))
+ {
+ return false;
+ }
+ }
+ }
+ break;
+ }
+
+ return true;
+}
+
+int prv_getRegisterPayload(lwm2m_context_t * contextP,
+ char * buffer,
+ size_t length)
+{
+ int index;
+ int i;
+ int result;
+
+ lwm2m_server_t * serverP;
+
+ // index can not be greater than length
+ index = 0;
+ for (serverP = contextP->serverList;
+ serverP != NULL;
+ serverP = serverP->next)
+ {
+ result = snprintf(buffer + index, length - index, "%hu/%hu>,", LWM2M_SERVER_OBJECT_ID, serverP->shortID);
+ if (result > 0 && result <= length - index)
+ {
+ index += result;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ for (i = 0 ; i < contextP->numObject ; i++)
+ {
+ if (contextP->objectList[i]->instanceList == NULL)
+ {
+ result = snprintf(buffer + index, length - index, "%hu>,", contextP->objectList[i]->objID);
+ if (result > 0 && result <= length - index)
+ {
+ index += result;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ lwm2m_list_t * targetP;
+ for (targetP = contextP->objectList[i]->instanceList ; targetP != NULL ; targetP = targetP->next)
+ {
+ int result;
+
+ result = snprintf(buffer + index, length - index, "%hu/%hu>,", contextP->objectList[i]->objID, targetP->id);
+ if (result > 0 && result <= length - index)
+ {
+ index += result;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+ }
+
+ if (index > 0)
+ {
+ index = index - 1; // remove trailing ','
+ }
+
+ buffer[index] = 0;
+
+ return index;
+}
+#endif
diff --git a/core/observe.c b/core/observe.c
new file mode 100644
index 000000000..92facef0a
--- /dev/null
+++ b/core/observe.c
@@ -0,0 +1,503 @@
+/*******************************************************************************
+ *
+ * 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
+
+*/
+
+#include "internals.h"
+#include
+
+
+#ifdef LWM2M_CLIENT_MODE
+static lwm2m_observed_t * prv_findObserved(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP)
+{
+ lwm2m_observed_t * targetP;
+
+ targetP = contextP->observedList;
+ while (targetP != NULL
+ && (targetP->uri.objectId != uriP->objectId
+ || targetP->uri.flag != uriP->flag
+ || (LWM2M_URI_IS_SET_INSTANCE(uriP) && targetP->uri.instanceId != uriP->instanceId)
+ || (LWM2M_URI_IS_SET_RESOURCE(uriP) && targetP->uri.resourceId != uriP->resourceId)))
+ {
+ targetP = targetP->next;
+ }
+
+ return targetP;
+}
+
+static obs_list_t * prv_getObservedList(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP)
+{
+ obs_list_t * resultP;
+ lwm2m_observed_t * targetP;
+
+ resultP = NULL;
+
+ targetP = contextP->observedList;
+ while (targetP != NULL)
+ {
+ if (targetP->uri.objectId == uriP->objectId)
+ {
+ if (!LWM2M_URI_IS_SET_INSTANCE(uriP)
+ || (targetP->uri.flag & LWM2M_URI_FLAG_INSTANCE_ID) == 0
+ || uriP->instanceId == targetP->uri.instanceId)
+ {
+ if (!LWM2M_URI_IS_SET_RESOURCE(uriP)
+ || (targetP->uri.flag & LWM2M_URI_FLAG_RESOURCE_ID) == 0
+ || uriP->resourceId == targetP->uri.resourceId)
+ {
+ obs_list_t * newP;
+
+ newP = (obs_list_t *)lwm2m_malloc(sizeof(obs_list_t));
+ if (newP != NULL)
+ {
+ newP->item = targetP;
+ newP->next = resultP;
+ resultP = newP;
+ }
+ }
+ }
+ }
+ targetP = targetP->next;
+ }
+
+ return resultP;
+}
+
+static void prv_unlinkObserved(lwm2m_context_t * contextP,
+ lwm2m_observed_t * observedP)
+{
+ if (contextP->observedList == observedP)
+ {
+ contextP->observedList = contextP->observedList->next;
+ }
+ else
+ {
+ lwm2m_observed_t * parentP;
+
+ parentP = contextP->observedList;
+ while (parentP->next != NULL
+ && parentP->next != observedP)
+ {
+ parentP = parentP->next;
+ }
+ if (parentP->next != NULL)
+ {
+ parentP->next = parentP->next->next;
+ }
+ }
+}
+
+static lwm2m_server_t * prv_findServer(lwm2m_context_t * contextP,
+ void * fromSessionH)
+{
+ lwm2m_server_t * targetP;
+
+ targetP = contextP->serverList;
+ while (targetP != NULL
+ && targetP->sessionH != fromSessionH)
+ {
+ targetP = targetP->next;
+ }
+
+ return targetP;
+}
+
+static lwm2m_watcher_t * prv_findWatcher(lwm2m_observed_t * observedP,
+ lwm2m_server_t * serverP)
+{
+ lwm2m_watcher_t * targetP;
+
+ targetP = observedP->watcherList;
+ while (targetP != NULL
+ && targetP->server != serverP)
+ {
+ targetP = targetP->next;
+ }
+
+ return targetP;
+}
+
+coap_status_t handle_observe_request(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP,
+ void * fromSessionH,
+ coap_packet_t * message,
+ coap_packet_t * response)
+{
+ lwm2m_observed_t * observedP;
+ lwm2m_watcher_t * watcherP;
+ lwm2m_server_t * serverP;
+
+ LOG("handle_observe_request()\r\n");
+
+ if (!LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST;
+ if (message->token_len == 0) return COAP_400_BAD_REQUEST;
+
+ serverP = prv_findServer(contextP, fromSessionH);
+ if (serverP == NULL || serverP->status != STATE_REGISTERED) return COAP_401_UNAUTHORIZED;
+
+ observedP = prv_findObserved(contextP, uriP);
+ if (observedP == NULL)
+ {
+ observedP = (lwm2m_observed_t *)lwm2m_malloc(sizeof(lwm2m_observed_t));
+ if (observedP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+ memset(observedP, 0, sizeof(lwm2m_observed_t));
+ memcpy(&(observedP->uri), uriP, sizeof(lwm2m_uri_t));
+ observedP->next = contextP->observedList;
+ contextP->observedList = observedP;
+ }
+
+ watcherP = prv_findWatcher(observedP, serverP);
+ if (watcherP == NULL)
+ {
+ watcherP = (lwm2m_watcher_t *)lwm2m_malloc(sizeof(lwm2m_watcher_t));
+ if (watcherP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+ memset(watcherP, 0, sizeof(lwm2m_watcher_t));
+ watcherP->server = serverP;
+ watcherP->tokenLen = message->token_len;
+ memcpy(watcherP->token, message->token, message->token_len);
+ watcherP->next = observedP->watcherList;
+ observedP->watcherList = watcherP;
+ }
+
+ coap_set_header_observe(response, watcherP->counter++);
+
+ return COAP_205_CONTENT;
+}
+
+void cancel_observe(lwm2m_context_t * contextP,
+ uint16_t mid,
+ void * fromSessionH)
+{
+ lwm2m_observed_t * observedP;
+
+ LOG("cancel_observe()\r\n");
+
+ for (observedP = contextP->observedList;
+ observedP != NULL;
+ observedP = observedP->next)
+ {
+ lwm2m_watcher_t * targetP = NULL;
+
+ if (observedP->watcherList->lastMid == mid
+ && observedP->watcherList->server->sessionH == fromSessionH)
+ {
+ targetP = observedP->watcherList;
+ observedP->watcherList = observedP->watcherList->next;
+ }
+ else
+ {
+ lwm2m_watcher_t * parentP;
+
+ parentP = observedP->watcherList;
+ while (parentP->next != NULL
+ && (parentP->next->lastMid != mid
+ || parentP->next->server->sessionH != fromSessionH))
+ {
+ parentP = parentP->next;
+ }
+ if (parentP->next != NULL)
+ {
+ targetP = parentP->next;
+ parentP->next = parentP->next->next;
+ }
+ }
+ if (targetP != NULL)
+ {
+ lwm2m_free(targetP);
+ if (observedP->watcherList == NULL)
+ {
+ prv_unlinkObserved(contextP, observedP);
+ lwm2m_free(observedP);
+ }
+ return;
+ }
+ }
+}
+
+void lwm2m_resource_value_changed(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP)
+{
+ int result;
+ obs_list_t * listP;
+ lwm2m_watcher_t * watcherP;
+
+ listP = prv_getObservedList(contextP, uriP);
+ while (listP != NULL)
+ {
+ obs_list_t * targetP;
+ char * buffer = NULL;
+ int length = 0;
+
+ result = object_read(contextP, &listP->item->uri, &buffer, &length);
+ if (result == COAP_205_CONTENT)
+ {
+ coap_packet_t message[1];
+
+ coap_init_message(message, COAP_TYPE_NON, COAP_204_CHANGED, 0);
+ coap_set_payload(message, buffer, length);
+
+ for (watcherP = listP->item->watcherList ; watcherP != NULL ; watcherP = watcherP->next)
+ {
+ watcherP->lastMid = contextP->nextMID++;
+ message->mid = watcherP->lastMid;
+ coap_set_header_token(message, watcherP->token, watcherP->tokenLen);
+ coap_set_header_observe(message, watcherP->counter++);
+ (void)message_send(contextP, message, watcherP->server->sessionH);
+ }
+ }
+
+ targetP = listP;
+ listP = listP->next;
+ lwm2m_free(targetP);
+ }
+
+}
+#endif
+
+#ifdef LWM2M_SERVER_MODE
+static lwm2m_observation_t * prv_findObservationByURI(lwm2m_client_t * clientP,
+ lwm2m_uri_t * uriP)
+{
+ lwm2m_observation_t * targetP;
+
+ targetP = clientP->observationList;
+ while (targetP != NULL)
+ {
+ if (targetP->uri.objectId == uriP->objectId
+ && targetP->uri.flag == uriP->flag
+ && targetP->uri.instanceId == uriP->instanceId
+ && targetP->uri.resourceId == uriP->resourceId)
+ {
+ return targetP;
+ }
+
+ targetP = targetP->next;
+ }
+
+ return targetP;
+}
+
+static void prv_observationRemove(lwm2m_client_t * clientP,
+ lwm2m_observation_t * observationP)
+{
+ if (clientP->observationList == observationP)
+ {
+ clientP->observationList = clientP->observationList->next;
+ }
+ else if (clientP->observationList != NULL)
+ {
+ lwm2m_observation_t * parentP;
+
+ parentP = clientP->observationList;
+
+ while (parentP->next != NULL
+ && parentP->next != observationP)
+ {
+ parentP = parentP->next;
+ }
+ if (parentP->next != NULL)
+ {
+ parentP->next = parentP->next->next;
+ }
+ }
+
+ lwm2m_free(observationP);
+}
+
+static void prv_obsRequestCallback(lwm2m_transaction_t * transacP,
+ void * message)
+{
+ lwm2m_observation_t * observationP = (lwm2m_observation_t *)transacP->userData;
+ coap_packet_t * packet = (coap_packet_t *)message;
+ uint8_t code;
+
+ if (message == NULL)
+ {
+ code = COAP_503_SERVICE_UNAVAILABLE;
+ }
+ else if (packet->code == COAP_205_CONTENT
+ && !IS_OPTION(packet, COAP_OPTION_OBSERVE))
+ {
+ code = COAP_405_METHOD_NOT_ALLOWED;
+ }
+ else
+ {
+ code = packet->code;
+ }
+
+ if (code != COAP_205_CONTENT)
+ {
+ observationP->callback(((lwm2m_client_t*)transacP->peerP)->internalID,
+ &observationP->uri,
+ code,
+ NULL, 0,
+ observationP->userData);
+ prv_observationRemove(((lwm2m_client_t*)transacP->peerP), observationP);
+ }
+ else
+ {
+ observationP->clientP->observationList = (lwm2m_observation_t *)LWM2M_LIST_ADD(observationP->clientP->observationList, observationP);
+ observationP->callback(((lwm2m_client_t*)transacP->peerP)->internalID,
+ &observationP->uri,
+ 0,
+ packet->payload, packet->payload_len,
+ observationP->userData);
+ }
+}
+
+int lwm2m_observe(lwm2m_context_t * contextP,
+ uint16_t clientID,
+ lwm2m_uri_t * uriP,
+ lwm2m_result_callback_t callback,
+ void * userData)
+{
+ lwm2m_client_t * clientP;
+ lwm2m_transaction_t * transactionP;
+ lwm2m_observation_t * observationP;
+ uint8_t token[4];
+
+ if (!LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST;
+
+ clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID);
+ if (clientP == NULL) return COAP_404_NOT_FOUND;
+
+ observationP = (lwm2m_observation_t *)lwm2m_malloc(sizeof(lwm2m_observation_t));
+ if (observationP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+ memset(observationP, 0, sizeof(lwm2m_observation_t));
+
+ transactionP = transaction_new(COAP_GET, uriP, contextP->nextMID++, ENDPOINT_CLIENT, (void *)clientP);
+ if (transactionP == NULL)
+ {
+ lwm2m_free(observationP);
+ return COAP_500_INTERNAL_SERVER_ERROR;
+ }
+
+ observationP->id = lwm2m_list_newId((lwm2m_list_t *)clientP->observationList);
+ memcpy(&observationP->uri, uriP, sizeof(lwm2m_uri_t));
+ observationP->clientP = clientP;
+ observationP->callback = callback;
+ observationP->userData = userData;
+
+ token[0] = clientP->internalID >> 8;
+ token[1] = clientP->internalID & 0xFF;
+ token[2] = observationP->id >> 8;
+ token[3] = observationP->id & 0xFF;
+
+ coap_set_header_observe(transactionP->message, 0);
+ coap_set_header_token(transactionP->message, token, sizeof(token));
+
+ transactionP->callback = prv_obsRequestCallback;
+ transactionP->userData = (void *)observationP;
+
+ contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transactionP);
+
+ return transaction_send(contextP, transactionP);
+}
+
+int lwm2m_observe_cancel(lwm2m_context_t * contextP,
+ uint16_t clientID,
+ lwm2m_uri_t * uriP,
+ lwm2m_result_callback_t callback,
+ void * userData)
+{
+ lwm2m_client_t * clientP;
+ lwm2m_observation_t * observationP;
+
+ clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID);
+ if (clientP == NULL) return COAP_404_NOT_FOUND;
+
+ observationP = prv_findObservationByURI(clientP, uriP);
+ if (observationP == NULL) return COAP_404_NOT_FOUND;
+
+ prv_observationRemove(clientP, observationP);
+
+ return 0;
+}
+
+void handle_observe_notify(lwm2m_context_t * contextP,
+ void * fromSessionH,
+ coap_packet_t * message)
+{
+ uint8_t * tokenP;
+ int token_len;
+ uint16_t clientID;
+ uint16_t obsID;
+ lwm2m_client_t * clientP;
+ lwm2m_observation_t * observationP;
+ uint32_t count;
+
+ token_len = coap_get_header_token(message, (const uint8_t **)&tokenP);
+ if (token_len != sizeof(uint32_t)) return;
+
+ if (1 != coap_get_header_observe(message, &count)) return;
+
+ clientID = (tokenP[0] << 8) | tokenP[1];
+ obsID = (tokenP[2] << 8) | tokenP[3];
+
+ clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID);
+ if (clientP == NULL) return;
+
+ observationP = (lwm2m_observation_t *)lwm2m_list_find((lwm2m_list_t *)clientP->observationList, obsID);
+ if (observationP == NULL)
+ {
+ coap_packet_t resetMsg;
+
+ coap_init_message(&resetMsg, COAP_TYPE_RST, 0, message->mid);
+
+ message_send(contextP, &resetMsg, fromSessionH);
+ }
+ else
+ {
+ observationP->callback(clientID,
+ &observationP->uri,
+ (int)count,
+ message->payload, message->payload_len,
+ observationP->userData);
+ }
+}
+#endif
diff --git a/core/packet.c b/core/packet.c
new file mode 100644
index 000000000..065678de4
--- /dev/null
+++ b/core/packet.c
@@ -0,0 +1,323 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+
+/*
+ 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
+
+*/
+
+/*
+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
+#include
+
+#include
+
+
+static void handle_reset(lwm2m_context_t * contextP,
+ void * fromSessionH,
+ coap_packet_t * message)
+{
+#ifdef LWM2M_CLIENT_MODE
+ cancel_observe(contextP, message->mid, fromSessionH);
+#endif
+}
+
+static coap_status_t handle_request(lwm2m_context_t * contextP,
+ void * fromSessionH,
+ coap_packet_t * message,
+ coap_packet_t * response)
+{
+ lwm2m_uri_t * uriP;
+ coap_status_t result = NOT_FOUND_4_04;
+
+ uriP = lwm2m_decode_uri(message->uri_path);
+ if (uriP == NULL) return BAD_REQUEST_4_00;
+
+ switch(uriP->flag & LWM2M_URI_MASK_TYPE)
+ {
+#ifdef LWM2M_CLIENT_MODE
+ case LWM2M_URI_FLAG_DM:
+ // TODO: Authentify server
+ result = handle_dm_request(contextP, uriP, fromSessionH, message, response);
+ break;
+
+ case LWM2M_URI_FLAG_BOOTSTRAP:
+ result = NOT_IMPLEMENTED_5_01;
+ break;
+#endif
+
+#ifdef LWM2M_SERVER_MODE
+ case LWM2M_URI_FLAG_REGISTRATION:
+ result = handle_registration_request(contextP, uriP, fromSessionH, message, response);
+ break;
+#endif
+ default:
+ result = BAD_REQUEST_4_00;
+ break;
+ }
+
+ coap_set_status_code(response, result);
+
+ if (result < BAD_REQUEST_4_00)
+ {
+ result = NO_ERROR;
+ }
+
+ lwm2m_free( uriP);
+ return result;
+}
+
+/* This function is an adaptation of function coap_receive() from Erbium's er-coap-13-engine.c.
+ * Erbium is Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
+ * All rights reserved.
+ */
+void lwm2m_handle_packet(lwm2m_context_t * contextP,
+ uint8_t * buffer,
+ int length,
+ void * fromSessionH)
+{
+ coap_status_t coap_error_code = NO_ERROR;
+ static coap_packet_t message[1];
+ static coap_packet_t response[1];
+
+ coap_error_code = coap_parse_message(message, buffer, (uint16_t)length);
+ if (coap_error_code==NO_ERROR)
+ {
+ LOG(" Parsed: ver %u, type %u, tkl %u, code %u, mid %u\r\n", message->version, message->type, message->token_len, message->code, message->mid);
+ LOG(" Payload: %.*s\r\n\n", 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;
+ int32_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, CONTENT_2_05, message->mid);
+ }
+ else
+ {
+ /* Unreliable NON requests are answered with a NON as well. */
+ coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05, coap_get_mid());
+ }
+
+ /* 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("Blockwise: block request %u (%u/%u) @ %u bytes\n", block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset);
+ block_size = MIN(block_size, REST_MAX_CHUNK_SIZE);
+ new_offset = block_offset;
+ }
+
+ coap_error_code = handle_request(contextP, fromSessionH, message, response);
+ if (coap_error_code==NO_ERROR)
+ {
+ /* Apply blockwise transfers. */
+ if ( IS_OPTION(message, COAP_OPTION_BLOCK1) && response->codepayload_len, block_size);
+ if (block_offset >= response->payload_len)
+ {
+ LOG("handle_incoming_data(): block_offset >= response->payload_len\n");
+
+ response->code = BAD_OPTION_4_02;
+ 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("Blockwise: blockwise resource, new offset %d\n", 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("Blockwise: no block option for blockwise resource, using block size %u\n", 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
+ {
+ /* Responses */
+ lwm2m_transaction_t * transaction;
+
+ if (message->type==COAP_TYPE_ACK)
+ {
+ LOG("Received ACK\n");
+ }
+ else if (message->type==COAP_TYPE_RST)
+ {
+ LOG("Received RST\n");
+ /* Cancel possible subscriptions. */
+ handle_reset(contextP, fromSessionH, message);
+ }
+
+#ifdef LWM2M_SERVER_MODE
+ if (message->code == COAP_204_CHANGED
+ && IS_OPTION(message, COAP_OPTION_OBSERVE))
+ {
+ handle_observe_notify(contextP, fromSessionH, message);
+ }
+ else
+#endif
+ {
+ transaction_handle_response(contextP, fromSessionH, message);
+ }
+ } /* Request or Response */
+
+ coap_free_header(message);
+
+ } /* if (parsed correctly) */
+ else
+ {
+ LOG("Message parsing failed %d\r\n", coap_error_code);
+ }
+
+ if (coap_error_code != NO_ERROR)
+ {
+ LOG("ERROR %u: %s\n", coap_error_code, coap_error_message);
+
+ /* Set to sendable error code. */
+ if (coap_error_code >= 192)
+ {
+ coap_error_code = INTERNAL_SERVER_ERROR_5_00;
+ }
+ /* Reuse input buffer for error message. */
+ coap_init_message(message, COAP_TYPE_ACK, coap_error_code, message->mid);
+ coap_set_payload(message, coap_error_message, strlen(coap_error_message));
+ message_send(contextP, message, fromSessionH);
+ }
+}
+
+
+coap_status_t message_send(lwm2m_context_t * contextP,
+ coap_packet_t * message,
+ void * sessionH)
+{
+ coap_status_t result = INTERNAL_SERVER_ERROR_5_00;
+ uint8_t pktBuffer[COAP_MAX_PACKET_SIZE+1];
+ size_t pktBufferLen = 0;
+
+ pktBufferLen = coap_serialize_message(message, pktBuffer);
+ if (0 != pktBufferLen)
+ {
+ result = contextP->bufferSendCallback(sessionH, pktBuffer, pktBufferLen, contextP->bufferSendUserData);
+ }
+
+ return result;
+}
+
diff --git a/core/registration.c b/core/registration.c
new file mode 100644
index 000000000..cc6d99f45
--- /dev/null
+++ b/core/registration.c
@@ -0,0 +1,480 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2013, 2014 Intel Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * The Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * David Navarro, Intel Corporation - initial API and implementation
+ * domedambrosio - Please refer to git log
+ * Fabien Fleutot - Please refer to git log
+ * Simon Bernard - Please refer to git log
+ * Toby Jaffey - Please refer to git log
+ * Manuel Sangoi - Please refer to git log
+ *
+ *******************************************************************************/
+
+/*
+ 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
+
+*/
+
+#include "internals.h"
+
+#include
+#include
+#include
+
+
+#define QUERY_TEMPLATE "ep="
+#define QUERY_LENGTH 3
+#define QUERY_DELIMITER '&'
+
+#define MAX_LOCATION_LENGTH 10 // strlen("/rd/65534") + 1
+
+
+#ifdef LWM2M_CLIENT_MODE
+static void prv_handleRegistrationReply(lwm2m_transaction_t * transacP,
+ void * message)
+{
+ lwm2m_server_t * targetP;
+ coap_packet_t * packet = (coap_packet_t *)message;
+
+ targetP = (lwm2m_server_t *)(transacP->peerP);
+
+ switch(targetP->status)
+ {
+ case STATE_REG_PENDING:
+ {
+ if (packet == NULL)
+ {
+ targetP->status = STATE_UNKNOWN;
+ targetP->mid = 0;
+ }
+ else if (packet->mid == targetP->mid
+ && packet->type == COAP_TYPE_ACK
+ && packet->location_path != NULL)
+ {
+ if (packet->code == CREATED_2_01)
+ {
+ targetP->status = STATE_REGISTERED;
+ targetP->location = coap_get_multi_option_as_string(packet->location_path);
+ }
+ else if (packet->code == BAD_REQUEST_4_00)
+ {
+ targetP->status = STATE_UNKNOWN;
+ targetP->mid = 0;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+int lwm2m_register(lwm2m_context_t * contextP)
+{
+ char * query;
+ char payload[512];
+ int payload_length;
+ lwm2m_server_t * targetP;
+
+ payload_length = prv_getRegisterPayload(contextP, payload, sizeof(payload));
+ if (payload_length == 0) return INTERNAL_SERVER_ERROR_5_00;
+
+ query = (char*)lwm2m_malloc(QUERY_LENGTH + strlen(contextP->endpointName) + 1);
+ if (query == NULL) return INTERNAL_SERVER_ERROR_5_00;
+ strcpy(query, QUERY_TEMPLATE);
+ strcpy(query + QUERY_LENGTH, contextP->endpointName);
+
+ targetP = contextP->serverList;
+ while (targetP != NULL)
+ {
+ lwm2m_transaction_t * transaction;
+
+ transaction = transaction_new(COAP_POST, NULL, contextP->nextMID++, ENDPOINT_SERVER, (void *)targetP);
+ if (transaction == NULL) return INTERNAL_SERVER_ERROR_5_00;
+
+ coap_set_header_uri_path(transaction->message, "/"URI_REGISTRATION_SEGMENT);
+ coap_set_header_uri_query(transaction->message, query);
+ coap_set_payload(transaction->message, payload, payload_length);
+
+ transaction->callback = prv_handleRegistrationReply;
+ transaction->userData = (void *) contextP;
+
+ contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction);
+ if (transaction_send(contextP, transaction) == 0)
+ {
+ targetP->status = STATE_REG_PENDING;
+ targetP->mid = transaction->mID;
+ }
+
+ targetP = targetP->next;
+ }
+
+ lwm2m_free(query);
+
+ return 0;
+}
+
+void registration_deregister(lwm2m_context_t * contextP,
+ lwm2m_server_t * serverP)
+{
+ coap_packet_t message[1];
+ uint8_t pktBuffer[COAP_MAX_PACKET_SIZE+1];
+ size_t pktBufferLen = 0;
+
+ if (serverP->status != STATE_REGISTERED) return;
+
+ coap_init_message(message, COAP_TYPE_CON, COAP_DELETE, contextP->nextMID++);
+ coap_set_header_uri_path(message, serverP->location);
+
+ pktBufferLen = coap_serialize_message(message, pktBuffer);
+ if (0 != pktBufferLen)
+ {
+ contextP->bufferSendCallback(serverP->sessionH, pktBuffer, pktBufferLen, contextP->bufferSendUserData);
+ }
+
+ serverP->status = STATE_UNKNOWN;
+}
+#endif
+
+#ifdef LWM2M_SERVER_MODE
+static void prv_getParameters(multi_option_t * query,
+ char ** nameP)
+{
+ const char * start;
+ int length;
+
+ *nameP = NULL;
+
+ while (query != NULL)
+ {
+ if (query->len > QUERY_LENGTH)
+ {
+ if (strncmp(query->data, QUERY_TEMPLATE, QUERY_LENGTH) == 0)
+ {
+ *nameP = (char *)lwm2m_malloc(query->len - QUERY_LENGTH + 1);
+ if (*nameP != NULL)
+ {
+ memcpy(*nameP, query->data + QUERY_LENGTH, query->len - QUERY_LENGTH);
+ (*nameP)[query->len - QUERY_LENGTH] = 0;
+ }
+ break;
+ }
+ }
+ query = query->next;
+ }
+}
+
+static int prv_getId(uint8_t * data,
+ uint16_t length,
+ uint16_t * objId,
+ uint16_t * instanceId)
+{
+ int value;
+ uint16_t limit;
+ uint16_t end;
+
+ // Expecting application/link-format (RFC6690)
+ // strip open and close tags
+ if (length >= 1 && data[0] == '<' && data[length-1] == '>')
+ {
+ data++;
+ length-=2;
+ }
+ else
+ {
+ return 0;
+ }
+
+ // If there is a preceding /, remove it
+ if (length >= 1 && data[0] == '/')
+ {
+ data++;
+ length-=1;
+ }
+
+ limit = 0;
+ while (limit < length && data[limit] != '/' && data[limit] != ' ') limit++;
+ value = prv_get_number(data, limit);
+ if (value < 0 || value >= LWM2M_MAX_ID) return 0;
+ *objId = value;
+
+ if (limit != length)
+ {
+ limit += 1;
+ end = limit;
+ while (end < length && data[end] != ' ') end++;
+ if (end != limit)
+ {
+ value = prv_get_number(data + limit, end - limit);
+ if (value >= 0 && value < LWM2M_MAX_ID)
+ {
+ *instanceId = value;
+ return 2;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static lwm2m_client_object_t * prv_decodeRegisterPayload(uint8_t * payload,
+ uint16_t payloadLength)
+{
+ lwm2m_client_object_t * objList;
+ uint16_t id;
+ uint16_t instance;
+ uint16_t start;
+ uint16_t end;
+ int result;
+
+ objList = NULL;
+ start = 0;
+ while (start < payloadLength)
+ {
+ while (start < payloadLength && payload[start] == ' ') start++;
+ if (start == payloadLength) return objList;
+ end = start;
+ while (end < payloadLength && payload[end] != ',') end++;
+ result = prv_getId(payload + start, end - start, &id, &instance);
+ if (result != 0)
+ {
+ lwm2m_client_object_t * objectP;
+
+ objectP = (lwm2m_client_object_t *)lwm2m_list_find((lwm2m_list_t *)objList, id);
+ if (objectP == NULL)
+ {
+ objectP = (lwm2m_client_object_t *)lwm2m_malloc(sizeof(lwm2m_client_object_t));
+ memset(objectP, 0, sizeof(lwm2m_client_object_t));
+ if (objectP == NULL) return objList;
+ objectP->id = id;
+ objList = (lwm2m_client_object_t *)LWM2M_LIST_ADD(objList, objectP);
+ }
+ if (result == 2)
+ {
+ lwm2m_list_t * instanceP;
+
+ instanceP = lwm2m_list_find(objectP->instanceList, instance);
+ if (instanceP == NULL)
+ {
+ instanceP = (lwm2m_list_t *)lwm2m_malloc(sizeof(lwm2m_list_t));
+ memset(instanceP, 0, sizeof(lwm2m_list_t));
+ instanceP->id = instance;
+ objectP->instanceList = LWM2M_LIST_ADD(objectP->instanceList, instanceP);
+ }
+ }
+ }
+ start = end + 1;
+ }
+
+ return objList;
+}
+
+static lwm2m_client_t * prv_getClientByName(lwm2m_context_t * contextP,
+ char * name)
+{
+ lwm2m_client_t * targetP;
+
+ targetP = contextP->clientList;
+ while (targetP != NULL && strcmp(name, targetP->name) != 0)
+ {
+ targetP = targetP->next;
+ }
+
+ return targetP;
+}
+
+static void prv_freeClientObjectList(lwm2m_client_object_t * objects)
+{
+ while (objects != NULL)
+ {
+ lwm2m_client_object_t * objP;
+
+ while (objects->instanceList != NULL)
+ {
+ lwm2m_list_t * target;
+
+ target = objects->instanceList;
+ objects->instanceList = objects->instanceList->next;
+ lwm2m_free(target);
+ }
+
+ objP = objects;
+ objects = objects->next;
+ lwm2m_free(objP);
+ }
+}
+
+void prv_freeClient(lwm2m_client_t * clientP)
+{
+ if (clientP->name != NULL) lwm2m_free(clientP->name);
+ prv_freeClientObjectList(clientP->objectList);
+ while(clientP->observationList != NULL)
+ {
+ lwm2m_observation_t * targetP;
+
+ targetP = clientP->observationList;
+ clientP->observationList = clientP->observationList->next;
+ lwm2m_free(targetP);
+ }
+ lwm2m_free(clientP);
+}
+
+static int prv_getLocationString(uint16_t id,
+ char location[MAX_LOCATION_LENGTH])
+{
+ int result;
+
+ memset(location, 0, MAX_LOCATION_LENGTH);
+
+ result = snprintf(location, MAX_LOCATION_LENGTH, "/"URI_REGISTRATION_SEGMENT"/%hu", id);
+ if (result <= 0 || result > MAX_LOCATION_LENGTH)
+ {
+ return 0;
+ }
+
+ return result;
+}
+
+coap_status_t handle_registration_request(lwm2m_context_t * contextP,
+ lwm2m_uri_t * uriP,
+ void * fromSessionH,
+ coap_packet_t * message,
+ coap_packet_t * response)
+{
+ coap_status_t result;
+
+ switch(message->code)
+ {
+ case COAP_POST:
+ {
+ char * name = NULL;
+ lwm2m_client_object_t * objects;
+ lwm2m_client_t * clientP;
+ char location[MAX_LOCATION_LENGTH];
+
+ if ((uriP->flag & LWM2M_URI_MASK_ID) != 0) return COAP_400_BAD_REQUEST;
+ prv_getParameters(message->uri_query, &name);
+ if (name == NULL) return COAP_400_BAD_REQUEST;
+ objects = prv_decodeRegisterPayload(message->payload, message->payload_len);
+ if (objects == NULL)
+ {
+ lwm2m_free(name);
+ return COAP_400_BAD_REQUEST;
+ }
+ clientP = prv_getClientByName(contextP, name);
+ if (clientP != NULL)
+ {
+ // we reset this registration
+ lwm2m_free(clientP->name);
+ prv_freeClientObjectList(clientP->objectList);
+ clientP->objectList = NULL;
+ }
+ else
+ {
+ clientP = (lwm2m_client_t *)lwm2m_malloc(sizeof(lwm2m_client_t));
+ if (clientP == NULL)
+ {
+ lwm2m_free(name);
+ prv_freeClientObjectList(objects);
+ return COAP_500_INTERNAL_SERVER_ERROR;
+ }
+ memset(clientP, 0, sizeof(lwm2m_client_t));
+ clientP->internalID = lwm2m_list_newId((lwm2m_list_t *)contextP->clientList);
+ contextP->clientList = (lwm2m_client_t *)LWM2M_LIST_ADD(contextP->clientList, clientP);
+ }
+ clientP->name = name;
+ clientP->objectList = objects;
+ clientP->sessionH = fromSessionH;
+
+ if (prv_getLocationString(clientP->internalID, location) == 0)
+ {
+ prv_freeClient(clientP);
+ return COAP_500_INTERNAL_SERVER_ERROR;
+ }
+ if (coap_set_header_location_path(response, location) == 0)
+ {
+ prv_freeClient(clientP);
+ return COAP_500_INTERNAL_SERVER_ERROR;
+ }
+
+ if (contextP->monitorCallback != NULL)
+ {
+ contextP->monitorCallback(clientP->internalID, NULL, CREATED_2_01, NULL, 0, contextP->monitorUserData);
+ }
+ result = CREATED_2_01;
+ }
+ break;
+
+ case COAP_PUT:
+ result = COAP_501_NOT_IMPLEMENTED;
+ break;
+
+ case COAP_DELETE:
+ {
+ lwm2m_client_t * clientP;
+
+ if ((uriP->flag & LWM2M_URI_MASK_ID) != LWM2M_URI_FLAG_OBJECT_ID) return COAP_400_BAD_REQUEST;
+
+ contextP->clientList = (lwm2m_client_t *)LWM2M_LIST_RM(contextP->clientList, uriP->objectId, &clientP);
+ if (clientP == NULL) return COAP_400_BAD_REQUEST;
+ if (contextP->monitorCallback != NULL)
+ {
+ contextP->monitorCallback(clientP->internalID, NULL, DELETED_2_02, NULL, 0, contextP->monitorUserData);
+ }
+ prv_freeClient(clientP);
+ result = DELETED_2_02;
+ }
+ break;
+
+ default:
+ return COAP_400_BAD_REQUEST;
+ }
+
+ return result;
+}
+
+void lwm2m_set_monitoring_callback(lwm2m_context_t * contextP,
+ lwm2m_result_callback_t callback,
+ void * userData)
+{
+ contextP->monitorCallback = callback;
+ contextP->monitorUserData = userData;
+}
+
+
+#endif
diff --git a/core/tlv.c b/core/tlv.c
new file mode 100644
index 000000000..d0fabec11
--- /dev/null
+++ b/core/tlv.c
@@ -0,0 +1,632 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+
+#include "internals.h"
+#include
+#include
+#include
+#include
+
+#define _PRV_64BIT_BUFFER_SIZE 8
+
+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_create_header(uint8_t * header,
+ lwm2m_tlv_type_t type,
+ uint16_t id,
+ size_t data_len)
+{
+ int header_len;
+
+ header_len = prv_getHeaderLength(id, data_len);
+
+ header[0] = 0;
+ switch (type)
+ {
+ case TLV_OBJECT_INSTANCE:
+ // do nothing
+ break;
+ case TLV_RESSOURCE_INSTANCE:
+ header[0] |= 0x40;
+ break;
+ case TLV_MULTIPLE_INSTANCE:
+ header[0] |= 0x80;
+ break;
+ case TLV_RESSOURCE:
+ header[0] |= 0xC0;
+ break;
+ default:
+ return 0;
+ }
+ if (id > 0xFF)
+ {
+ header[0] |= 0x20;
+ header[1] = (id >> 8) & 0XFF;
+ header[2] = id & 0XFF;
+ }
+ else
+ {
+ header[1] = id;
+ }
+ if (data_len <= 7)
+ {
+ header[0] += data_len;
+ }
+ else if (data_len <= 0xFF)
+ {
+ header[0] |= 0x08;
+ header[2] = data_len;
+ }
+ else if (data_len <= 0xFFFF)
+ {
+ header[0] |= 0x10;
+ header[2] = (data_len >> 8) & 0XFF;
+ header[3] = data_len & 0XFF;
+ }
+ else if (data_len <= 0xFFFFFF)
+ {
+ header[0] |= 0x18;
+ header[2] = (data_len >> 16) & 0XFF;
+ header[3] = (data_len >> 8) & 0XFF;
+ header[4] = data_len & 0XFF;
+ }
+
+ return header_len;
+}
+
+int lwm2m_opaqueToTLV(lwm2m_tlv_type_t type,
+ uint8_t* dataP,
+ size_t data_len,
+ uint16_t id,
+ char * buffer,
+ size_t buffer_len)
+{
+ uint8_t header[LWM2M_TLV_HEADER_MAX_LENGTH];
+ size_t header_len;
+
+ header_len = prv_create_header(header, type, id, data_len);
+
+ if (buffer_len < data_len + header_len) return 0;
+
+ memmove(buffer, header, header_len);
+
+ memmove(buffer + header_len, dataP, data_len);
+
+ return header_len + data_len;
+}
+
+int lwm2m_boolToTLV(lwm2m_tlv_type_t type,
+ bool value,
+ uint16_t id,
+ char * buffer,
+ size_t buffer_len)
+{
+ return lwm2m_intToTLV(type, value?1:0, id, buffer, buffer_len);
+}
+
+int lwm2m_intToTLV(lwm2m_tlv_type_t type,
+ int64_t data,
+ uint16_t id,
+ char * buffer,
+ size_t buffer_len)
+{
+ uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE];
+ size_t length = 0;
+ uint64_t value;
+ int negative = 0;
+
+ if (type != TLV_RESSOURCE_INSTANCE && type != TLV_RESSOURCE)
+ return 0;
+
+ memset(data_buffer, 0, 8);
+
+ if (data < 0)
+ {
+ negative = 1;
+ value = 0 - data;
+ }
+ else
+ {
+ value = data;
+ }
+
+ do
+ {
+ length++;
+ data_buffer[_PRV_64BIT_BUFFER_SIZE - length] = (value >> (8*(length-1))) & 0xFF;
+ } while (value > (((uint64_t)1 << ((8 * length)-1)) - 1));
+
+
+ if (1 == negative)
+ {
+ data_buffer[_PRV_64BIT_BUFFER_SIZE - length] |= 0x80;
+ }
+
+ return lwm2m_opaqueToTLV(type, data_buffer + (_PRV_64BIT_BUFFER_SIZE - length), length, id, buffer, buffer_len);
+}
+
+int lwm2m_decodeTLV(char * buffer,
+ size_t buffer_len,
+ lwm2m_tlv_type_t * oType,
+ uint16_t * oID,
+ size_t * oDataIndex,
+ size_t * oDataLen)
+{
+
+ if (buffer_len < 2) return 0;
+
+ *oDataIndex = 2;
+
+ switch (buffer[0]&0xC0)
+ {
+ case 0x00:
+ *oType = TLV_OBJECT_INSTANCE;
+ break;
+ case 0x40:
+ *oType = TLV_RESSOURCE_INSTANCE;
+ break;
+ case 0x80:
+ *oType = TLV_MULTIPLE_INSTANCE;
+ break;
+ case 0xC0:
+ *oType = TLV_RESSOURCE;
+ break;
+ default:
+ // can't happen
+ return 0;
+ }
+
+ if ((uint8_t)(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 lwm2m_opaqueToInt(char * buffer,
+ size_t buffer_len,
+ int64_t * dataP)
+{
+ int i;
+
+ if (buffer_len == 0 || buffer_len > 8) return 0;
+
+ // first bit is the sign
+ *dataP = buffer[0]&0x7F;
+
+ for (i = 1 ; i < buffer_len ; i++)
+ {
+ *dataP = (*dataP << 8) + buffer[i];
+ }
+
+ // first bit is the sign
+ if ((uint8_t)(buffer[0]&0x80) == 0x80)
+ {
+ *dataP = 0 - *dataP;
+ }
+
+ return i;
+}
+
+lwm2m_tlv_t * lwm2m_tlv_new(int size)
+{
+ lwm2m_tlv_t * tlvP;
+
+ if (size <= 0) return NULL;
+
+ tlvP = (lwm2m_tlv_t *)lwm2m_malloc(size * sizeof(lwm2m_tlv_t));
+
+ if (tlvP != NULL)
+ {
+ memset(tlvP, 0, size * sizeof(lwm2m_tlv_t));
+ }
+
+ return tlvP;
+}
+
+int lwm2m_tlv_parse(char * buffer,
+ size_t bufferLen,
+ lwm2m_tlv_t ** dataP)
+{
+ lwm2m_tlv_type_t type;
+ uint16_t id;
+ size_t dataIndex;
+ size_t dataLen;
+ int length = 0;
+ int result;
+ int size = 0;
+
+ *dataP = NULL;
+
+ while (0 != (result = lwm2m_decodeTLV(buffer + length, bufferLen - length, &type, &id, &dataIndex, &dataLen)))
+ {
+ lwm2m_tlv_t * newTlvP;
+
+ newTlvP = lwm2m_tlv_new(size + 1);
+ if (size >= 1)
+ {
+ if (newTlvP == NULL)
+ {
+ lwm2m_tlv_free(size, *dataP);
+ return 0;
+ }
+ else
+ {
+ memcpy(newTlvP, *dataP, size * sizeof(lwm2m_tlv_t));
+ lwm2m_free(*dataP);
+ }
+ }
+ *dataP = newTlvP;
+
+ switch (type)
+ {
+ case TLV_OBJECT_INSTANCE:
+ (*dataP)[size].type = LWM2M_TYPE_OBJECT_INSTANCE;
+ break;
+ case TLV_RESSOURCE_INSTANCE:
+ (*dataP)[size].type = LWM2M_TYPE_RESSOURCE_INSTANCE;
+ break;
+ case TLV_MULTIPLE_INSTANCE:
+ (*dataP)[size].type = LWM2M_TYPE_MULTIPLE_RESSOURCE;
+ break;
+ case TLV_RESSOURCE:
+ (*dataP)[size].type = LWM2M_TYPE_RESSOURCE;
+ break;
+ default:
+ lwm2m_tlv_free(size, *dataP);
+ return 0;
+ }
+
+ (*dataP)[size].id = id;
+ if (type == TLV_OBJECT_INSTANCE || type == TLV_MULTIPLE_INSTANCE)
+ {
+ (*dataP)[size].length = lwm2m_tlv_parse(buffer + length + dataIndex,
+ dataLen,
+ (lwm2m_tlv_t **)&((*dataP)[size].value));
+ if ((*dataP)[size].length == 0)
+ {
+ lwm2m_tlv_free(size + 1, *dataP);
+ return 0;
+ }
+ }
+ else
+ {
+ (*dataP)[size].flags = LWM2M_TLV_FLAG_STATIC_DATA;
+ (*dataP)[size].length = dataLen;
+ (*dataP)[size].value = buffer + length + dataIndex;
+ }
+ size++;
+ length += result;
+ }
+
+ return size;
+}
+
+static int prv_getLength(int size,
+ lwm2m_tlv_t * tlvP)
+{
+ int length;
+ int i;
+
+ length = 0;
+
+ for (i = 0 ; i < size && length != -1 ; i++)
+ {
+ switch (tlvP[i].type)
+ {
+ case LWM2M_TYPE_OBJECT_INSTANCE:
+ case LWM2M_TYPE_MULTIPLE_RESSOURCE:
+ {
+ int subLength;
+
+ subLength = prv_getLength(tlvP[i].length, (lwm2m_tlv_t *)(tlvP[i].value));
+ if (subLength == -1)
+ {
+ length = -1;
+ }
+ else
+ {
+ length += prv_getHeaderLength(tlvP[i].id, subLength) + subLength;
+ }
+ }
+ break;
+ case LWM2M_TYPE_RESSOURCE_INSTANCE:
+ case LWM2M_TYPE_RESSOURCE:
+ length += prv_getHeaderLength(tlvP[i].id, tlvP[i].length) + tlvP[i].length;
+ break;
+ default:
+ length = -1;
+ break;
+ }
+ }
+
+ return length;
+}
+
+
+int lwm2m_tlv_serialize(int size,
+ lwm2m_tlv_t * tlvP,
+ char ** bufferP)
+{
+ int length;
+ int index;
+ int i;
+
+ *bufferP = NULL;
+ length = prv_getLength(size, tlvP);
+ if (length <= 0) return length;
+
+ *bufferP = (char *)lwm2m_malloc(length);
+ if (*bufferP == NULL) return 0;
+
+ index = 0;
+ for (i = 0 ; i < size && length != 0 ; i++)
+ {
+ int headerLen;
+
+ switch (tlvP[i].type)
+ {
+ case LWM2M_TYPE_OBJECT_INSTANCE:
+ case LWM2M_TYPE_MULTIPLE_RESSOURCE:
+ {
+ char * tmpBuffer;
+ int tmpLength;
+
+ tmpLength = lwm2m_tlv_serialize(tlvP[i].length, (lwm2m_tlv_t *)tlvP[i].value, &tmpBuffer);
+ if (tmpLength == 0)
+ {
+ length = 0;
+ }
+ else
+ {
+ headerLen = prv_create_header(*bufferP + index, tlvP[i].type, tlvP[i].id, tmpLength);
+ index += headerLen;
+ memcpy(*bufferP + index, tmpBuffer, tmpLength);
+ index += tmpLength;
+ lwm2m_free(tmpBuffer);
+ }
+ }
+ break;
+
+ case LWM2M_TYPE_RESSOURCE_INSTANCE:
+ case LWM2M_TYPE_RESSOURCE:
+ {
+ headerLen = prv_create_header(*bufferP + index, tlvP[i].type, tlvP[i].id, tlvP[i].length);
+ if (headerLen == 0)
+ {
+ length = 0;
+ }
+ else
+ {
+ index += headerLen;
+ memcpy(*bufferP + index, tlvP[i].value, tlvP[i].length);
+ index += tlvP[i].length;
+ }
+ }
+ break;
+
+ default:
+ length = 0;
+ break;
+ }
+ }
+
+ if (length == 0)
+ {
+ lwm2m_free(*bufferP);
+ }
+ return length;
+}
+
+void lwm2m_tlv_free(int size,
+ lwm2m_tlv_t * tlvP)
+{
+ int i;
+
+ if (size == 0 || tlvP == NULL) return;
+
+ for (i = 0 ; i < size ; i++)
+ {
+ if ((tlvP[i].flags & LWM2M_TLV_FLAG_STATIC_DATA) == 0)
+ {
+ if (tlvP[i].type == LWM2M_TYPE_MULTIPLE_RESSOURCE
+ || tlvP[i].type == TLV_OBJECT_INSTANCE)
+ {
+ lwm2m_tlv_free(tlvP[i].length, (lwm2m_tlv_t *)(tlvP[i].value));
+ }
+ else
+ {
+ lwm2m_free(tlvP[i].value);
+ }
+ }
+ }
+ lwm2m_free(tlvP);
+}
+
+void lwm2m_tlv_encode_int(int64_t data,
+ lwm2m_tlv_t * tlvP)
+{
+ tlvP->length = 0;
+
+ if ((tlvP->flags & LWM2M_TLV_FLAG_TEXT_FORMAT) != 0)
+ {
+ char string[32];
+ int length;
+
+ length = snprintf(string, 32, "%" PRId64, data);
+ if (length > 0)
+ {
+ tlvP->value = (uint8_t *)malloc(length);
+ if (tlvP->value != NULL)
+ {
+ strncpy(tlvP->value, string, length);
+ tlvP->flags &= ~LWM2M_TLV_FLAG_STATIC_DATA;
+ tlvP->length = length;
+ }
+ }
+ }
+ else
+ {
+ uint8_t buffer[_PRV_64BIT_BUFFER_SIZE];
+ size_t length = 0;
+ uint64_t value;
+ int negative = 0;
+
+ memset(buffer, 0, _PRV_64BIT_BUFFER_SIZE);
+
+ if (data < 0)
+ {
+ negative = 1;
+ value = 0 - data;
+ }
+ else
+ {
+ value = data;
+ }
+
+ do
+ {
+ length++;
+ buffer[_PRV_64BIT_BUFFER_SIZE - length] = (value >> (8*(length-1))) & 0xFF;
+ } while (value > (((uint64_t)1 << ((8 * length)-1)) - 1));
+
+
+ if (1 == negative)
+ {
+ buffer[_PRV_64BIT_BUFFER_SIZE - length] |= 0x80;
+ }
+
+ tlvP->value = (uint8_t *)malloc(length);
+ if (tlvP->value != NULL)
+ {
+ memcpy(tlvP->value,
+ buffer + (_PRV_64BIT_BUFFER_SIZE - length),
+ length);
+ tlvP->flags &= ~LWM2M_TLV_FLAG_STATIC_DATA;
+ tlvP->length = length;
+ }
+ }
+}
+
+int lwm2m_tlv_decode_int(lwm2m_tlv_t * tlvP,
+ int64_t * dataP)
+{
+ int i;
+
+ if (tlvP->length == 0) return 0;
+
+ if ((tlvP->flags & LWM2M_TLV_FLAG_TEXT_FORMAT) != 0)
+ {
+ char string[32];
+ int result;
+
+ // int64 is 20 digit max
+ if (tlvP->length > 32) return 0;
+
+ memcpy(string, tlvP->value, tlvP->length);
+ string[tlvP->length] = 0;
+ result = sscanf(string, "%" PRId64, dataP);
+ if (result != 1) return 0;
+ }
+ else
+ {
+ if (tlvP->length > 8) return 0;
+
+ // first bit is the sign
+ *dataP = tlvP->value[0]&0x7F;
+
+ for (i = 1 ; i < tlvP->length ; i++)
+ {
+ *dataP = (*dataP << 8) + tlvP->value[i];
+ }
+
+ // first bit is the sign
+ if ((tlvP->value[0]&0x80) == 0x80)
+ {
+ *dataP = 0 - *dataP;
+ }
+ }
+
+ return 1;
+}
diff --git a/core/transaction.c b/core/transaction.c
new file mode 100644
index 000000000..5bd694887
--- /dev/null
+++ b/core/transaction.c
@@ -0,0 +1,325 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+
+/*
+ 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
+
+*/
+
+/*
+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"
+
+
+/*
+ * 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_check_addr(void * leftSessionH,
+ void * rightSessionH)
+{
+ if ((leftSessionH == NULL)
+ || (rightSessionH == NULL)
+ || (leftSessionH != rightSessionH))
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+lwm2m_transaction_t * transaction_new(coap_method_t method,
+ lwm2m_uri_t * uriP,
+ uint16_t mID,
+ lwm2m_endpoint_type_t peerType,
+ void * peerP)
+{
+ lwm2m_transaction_t * transacP;
+ int result;
+
+ transacP = (lwm2m_transaction_t *)lwm2m_malloc(sizeof(lwm2m_transaction_t));
+
+ if (transacP == NULL) return NULL;
+ memset(transacP, 0, sizeof(lwm2m_transaction_t));
+
+ transacP->message = lwm2m_malloc(sizeof(coap_packet_t));
+ if (transacP->message == NULL) goto error;
+
+ coap_init_message(transacP->message, COAP_TYPE_CON, method, mID);
+
+ transacP->mID = mID;
+ transacP->peerType = peerType;
+ transacP->peerP = peerP;
+
+ if (uriP != NULL)
+ {
+ result = snprintf(transacP->objStringID, LWM2M_STRING_ID_MAX_LEN, "%hu", uriP->objectId);
+ if (result < 0 || result > LWM2M_STRING_ID_MAX_LEN) goto error;
+
+ coap_set_header_uri_path_segment(transacP->message, transacP->objStringID);
+
+ if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+ {
+ result = snprintf(transacP->instanceStringID, LWM2M_STRING_ID_MAX_LEN, "%hu", uriP->instanceId);
+ if (result < 0 || result > LWM2M_STRING_ID_MAX_LEN) goto error;
+ coap_set_header_uri_path_segment(transacP->message, transacP->instanceStringID);
+ }
+ else
+ {
+ if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+ {
+ coap_set_header_uri_path_segment(transacP->message, NULL);
+ }
+ }
+ if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+ {
+ result = snprintf(transacP->resourceStringID, LWM2M_STRING_ID_MAX_LEN, "%hu", uriP->resourceId);
+ if (result < 0 || result > LWM2M_STRING_ID_MAX_LEN) goto error;
+ coap_set_header_uri_path_segment(transacP->message, transacP->resourceStringID);
+ }
+ }
+
+ return transacP;
+
+error:
+ lwm2m_free(transacP);
+ return NULL;
+}
+
+void transaction_free(lwm2m_transaction_t * transacP)
+{
+ if (transacP->message) lwm2m_free(transacP->message);
+ if (transacP->buffer) lwm2m_free(transacP->buffer);
+ lwm2m_free(transacP);
+}
+
+void transaction_remove(lwm2m_context_t * contextP,
+ lwm2m_transaction_t * transacP)
+{
+ if (NULL != contextP->transactionList)
+ {
+ if (transacP == contextP->transactionList)
+ {
+ contextP->transactionList = contextP->transactionList->next;
+ }
+ else
+ {
+ lwm2m_transaction_t *previous = contextP->transactionList;
+ while (previous->next && previous->next != transacP)
+ {
+ previous = previous->next;
+ }
+ if (NULL != previous->next)
+ {
+ previous->next = previous->next->next;
+ }
+ }
+ }
+ transaction_free(transacP);
+}
+
+
+void transaction_handle_response(lwm2m_context_t * contextP,
+ void * fromSessionH,
+ coap_packet_t * message)
+{
+ lwm2m_transaction_t * transacP;
+
+ transacP = contextP->transactionList;
+
+ while (transacP != NULL)
+ {
+ void * targetSessionH;
+
+ targetSessionH = NULL;
+ switch (transacP->peerType)
+ {
+ #ifdef LWM2M_SERVER_MODE
+ case ENDPOINT_CLIENT:
+ targetSessionH = ((lwm2m_client_t *)transacP->peerP)->sessionH;
+ break;
+ #endif
+
+ #ifdef LWM2M_CLIENT_MODE
+ case ENDPOINT_SERVER:
+ targetSessionH = ((lwm2m_server_t *)transacP->peerP)->sessionH;
+ break;
+ #endif
+
+ default:
+ break;
+ }
+
+ if (prv_check_addr(fromSessionH, targetSessionH))
+ {
+ if (transacP->mID == message->mid)
+ {
+ // 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 (message->code != COAP_401_UNAUTHORIZED || transacP->retrans_counter >= COAP_MAX_RETRANSMIT)
+ {
+ if (transacP->callback != NULL)
+ {
+ transacP->callback(transacP, message);
+ }
+ transaction_remove(contextP, transacP);
+ }
+ // we found our guy, exit
+ return;
+ }
+ }
+
+ transacP = transacP->next;
+ }
+}
+
+int transaction_send(lwm2m_context_t * contextP,
+ lwm2m_transaction_t * transacP)
+{
+ if (transacP->buffer == NULL)
+ {
+ uint8_t tempBuffer[LWM2M_MAX_PACKET_SIZE];
+ int length;
+
+ length = coap_serialize_message(transacP->message, tempBuffer);
+ if (length <= 0) return COAP_500_INTERNAL_SERVER_ERROR;
+
+ transacP->buffer = (uint8_t*)lwm2m_malloc(length);
+ if (transacP->buffer == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+ memcpy(transacP->buffer, tempBuffer, length);
+ transacP->buffer_len = length;
+ }
+
+ switch(transacP->peerType)
+ {
+ case ENDPOINT_CLIENT:
+ LOG("Sending %d bytes\r\n", transacP->buffer_len);
+ contextP->bufferSendCallback(((lwm2m_client_t*)transacP->peerP)->sessionH,
+ transacP->buffer, transacP->buffer_len, contextP->bufferSendUserData);
+
+ break;
+
+ case ENDPOINT_SERVER:
+ LOG("Sending %d bytes\r\n", transacP->buffer_len);
+ contextP->bufferSendCallback(((lwm2m_server_t*)transacP->peerP)->sessionH,
+ transacP->buffer, transacP->buffer_len, contextP->bufferSendUserData);
+ break;
+
+ case ENDPOINT_BOOTSTRAP:
+ // not implemented yet
+ break;
+
+ default:
+ return 0;
+ }
+
+ if (transacP->retrans_counter == 0)
+ {
+ struct timeval tv;
+
+ if (0 == lwm2m_gettimeofday(&tv, NULL))
+ {
+ transacP->retrans_time = tv.tv_sec;
+ transacP->retrans_counter = 1;
+ }
+ else
+ {
+ // crude error handling
+ transacP->retrans_counter = COAP_MAX_RETRANSMIT;
+ }
+ }
+
+ if (transacP->retrans_counter < COAP_MAX_RETRANSMIT)
+ {
+ transacP->retrans_time += COAP_RESPONSE_TIMEOUT * transacP->retrans_counter;
+ transacP->retrans_counter++;
+ }
+ else
+ {
+ if (transacP->callback)
+ {
+ transacP->callback(transacP, NULL);
+ }
+ transaction_remove(contextP, transacP);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/core/uri.c b/core/uri.c
new file mode 100644
index 000000000..cc575f0aa
--- /dev/null
+++ b/core/uri.c
@@ -0,0 +1,220 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+
+/*
+ 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
+
+*/
+
+#include "internals.h"
+#include
+#include
+#include
+
+
+static int prv_parse_number(const char * uriString,
+ size_t uriLength,
+ int * 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 prv_get_number(const char * uriString,
+ size_t uriLength)
+{
+ int index = 0;
+
+ return prv_parse_number(uriString, uriLength, &index);
+}
+
+
+lwm2m_uri_t * lwm2m_decode_uri(multi_option_t *uriPath)
+{
+ lwm2m_uri_t * uriP;
+ int readNum;
+
+ if (NULL == uriPath) return NULL;
+
+ 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 (URI_REGISTRATION_SEGMENT_LEN == uriPath->len
+ && 0 == strncmp(URI_REGISTRATION_SEGMENT, uriPath->data, uriPath->len))
+ {
+ uriP->flag |= LWM2M_URI_FLAG_REGISTRATION;
+ uriPath = uriPath->next;
+ if (uriPath == NULL) return uriP;
+ }
+ else if (URI_BOOTSTRAP_SEGMENT_LEN == uriPath->len
+ && 0 == strncmp(URI_BOOTSTRAP_SEGMENT, uriPath->data, uriPath->len))
+ {
+ uriP->flag |= LWM2M_URI_FLAG_BOOTSTRAP;
+ uriPath = uriPath->next;
+ if (uriPath != NULL) goto error;
+ return uriP;
+ }
+
+ readNum = prv_get_number(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 = prv_get_number(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 = prv_get_number(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) return uriP;
+
+error:
+ lwm2m_free(uriP);
+ return NULL;
+}
+
+int lwm2m_stringToUri(char * buffer,
+ size_t buffer_len,
+ lwm2m_uri_t * uriP)
+{
+ int head;
+ int readNum;
+
+ 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]))
+ {
+ 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_parse_number(buffer, buffer_len, &head);
+ if (readNum < 0 || readNum > LWM2M_MAX_ID) return 0;
+ uriP->objectId = (uint16_t)readNum;
+
+ if (buffer[head] == '/') head += 1;
+ if (head >= buffer_len) return head;
+
+ readNum = prv_parse_number(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) return head;
+
+ readNum = prv_parse_number(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;
+
+ return head;
+}
+
diff --git a/core/utils.c b/core/utils.c
new file mode 100644
index 000000000..3d9caa4c8
--- /dev/null
+++ b/core/utils.c
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ *
+ * 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
+
+*/
+
+#include "internals.h"
+#include
+#include
+#include
+#include
+
+
+int lwm2m_PlainTextToInt64(char * buffer,
+ int length,
+ int64_t * dataP)
+{
+ uint64_t result = 0;
+ int sign = 1;
+ int mul = 0;
+ 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 (0 == mul)
+ {
+ mul = 10;
+ }
+ else
+ {
+ result *= mul;
+ }
+ result += buffer[i] - '0';
+ }
+ else
+ {
+ return 0;
+ }
+ i++;
+ }
+
+ *dataP = result * sign;
+ return 1;
+}
+
+int lwm2m_int8ToPlainText(int8_t data,
+ char ** bufferP)
+{
+ return lwm2m_int64ToPlainText((int64_t)data, bufferP);
+}
+
+int lwm2m_int16ToPlainText(int16_t data,
+ char ** bufferP)
+{
+ return lwm2m_int64ToPlainText((int64_t)data, bufferP);
+}
+
+int lwm2m_int32ToPlainText(int32_t data,
+ char ** bufferP)
+{
+ return lwm2m_int64ToPlainText((int64_t)data, bufferP);
+}
+
+int lwm2m_int64ToPlainText(int64_t data,
+ char ** bufferP)
+{
+ char string[32];
+ int len;
+
+ len = snprintf(string, 32, "%" PRId64, data);
+ if (len > 0)
+ {
+ *bufferP = (char *)lwm2m_malloc(len);
+
+ if (NULL != *bufferP)
+ {
+ strncpy(*bufferP, string, len);
+ }
+ else
+ {
+ len = 0;
+ }
+ }
+
+ return len;
+}
+
+
+int lwm2m_float32ToPlainText(float data,
+ char ** bufferP)
+{
+ return lwm2m_float64ToPlainText((double)data, bufferP);
+}
+
+
+int lwm2m_float64ToPlainText(double data,
+ char ** bufferP)
+{
+ char string[64];
+ int len;
+
+ len = snprintf(string, 64, "%lf", data);
+ if (len > 0)
+ {
+ *bufferP = (char *)lwm2m_malloc(len);
+
+ if (NULL != *bufferP)
+ {
+ strncpy(*bufferP, string, len);
+ }
+ else
+ {
+ len = 0;
+ }
+ }
+
+ return len;
+}
+
+
+int lwm2m_boolToPlainText(bool data,
+ char ** bufferP)
+{
+ return lwm2m_int64ToPlainText((int64_t)(data?1:0), bufferP);
+}
+
diff --git a/notice.html b/notice.html
new file mode 100644
index 000000000..c3d34c3c2
--- /dev/null
+++ b/notice.html
@@ -0,0 +1,107 @@
+
+
+
+
+
+Eclipse Foundation Software User Agreement
+
+
+
+Eclipse Foundation Software User Agreement
+April 9, 2014
+
+Usage Of Content
+
+THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
+ (COLLECTIVELY "CONTENT"). USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND
+ CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE
+ OF THE CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR
+ NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND
+ CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.
+
+Applicable Licenses
+
+Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 1.0
+ ("EPL"). A copy of the EPL is provided with this Content and is also available at http://www.eclipse.org/legal/epl-v10.html.
+ For purposes of the EPL, "Program" will mean the Content.
+
+Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse Foundation source code
+ repository ("Repository") in software modules ("Modules") and made available as downloadable archives ("Downloads").
+
+
+ - Content may be structured and packaged into modules to facilitate delivering, extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"), plug-in fragments ("Fragments"), and features ("Features").
+ - Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java™ ARchive) in a directory named "plugins".
+ - A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material. Each Feature may be packaged as a sub-directory in a directory named "features". Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of the Plug-ins
+ and/or Fragments associated with that Feature.
+ - Features may also include other Features ("Included Features"). Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of Included Features.
+
+
+The terms and conditions governing Plug-ins and Fragments should be contained in files named "about.html" ("Abouts"). The terms and conditions governing Features and
+Included Features should be contained in files named "license.html" ("Feature Licenses"). Abouts and Feature Licenses may be located in any directory of a Download or Module
+including, but not limited to the following locations:
+
+
+ - The top-level (root) directory
+ - Plug-in and Fragment directories
+ - Inside Plug-ins and Fragments packaged as JARs
+ - Sub-directories of the directory named "src" of certain Plug-ins
+ - Feature directories
+
+
+Note: if a Feature made available by the Eclipse Foundation is installed using the Provisioning Technology (as defined below), you must agree to a license ("Feature Update License") during the
+installation process. If the Feature contains Included Features, the Feature Update License should either provide you with the terms and conditions governing the Included Features or
+inform you where you can locate them. Feature Update Licenses may be found in the "license" property of files named "feature.properties" found within a Feature.
+Such Abouts, Feature Licenses, and Feature Update Licenses contain the terms and conditions (or references to such terms and conditions) that govern your use of the associated Content in
+that directory.
+
+THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE
+OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):
+
+
+
+IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License is provided, please
+contact the Eclipse Foundation to determine what terms and conditions govern that particular Content.
+
+
+Use of Provisioning Technology
+
+The Eclipse Foundation makes available provisioning software, examples of which include, but are not limited to, p2 and the Eclipse
+ Update Manager ("Provisioning Technology") for the purpose of allowing users to install software, documentation, information and/or
+ other materials (collectively "Installable Software"). This capability is provided with the intent of allowing such users to
+ install, extend and update Eclipse-based products. Information about packaging Installable Software is available at http://eclipse.org/equinox/p2/repository_packaging.html
+ ("Specification").
+
+You may use Provisioning Technology to allow other parties to install Installable Software. You shall be responsible for enabling the
+ applicable license agreements relating to the Installable Software to be presented to, and accepted by, the users of the Provisioning Technology
+ in accordance with the Specification. By using Provisioning Technology in such a manner and making it available in accordance with the
+ Specification, you further acknowledge your agreement to, and the acquisition of all necessary rights to permit the following:
+
+
+ - A series of actions may occur ("Provisioning Process") in which a user may execute the Provisioning Technology
+ on a machine ("Target Machine") with the intent of installing, extending or updating the functionality of an Eclipse-based
+ product.
+ - During the Provisioning Process, the Provisioning Technology may cause third party Installable Software or a portion thereof to be
+ accessed and copied to the Target Machine.
+ - Pursuant to the Specification, you will provide to the user the terms and conditions that govern the use of the Installable
+ Software ("Installable Software Agreement") and such Installable Software Agreement shall be accessed from the Target
+ Machine in accordance with the Specification. Such Installable Software Agreement must inform the user of the terms and conditions that govern
+ the Installable Software and must solicit acceptance by the end user in the manner prescribed in such Installable Software Agreement. Upon such
+ indication of agreement by the user, the provisioning Technology will complete installation of the Installable Software.
+
+
+Cryptography
+
+Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to
+ another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import,
+ possession, or use, and re-export of encryption software, to see if this is permitted.
+
+Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.
+
+
diff --git a/tests/TLV/CMakeLists.txt b/tests/TLV/CMakeLists.txt
new file mode 100644
index 000000000..94b2a0d76
--- /dev/null
+++ b/tests/TLV/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required (VERSION 2.6)
+
+project (tlvdecode)
+
+include_directories ("${PROJECT_SOURCE_DIR}/../..")
+
+SET(SOURCES decode.c)
+SET(CORE_SOURCES ${PROJECT_SOURCE_DIR}/../../core/tlv.c)
+
+add_executable(tlvdecode ${SOURCES} ${CORE_SOURCES})
diff --git a/tests/TLV/decode.c b/tests/TLV/decode.c
new file mode 100644
index 000000000..b5f4243cd
--- /dev/null
+++ b/tests/TLV/decode.c
@@ -0,0 +1,244 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+
+#include "core/liblwm2m.h"
+
+#include
+#include
+#include
+#include
+
+static void prv_output_buffer(uint8_t * buffer,
+ int length)
+{
+ int i;
+ uint8_t array[16];
+
+ if (length == 0 || length > 16) printf("\n");
+
+ i = 0;
+ while (i < length)
+ {
+ int j;
+ printf(" ");
+
+ memcpy(array, buffer+i, 16);
+
+ for (j = 0 ; j < 16 && i+j < length; j++)
+ {
+ printf("%02X ", array[j]);
+ }
+ if (length > 16)
+ {
+ while (j < 16)
+ {
+ printf(" ");
+ j++;
+ }
+ }
+ printf(" ");
+ for (j = 0 ; j < 16 && i+j < length; j++)
+ {
+ if (isprint(array[j]))
+ printf("%c ", array[j]);
+ else
+ printf(". ");
+ }
+ printf("\n");
+
+ i += 16;
+ }
+}
+
+void print_indent(int num)
+{
+ int i;
+
+ for ( i = 0 ; i< num ; i++)
+ printf("\t");
+}
+
+void dump_tlv(int size,
+ lwm2m_tlv_t * tlvP,
+ int indent)
+{
+ int i;
+
+ for(i= 0 ; i < size ; i++)
+ {
+ print_indent(indent);
+ printf("type: ");
+ switch (tlvP[i].type)
+ {
+ case LWM2M_TYPE_OBJECT_INSTANCE:
+ printf("TLV_OBJECT_INSTANCE\r\n");
+ break;
+ case LWM2M_TYPE_RESSOURCE_INSTANCE:
+ printf("TLV_RESSOURCE_INSTANCE\r\n");
+ break;
+ case LWM2M_TYPE_MULTIPLE_RESSOURCE:
+ printf("TLV_MULTIPLE_INSTANCE\r\n");
+ break;
+ case LWM2M_TYPE_RESSOURCE:
+ printf("TLV_RESSOURCE\r\n");
+ break;
+ default:
+ printf("unknown (%d)\r\n", (int)tlvP[i].type);
+ break;
+ }
+ print_indent(indent);
+ printf("id: %d\r\n", tlvP[i].id);
+ print_indent(indent);
+ printf("data (%d bytes): ", tlvP[i].length);
+ prv_output_buffer(tlvP[i].value, tlvP[i].length);
+ if (tlvP[i].type == LWM2M_TYPE_OBJECT_INSTANCE
+ || tlvP[i].type == LWM2M_TYPE_MULTIPLE_RESSOURCE)
+ {
+ dump_tlv(tlvP[i].length, (lwm2m_tlv_t *)(tlvP[i].value), indent+1);
+ }
+ else if (tlvP[i].length <= 8)
+ {
+ int64_t value;
+ if (0 != lwm2m_opaqueToInt(tlvP[i].value, tlvP[i].length, &value))
+ {
+ print_indent(indent);
+ printf(" as int: %ld\r\n", value);
+ }
+ }
+ }
+}
+
+void decode(char * buffer,
+ size_t buffer_len,
+ int indent)
+{
+ lwm2m_tlv_type_t type;
+ uint16_t id;
+ size_t dataIndex;
+ size_t dataLen;
+ int length = 0;
+ int result;
+
+ while (0 != (result = lwm2m_decodeTLV(buffer + length, buffer_len - length, &type, &id, &dataIndex, &dataLen)))
+ {
+ print_indent(indent);
+ printf("type: ");
+ switch (type)
+ {
+ case TLV_OBJECT_INSTANCE:
+ printf("TLV_OBJECT_INSTANCE\r\n");
+ break;
+ case TLV_RESSOURCE_INSTANCE:
+ printf("TLV_RESSOURCE_INSTANCE\r\n");
+ break;
+ case TLV_MULTIPLE_INSTANCE:
+ printf("TLV_MULTIPLE_INSTANCE\r\n");
+ break;
+ case TLV_RESSOURCE:
+ printf("TLV_RESSOURCE\r\n");
+ break;
+ default:
+ printf("unknown (%d)\r\n", (int)type);
+ break;
+ }
+ print_indent(indent);
+ printf("id: %d\r\n", id);
+ print_indent(indent);
+ printf("data (%d bytes): ", dataLen);
+ prv_output_buffer(buffer + length + dataIndex, dataLen);
+ if (type == TLV_OBJECT_INSTANCE || type == TLV_MULTIPLE_INSTANCE)
+ {
+ decode(buffer + length + dataIndex, dataLen, indent+1);
+ }
+ else if (dataLen <= 8)
+ {
+ int64_t value;
+ if (0 != lwm2m_opaqueToInt(buffer + length + dataIndex, dataLen, &value))
+ {
+ print_indent(indent);
+ printf(" as int: %ld\r\n", value);
+ }
+ }
+
+ length += result;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ lwm2m_tlv_t * tlvP;
+ int size;
+ int length;
+ char * buffer;
+
+ char buffer1[] = {0x03, 0x0A, 0xC1, 0x01, 0x14, 0x03, 0x0B, 0xC1, 0x01, 0x15, 0x03, 0x0C, 0xC1, 0x01, 0x16};
+ char buffer2[] = {0xC8, 0x00, 0x14, 0x4F, 0x70, 0x65, 0x6E, 0x20, 0x4D, 0x6F, 0x62, 0x69, 0x6C, 0x65, 0x20,
+ 0x41, 0x6C, 0x6C, 0x69, 0x61, 0x6E, 0x63, 0x65, 0xC8, 0x01, 0x16, 0x4C, 0x69, 0x67, 0x68,
+ 0x74, 0x77 , 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x4D, 0x32, 0x4D, 0x20, 0x43, 0x6C, 0x69,
+ 0x65, 0x6E, 0x74 , 0xC8, 0x02, 0x09, 0x33, 0x34, 0x35, 0x30, 0x30, 0x30, 0x31, 0x32, 0x33,
+ 0xC3, 0x03, 0x31, 0x2E , 0x30, 0x86, 0x06, 0x41, 0x00, 0x01, 0x41, 0x01, 0x05, 0x88, 0x07,
+ 0x08, 0x42, 0x00, 0x0E, 0xD8 , 0x42, 0x01, 0x13, 0x88, 0x87, 0x08, 0x41, 0x00, 0x7D, 0x42,
+ 0x01, 0x03, 0x84, 0xC1, 0x09, 0x64 , 0xC1, 0x0A, 0x0F, 0x83, 0x0B, 0x41, 0x00, 0x00, 0xC4,
+ 0x0D, 0x51, 0x82, 0x42, 0x8F, 0xC6, 0x0E, 0x2B, 0x30, 0x32, 0x3A, 0x30, 0x30, 0xC1, 0x0F, 0x55};
+
+ printf("Buffer 1:\n");
+ decode(buffer1, sizeof(buffer1), 0);
+ printf("\n\nBuffer 1 using lwm2m_tlv_t:\n");
+ size = lwm2m_tlv_parse(buffer1, sizeof(buffer1), &tlvP);
+ dump_tlv(size, tlvP, 0);
+ length = lwm2m_tlv_serialize(size, tlvP, &buffer);
+ if (length != sizeof(buffer1))
+ {
+ printf("\n\nSerialize Buffer 1 failed: %d bytes instead of %d\n", length, sizeof(buffer1));
+ }
+ else if (memcmp(buffer, buffer1, length) != 0)
+ {
+ printf("\n\nSerialize Buffer 1 failed:\n");
+ prv_output_buffer(buffer, length);
+ printf("\ninstead of:\n");
+ prv_output_buffer(buffer1, length);
+ }
+ else
+ {
+ printf("\n\nSerialize Buffer 1 OK\n");
+ }
+ lwm2m_tlv_free(size, tlvP);
+
+ printf("\n\n============\n\nBuffer 2: \r\r\n");
+ decode(buffer2, sizeof(buffer2), 0);
+ printf("\n\nBuffer 2 using lwm2m_tlv_t: \r\r\n");
+ size = lwm2m_tlv_parse(buffer2, sizeof(buffer2), &tlvP);
+ dump_tlv(size, tlvP, 0);
+ length = lwm2m_tlv_serialize(size, tlvP, &buffer);
+ if (length != sizeof(buffer2))
+ {
+ printf("\n\nSerialize Buffer 2 failed: %d bytes instead of %d\n", length, sizeof(buffer2));
+ }
+ else if (memcmp(buffer, buffer2, length) != 0)
+ {
+ printf("\n\nSerialize Buffer 2 failed:\n");
+ prv_output_buffer(buffer, length);
+ printf("\ninstead of:\n");
+ prv_output_buffer(buffer2, length);
+ }
+ else
+ {
+ printf("\n\nSerialize Buffer 2 OK\n\n");
+ }
+ lwm2m_tlv_free(size, tlvP);
+}
+
diff --git a/tests/client/CMakeLists.txt b/tests/client/CMakeLists.txt
new file mode 100644
index 000000000..acb485851
--- /dev/null
+++ b/tests/client/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required (VERSION 2.8.3)
+
+project (lwm2mclient)
+
+SET(LIBLWM2M_DIR ${PROJECT_SOURCE_DIR}/../../core)
+
+add_definitions(-DLWM2M_CLIENT_MODE)
+
+include_directories (${LIBLWM2M_DIR} ${PROJECT_SOURCE_DIR}/../utils)
+
+add_subdirectory(${LIBLWM2M_DIR} ${CMAKE_CURRENT_BINARY_DIR}/core)
+
+SET(SOURCES lwm2mclient.c ../utils/commandline.c ../utils/connection.c object_device.c object_firmware.c test_object.c)
+
+add_executable(lwm2mclient ${SOURCES} ${CORE_SOURCES})
diff --git a/tests/client/lwm2mclient.c b/tests/client/lwm2mclient.c
new file mode 100644
index 000000000..6556f0f26
--- /dev/null
+++ b/tests/client/lwm2mclient.c
@@ -0,0 +1,483 @@
+/*******************************************************************************
+ *
+ * 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
+ * Benjamin Cabé - Please refer to git log
+ * Fabien Fleutot - Please refer to git log
+ * Simon Bernard - Please refer to git log
+ * Julien Vermillard - Please refer to git log
+ * Axel Lorente - Please refer to git log
+ * 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
+
+*/
+
+
+#include "liblwm2m.h"
+#include "commandline.h"
+#include "connection.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MAX_PACKET_SIZE 128
+
+static int g_quit = 0;
+
+extern lwm2m_object_t * get_object_device();
+extern lwm2m_object_t * get_object_firmware();
+extern lwm2m_object_t * get_test_object();
+
+static void prv_quit(char * buffer,
+ void * user_data)
+{
+ g_quit = 1;
+}
+
+void handle_sigint(int signum)
+{
+ g_quit = 2;
+}
+
+void print_usage(void)
+{
+ fprintf(stderr, "Usage: lwm2mclient\r\n");
+ fprintf(stderr, "Launch a LWM2M client.\r\n\n");
+}
+
+static uint8_t prv_buffer_send(void * sessionH,
+ uint8_t * buffer,
+ size_t length,
+ void * userdata)
+{
+ connection_t * connP = (connection_t*) sessionH;
+
+ if (-1 == connection_send(connP, buffer, length))
+ {
+ return COAP_500_INTERNAL_SERVER_ERROR;
+ }
+ return COAP_NO_ERROR;
+}
+
+static void prv_output_servers(char * buffer,
+ void * user_data)
+{
+ lwm2m_context_t * lwm2mH = (lwm2m_context_t *) user_data;
+ lwm2m_server_t * targetP;
+
+ targetP = lwm2mH->serverList;
+
+ if (targetP == NULL)
+ {
+ fprintf(stdout, "No server.\r\n");
+ return;
+ }
+
+ for (targetP = lwm2mH->serverList ; targetP != NULL ; targetP = targetP->next)
+ {
+ fprintf(stdout, "Server ID %d:\r\n", targetP->shortID);
+ fprintf(stdout, "\tstatus: ");
+ switch(targetP->status)
+ {
+ case STATE_UNKNOWN:
+ fprintf(stdout, "UNKNOWN\r\n");
+ break;
+ case STATE_REG_PENDING:
+ fprintf(stdout, "REGISTRATION PENDING\r\n");
+ break;
+ case STATE_REGISTERED:
+ fprintf(stdout, "REGISTERED location: \"%s\"\r\n", targetP->location);
+ break;
+ }
+ fprintf(stdout, "\r\n");
+ }
+}
+
+static void prv_change(char * buffer,
+ void * user_data)
+{
+ lwm2m_context_t * lwm2mH = (lwm2m_context_t *) user_data;
+ lwm2m_uri_t uri;
+ int result;
+ size_t length;
+
+ if (buffer[0] == 0) goto syntax_error;
+
+ length = 0;
+ // remove white space
+ while (length < strlen(buffer) && isspace(buffer[length]))
+ {
+ length++;
+ }
+ // find end of URI
+ while (length < strlen(buffer) && !isspace(buffer[length]))
+ {
+ length++;
+ }
+
+ result = lwm2m_stringToUri(buffer, length, &uri);
+ if (result == 0) goto syntax_error;
+
+ buffer += length;
+ while (buffer[0] != 0 && isspace(buffer[0])) buffer++;
+ if (buffer[0] == 0)
+ {
+ lwm2m_resource_value_changed(lwm2mH, &uri);
+ }
+ else
+ {
+ int i;
+
+ i = 0;
+ while (i < lwm2mH->numObject)
+ {
+ if (uri.objectId == lwm2mH->objectList[i]->objID)
+ {
+ if (lwm2mH->objectList[i]->writeFunc != NULL)
+ {
+ lwm2m_tlv_t * tlvP;
+
+ tlvP = lwm2m_tlv_new(1);
+ if (tlvP == NULL)
+ {
+ fprintf(stdout, "Internal allocation failure !\n");
+ return;
+ }
+ tlvP->flags = LWM2M_TLV_FLAG_STATIC_DATA | LWM2M_TLV_FLAG_TEXT_FORMAT;
+ tlvP->id = uri.resourceId;
+ tlvP->length = strlen(buffer);
+ tlvP->value = buffer;
+
+ if (COAP_204_CHANGED != lwm2mH->objectList[i]->writeFunc(uri.instanceId,
+ 1, tlvP,
+ lwm2mH->objectList[i]))
+ {
+ fprintf(stdout, "Failed to change value !\n");
+ }
+ else
+ {
+ lwm2m_resource_value_changed(lwm2mH, &uri);
+ }
+ lwm2m_tlv_free(1, tlvP);
+ return;
+ }
+ return;
+ }
+ i++;
+ }
+
+ fprintf(stdout, "Object not found !\n");
+ }
+ return;
+
+syntax_error:
+ fprintf(stdout, "Syntax error !\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int sock;
+ int result;
+ lwm2m_context_t * lwm2mH = NULL;
+ lwm2m_object_t * objArray[3];
+ lwm2m_security_t security;
+ int i;
+ connection_t * connList;
+
+ connList = NULL;
+
+ /*
+ * The function start by setting up the command line interface (which may or not be useful depending on your project)
+ *
+ * This is an array of commands describes as { name, description, long description, callback, userdata }.
+ * The firsts tree are easy to understand, the callback is the function that will be called when this command is typed
+ * and in the last one will be stored the lwm2m context (allowing access to the server settings and the objects).
+ */
+ command_desc_t commands[] =
+ {
+ {"list", "List known servers.", NULL, prv_output_servers, NULL},
+ {"change", "Change the value of resource.", " change URI [DATA]\r\n"
+ " URI: uri of the resource such as /3/0, /3/0/2\r\n"
+ " DATA: (optional) new value\r\n", prv_change, NULL},
+ {"quit", "Quit the client gracefully.", NULL, prv_quit, NULL},
+ {"^C", "Quit the client abruptly (without sending a de-register message).", NULL, NULL, NULL},
+
+ COMMAND_END_LIST
+ };
+
+ /*
+ *This call an internal function that create an IPV6 socket on the port 5683.
+ */
+ sock = create_socket("5683");
+ if (sock < 0)
+ {
+ fprintf(stderr, "Failed to open socket: %d\r\n", errno);
+ return -1;
+ }
+
+ /*
+ * Now the main function fill an array with each object, this list will be later passed to liblwm2m.
+ * Those functions are located in their respective object file.
+ */
+ objArray[0] = get_object_device();
+ if (NULL == objArray[0])
+ {
+ fprintf(stderr, "Failed to create Device object\r\n");
+ return -1;
+ }
+
+ objArray[1] = get_object_firmware();
+ if (NULL == objArray[1])
+ {
+ fprintf(stderr, "Failed to create Firmware object\r\n");
+ return -1;
+ }
+
+ objArray[2] = get_test_object();
+ if (NULL == objArray[2])
+ {
+ fprintf(stderr, "Failed to create test object\r\n");
+ return -1;
+ }
+
+ /*
+ * The liblwm2m library is now initialized with the name of the client - which shall be unique for each client -
+ * the number of objects we will be passing through, the object constructor array and the function that will be in
+ * charge to send the buffer (containing the LWM2M packets) to the network
+ */
+ lwm2mH = lwm2m_init("testlwm2mclient", 3, objArray, prv_buffer_send, NULL);
+ if (NULL == lwm2mH)
+ {
+ fprintf(stderr, "lwm2m_init() failed\r\n");
+ return -1;
+ }
+
+ signal(SIGINT, handle_sigint);
+
+ connList = connection_create(connList, sock, "localhost", LWM2M_STANDARD_PORT);
+ if (connList == NULL)
+ {
+ fprintf(stderr, "Connection creation failed.\r\n");
+ return -1;
+ }
+
+ memset(&security, 0, sizeof(lwm2m_security_t));
+
+ /*
+ * This function add a server to the lwm2m context by passing an identifier, an opaque connection handler and a security
+ * context.
+ * You can add as many server as your application need and there will be thereby allowed to interact with your object
+ */
+ result = lwm2m_add_server(lwm2mH, 123, (void *)connList, &security);
+ if (result != 0)
+ {
+ fprintf(stderr, "lwm2m_add_server() failed: 0x%X\r\n", result);
+ return -1;
+ }
+
+ /*
+ * This function register your client to all the servers you added with the precedent one
+ */
+ result = lwm2m_register(lwm2mH);
+ if (result != 0)
+ {
+ fprintf(stderr, "lwm2m_register() failed: 0x%X\r\n", result);
+ return -1;
+ }
+
+ /*
+ * As you now have your lwm2m context complete you can pass it as an argument to all the command line functions
+ * precedently viewed (first point)
+ */
+ for (i = 0 ; commands[i].name != NULL ; i++)
+ {
+ commands[i].userData = (void *)lwm2mH;
+ }
+ fprintf(stdout, "> "); fflush(stdout);
+
+ /*
+ * We now enter in a while loop that will handle the communications from the server
+ */
+ while (0 == g_quit)
+ {
+ struct timeval tv;
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+ FD_SET(sock, &readfds);
+ FD_SET(STDIN_FILENO, &readfds);
+
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+
+ /*
+ * This function does two things:
+ * - first it does the work needed by liblwm2m (eg. (re)sending some packets).
+ * - Secondly it adjust the timeout value (default 60s) depending on the state of the transaction
+ * (eg. retransmission) and the time between the next operation
+ */
+ result = lwm2m_step(lwm2mH, &tv);
+ if (result != 0)
+ {
+ fprintf(stderr, "lwm2m_step() failed: 0x%X\r\n", result);
+ return -1;
+ }
+
+ /*
+ * This part will set up an interruption until an event happen on SDTIN or the socket until "tv" timed out (set
+ * with the precedent function)
+ */
+ result = select(FD_SETSIZE, &readfds, NULL, NULL, &tv);
+
+ if ( result < 0 )
+ {
+ if (errno != EINTR)
+ {
+ fprintf(stderr, "Error in select(): %d\r\n", errno);
+ }
+ }
+ else if (result > 0)
+ {
+ uint8_t buffer[MAX_PACKET_SIZE];
+ int numBytes;
+
+ /*
+ * If an event happen on the socket
+ */
+ if (FD_ISSET(sock, &readfds))
+ {
+ struct sockaddr_storage addr;
+ socklen_t addrLen;
+
+ addrLen = sizeof(addr);
+
+ /*
+ * We retrieve the data received
+ */
+ numBytes = recvfrom(sock, buffer, MAX_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrLen);
+
+ if (numBytes == -1)
+ {
+ fprintf(stderr, "Error in recvfrom(): %d\r\n", errno);
+ }
+ else
+ {
+ char s[INET6_ADDRSTRLEN];
+ connection_t * connP;
+
+ fprintf(stderr, "%d bytes received from [%s]:%hu\r\n",
+ numBytes,
+ inet_ntop(addr.ss_family,
+ &(((struct sockaddr_in6*)&addr)->sin6_addr),
+ s,
+ INET6_ADDRSTRLEN),
+ ntohs(((struct sockaddr_in6*)&addr)->sin6_port));
+
+ /*
+ * Display it in the STDERR
+ */
+ output_buffer(stderr, buffer, numBytes);
+
+ connP = connection_find(connList, &addr, addrLen);
+ if (connP != NULL)
+ {
+ /*
+ * Let liblwm2m respond to the query depending on the context
+ */
+ lwm2m_handle_packet(lwm2mH, buffer, numBytes, connP);
+ }
+ }
+ }
+
+ /*
+ * If the event happened on the SDTIN
+ */
+ else if (FD_ISSET(STDIN_FILENO, &readfds))
+ {
+ numBytes = read(STDIN_FILENO, buffer, MAX_PACKET_SIZE);
+
+ if (numBytes > 1)
+ {
+ buffer[numBytes - 1] = 0;
+
+ /*
+ * We call the corresponding callback of the typed command passing it the buffer for further arguments
+ */
+ handle_command(commands, buffer);
+ }
+ if (g_quit == 0)
+ {
+ fprintf(stdout, "\r\n> ");
+ fflush(stdout);
+ }
+ else
+ {
+ fprintf(stdout, "\r\n");
+ }
+ }
+ }
+ }
+
+ /*
+ * Finally when the loop is left smoothly - asked by user in the command line interface - we unregister our client from it
+ */
+ if (g_quit == 1)
+ {
+ lwm2m_close(lwm2mH);
+ }
+ close(sock);
+ connection_free(connList);
+
+ return 0;
+}
diff --git a/tests/client/object_device.c b/tests/client/object_device.c
new file mode 100644
index 000000000..08042c603
--- /dev/null
+++ b/tests/client/object_device.c
@@ -0,0 +1,490 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+
+/*
+ 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
+
+*/
+
+/*
+ * This object is single instance only, and is mandatory to all LWM2M device as it describe the object such as its
+ * manufacturer, model, etc...
+ */
+
+#include "liblwm2m.h"
+
+#include
+#include
+#include
+#include
+
+
+#define PRV_MANUFACTURER "Open Mobile Alliance"
+#define PRV_MODEL_NUMBER "Lightweight M2M Client"
+#define PRV_SERIAL_NUMBER "345000123"
+#define PRV_FIRMWARE_VERSION "1.0"
+#define PRV_POWER_SOURCE_1 1
+#define PRV_POWER_SOURCE_2 5
+#define PRV_POWER_VOLTAGE_1 3800
+#define PRV_POWER_VOLTAGE_2 5000
+#define PRV_POWER_CURRENT_1 125
+#define PRV_POWER_CURRENT_2 900
+#define PRV_BATTERY_LEVEL 100
+#define PRV_MEMORY_FREE 15
+#define PRV_ERROR_CODE 0
+#define PRV_BINDING_MODE "U"
+
+#define PRV_OFFSET_MAXLEN 7 //+HH:MM\0 at max
+#define PRV_TLV_BUFFER_SIZE 128
+
+typedef struct
+{
+ int64_t time;
+ char time_offset[PRV_OFFSET_MAXLEN];
+} device_data_t;
+
+
+// basic check that the time offset value is at ISO 8601 format
+// bug: +12:30 is considered a valid value by this function
+static int prv_check_time_offset(char * buffer,
+ int length)
+{
+ int min_index;
+
+ if (length != 3 && length != 5 && length != 6) return 0;
+ if (buffer[0] != '-' && buffer[0] != '+') return 0;
+ switch (buffer[1])
+ {
+ case '0':
+ if (buffer[2] < '0' || buffer[2] > '9') return 0;
+ break;
+ case '1':
+ if (buffer[2] < '0' || buffer[2] > '2') return 0;
+ break;
+ default:
+ return 0;
+ }
+ switch (length)
+ {
+ case 3:
+ return 1;
+ case 5:
+ min_index = 3;
+ break;
+ case 6:
+ if (buffer[3] != ':') return 0;
+ min_index = 4;
+ break;
+ default:
+ // never happen
+ return 0;
+ }
+ if (buffer[min_index] < '0' || buffer[min_index] > '5') return 0;
+ if (buffer[min_index+1] < '0' || buffer[min_index+1] > '9') return 0;
+
+ return 1;
+}
+
+static uint8_t prv_set_value(lwm2m_tlv_t * tlvP,
+ device_data_t * devDataP)
+{
+ // a simple switch structure is used to respond at the specified resource asked
+ switch (tlvP->id)
+ {
+ case 0:
+ tlvP->value = PRV_MANUFACTURER;
+ tlvP->length = strlen(PRV_MANUFACTURER);
+ tlvP->flags = LWM2M_TLV_FLAG_STATIC_DATA;
+ tlvP->type = LWM2M_TYPE_RESSOURCE;
+ return COAP_205_CONTENT;
+
+ case 1:
+ tlvP->value = PRV_MODEL_NUMBER;
+ tlvP->length = strlen(PRV_MODEL_NUMBER);
+ tlvP->flags = LWM2M_TLV_FLAG_STATIC_DATA;
+ tlvP->type = LWM2M_TYPE_RESSOURCE;
+ return COAP_205_CONTENT;
+
+ case 2:
+ tlvP->value = PRV_SERIAL_NUMBER;
+ tlvP->length = strlen(PRV_SERIAL_NUMBER);
+ tlvP->flags = LWM2M_TLV_FLAG_STATIC_DATA;
+ tlvP->type = LWM2M_TYPE_RESSOURCE;
+ return COAP_205_CONTENT;
+
+ case 3:
+ tlvP->value = PRV_FIRMWARE_VERSION;
+ tlvP->length = strlen(PRV_FIRMWARE_VERSION);
+ tlvP->flags = LWM2M_TLV_FLAG_STATIC_DATA;
+ tlvP->type = LWM2M_TYPE_RESSOURCE;
+ return COAP_205_CONTENT;
+
+ case 4:
+ return COAP_405_METHOD_NOT_ALLOWED;
+
+ case 5:
+ return COAP_405_METHOD_NOT_ALLOWED;
+
+ case 6:
+ {
+ lwm2m_tlv_t * subTlvP;
+
+ subTlvP = lwm2m_tlv_new(2);
+
+ subTlvP[0].flags = 0;
+ subTlvP[0].id = 0;
+ subTlvP[0].type = LWM2M_TYPE_RESSOURCE_INSTANCE;
+ lwm2m_tlv_encode_int(PRV_POWER_SOURCE_1, subTlvP);
+ if (0 == subTlvP[0].length)
+ {
+ lwm2m_tlv_free(2, subTlvP);
+ return COAP_500_INTERNAL_SERVER_ERROR;
+ }
+
+ subTlvP[1].flags = 0;
+ subTlvP[1].id = 1;
+ subTlvP[1].type = LWM2M_TYPE_RESSOURCE_INSTANCE;
+ lwm2m_tlv_encode_int(PRV_POWER_SOURCE_2, subTlvP + 1);
+ if (0 == subTlvP[1].length)
+ {
+ lwm2m_tlv_free(2, subTlvP);
+ return COAP_500_INTERNAL_SERVER_ERROR;
+ }
+
+ tlvP->flags = 0;
+ tlvP->type = LWM2M_TYPE_MULTIPLE_RESSOURCE;
+ tlvP->length = 2;
+ tlvP->value = (uint8_t *)subTlvP;
+
+ return COAP_205_CONTENT;
+ }
+
+ case 7:
+ {
+ lwm2m_tlv_t * subTlvP;
+
+ subTlvP = lwm2m_tlv_new(2);
+
+ subTlvP[0].flags = 0;
+ subTlvP[0].id = 0;
+ subTlvP[0].type = LWM2M_TYPE_RESSOURCE_INSTANCE;
+ lwm2m_tlv_encode_int(PRV_POWER_VOLTAGE_1, subTlvP);
+ if (0 == subTlvP[0].length)
+ {
+ lwm2m_tlv_free(2, subTlvP);
+ return COAP_500_INTERNAL_SERVER_ERROR;
+ }
+
+ subTlvP[1].flags = 0;
+ subTlvP[1].id = 1;
+ subTlvP[1].type = LWM2M_TYPE_RESSOURCE_INSTANCE;
+ lwm2m_tlv_encode_int(PRV_POWER_VOLTAGE_2, subTlvP + 1);
+ if (0 == subTlvP[1].length)
+ {
+ lwm2m_tlv_free(2, subTlvP);
+ return COAP_500_INTERNAL_SERVER_ERROR;
+ }
+
+ tlvP->flags = 0;
+ tlvP->type = LWM2M_TYPE_MULTIPLE_RESSOURCE;
+ tlvP->length = 2;
+ tlvP->value = (uint8_t *)subTlvP;
+
+ return COAP_205_CONTENT;
+ }
+
+ case 8:
+ {
+ lwm2m_tlv_t * subTlvP;
+
+ subTlvP = lwm2m_tlv_new(2);
+
+ subTlvP[0].flags = 0;
+ subTlvP[0].id = 0;
+ subTlvP[0].type = LWM2M_TYPE_RESSOURCE_INSTANCE;
+ lwm2m_tlv_encode_int(PRV_POWER_CURRENT_1, subTlvP);
+ if (0 == subTlvP[0].length)
+ {
+ lwm2m_tlv_free(2, subTlvP);
+ return COAP_500_INTERNAL_SERVER_ERROR;
+ }
+
+ subTlvP[1].flags = 0;
+ subTlvP[1].id = 1;
+ subTlvP[1].type = LWM2M_TYPE_RESSOURCE_INSTANCE;
+ lwm2m_tlv_encode_int(PRV_POWER_CURRENT_2, subTlvP + 1);
+ if (0 == subTlvP[1].length)
+ {
+ lwm2m_tlv_free(2, subTlvP);
+ return COAP_500_INTERNAL_SERVER_ERROR;
+ }
+
+ tlvP->flags = 0;
+ tlvP->type = LWM2M_TYPE_MULTIPLE_RESSOURCE;
+ tlvP->length = 2;
+ tlvP->value = (uint8_t *)subTlvP;
+
+ return COAP_205_CONTENT;
+ }
+
+ case 9:
+ lwm2m_tlv_encode_int(PRV_BATTERY_LEVEL, tlvP);
+ tlvP->type = LWM2M_TYPE_RESSOURCE;
+
+ if (0 != tlvP->length) return COAP_205_CONTENT;
+ else return COAP_500_INTERNAL_SERVER_ERROR;
+
+ case 10:
+ lwm2m_tlv_encode_int(PRV_MEMORY_FREE, tlvP);
+ tlvP->type = LWM2M_TYPE_RESSOURCE;
+
+ if (0 != tlvP->length) return COAP_205_CONTENT;
+ else return COAP_500_INTERNAL_SERVER_ERROR;
+
+ case 11:
+ lwm2m_tlv_encode_int(PRV_ERROR_CODE, tlvP);
+ tlvP->type = LWM2M_TYPE_RESSOURCE;
+
+ if (0 != tlvP->length) return COAP_205_CONTENT;
+ else return COAP_500_INTERNAL_SERVER_ERROR;
+
+ case 12:
+ return COAP_405_METHOD_NOT_ALLOWED;
+
+ case 13:
+ lwm2m_tlv_encode_int(devDataP->time, tlvP);
+ tlvP->type = LWM2M_TYPE_RESSOURCE;
+
+ if (0 != tlvP->length) return COAP_205_CONTENT;
+ else return COAP_500_INTERNAL_SERVER_ERROR;
+
+ case 14:
+ tlvP->value = devDataP->time_offset;
+ tlvP->length = strlen(devDataP->time_offset);
+ tlvP->flags = LWM2M_TLV_FLAG_STATIC_DATA;
+ tlvP->type = LWM2M_TYPE_RESSOURCE;
+ return COAP_205_CONTENT;
+
+ case 15:
+ tlvP->value = PRV_BINDING_MODE;
+ tlvP->length = strlen(PRV_BINDING_MODE);
+ tlvP->flags = LWM2M_TLV_FLAG_STATIC_DATA;
+ tlvP->type = LWM2M_TYPE_RESSOURCE;
+ return COAP_205_CONTENT;
+
+ default:
+ return COAP_404_NOT_FOUND;
+ }
+}
+
+static uint8_t prv_device_read(uint16_t instanceId,
+ int * numDataP,
+ lwm2m_tlv_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[] = {0, 1, 2, 3, 6, 7, 8, 9, 10, 11, 13, 14, 15};
+ int nbRes = sizeof(resList)/sizeof(uint16_t);
+
+ *dataArrayP = lwm2m_tlv_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, (device_data_t*)(objectP->userData));
+ i++;
+ } while (i < *numDataP && result == COAP_205_CONTENT);
+
+ return result;
+}
+
+static uint8_t prv_device_write(uint16_t instanceId,
+ int numData,
+ lwm2m_tlv_t * dataArray,
+ lwm2m_object_t * objectP)
+{
+ int i;
+ uint8_t result;
+
+ // this is a single instance object
+ if (instanceId != 0)
+ {
+ return COAP_404_NOT_FOUND;
+ }
+
+ i = 0;
+
+ do
+ {
+ switch (dataArray[i].id)
+ {
+ case 13:
+ if (1 == lwm2m_tlv_decode_int(dataArray + i, &((device_data_t*)(objectP->userData))->time))
+ {
+ result = COAP_204_CHANGED;
+ }
+ else
+ {
+ result = COAP_400_BAD_REQUEST;
+ }
+ break;
+
+ case 14:
+ if (1 == prv_check_time_offset(dataArray[i].value, dataArray[i].length))
+ {
+ strncpy(((device_data_t*)(objectP->userData))->time_offset, dataArray[i].value, dataArray[i].length);
+ ((device_data_t*)(objectP->userData))->time_offset[dataArray[i].length] = 0;
+ result = COAP_204_CHANGED;
+ }
+ else
+ {
+ result = COAP_400_BAD_REQUEST;
+ }
+ break;
+
+ default:
+ result = COAP_405_METHOD_NOT_ALLOWED;
+ }
+
+ i++;
+ } while (i < numData && result == COAP_204_CHANGED);
+
+ return result;
+}
+
+static uint8_t prv_device_execute(uint16_t instanceId,
+ uint16_t resourceId,
+ char * 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;
+
+ switch (resourceId)
+ {
+ case 4:
+ fprintf(stdout, "\n\t REBOOT\r\n\n");
+ return COAP_204_CHANGED;
+ case 5:
+ fprintf(stdout, "\n\t FACTORY RESET\r\n\n");
+ return COAP_204_CHANGED;
+ case 12:
+ fprintf(stdout, "\n\t RESET ERROR CODE\r\n\n");
+ return COAP_204_CHANGED;
+ default:
+ 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 *)malloc(sizeof(lwm2m_object_t));
+
+ if (NULL != deviceObj)
+ {
+ memset(deviceObj, 0, sizeof(lwm2m_object_t));
+
+ /*
+ * It assign his unique ID
+ * The 3 is the standard ID for the mandatory object "Object device".
+ */
+ deviceObj->objID = 3;
+
+ /*
+ * 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->writeFunc = prv_device_write;
+ deviceObj->executeFunc = prv_device_execute;
+ deviceObj->userData = malloc(sizeof(device_data_t));
+
+ /*
+ * Also some user data can be stored in the object with a private structure containing the needed variables
+ */
+ if (NULL != deviceObj->userData)
+ {
+ ((device_data_t*)deviceObj->userData)->time = 1367491215;
+ strcpy(((device_data_t*)deviceObj->userData)->time_offset, "+02:00");
+ }
+ else
+ {
+ free(deviceObj);
+ deviceObj = NULL;
+ }
+ }
+
+ return deviceObj;
+}
diff --git a/tests/client/object_firmware.c b/tests/client/object_firmware.c
new file mode 100644
index 000000000..0faa66abf
--- /dev/null
+++ b/tests/client/object_firmware.c
@@ -0,0 +1,271 @@
+/*******************************************************************************
+ *
+ * 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:
+ * Julien Vermillard - initial implementation
+ * Fabien Fleutot - Please refer to git log
+ * David Navarro, Intel Corporation - Please refer to git log
+ *
+ *******************************************************************************/
+
+/*
+ * This object is single instance only, and provide firmware upgrade functionality.
+ * Object ID is 5.
+ */
+
+/*
+ * resources:
+ * 0 package write
+ * 1 package url write
+ * 2 update exec
+ * 3 state read
+ * 4 update supported objects read/write
+ * 5 update result read
+ */
+
+#include "liblwm2m.h"
+
+
+#include
+#include
+#include
+#include
+
+#define PRV_TLV_BUFFER_SIZE 128
+
+typedef struct
+{
+ uint8_t state;
+ uint8_t supported;
+ uint8_t result;
+} firmware_data_t;
+
+
+static uint8_t prv_firmware_read(uint16_t instanceId,
+ int * numDataP,
+ lwm2m_tlv_t ** dataArrayP,
+ lwm2m_object_t * objectP)
+{
+ int i;
+ uint8_t result;
+ firmware_data_t * data = (firmware_data_t*)(objectP->userData);
+
+ // 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[] = {0, 1, 2, 3, 6, 7, 8, 9, 10, 11, 13, 14, 15};
+ int nbRes = sizeof(resList)/sizeof(uint16_t);
+
+ *dataArrayP = lwm2m_tlv_new(3);
+ if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+ *numDataP = 3;
+ (*dataArrayP)[0].id = 3;
+ (*dataArrayP)[1].id = 4;
+ (*dataArrayP)[2].id = 5;
+ }
+
+ i = 0;
+ do
+ {
+ switch ((*dataArrayP)[i].id)
+ {
+ case 0:
+ case 1:
+ case 2:
+ result = COAP_405_METHOD_NOT_ALLOWED;
+ break;
+
+ case 3:
+ // firmware update state (int)
+ lwm2m_tlv_encode_int(data->state, *dataArrayP + i);
+ (*dataArrayP)[i].type = LWM2M_TYPE_RESSOURCE;
+
+ if (0 != (*dataArrayP)[i].length) result = COAP_205_CONTENT;
+ else result = COAP_500_INTERNAL_SERVER_ERROR;
+
+ break;
+
+ case 4:
+ lwm2m_tlv_encode_int(data->supported, *dataArrayP + i);
+ (*dataArrayP)[i].type = LWM2M_TYPE_RESSOURCE;
+
+ if (0 != (*dataArrayP)[i].length) result = COAP_205_CONTENT;
+ else result = COAP_500_INTERNAL_SERVER_ERROR;
+
+ break;
+
+ case 5:
+ lwm2m_tlv_encode_int(data->result, *dataArrayP + i);
+ (*dataArrayP)[i].type = LWM2M_TYPE_RESSOURCE;
+
+ if (0 != (*dataArrayP)[i].length) result = COAP_205_CONTENT;
+ else result = COAP_500_INTERNAL_SERVER_ERROR;
+
+ break;
+
+ default:
+ result = COAP_404_NOT_FOUND;
+ }
+
+ i++;
+ } while (i < *numDataP && result == COAP_205_CONTENT);
+
+ return result;
+}
+
+static uint8_t prv_firmware_write(uint16_t instanceId,
+ int numData,
+ lwm2m_tlv_t * dataArray,
+ lwm2m_object_t * objectP)
+{
+ int i;
+ uint8_t result;
+ firmware_data_t * data = (firmware_data_t*)(objectP->userData);
+
+ // this is a single instance object
+ if (instanceId != 0)
+ {
+ return COAP_404_NOT_FOUND;
+ }
+
+ i = 0;
+
+ do
+ {
+ switch (dataArray[i].id)
+ {
+ case 0:
+ // inline firmware binary
+ result = COAP_204_CHANGED;
+ break;
+
+ case 1:
+ // URL for download the firmware
+ result = COAP_204_CHANGED;
+ break;
+
+ case 4:
+ if (1 == dataArray[i].length && dataArray[i].value[0] == '0')
+ {
+ data->supported = 0;
+ result = COAP_204_CHANGED;
+ }
+ else if (1 == dataArray[i].length && dataArray[i].value[0] == '1')
+ {
+ data->supported = 1;
+ result = COAP_204_CHANGED;
+
+ }
+ else
+ {
+ result = COAP_400_BAD_REQUEST;
+ }
+ break;
+
+ default:
+ result = COAP_405_METHOD_NOT_ALLOWED;
+ }
+
+ i++;
+ } while (i < numData && result == COAP_204_CHANGED);
+
+ return result;
+}
+
+static uint8_t prv_firmware_execute(uint16_t instanceId,
+ uint16_t resourceId,
+ char * buffer,
+ int length,
+ lwm2m_object_t * objectP)
+{
+ firmware_data_t * data = (firmware_data_t*)(objectP->userData);
+
+ // this is a single instance object
+ if (instanceId != 0)
+ {
+ return COAP_404_NOT_FOUND;
+ }
+
+ if (length != 0) return COAP_400_BAD_REQUEST;
+
+ // for execute callback, resId is always set.
+ switch (resourceId)
+ {
+ case 2:
+ if (data->state == 1)
+ {
+ fprintf(stdout, "\n\t FIRMWARE UPDATE\r\n\n");
+ // trigger your firmware download and update logic
+ data->state = 2;
+ return COAP_204_CHANGED;
+ } else {
+ // firmware update already running
+ return COAP_400_BAD_REQUEST;
+ }
+ default:
+ return COAP_405_METHOD_NOT_ALLOWED;
+ }
+}
+
+lwm2m_object_t * get_object_firmware()
+{
+ /*
+ * The get_object_firmware function create the object itself and return a pointer to the structure that represent it.
+ */
+ lwm2m_object_t * firmwareObj;
+
+ firmwareObj = (lwm2m_object_t *)malloc(sizeof(lwm2m_object_t));
+
+ if (NULL != firmwareObj)
+ {
+ memset(firmwareObj, 0, sizeof(lwm2m_object_t));
+
+ /*
+ * It assign his unique ID
+ * The 5 is the standard ID for the optional object "Object firmware".
+ */
+ firmwareObj->objID = 5;
+
+ /*
+ * 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.
+ */
+ firmwareObj->readFunc = prv_firmware_read;
+ firmwareObj->writeFunc = prv_firmware_write;
+ firmwareObj->executeFunc = prv_firmware_execute;
+ firmwareObj->userData = malloc(sizeof(firmware_data_t));
+
+ /*
+ * Also some user data can be stored in the object with a private structure containing the needed variables
+ */
+ if (NULL != firmwareObj->userData)
+ {
+ ((firmware_data_t*)firmwareObj->userData)->state = 1;
+ ((firmware_data_t*)firmwareObj->userData)->supported = 0;
+ ((firmware_data_t*)firmwareObj->userData)->result = 0;
+ }
+ else
+ {
+ free(firmwareObj);
+ firmwareObj = NULL;
+ }
+ }
+
+ return firmwareObj;
+}
diff --git a/tests/client/test_object.c b/tests/client/test_object.c
new file mode 100644
index 000000000..19faef675
--- /dev/null
+++ b/tests/client/test_object.c
@@ -0,0 +1,289 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+
+/*
+ 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
+
+*/
+
+/*
+ * Implements an object for testing purpose
+ *
+ * Multiple
+ * Object | ID | Instances | Mandatoty |
+ * Test | 1024 | Yes | No |
+ *
+ * Ressources:
+ * 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 | | | | |
+ *
+ */
+
+#include "liblwm2m.h"
+
+#include
+#include
+#include
+#include
+
+
+#define PRV_TLV_BUFFER_SIZE 64
+
+/*
+ * 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;
+} prv_instance_t;
+
+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;
+ }
+}
+
+static uint8_t prv_read(uint16_t instanceId,
+ int * numDataP,
+ lwm2m_tlv_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_tlv_new(1);
+ if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+ *numDataP = 1;
+ (*dataArrayP)->id = 1;
+ }
+
+ if (*numDataP != 1) return COAP_404_NOT_FOUND;
+ if ((*dataArrayP)->id != 1) return COAP_404_NOT_FOUND;
+
+ (*dataArrayP)->type = LWM2M_TYPE_RESSOURCE;
+ lwm2m_tlv_encode_int(targetP->test, *dataArrayP);
+
+ if ((*dataArrayP)->length == 0) return COAP_500_INTERNAL_SERVER_ERROR;
+
+ return COAP_205_CONTENT;
+}
+
+static uint8_t prv_write(uint16_t instanceId,
+ int numData,
+ lwm2m_tlv_t * dataArray,
+ lwm2m_object_t * objectP)
+{
+ prv_instance_t * targetP;
+ int64_t value;
+
+ targetP = (prv_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId);
+ if (NULL == targetP) return COAP_404_NOT_FOUND;
+
+ if (numData != 1 || dataArray->id != 1) return COAP_404_NOT_FOUND;
+
+ if (1 != lwm2m_tlv_decode_int(dataArray, &value)
+ || value < 0 || value > 0xFF)
+ {
+ return COAP_400_BAD_REQUEST;
+ }
+ targetP->test = (uint8_t)value;
+
+ 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;
+
+ free(targetP);
+
+ return COAP_202_DELETED;
+}
+
+static uint8_t prv_create(uint16_t instanceId,
+ int numData,
+ lwm2m_tlv_t * dataArray,
+ lwm2m_object_t * objectP)
+{
+ prv_instance_t * targetP;
+ uint8_t result;
+
+
+ targetP = (prv_instance_t *)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,
+ char * 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(buffer, length);
+ fprintf(stdout, "-----------------\r\n\r\n");
+ return COAP_204_CHANGED;
+ default:
+ return COAP_404_NOT_FOUND;
+ }
+}
+
+lwm2m_object_t * get_test_object()
+{
+ lwm2m_object_t * testObj;
+
+ testObj = (lwm2m_object_t *)malloc(sizeof(lwm2m_object_t));
+
+ if (NULL != testObj)
+ {
+ int i;
+ prv_instance_t * targetP;
+
+ memset(testObj, 0, sizeof(lwm2m_object_t));
+
+ testObj->objID = 1024;
+ for (i=0 ; i < 3 ; i++)
+ {
+ targetP = (prv_instance_t *)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;
+ 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->createFunc = prv_create;
+ testObj->deleteFunc = prv_delete;
+ testObj->executeFunc = prv_exec;
+ }
+
+ return testObj;
+}
diff --git a/tests/server/CMakeLists.txt b/tests/server/CMakeLists.txt
new file mode 100644
index 000000000..32957e030
--- /dev/null
+++ b/tests/server/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required (VERSION 2.8.3)
+
+project (lwm2mserver)
+
+SET(LIBLWM2M_DIR ${PROJECT_SOURCE_DIR}/../../core)
+
+add_definitions(-DLWM2M_SERVER_MODE)
+
+include_directories (${LIBLWM2M_DIR} ${PROJECT_SOURCE_DIR}/../utils)
+
+add_subdirectory(${LIBLWM2M_DIR} ${CMAKE_CURRENT_BINARY_DIR}/core)
+
+SET(SOURCES lwm2mserver.c ../utils/commandline.c ../utils/connection.c)
+
+add_executable(lwm2mserver ${SOURCES} ${CORE_SOURCES})
diff --git a/tests/server/lwm2mserver.c b/tests/server/lwm2mserver.c
new file mode 100644
index 000000000..16122f16c
--- /dev/null
+++ b/tests/server/lwm2mserver.c
@@ -0,0 +1,792 @@
+/*******************************************************************************
+ *
+ * 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
+ * Simon Bernard - Please refer to git log
+ * Toby Jaffey - Please refer to git log
+ * Julien Vermillard - 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
+
+*/
+
+
+#include "liblwm2m.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "commandline.h"
+#include "connection.h"
+
+#define MAX_PACKET_SIZE 128
+
+static int g_quit = 0;
+
+static uint8_t prv_buffer_send(void * sessionH,
+ uint8_t * buffer,
+ size_t length,
+ void * userdata)
+{
+ connection_t * connP = (connection_t*) sessionH;
+
+ if (-1 == connection_send(connP, buffer, length))
+ {
+ return COAP_500_INTERNAL_SERVER_ERROR;
+ }
+ return COAP_NO_ERROR;
+}
+
+static void prv_dump_client(lwm2m_client_t * targetP)
+{
+ lwm2m_client_object_t * objectP;
+
+ fprintf(stdout, "Client #%d:\r\n", targetP->internalID);
+ fprintf(stdout, "\tname: \"%s\"\r\n", targetP->name);
+ fprintf(stdout, "\tobjects: ");
+ for (objectP = targetP->objectList; objectP != NULL ; objectP = objectP->next)
+ {
+ if (objectP->instanceList == NULL)
+ {
+ fprintf(stdout, "%d, ", objectP->id);
+ }
+ else
+ {
+ lwm2m_list_t * instanceP;
+
+ for (instanceP = objectP->instanceList; instanceP != NULL ; instanceP = instanceP->next)
+ {
+ fprintf(stdout, "%d/%d, ", objectP->id, instanceP->id);
+ }
+ }
+ }
+ fprintf(stdout, "\r\n");
+}
+
+static void prv_output_clients(char * buffer,
+ void * user_data)
+{
+ lwm2m_context_t * lwm2mH = (lwm2m_context_t *) user_data;
+ lwm2m_client_t * targetP;
+
+ targetP = lwm2mH->clientList;
+
+ if (targetP == NULL)
+ {
+ fprintf(stdout, "No client.\r\n");
+ return;
+ }
+
+ for (targetP = lwm2mH->clientList ; targetP != NULL ; targetP = targetP->next)
+ {
+ prv_dump_client(targetP);
+ }
+}
+
+static void print_indent(int num)
+{
+ int i;
+
+ for ( i = 0 ; i< num ; i++)
+ fprintf(stdout, " ");
+}
+
+static void output_tlv(char * buffer,
+ size_t buffer_len,
+ int indent)
+{
+ lwm2m_tlv_type_t type;
+ uint16_t id;
+ size_t dataIndex;
+ size_t dataLen;
+ int length = 0;
+ int result;
+
+ while (0 != (result = lwm2m_decodeTLV(buffer + length, buffer_len - length, &type, &id, &dataIndex, &dataLen)))
+ {
+ print_indent(indent);
+ fprintf(stdout, "ID: %d", id);
+ fprintf(stdout, " type: ");
+ switch (type)
+ {
+ case TLV_OBJECT_INSTANCE:
+ fprintf(stdout, "Object Instance");
+ break;
+ case TLV_RESSOURCE_INSTANCE:
+ fprintf(stdout, "Ressource Instance");
+ break;
+ case TLV_MULTIPLE_INSTANCE:
+ fprintf(stdout, "Multiple Instances");
+ break;
+ case TLV_RESSOURCE:
+ fprintf(stdout, "Ressource");
+ break;
+ default:
+ printf("unknown (%d)", (int)type);
+ break;
+ }
+ fprintf(stdout, "\n");
+ print_indent(indent);
+ fprintf(stdout, "{\n");
+ if (type == TLV_OBJECT_INSTANCE || type == TLV_MULTIPLE_INSTANCE)
+ {
+ output_tlv(buffer + length + dataIndex, dataLen, indent+2);
+ }
+ else
+ {
+ print_indent(indent+2);
+ fprintf(stdout, "data (%d bytes): ", dataLen);
+ if (dataLen >= 16) fprintf(stdout, "\n");
+ output_buffer(stdout, buffer + length + dataIndex, dataLen);
+ }
+ print_indent(indent);
+ fprintf(stdout, "}\n");
+ length += result;
+ }
+}
+
+static int prv_read_id(char * buffer,
+ uint16_t * idP)
+{
+ int nb;
+ int value;
+
+ nb = sscanf(buffer, "%d", &value);
+ if (nb == 1)
+ {
+ if (value < 0 || value > LWM2M_MAX_ID)
+ {
+ nb = 0;
+ }
+ else
+ {
+ *idP = value;
+ }
+ }
+
+ return nb;
+}
+
+static void prv_result_callback(uint16_t clientID,
+ lwm2m_uri_t * uriP,
+ int status,
+ uint8_t * data,
+ int dataLength,
+ void * userData)
+{
+ fprintf(stdout, "\r\nClient #%d %d", clientID, uriP->objectId);
+ if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+ fprintf(stdout, "/%d", uriP->instanceId);
+ else if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+ fprintf(stdout, "/");
+ if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+ fprintf(stdout, "/%d", uriP->resourceId);
+ fprintf(stdout, " returned status %d.%2d\r\n", (status&0xE0)>>5, status&0x1F);
+
+ if (data != NULL)
+ {
+ fprintf(stdout, "%d bytes received:\r\n", dataLength);
+ if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+ {
+ output_buffer(stdout, data, dataLength);
+ }
+ else
+ {
+ output_tlv(data, dataLength, 2);
+ }
+ }
+
+ fprintf(stdout, "\r\n> ");
+ fflush(stdout);
+}
+
+static void prv_notify_callback(uint16_t clientID,
+ lwm2m_uri_t * uriP,
+ int count,
+ uint8_t * data,
+ int dataLength,
+ void * userData)
+{
+ fprintf(stdout, "\r\nNotify from client #%d %d", clientID, uriP->objectId);
+ if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+ fprintf(stdout, "/%d", uriP->instanceId);
+ else if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+ fprintf(stdout, "/");
+ if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+ fprintf(stdout, "/%d", uriP->resourceId);
+ fprintf(stdout, " number %d\r\n", count);
+
+ if (data != NULL)
+ {
+ fprintf(stdout, "%d bytes received:\r\n", dataLength);
+ output_buffer(stdout, data, dataLength);
+ }
+
+ fprintf(stdout, "\r\n> ");
+ fflush(stdout);
+}
+
+static void prv_read_client(char * buffer,
+ void * user_data)
+{
+ lwm2m_context_t * lwm2mH = (lwm2m_context_t *) user_data;
+ uint16_t clientId;
+ lwm2m_uri_t uri;
+ int result;
+
+ result = prv_read_id(buffer, &clientId);
+ if (result != 1) goto syntax_error;
+
+ buffer = get_next_arg(buffer);
+ if (buffer[0] == 0) goto syntax_error;
+
+ result = lwm2m_stringToUri(buffer, strlen(buffer), &uri);
+ if (result == 0) goto syntax_error;
+
+ result = lwm2m_dm_read(lwm2mH, clientId, &uri, prv_result_callback, NULL);
+
+ if (result == 0)
+ {
+ fprintf(stdout, "OK");
+ }
+ else
+ {
+ fprintf(stdout, "Error %d.%2d", (result&0xE0)>>5, result&0x1F);
+ }
+ return;
+
+syntax_error:
+ fprintf(stdout, "Syntax error !");
+}
+
+static void prv_write_client(char * buffer,
+ void * user_data)
+{
+ lwm2m_context_t * lwm2mH = (lwm2m_context_t *) user_data;
+ uint16_t clientId;
+ lwm2m_uri_t uri;
+ char * uriString;
+ int i;
+ int result;
+
+ result = prv_read_id(buffer, &clientId);
+ if (result != 1) goto syntax_error;
+
+ buffer = get_next_arg(buffer);
+ if (buffer[0] == 0) goto syntax_error;
+ uriString = buffer;
+
+ buffer = get_next_arg(buffer);
+ if (buffer[0] == 0) goto syntax_error;
+
+ i = 0;
+ while (uriString + i < buffer && !isspace(uriString[i]))
+ {
+ i++;
+ }
+ result = lwm2m_stringToUri(uriString, i, &uri);
+ if (result == 0) goto syntax_error;
+
+ result = lwm2m_dm_write(lwm2mH, clientId, &uri, buffer, strlen(buffer), prv_result_callback, NULL);
+
+ if (result == 0)
+ {
+ fprintf(stdout, "OK");
+ }
+ else
+ {
+ fprintf(stdout, "Error %d.%2d", (result&0xE0)>>5, result&0x1F);
+ }
+ return;
+
+syntax_error:
+ fprintf(stdout, "Syntax error !");
+}
+
+static void prv_exec_client(char * buffer,
+ void * user_data)
+{
+ lwm2m_context_t * lwm2mH = (lwm2m_context_t *) user_data;
+ uint16_t clientId;
+ lwm2m_uri_t uri;
+ char * uriString;
+ int i;
+ int result;
+
+ result = prv_read_id(buffer, &clientId);
+ if (result != 1) goto syntax_error;
+
+ buffer = get_next_arg(buffer);
+ if (buffer[0] == 0) goto syntax_error;
+ uriString = buffer;
+
+ buffer = get_next_arg(buffer);
+
+ i = 0;
+ while (uriString + i < buffer && !isspace(uriString[i]))
+ {
+ i++;
+ }
+ result = lwm2m_stringToUri(uriString, i, &uri);
+ if (result == 0) goto syntax_error;
+
+ if (buffer[0] == 0)
+ {
+ result = lwm2m_dm_execute(lwm2mH, clientId, &uri, NULL, 0, prv_result_callback, NULL);
+ }
+ else
+ {
+ result = lwm2m_dm_execute(lwm2mH, clientId, &uri, buffer, strlen(buffer), prv_result_callback, NULL);
+ }
+
+ if (result == 0)
+ {
+ fprintf(stdout, "OK");
+ }
+ else
+ {
+ fprintf(stdout, "Error %d.%2d", (result&0xE0)>>5, result&0x1F);
+ }
+ return;
+
+syntax_error:
+ fprintf(stdout, "Syntax error !");
+}
+
+static void prv_create_client(char * buffer,
+ void * user_data)
+{
+ lwm2m_context_t * lwm2mH = (lwm2m_context_t *) user_data;
+ uint16_t clientId;
+ lwm2m_uri_t uri;
+ char * uriString;
+ int i;
+ int result;
+ uint64_t value;
+ char temp_buffer[MAX_PACKET_SIZE];
+ int temp_length = 0;
+
+
+ //Get Client ID
+ result = prv_read_id(buffer, &clientId);
+ if (result != 1) goto syntax_error;
+
+ buffer = get_next_arg(buffer);
+ if (buffer[0] == 0) goto syntax_error;
+
+ //save Uri start address
+ uriString = buffer;
+
+ //Get Data to Post
+ buffer = get_next_arg(buffer);
+ if (buffer[0] == 0) goto syntax_error;
+
+ //Get Uri
+ i = 0;
+ while (uriString + i < buffer && !isspace(uriString[i]))
+ {
+ i++;
+ }
+ result = lwm2m_stringToUri(uriString, i, &uri);
+ if (result == 0) goto syntax_error;
+
+ // TLV
+
+ /* Client dependent part */
+
+ if (uri.objectId == 1024)
+ {
+ result = lwm2m_PlainTextToInt64(buffer, strlen(buffer), &value);
+ temp_length = lwm2m_intToTLV(TLV_RESSOURCE, value, (uint16_t) 1, temp_buffer, MAX_PACKET_SIZE);
+ }
+ /* End Client dependent part*/
+
+ //Create
+ result = lwm2m_dm_create(lwm2mH, clientId,&uri, temp_buffer, temp_length, prv_result_callback, NULL);
+
+ if (result == 0)
+ {
+ fprintf(stdout, "OK");
+ }
+ else
+ {
+ fprintf(stdout, "Error %d.%2d", (result&0xE0)>>5, result&0x1F);
+ }
+ return;
+
+syntax_error:
+ fprintf(stdout, "Syntax error !");
+}
+
+static void prv_delete_client(char * buffer,
+ void * user_data)
+{
+ lwm2m_context_t * lwm2mH = (lwm2m_context_t *) user_data;
+ uint16_t clientId;
+ lwm2m_uri_t uri;
+ int result;
+
+ result = prv_read_id(buffer, &clientId);
+ if (result != 1) goto syntax_error;
+
+ buffer = get_next_arg(buffer);
+ if (buffer[0] == 0) goto syntax_error;
+
+ result = lwm2m_stringToUri(buffer, strlen(buffer), &uri);
+ if (result == 0) goto syntax_error;
+
+ result = lwm2m_dm_delete(lwm2mH, clientId, &uri, prv_result_callback, NULL);
+
+ if (result == 0)
+ {
+ fprintf(stdout, "OK");
+ }
+ else
+ {
+ fprintf(stdout, "Error %d.%2d", (result&0xE0)>>5, result&0x1F);
+ }
+ return;
+
+syntax_error:
+ fprintf(stdout, "Syntax error !");
+}
+
+static void prv_observe_client(char * buffer,
+ void * user_data)
+{
+ lwm2m_context_t * lwm2mH = (lwm2m_context_t *) user_data;
+ uint16_t clientId;
+ lwm2m_uri_t uri;
+ int result;
+
+ result = prv_read_id(buffer, &clientId);
+ if (result != 1) goto syntax_error;
+
+ buffer = get_next_arg(buffer);
+ if (buffer[0] == 0) goto syntax_error;
+
+ result = lwm2m_stringToUri(buffer, strlen(buffer), &uri);
+ if (result == 0) goto syntax_error;
+
+ result = lwm2m_observe(lwm2mH, clientId, &uri, prv_notify_callback, NULL);
+
+ if (result == 0)
+ {
+ fprintf(stdout, "OK");
+ }
+ else
+ {
+ fprintf(stdout, "Error %d.%2d", (result&0xE0)>>5, result&0x1F);
+ }
+ return;
+
+syntax_error:
+ fprintf(stdout, "Syntax error !");
+}
+
+static void prv_cancel_client(char * buffer,
+ void * user_data)
+{
+ lwm2m_context_t * lwm2mH = (lwm2m_context_t *) user_data;
+ uint16_t clientId;
+ lwm2m_uri_t uri;
+ int result;
+
+ result = prv_read_id(buffer, &clientId);
+ if (result != 1) goto syntax_error;
+
+ buffer = get_next_arg(buffer);
+ if (buffer[0] == 0) goto syntax_error;
+
+ result = lwm2m_stringToUri(buffer, strlen(buffer), &uri);
+ if (result == 0) goto syntax_error;
+
+ result = lwm2m_observe_cancel(lwm2mH, clientId, &uri, prv_result_callback, NULL);
+
+ if (result == 0)
+ {
+ fprintf(stdout, "OK");
+ }
+ else
+ {
+ fprintf(stdout, "Error %d.%2d", (result&0xE0)>>5, result&0x1F);
+ }
+ return;
+
+syntax_error:
+ fprintf(stdout, "Syntax error !");
+}
+
+static void prv_monitor_callback(uint16_t clientID,
+ lwm2m_uri_t * uriP,
+ int status,
+ uint8_t * data,
+ int dataLength,
+ void * userData)
+{
+ lwm2m_context_t * lwm2mH = (lwm2m_context_t *) userData;
+ lwm2m_client_t * targetP;
+ lwm2m_client_object_t * objectP;
+
+ switch (status)
+ {
+ case COAP_201_CREATED:
+ fprintf(stdout, "\r\nNew client #%d registered.\r\n", clientID);
+
+ targetP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)lwm2mH->clientList, clientID);
+
+ prv_dump_client(targetP);
+ break;
+
+ case COAP_202_DELETED:
+ fprintf(stdout, "\r\nClient #%d unregistered.\r\n", clientID);
+ break;
+
+ default:
+ fprintf(stdout, "\r\nMonitor callback called with an unknown status: %d.\r\n", status);
+ break;
+ }
+
+ fprintf(stdout, "\r\n> ");
+ fflush(stdout);
+}
+
+
+static void prv_quit(char * buffer,
+ void * user_data)
+{
+ g_quit = 1;
+}
+
+void handle_sigint(int signum)
+{
+ prv_quit(NULL, NULL);
+}
+
+void print_usage(void)
+{
+ fprintf(stderr, "Usage: lwm2mserver\r\n");
+ fprintf(stderr, "Launch a LWM2M server on localhost port "LWM2M_STANDARD_PORT_STR".\r\n\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+ int sock;
+ fd_set readfds;
+ struct timeval tv;
+ int result;
+ lwm2m_context_t * lwm2mH = NULL;
+ int i;
+ connection_t * connList = NULL;
+
+ command_desc_t commands[] =
+ {
+ {"list", "List registered clients.", NULL, prv_output_clients, NULL},
+ {"read", "Read from a client.", " read CLIENT# URI\r\n"
+ " CLIENT#: client number as returned by command 'list'\r\n"
+ " URI: uri to read such as /3, /3//2, /3/0/2, /1024/11, /1024//1\r\n"
+ "Result will be displayed asynchronously.", prv_read_client, NULL},
+ {"write", "Write to a client.", " write CLIENT# URI DATA\r\n"
+ " CLIENT#: client number as returned by command 'list'\r\n"
+ " URI: uri to write to such as /3, /3//2, /3/0/2, /1024/11, /1024//1\r\n"
+ " DATA: data to write\r\n"
+ "Result will be displayed asynchronously.", prv_write_client, NULL},
+ {"exec", "Execute a client resource.", " exec CLIENT# URI\r\n"
+ " CLIENT#: client number as returned by command 'list'\r\n"
+ " URI: uri of the resource to execute such as /3/0/2\r\n"
+ "Result will be displayed asynchronously.", prv_exec_client, NULL},
+ {"del", "Delete a client Object instance.", " del CLIENT# URI\r\n"
+ " CLIENT#: client number as returned by command 'list'\r\n"
+ " URI: uri of the instance to delete such as /1024/11\r\n"
+ "Result will be displayed asynchronously.", prv_delete_client, NULL},
+ {"create", "create an Object instance.", " create CLIENT# URI DATA\r\n"
+ " CLIENT#: client number as returned by command 'list'\r\n"
+ " URI: uri to which create the Object Instance such as /1024, /1024/45 \r\n"
+ " DATA: data to initialize the new Object Instance (0-255 for object 1024) \r\n"
+ "Result will be displayed asynchronously.", prv_create_client, NULL},
+ {"observe", "Observe from a client.", " observe CLIENT# URI\r\n"
+ " CLIENT#: client number as returned by command 'list'\r\n"
+ " URI: uri to observe such as /3, /3/0/2, /1024/11\r\n"
+ "Result will be displayed asynchronously.", prv_observe_client, NULL},
+ {"cancel", "Cancel an observe.", " cancel CLIENT# URI\r\n"
+ " CLIENT#: client number as returned by command 'list'\r\n"
+ " URI: uri on which to cancel an observe such as /3, /3/0/2, /1024/11\r\n"
+ "Result will be displayed asynchronously.", prv_cancel_client, NULL},
+
+ {"quit", "Quit the server.", NULL, prv_quit, NULL},
+
+ COMMAND_END_LIST
+ };
+
+ sock = create_socket(LWM2M_STANDARD_PORT_STR);
+ if (sock < 0)
+ {
+ fprintf(stderr, "Error opening socket: %d\r\n", errno);
+ return -1;
+ }
+
+ lwm2mH = lwm2m_init("testlwm2mserver", 0, NULL, prv_buffer_send, NULL);
+ if (NULL == lwm2mH)
+ {
+ fprintf(stderr, "lwm2m_init() failed\r\n");
+ return -1;
+ }
+
+ signal(SIGINT, handle_sigint);
+
+ for (i = 0 ; commands[i].name != NULL ; i++)
+ {
+ commands[i].userData = (void *)lwm2mH;
+ }
+ fprintf(stdout, "> "); fflush(stdout);
+
+ lwm2m_set_monitoring_callback(lwm2mH, prv_monitor_callback, lwm2mH);
+
+ while (0 == g_quit)
+ {
+ FD_ZERO(&readfds);
+ FD_SET(sock, &readfds);
+ FD_SET(STDIN_FILENO, &readfds);
+
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+
+ result = lwm2m_step(lwm2mH, &tv);
+ if (result != 0)
+ {
+ fprintf(stderr, "lwm2m_step() failed: 0x%X\r\n", result);
+ return -1;
+ }
+
+ result = select(FD_SETSIZE, &readfds, 0, 0, &tv);
+
+ if ( result < 0 )
+ {
+ if (errno != EINTR)
+ {
+ fprintf(stderr, "Error in select(): %d\r\n", errno);
+ }
+ }
+ else if (result > 0)
+ {
+ uint8_t buffer[MAX_PACKET_SIZE];
+ int numBytes;
+
+ if (FD_ISSET(sock, &readfds))
+ {
+ struct sockaddr_storage addr;
+ socklen_t addrLen;
+
+ addrLen = sizeof(addr);
+ numBytes = recvfrom(sock, buffer, MAX_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrLen);
+
+ if (numBytes == -1)
+ {
+ fprintf(stderr, "Error in recvfrom(): %d\r\n", errno);
+ }
+ else
+ {
+ char s[INET6_ADDRSTRLEN];
+ connection_t * connP;
+
+ fprintf(stderr, "%d bytes received from [%s]:%hu\r\n",
+ numBytes,
+ inet_ntop(addr.ss_family,
+ &(((struct sockaddr_in6*)&addr)->sin6_addr),
+ s,
+ INET6_ADDRSTRLEN),
+ ntohs(((struct sockaddr_in6*)&addr)->sin6_port));
+ output_buffer(stderr, buffer, numBytes);
+
+ connP = connection_find(connList, &addr, addrLen);
+ if (connP == NULL)
+ {
+ connList = connection_new_incoming(connList, sock, (struct sockaddr *)&addr, addrLen);
+ connP = connList;
+ }
+ if (connP != NULL)
+ {
+ lwm2m_handle_packet(lwm2mH, buffer, numBytes, connP);
+ }
+ }
+ }
+ else if (FD_ISSET(STDIN_FILENO, &readfds))
+ {
+ numBytes = read(STDIN_FILENO, buffer, MAX_PACKET_SIZE);
+
+ if (numBytes > 1)
+ {
+ buffer[numBytes - 1] = 0;
+ handle_command(commands, buffer);
+ }
+ if (g_quit == 0)
+ {
+ fprintf(stdout, "\r\n> ");
+ fflush(stdout);
+ }
+ else
+ {
+ fprintf(stdout, "\r\n");
+ }
+ }
+ }
+ }
+
+ lwm2m_close(lwm2mH);
+ close(sock);
+ connection_free(connList);
+
+ return 0;
+}
diff --git a/tests/utils/commandline.c b/tests/utils/commandline.c
new file mode 100644
index 000000000..35f5b06b4
--- /dev/null
+++ b/tests/utils/commandline.c
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+
+#include
+#include
+#include
+#include
+
+#include "commandline.h"
+
+#define HELP_COMMAND "help"
+#define HELP_DESC "Type '"HELP_COMMAND" [COMMAND]' for more details on a command."
+#define UNKNOWN_CMD_MSG "Unknown command. Type '"HELP_COMMAND"' for help."
+
+
+static command_desc_t * prv_find_command(command_desc_t * commandArray,
+ char * buffer,
+ int length)
+{
+ int i;
+
+ if (length == 0) return NULL;
+
+ i = 0;
+ while (commandArray[i].name != NULL
+ && (strlen(commandArray[i].name) != length || strncmp(buffer, commandArray[i].name, length)))
+ {
+ i++;
+ }
+
+ if (commandArray[i].name == NULL)
+ {
+ return NULL;
+ }
+ else
+ {
+ return &commandArray[i];
+ }
+}
+
+static void prv_displayHelp(command_desc_t * commandArray,
+ char * buffer)
+{
+ command_desc_t * cmdP;
+ int length;
+
+ // find end of first argument
+ length = 0;
+ while (buffer[length] != 0 && !isspace(buffer[length]))
+ length++;
+
+ cmdP = prv_find_command(commandArray, buffer, length);
+
+ if (cmdP == NULL)
+ {
+ int i;
+
+ fprintf(stdout, HELP_COMMAND"\t"HELP_DESC"\r\n");
+
+ for (i = 0 ; commandArray[i].name != NULL ; i++)
+ {
+ fprintf(stdout, "%s\t%s\r\n", commandArray[i].name, commandArray[i].shortDesc);
+ }
+ }
+ else
+ {
+ fprintf(stdout, "%s\r\n", cmdP->longDesc?cmdP->longDesc:cmdP->shortDesc);
+ }
+}
+
+
+void handle_command(command_desc_t * commandArray,
+ char * buffer)
+{
+ command_desc_t * cmdP;
+ int length;
+
+ // find end of command name
+ length = 0;
+ while (buffer[length] != 0 && !isspace(buffer[length]))
+ length++;
+
+ cmdP = prv_find_command(commandArray, buffer, length);
+ if (cmdP != NULL)
+ {
+ while (buffer[length] != 0 && isspace(buffer[length]))
+ length++;
+ cmdP->callback(buffer + length, cmdP->userData);
+ }
+ else
+ {
+ if (!strncmp(buffer, HELP_COMMAND, length))
+ {
+ while (buffer[length] != 0 && isspace(buffer[length]))
+ length++;
+ prv_displayHelp(commandArray, buffer + length);
+ }
+ else
+ {
+ fprintf(stdout, UNKNOWN_CMD_MSG"\r\n");
+ }
+ }
+}
+
+char * get_next_arg(char * buffer)
+{
+ while (buffer[0] != 0 && !isspace(buffer[0])) buffer++;
+ while (buffer[0] != 0 && isspace(buffer[0])) buffer++;
+
+ return buffer;
+}
+
diff --git a/tests/utils/commandline.h b/tests/utils/commandline.h
new file mode 100644
index 000000000..439cacc8a
--- /dev/null
+++ b/tests/utils/commandline.h
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+
+#define COMMAND_END_LIST {NULL, NULL, NULL, NULL, NULL}
+
+typedef void (*command_handler_t) (char * args, void * user_data);
+
+typedef struct
+{
+ char * name;
+ char * shortDesc;
+ char * longDesc;
+ command_handler_t callback;
+ void * userData;
+} command_desc_t;
+
+
+void handle_command(command_desc_t * commandArray, char * buffer);
+char * get_next_arg(char * buffer);
diff --git a/tests/utils/connection.c b/tests/utils/connection.c
new file mode 100644
index 000000000..5e51c5be9
--- /dev/null
+++ b/tests/utils/connection.c
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ *
+ * 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
+ *
+ *******************************************************************************/
+
+#include
+#include
+#include "connection.h"
+
+
+int create_socket(char * portStr)
+{
+ int s = -1;
+ struct addrinfo hints;
+ struct addrinfo *res;
+ struct addrinfo *p;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ if (0 != getaddrinfo(NULL, portStr, &hints, &res))
+ {
+ return -1;
+ }
+
+ for(p = res ; p != NULL && s == -1 ; p = p->ai_next)
+ {
+ s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+ if (s >= 0)
+ {
+ if (-1 == bind(s, p->ai_addr, p->ai_addrlen))
+ {
+ close(s);
+ s = -1;
+ }
+ }
+ }
+
+ freeaddrinfo(res);
+
+ return s;
+}
+
+connection_t * connection_find(connection_t * connList,
+ struct sockaddr_storage * addr,
+ size_t addrLen)
+{
+ connection_t * connP;
+
+ connP = connList;
+ while (connP != NULL)
+ {
+ if ((connP->addrLen == addrLen)
+ && (memcmp(&(connP->addr), addr, addrLen) == 0))
+ {
+ return connP;
+ }
+ connP = connP->next;
+ }
+
+ return connP;
+}
+
+connection_t * connection_new_incoming(connection_t * connList,
+ int sock,
+ struct sockaddr * addr,
+ size_t addrLen)
+{
+ connection_t * connP;
+
+ connP = (connection_t *)malloc(sizeof(connection_t));
+ if (connP != NULL)
+ {
+ connP->sock = sock;
+ memcpy(&(connP->addr), addr, addrLen);
+ connP->addrLen = addrLen;
+ connP->next = connList;
+ }
+
+ return connP;
+}
+
+connection_t * connection_create(connection_t * connList,
+ int sock,
+ char * host,
+ uint16_t port)
+{
+ char portStr[6];
+ struct addrinfo hints;
+ struct addrinfo *servinfo = NULL;
+ struct addrinfo *p;
+ int s;
+ struct sockaddr *sa;
+ socklen_t sl;
+ connection_t * connP = NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ if (0 >= sprintf(portStr, "%hu", port)) return NULL;
+ if (0 != getaddrinfo(host, portStr, &hints, &servinfo) || servinfo == NULL) return NULL;
+
+ // we test the various addresses
+ s = -1;
+ for(p = servinfo ; p != NULL && s == -1 ; p = p->ai_next)
+ {
+ s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+ if (s >= 0)
+ {
+ sa = p->ai_addr;
+ sl = p->ai_addrlen;
+ if (-1 == connect(s, p->ai_addr, p->ai_addrlen))
+ {
+ close(s);
+ s = -1;
+ }
+ }
+ }
+
+ if (s >= 0)
+ {
+ connP = connection_new_incoming(connList, sock, sa, sl);
+ close(s);
+ }
+
+ return connP;
+}
+
+void connection_free(connection_t * connList)
+{
+ if (connList != NULL)
+ {
+ connection_t * nextP;
+
+ nextP = connList->next;
+ free(connList);
+
+ connection_free(nextP);
+ }
+}
+
+int connection_send(connection_t *connP,
+ uint8_t * buffer,
+ size_t length)
+{
+ size_t nbSent;
+ size_t offset;
+
+ offset = 0;
+ while (offset != length)
+ {
+ nbSent = sendto(connP->sock, buffer + offset, length - offset, 0, (struct sockaddr *)&(connP->addr), connP->addrLen);
+ if (nbSent == -1) return -1;
+ offset += nbSent;
+ }
+ return 0;
+}
+
+void output_buffer(FILE * stream,
+ uint8_t * buffer,
+ int length)
+{
+ int i;
+ int j;
+ uint8_t array[16];
+
+ i = 0;
+ while (i < length)
+ {
+
+ fprintf(stream, " ");
+ memcpy(array, buffer+i, 16);
+
+ for (j = 0 ; j < 16 && i+j < length; j++)
+ {
+ fprintf(stream, "%02X ", array[j]);
+ }
+ if (i != 0)
+ {
+ while (j < 16)
+ {
+ fprintf(stream, " ");
+ j++;
+ }
+ }
+ fprintf(stream, " ");
+ for (j = 0 ; j < 16 && i+j < length; j++)
+ {
+ if (isprint(array[j]))
+ {
+ fprintf(stream, "%c ", array[j]);
+ }
+ else
+ {
+ fprintf(stream, ". ");
+ }
+ }
+ fprintf(stream, "\n");
+
+ i += 16;
+ }
+}
+
+
diff --git a/tests/utils/connection.h b/tests/utils/connection.h
new file mode 100644
index 000000000..c228ec470
--- /dev/null
+++ b/tests/utils/connection.h
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ *
+ * 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
+#include
+#include
+#include
+#include
+#include
+
+#define LWM2M_STANDARD_PORT_STR "5684"
+#define LWM2M_STANDARD_PORT 5684
+
+typedef struct _connection_t
+{
+ struct _connection_t * next;
+ int sock;
+ struct sockaddr_in6 addr;
+ size_t addrLen;
+} connection_t;
+
+int create_socket(char * portStr);
+
+connection_t * connection_find(connection_t * connList, struct sockaddr_storage * addr, size_t addrLen);
+connection_t * connection_new_incoming(connection_t * connList, int sock, struct sockaddr * addr, size_t addrLen);
+connection_t * connection_create(connection_t * connList, int sock, char * host, uint16_t port);
+
+void connection_free(connection_t * connList);
+
+int connection_send(connection_t *connP, uint8_t * buffer, size_t length);
+
+void output_buffer(FILE * stream, uint8_t * buffer, int length);
+
+#endif