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, ",", 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, ",", 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, ",", 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:

+ +
    +
  1. 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.
  2. +
  3. 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.
  4. +
  5. 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.
  6. +
+ +

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