├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── PortingGuide.md ├── README.md ├── library.json ├── library.properties ├── src ├── coap.h ├── coap_interaction.c ├── coap_interaction.h ├── coap_main.c ├── coap_main.h ├── coap_mem.c ├── coap_mem.h ├── coap_message.c ├── coap_message.h ├── coap_options.c ├── coap_options.h ├── coap_resource.c ├── coap_resource.h ├── diagnostic.c ├── diagnostic.h ├── interface │ ├── coap_interface.h │ ├── debug │ │ ├── coap_debug.c │ │ └── coap_debug.h │ └── network │ │ ├── net_Endpoint.c │ │ ├── net_Endpoint.h │ │ ├── net_Packet.c │ │ ├── net_Packet.h │ │ ├── net_Socket.c │ │ └── net_Socket.h ├── liblobaro_coap.c ├── liblobaro_coap.h └── option-types │ ├── coap_option_ETag.c │ ├── coap_option_ETag.h │ ├── coap_option_blockwise.c │ ├── coap_option_blockwise.h │ ├── coap_option_cf.c │ ├── coap_option_cf.h │ ├── coap_option_observe.c │ ├── coap_option_observe.h │ ├── coap_option_uri.c │ └── coap_option_uri.h └── test ├── ArduinoBuildTest ├── ArduinoBuildTest.ino ├── about_res.cpp └── about_res.h ├── CMakeLists.txt ├── arduino.sh ├── basic_tests.cpp ├── cmake.sh ├── coap_interface.cpp └── platformio.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | 34 | # CMake 35 | *.cmake 36 | *.cbp 37 | CMakeCache.txt 38 | /CmakeFiles 39 | /cmake-build-debug 40 | Makefile 41 | 42 | # JetBrains IDE 43 | *.iml 44 | /.idea -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: cpp 3 | 4 | env: 5 | global: 6 | - GTEST_DIR=${TRAVIS_BUILD_DIR}/gtest 7 | 8 | matrix: 9 | include: 10 | - compiler: gcc 11 | addons: 12 | apt: 13 | sources: ['ubuntu-toolchain-r-test'] 14 | packages: ['g++-4.8','libgtest-dev'] 15 | env: SCRIPT=cmake CMAKE_CXX_COMPILER=g++-4.8 16 | - compiler: gcc 17 | addons: 18 | apt: 19 | sources: ['ubuntu-toolchain-r-test'] 20 | packages: ['g++-4.9','libgtest-dev'] 21 | env: SCRIPT=cmake CMAKE_CXX_COMPILER=g++-4.9 22 | - compiler: gcc 23 | addons: 24 | apt: 25 | sources: ['ubuntu-toolchain-r-test'] 26 | packages: ['g++-5','libgtest-dev'] 27 | env: SCRIPT=cmake CMAKE_CXX_COMPILER=g++-5 28 | - compiler: clang 29 | addons: 30 | apt: 31 | sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5'] 32 | packages: ['clang-3.5','libgtest-dev'] 33 | env: SCRIPT=cmake CMAKE_CXX_COMPILER=clang++-3.5 34 | - compiler: clang 35 | addons: 36 | apt: 37 | sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.6'] 38 | packages: ['clang-3.6','libgtest-dev'] 39 | env: SCRIPT=cmake CMAKE_CXX_COMPILER=clang++-3.6 40 | - compiler: clang 41 | addons: 42 | apt: 43 | sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.7'] 44 | packages: ['clang-3.7','libgtest-dev'] 45 | env: SCRIPT=cmake CMAKE_CXX_COMPILER=clang++-3.7 46 | - env: SCRIPT=arduino VERSION=1.6.7 BOARD=arduino:avr:mega:cpu=atmega1280 47 | - env: SCRIPT=platformio BOARD=megaatmega1280 48 | - env: SCRIPT=platformio BOARD=esp01 49 | cache: 50 | directories: 51 | - "~/.platformio" 52 | script: test/$SCRIPT.sh -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (DEFINED __INIT_LOBARO_BUILD) 2 | # cmake configuration used in lobaro's internal build configuration 3 | project(lobaro-coap) 4 | 5 | include(${CPATH}/src/github.com/lobaro/c-build/build.cmake) 6 | 7 | add_definitions( 8 | -DCOAP_MAX_RETRANSMIT=2 # need +1 timeout values 9 | -DCOAP_EXPLICIT_TIMEOUT0=20 10 | -DCOAP_EXPLICIT_TIMEOUT1=20 11 | -DCOAP_EXPLICIT_TIMEOUT2=20 12 | ) 13 | 14 | cbuild_module("lobaro/lobaro-coap/test") 15 | 16 | # cbuild_dependency(_ github.com/lobaro/cpp-utils) 17 | else() 18 | # general cmake configuration used outside of lobaro 19 | cmake_minimum_required(VERSION 3.1) 20 | 21 | project(lobaro_coap C) 22 | 23 | file(GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/src/*.h ${CMAKE_CURRENT_LIST_DIR}/src/*.c) 24 | add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES} src/liblobaro_coap.c) 25 | target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}/src) 26 | 27 | set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99) 28 | endif() 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /PortingGuide.md: -------------------------------------------------------------------------------- 1 | 2 | # Porting Guide 3 | 4 | This library is independent of the underlying framework it works on, so you can easily use it on whatever platform. This guide explains how to use the library on your specific platform. 5 | 6 | In summary, you need to implement the following items: 7 | 8 | 1. Configure a few standard functions the library needs to call (ex: for memory management, printing, etc.) using the `CoAP_Init` function to set them up. 9 | 2. Implement a function to create a socket (network interface) that will be used by the library. 10 | 3. Implement a transmission function that will be called by the library to send a packet on the given interface. 11 | 4. Have your application periodically call the `CoAP_doWork()` function to process the CoAP interactions that are pending. This can be done in a separate thread/process. 12 | 5. Have your application periodically read the packets from the network interface and transfer them to the library using `CoAP_HandleIncomingPacket` function. 13 | 14 | ## Example 15 | 16 | To demonstrate those items, we'll use a simple example of an embedded system running FreeRTOS, with a POSIX socket network interface and configured to run as a CoAP client. 17 | The FreeRTOS allows us to create a separate task to handle the periodical work (item 4 and 5). The Posix socket API is how we access the network interface, we will have lobaro-coap indirectly talk to this API. 18 | 19 | Lets start by the `main` in which we configure the standard functions to use (*item 1*). For memory allocation, we use the FreeRTOS version of `malloc` (`pvPortMalloc`) and `free` (`vPortFree`). For the rest we wrote small custom functions. 20 | 21 | ```cpp 22 | #include 23 | #include 24 | #include "lobaro-coap/src/coap.h" 25 | 26 | void debugPuts(char* s) 27 | { 28 | printf("%s", s); 29 | } 30 | 31 | uint32_t elapsedTime_seconds() 32 | { 33 | return xTaskGetTickCount() / configTICK_RATE_HZ; // Returns the elapsed time since reset, in seconds 34 | } 35 | 36 | int generateRandom() 37 | { 38 | // Use whatever random number generator you have access to to generate the number here 39 | // int randomNumber = ... 40 | return randomNumber; 41 | } 42 | 43 | void main() 44 | { 45 | // Bind system functions to the CoAP library 46 | CoAP_API_t api = { 47 | .malloc = pvPortMalloc, // Function for allocating memory 48 | .free = vPortFree, // Function for freeing memory 49 | .rtc1HzCnt = elapsedTime_seconds, // Function that returns a time in seconds 50 | .rand = generateRandom, // Function to generate random numbers 51 | .debugPuts = debugPuts, // Function to print info for debugging 52 | }; 53 | 54 | CoAP_Init(api); 55 | 56 | // Create the task that will process the CoAP work 57 | if (pdPASS != xTaskCreate(coapClient_work_task, "CoapWork", COAP_CLIENT_TASK_STACK_SIZE, NULL, COAP_CLIENT_TASK_PRIORITY, NULL)) { 58 | // Handle error 59 | } 60 | 61 | vTaskStartScheduler(); // Run the task 62 | 63 | return 0; 64 | } 65 | 66 | ``` 67 | 68 | In the main we also create a new task that will do the CoAP work. The function that this task will execute is implemented below. 69 | It runs continuously as a separate thread. It has two roles: 1) transfers received packets to the library and 2) calls `CoAP_doWork()`, both done periodically. 70 | 71 | ```cpp 72 | static void coapClient_work_task() 73 | { 74 | // Create the buffer we need to read packets from the network 75 | #define RX_BUFFER_SIZE (MAX_PAYLOAD_SIZE + 127) 76 | static uint8_t rxBuffer[RX_BUFFER_SIZE]; 77 | static SocketHandle_t sockHandle = 0; 78 | 79 | // Create the socket (network interface) 80 | if(!CoAP_Posix_CreateSocket(&sockHandle, IPV4)) { 81 | // Handle the error 82 | } 83 | 84 | while(true) { 85 | 86 | // Firts, read all the pending packets from the network 87 | // interface and transfer them to the coap library 88 | int res = 0; 89 | do { 90 | // Read from network interface (using Posix socket api) 91 | res = recv(sockHandle, rxBuffer, RX_BUFFER_SIZE, MSG_DONTWAIT); 92 | 93 | if(res > 0) 94 | { 95 | printf("New packet received on interface, bytes read = %d", res); 96 | 97 | // Format the packet to the proper structure 98 | NetPacket_t pckt; 99 | memset(&pckt, 0, sizeof(pckt)); 100 | pckt.pData = rxBuffer; 101 | pckt.size = res; 102 | pckt.remoteEp = serverEp; 103 | 104 | CoAP_HandleIncomingPacket(sockHandle, &pckt); // Feed the received packet to the CoAP library 105 | // Note: this will copy the data to a new 106 | // buffer (we can reuse the rxBuffer) 107 | } 108 | 109 | } while(res > 0); 110 | 111 | // Then process any pending work 112 | CoAP_doWork(); 113 | 114 | // Then sleep for 100 ms 115 | vTaskDelay(100); 116 | } 117 | } 118 | ``` 119 | 120 | At the begining of the task, we create a new socket that the library will use. The function for creating that socket is shown below (*item 2*). 121 | 122 | ```cpp 123 | // Function to create a "CoAP" socket that can be used with the CoAP library 124 | // Returns true and sets the `handle` on success 125 | // Returns false if the socket could not be created 126 | bool CoAP_Posix_CreateSocket(SocketHandle_t *handle, NetInterfaceType_t type) 127 | { 128 | if(type == IPV4) { 129 | 130 | // Create the actual Posix socket 131 | int posixSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 132 | 133 | if(posixSocket < 0) { 134 | ERROR("Could not create socket, errno = %d", errno); 135 | return false; 136 | } 137 | 138 | // Allocate a new CoAP_Socket_t space for this socket 139 | CoAP_Socket_t *newSocket = AllocSocket(); 140 | 141 | if(newSocket == NULL) { 142 | ERROR("Could not allocate memory for new socket"); 143 | close(socket); 144 | return false; 145 | } 146 | 147 | newSocket->Handle = posixSocket; 148 | newSocket->Tx = CoAP_Posix_SendDatagram; // Function to transmit packets 149 | newSocket->Alive = true; // UDP sockets don't need to be connected 150 | *handle = posixSocket; 151 | return true; 152 | } 153 | else { 154 | ERROR("Unsupported net type %d", type); 155 | } 156 | 157 | return false; 158 | } 159 | ``` 160 | 161 | When creating the socket, we need to configure which function to use to transmit a packet (*item 3*) on this interface. We called it `CoAP_Posix_SendDatagram`.The function basically needs to send the given packet (`pckt`) to the specified interface (`socketHandle`) and return true on success or false on failure. 162 | 163 | ```cpp 164 | // Function to send a packet to the network interface 165 | bool CoAP_Posix_SendDatagram(SocketHandle_t socketHandle, NetPacket_t *pckt) 166 | { 167 | int sock = (int)socketHandle; 168 | 169 | // Format the endpoint info from the pckt to the right structure 170 | // that we need in our specific network (Posix socket api) 171 | struct sockaddr addr; 172 | size_t sockaddrSize; 173 | if(pckt->remoteEp.NetType == IPV4) 174 | { 175 | struct sockaddr_in *remote = &addr; 176 | remote->sin_family = AF_INET; 177 | remote->sin_port = htons(pckt->remoteEp.NetPort); 178 | for(uint8_t i = 0; i < 4; i++) 179 | remote->sin_addr.s4_addr[i] = pckt->remoteEp.NetAddr.IPv4.u8[i]; 180 | sockaddrSize = sizeof(struct sockaddr_in); 181 | } 182 | else 183 | { 184 | ERROR("Unsupported NetType : %d\n", pckt->remoteEp.NetType); 185 | return false; 186 | } 187 | 188 | // Actually send the packet to the network (Posix socket api) 189 | int ret = sendto(sock, pckt->pData, pckt->size, 0, &addr, sockaddrSize); 190 | 191 | if(ret < 0) 192 | ERROR("sendto() returned %d (errno = %d)\n", ret, errno); 193 | 194 | return ret > 0; 195 | } 196 | ``` 197 | 198 | This transmission function will be called by the lobaro-coap library to send CoAP packets on the network. 199 | 200 | To send a message from your application, you can use the `CoAP_StartNewRequest` function provided by lobaro-coap. 201 | 202 | ```cpp 203 | void sendCoapMessage() 204 | { 205 | // Send a CoAP message 206 | CoAP_Result_t result = CoAP_StartNewRequest( 207 | REQ_POST, // (CoAP_MessageCode_t) 208 | "/path/to/ressource", // (char*) 209 | sockHandle, // (SocketHandle_t) 210 | &serverEndpoint, // (NetEp_t*) 211 | CoAP_RespHandler_fn, // The function that will be called 212 | // when the message gets a response 213 | // or fails to be sent 214 | data, // Message data buffer (uint8_t *) 215 | length // Message data length (size_t) 216 | ); 217 | } 218 | ``` 219 | 220 | As shown above, when using `CoAP_StartNewRequest` you may define a response handler function that will be called when the message gets a reply (for confirmable messages) or if it fails to get acknowledge after a few retries. 221 | 222 | ```cpp 223 | // Response handler function 224 | CoAP_Result_t CoAP_RespHandler_fn(CoAP_Message_t* pRespMsg, CoAP_Message_t* pReqMsg, NetEp_t* sender) 225 | { 226 | if(pRespMsg == NULL) { 227 | printf("CoAP message transmission failed after all retries (timeout) for MessageId %d", pReqMsg->MessageID); 228 | return COAP_OK; 229 | } 230 | 231 | printf("Got a reply for MiD: %d", pRespMsg->MessageID); 232 | CoAP_PrintMsg(pRespMsg); 233 | 234 | return COAP_OK; 235 | } 236 | ``` 237 | 238 | The packet flow withing the library looks like this: 239 | 240 | 1. You send a message by calling `CoAP_StartNewRequest` 241 | 2. The lobaro-coap library will send that message for you calling the `CoAP_Posix_SendDatagram` function. 242 | 3. Eventually the server will reply and the response will be picked up by the `recv` function called in the `coapClient_work_task`. The reply will be transfered to the lobaro-coap library with our call to `CoAP_HandleIncomingPacket` 243 | 4. The lobaro-coap will call our `CoAP_RespHandler_fn` function upon the receiption of the reply 244 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lobaro-CoAP 2 | 3 | [![Build Status](https://travis-ci.org/Lobaro/lobaro-coap.svg?branch=master)](https://travis-ci.org/Lobaro/lobaro-coap) 4 | [![Gitter Chat](http://img.shields.io/badge/chat-online-brightgreen.svg)](https://gitter.im/lobaro-iot/Lobby) 5 | 6 | Complete CoAP Implementation in C. 7 | Despite designed for embedded systems (e.g. ARM Cortex-M0/M3, AVR, ESP8266) it can be used on nearly every system that has c-lang support. 8 | 9 | * Royalty-free CoAP stack 10 | * complete request/response logic 11 | * unified Client & Server 12 | * easy to use 13 | * small memory footprint 14 | * using C99 stdlib, suitable for embedded projects 15 | * detached packet receive/send logic 16 | * Arduino support (experimental) 17 | 18 | There is also a working OpenSource **client lib available in GoLang** @ https://github.com/lobaro/coap-go 19 | 20 | Follow [Lobaro on Twitter](https://twitter.com/LobaroHH) to get latest news about iot projects using our CoAP implementation! 21 | 22 | # Getting started 23 | 24 | Read the [Porting Guide](./PortingGuide.md) for information on how to port the library to your framework. 25 | 26 | # Demo/Example 27 | 28 | ESP8266, cheap WIFI Soc: 29 | 30 | - [Example Eclipse Project on GitHub](https://github.com/lobaro/lobaro-coap-on-esp8266) 31 | - [Step by Step Tutorial](http://www.lobaro.com/lobaro-coap-on-esp8266/) 32 | 33 | 34 | 35 | 36 | # Related 37 | * GoLang CoAP Client implementation & CGO wrapper for this C lib: [Lobaro CoAP-go](https://github.com/lobaro/coap-go) 38 | 39 | # Future development 40 | We use the stack internally at lobaro to build universal gateway / sensor systems for our customers. Additions will be constantly merged into this repository. 41 | 42 | # Contribute 43 | We appreciate any feedback, do not hesitate to create issues or pull requests. 44 | 45 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lobaro-coap", 3 | "keywords": "coap, rest, http, web, json", 4 | "description": "Complete CoAP Implementation in C for embedded systems. See http://www.lobaro.com/lobaro-coap/ for further information.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/lobaro/lobaro-coap.git" 8 | }, 9 | "version": "1.2", 10 | "authors": { 11 | "name": "Tobias Rohde", 12 | "url": "http://www.lobaro.com" 13 | }, 14 | "exclude": [ 15 | "test" 16 | ], 17 | "frameworks": "arduino", 18 | "platforms": "*" 19 | } -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=lobaro-coap 2 | version=1.2 3 | author=Tobias Rohde 4 | maintainer=http://www.lobaro.com 5 | sentence=Complete CoAP Implementation in C for embedded systems 6 | paragraph=See http://www.lobaro.com/lobaro-coap/ for further information. At least 6kb RAM is required. 7 | category=Communication 8 | url=https://github.com/lobaro/lobaro-coap 9 | architectures=* -------------------------------------------------------------------------------- /src/coap.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef COAP_H_ 24 | #define COAP_H_ 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | //"glue" and actual system related functions 31 | //go there to see what to do adapting the library to your platform 32 | #include "interface/coap_interface.h" 33 | #include "liblobaro_coap.h" 34 | 35 | //Internal stack functions 36 | 37 | /* Determine if time a is "after" time b. 38 | * Times a and b are unsigned, but performing the comparison 39 | * using signed arithmetic automatically handles wrapping. 40 | * The disambiguation window is half the maximum value. */ 41 | #if (configUSE_16_BIT_TICKS == 1) 42 | #define timeAfter(a,b) (((int16_t)(a) - (int16_t)(b)) >= 0) 43 | #else 44 | #define timeAfter(a,b) (((int32_t)(a) - (int32_t)(b)) >= 0) 45 | #endif 46 | 47 | #define MAX_PAYLOAD_SIZE (1024) //should not exceed 1024 bytes (see 4.6 RFC7252) (must be power of 2 to fit with blocksize option!) 48 | #define PREFERED_PAYLOAD_SIZE (64) //also size of inital pResp message payload buffer in user resource handler 49 | 50 | // Simulate network loss, set percentage of uploads or downloads that are dropped randomly: 51 | #define DEBUG_RANDOM_DROP_INCOMING_PERCENTAGE 0 52 | #define DEBUG_RANDOM_DROP_OUTGOING_PERCENTAGE 0 53 | 54 | // check for valid `MAX_PAYLOAD_SIZE` at compile time 55 | // allowing other values here will not work! 56 | #if !((MAX_PAYLOAD_SIZE==1024) || (MAX_PAYLOAD_SIZE==512) || (MAX_PAYLOAD_SIZE==256) || (MAX_PAYLOAD_SIZE==128) || (MAX_PAYLOAD_SIZE==64) || (MAX_PAYLOAD_SIZE==32)) 57 | #error "MAX_PAYLOAD_SIZE must be power of two and 32<=MAX_PAYLOAD_SIZE<=1024" 58 | #endif 59 | 60 | #define COAP_VERSION (1u) 61 | 62 | //V1.2 63 | #define LOBARO_COAP_VERSION_MAJOR (1u) 64 | #define LOBARO_COAP_VERSION_MINOR (2u) 65 | 66 | #include "coap_options.h" 67 | #include "coap_message.h" 68 | #include "option-types/coap_option_blockwise.h" 69 | #include "option-types/coap_option_ETag.h" 70 | #include "option-types/coap_option_cf.h" 71 | #include "option-types/coap_option_uri.h" 72 | #include "option-types/coap_option_observe.h" 73 | #include "coap_resource.h" 74 | #include "coap_interaction.h" 75 | #include "coap_main.h" 76 | #include "diagnostic.h" 77 | 78 | 79 | #ifdef __cplusplus 80 | } 81 | #endif 82 | 83 | #endif /* COAP_H_ */ 84 | -------------------------------------------------------------------------------- /src/coap_interaction.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #include "coap.h" 23 | #include "coap_main.h" 24 | #include "liblobaro_coap.h" 25 | #include "coap_mem.h" 26 | 27 | CoAP_Result_t CoAP_HandleObservationInReq(CoAP_Interaction_t* pIA); 28 | 29 | static CoAP_Interaction_t* _rom CoAP_AllocNewInteraction() { 30 | CoAP_Interaction_t* newInteraction = (CoAP_Interaction_t*) (CoAP_malloc0(sizeof(CoAP_Interaction_t))); 31 | if (newInteraction == NULL) { 32 | // coap_mem_stats(); 33 | INFO("- (!!!) CoAP_AllocNewInteraction() Out of Memory (Needed %zu bytes) !!!\r\n", sizeof(CoAP_Interaction_t)); 34 | return NULL; 35 | } 36 | 37 | memset(newInteraction, 0, sizeof(CoAP_Interaction_t)); 38 | assert_coap(newInteraction->pObserver == NULL); 39 | return newInteraction; 40 | } 41 | 42 | CoAP_Result_t _rom CoAP_FreeInteraction(CoAP_Interaction_t** pInteraction) { 43 | DEBUG("Releasing Interaction...\r\n"); 44 | // coap_mem_stats(); 45 | CoAP_free_Message(&(*pInteraction)->pReqMsg); 46 | CoAP_free_Message(&(*pInteraction)->pRespMsg); 47 | CoAP_free((void*) (*pInteraction)); 48 | // coap_mem_stats(); 49 | *pInteraction = NULL; 50 | return COAP_OK; 51 | } 52 | 53 | static CoAP_Result_t _rom CoAP_UnlinkInteractionFromList(CoAP_Interaction_t** pListStart, CoAP_Interaction_t* pInteractionToRemove, bool FreeUnlinked) { 54 | CoAP_Interaction_t* currP; 55 | CoAP_Interaction_t* prevP; 56 | 57 | // For 1st node, indicate there is no previous. 58 | prevP = NULL; 59 | 60 | //Visit each node, maintaining a pointer to 61 | //the previous node we just visited. 62 | for (currP = *pListStart; currP != NULL; 63 | prevP = currP, currP = currP->next) { 64 | 65 | if (currP == pInteractionToRemove) { // Found it. 66 | if (prevP == NULL) { 67 | //Fix beginning pointer. 68 | *pListStart = currP->next; 69 | } else { 70 | //Fix previous node's next to 71 | //skip over the removed node. 72 | prevP->next = currP->next; 73 | } 74 | 75 | // Deallocate the node. 76 | if (FreeUnlinked) { 77 | CoAP_FreeInteraction(&currP); 78 | } 79 | //Done searching. 80 | return COAP_OK; 81 | } 82 | } 83 | return COAP_OK; 84 | } 85 | 86 | static CoAP_Result_t _rom CoAP_UnlinkInteractionFromListByHandle(CoAP_Interaction_t** pListStart, uint16_t MsgID, SocketHandle_t socketHandle, NetEp_t* RstEp, bool FreeUnlinked) { 87 | CoAP_Interaction_t* currP; 88 | CoAP_Interaction_t* prevP; 89 | 90 | // For 1st node, indicate there is no previous. 91 | prevP = NULL; 92 | 93 | //Visit each node, maintaining a pointer to 94 | //the previous node we just visited. 95 | for (currP = *pListStart; currP != NULL; 96 | prevP = currP, currP = currP->next) { 97 | 98 | if (currP->socketHandle == socketHandle 99 | && ((currP->pReqMsg && currP->pReqMsg->MessageID == MsgID) || (currP->pRespMsg && currP->pRespMsg->MessageID == MsgID)) 100 | && EpAreEqual(&(currP->RemoteEp), RstEp)) { // Found it. 101 | 102 | if (prevP == NULL) { 103 | //Fix beginning pointer. 104 | *pListStart = currP->next; 105 | } else { 106 | //Fix previous node's next to 107 | //skip over the removed node. 108 | prevP->next = currP->next; 109 | } 110 | 111 | // Deallocate the node. 112 | if (FreeUnlinked) 113 | CoAP_FreeInteraction(&currP); 114 | //Done searching. 115 | return COAP_OK; 116 | } 117 | } 118 | return COAP_NOT_FOUND; 119 | } 120 | 121 | static CoAP_Result_t _rom CoAP_AppendInteractionToList(CoAP_Interaction_t** pListStart, CoAP_Interaction_t* pInteractionToAdd) { 122 | if (pInteractionToAdd == NULL) 123 | return COAP_ERR_ARGUMENT; 124 | 125 | if (*pListStart == NULL) //List empty? create new first element 126 | { 127 | *pListStart = pInteractionToAdd; 128 | (*pListStart)->next = NULL; 129 | } else //append new element at end 130 | { 131 | CoAP_Interaction_t* pTrans = *pListStart; 132 | while (pTrans->next != NULL) 133 | pTrans = pTrans->next; 134 | 135 | pTrans->next = pInteractionToAdd; 136 | pTrans = pTrans->next; 137 | pTrans->next = NULL; 138 | } 139 | return COAP_OK; 140 | } 141 | 142 | static CoAP_Result_t _rom CoAP_MoveInteractionToListEnd(CoAP_Interaction_t** pListStart, CoAP_Interaction_t* pInteractionToMove) { 143 | CoAP_Interaction_t* currP; 144 | CoAP_Interaction_t* prevP; 145 | 146 | // For 1st node, indicate there is no previous. 147 | prevP = NULL; 148 | 149 | //is interaction in List? if so delete it temporarily and add it to the back then 150 | 151 | //Visit each node, maintaining a pointer to 152 | //the previous node we just visited. 153 | for (currP = *pListStart; 154 | currP != NULL; 155 | prevP = currP, currP = currP->next) { 156 | 157 | if (currP == pInteractionToMove) { // Found it. 158 | if (prevP == NULL) { 159 | //Fix beginning pointer. 160 | *pListStart = currP->next; 161 | } else { 162 | //Fix previous node's next to 163 | //skip over the removed node. 164 | prevP->next = currP->next; 165 | } 166 | } 167 | } 168 | //node removed now put it at end of list 169 | CoAP_AppendInteractionToList(pListStart, pInteractionToMove); 170 | 171 | return COAP_OK; 172 | } 173 | 174 | CoAP_Result_t _rom CoAP_SetSleepInteraction(CoAP_Interaction_t* pIA, uint32_t seconds) { 175 | pIA->SleepUntil = CoAP.api.rtc1HzCnt() + seconds; 176 | return COAP_OK; 177 | } 178 | 179 | #ifdef COAP_EXPLICIT_TIMEOUT0 180 | const uint8_t TIMEOUTS[] = {COAP_EXPLICIT_TIMEOUT0,COAP_EXPLICIT_TIMEOUT1,COAP_EXPLICIT_TIMEOUT2}; 181 | #endif 182 | 183 | CoAP_Result_t _rom CoAP_EnableAckTimeout(CoAP_Interaction_t* pIA, uint8_t retryNum) { 184 | #ifdef COAP_EXPLICIT_TIMEOUT0 185 | uint32_t waitTime; 186 | if (retryNum < sizeof(TIMEOUTS)) { 187 | waitTime = TIMEOUTS[retryNum]; 188 | } else { 189 | waitTime = TIMEOUTS[sizeof(TIMEOUTS) - 1]; 190 | } 191 | INFO("CoAP timeout: %lus\n", waitTime); 192 | pIA->AckTimeout = CoAP.api.rtc1HzCnt() + waitTime; 193 | return COAP_OK; 194 | #else 195 | uint32_t waitTime = ACK_TIMEOUT; 196 | int i; 197 | for (i = 0; i < retryNum; i++) { //"exponential backoff" 198 | waitTime *= 2; 199 | } 200 | INFO("CoAP timeout: %lus\n", waitTime); 201 | pIA->AckTimeout = CoAP.api.rtc1HzCnt() + waitTime; 202 | return COAP_OK; 203 | #endif 204 | } 205 | 206 | CoAP_Interaction_t* _rom CoAP_GetLongestPendingInteraction() { 207 | return CoAP.pInteractions; 208 | } 209 | 210 | // Each incoming message belongs to an interaction 211 | // CON <-> ACK/RST are matched by id and endpoint 212 | CoAP_Interaction_t* _rom CoAP_FindInteractionByMessageIdAndEp(CoAP_Interaction_t* pList, uint16_t mID, NetEp_t* fromEp) { 213 | 214 | // A received RST message rejects a former send CON message or (optional) NON message send by us 215 | // A received ACK message acknowledges a former send CON message or (optional) NON message send by us 216 | // servers and notificators use CON only in responses, clients in requests 217 | while (pList != NULL) { 218 | if (((pList->pRespMsg != NULL && pList->pRespMsg->MessageID == mID) || (pList->pReqMsg != NULL && pList->pReqMsg->MessageID == mID)) && EpAreEqual(fromEp, &(pList->RemoteEp))) { 219 | return pList; 220 | } 221 | pList = pList->next; 222 | } 223 | 224 | return NULL; 225 | } 226 | 227 | CoAP_Result_t _rom CoAP_DeleteInteraction(CoAP_Interaction_t* pInteractionToDelete) { 228 | return CoAP_UnlinkInteractionFromList(&(CoAP.pInteractions), pInteractionToDelete, true); 229 | } 230 | 231 | CoAP_Result_t _rom CoAP_ResetInteractionByHandle(uint16_t MsgID, SocketHandle_t socketHandle, NetEp_t* RstEp) { 232 | return CoAP_UnlinkInteractionFromListByHandle(&(CoAP.pInteractions), MsgID, socketHandle, RstEp, true); 233 | } 234 | 235 | CoAP_Result_t _rom CoAP_EnqueueLastInteraction(CoAP_Interaction_t* pInteractionToEnqueue) { 236 | return CoAP_MoveInteractionToListEnd(&(CoAP.pInteractions), pInteractionToEnqueue); 237 | } 238 | 239 | //we act as a CoAP Client (sending requests) in this interaction 240 | CoAP_Result_t _rom CoAP_StartNewClientInteraction(CoAP_Message_t* pMsgReq, SocketHandle_t socketHandle, NetEp_t* ServerEp, CoAP_RespHandler_fn_t cb) { 241 | if (pMsgReq == NULL || CoAP_MsgIsRequest(pMsgReq) == false) 242 | return COAP_ERR_ARGUMENT; 243 | 244 | CoAP_Interaction_t* newIA = CoAP_AllocNewInteraction(); 245 | if (newIA == NULL) 246 | return COAP_ERR_OUT_OF_MEMORY; 247 | 248 | //attach request message 249 | newIA->pReqMsg = pMsgReq; 250 | 251 | newIA->socketHandle = socketHandle; 252 | CopyEndpoints(&(newIA->RemoteEp), ServerEp); 253 | newIA->RespCB = cb; 254 | 255 | newIA->Role = COAP_ROLE_CLIENT; 256 | newIA->State = COAP_STATE_READY_TO_REQUEST; 257 | 258 | CoAP_AppendInteractionToList(&(CoAP.pInteractions), newIA); 259 | 260 | return COAP_OK; 261 | } 262 | 263 | CoAP_Result_t _rom CoAP_StartNewGetRequest(char* UriString, SocketHandle_t socketHandle, NetEp_t* ServerEp, CoAP_RespHandler_fn_t cb) { 264 | 265 | CoAP_Message_t* pReqMsg = CoAP_CreateMessage(CON, REQ_GET, CoAP_GetNextMid(), NULL, 0, 0, CoAP_GenerateToken()); 266 | 267 | if (pReqMsg != NULL) { 268 | CoAP_AppendUriOptionsFromString(&(pReqMsg->pOptionsList), UriString); 269 | return CoAP_StartNewClientInteraction(pReqMsg, socketHandle, ServerEp, cb); 270 | } 271 | 272 | INFO("- New GetRequest failed: Out of Memory\r\n"); 273 | 274 | return COAP_ERR_OUT_OF_MEMORY; 275 | } 276 | 277 | CoAP_Result_t _rom CoAP_StartNewRequest(CoAP_MessageCode_t type, const char* UriString, SocketHandle_t socketHandle, NetEp_t* ServerEp, CoAP_RespHandler_fn_t cb, uint8_t *buf, size_t size) { 278 | if (type == EMPTY || type > REQ_LAST) { 279 | ERROR("- Invalid request type\r\n"); 280 | return COAP_ERR_ARGUMENT; 281 | } 282 | 283 | CoAP_Message_t* pReqMsg = CoAP_CreateMessage(CON, type, CoAP_GetNextMid(), buf, size, size, CoAP_GenerateToken()); 284 | 285 | if (pReqMsg != NULL) { 286 | CoAP_AppendUriOptionsFromString(&(pReqMsg->pOptionsList), UriString); 287 | return CoAP_StartNewClientInteraction(pReqMsg, socketHandle, ServerEp, cb); 288 | } 289 | 290 | INFO("- New GetRequest failed: Out of Memory\r\n"); 291 | 292 | return COAP_ERR_OUT_OF_MEMORY; 293 | } 294 | 295 | CoAP_Result_t _rom CoAP_StartNotifyInteractions(CoAP_Res_t* pRes) { 296 | // TODO: find all notification interactions that match this resource 297 | // CoAP.pInteractions->pRes == pRes && CoAP.pInteractions->Role == COAP_ROLE_NOTIFICATION 298 | // and call the pRes->Notifier(...) to get a new response 299 | // the new response is then send to the client and the interaction must wait for the ACK (or not for NON) 300 | // While the interaction is waiting for an ACK or doing retries the pending response might change but NOT the messageId 301 | // ------------ 302 | 303 | CoAP_Observer_t* pObserver = pRes->pListObservers; 304 | CoAP_Interaction_t* newIA; 305 | bool sameNotificationOngoing = false; 306 | 307 | int cnt = 0; 308 | while (pObserver != NULL) { 309 | cnt++; 310 | pObserver = pObserver->next; 311 | } 312 | INFO("Notify %d observers for res at %p, description: %s\n", cnt,pRes, (pRes->pDescription == NULL)?"N/A":pRes->pDescription); 313 | 314 | 315 | 316 | //iterate over all observers of this resource and check if 317 | //a) a new notification interaction has to be created 318 | //or 319 | //b) wait for currently pending notification to end 320 | //or 321 | //c) a currently pending older notification has be updated to the new resource representation 322 | // as stated in Observe RFC7641 ("4.5.2. Advanced Transmission") 323 | pObserver = pRes->pListObservers; 324 | while (pObserver != NULL) { 325 | 326 | //search for pending notification of this resource to this observer and set update flag of representation 327 | sameNotificationOngoing = false; 328 | CoAP_Interaction_t* pIA; 329 | for (pIA = CoAP.pInteractions; pIA != NULL; pIA = pIA->next) { 330 | if (pIA->Role == COAP_ROLE_NOTIFICATION && 331 | pIA->pRes == pRes && 332 | pIA->socketHandle == pObserver->socketHandle && 333 | EpAreEqual(&(pIA->RemoteEp), &(pObserver->Ep))) { 334 | sameNotificationOngoing = true; 335 | 336 | #if USE_RFC7641_ADVANCED_TRANSMISSION == 1 337 | pIA->UpdatePendingNotification = true; //will try to update the ongoing resource representation on ongoing transfer 338 | pIA->SleepUntil = 0; // Wakeup interaction 339 | #endif 340 | 341 | break; 342 | } 343 | } 344 | 345 | #if USE_RFC7641_ADVANCED_TRANSMISSION == 1 346 | // If there is already a running interaction for this notification 347 | // it will be updated for next retry 348 | // or just send another message with fresh data after it is finished 349 | // Thus we do not need to create a new interaction 350 | if (sameNotificationOngoing) { 351 | pObserver = pObserver->next; //skip this observer, don't start a 2nd notification now 352 | continue; 353 | } 354 | #endif 355 | 356 | //Start new IA for this observer 357 | newIA = CoAP_AllocNewInteraction(); 358 | if (newIA == NULL) { 359 | return COAP_ERR_OUT_OF_MEMORY; 360 | } 361 | 362 | //Create fresh response message 363 | newIA->pRespMsg = CoAP_CreateMessage(CON, RESP_SUCCESS_CONTENT_2_05, CoAP_GetNextMid(), NULL, 0, PREFERED_PAYLOAD_SIZE, pObserver->Token); 364 | 365 | //Call Notify Handler of resource and add to interaction list 366 | if (newIA->pRespMsg != NULL && pRes->Notifier != NULL && 367 | pRes->Notifier(pObserver, newIA->pRespMsg) == HANDLER_OK) { //<------ call notify handler of resource 368 | 369 | newIA->Role = COAP_ROLE_NOTIFICATION; 370 | newIA->State = COAP_STATE_READY_TO_NOTIFY; 371 | newIA->RemoteEp = pObserver->Ep; 372 | newIA->socketHandle = pObserver->socketHandle; 373 | newIA->pRes = pRes; 374 | newIA->pObserver = pObserver; 375 | 376 | if (newIA->pRespMsg->Code >= RESP_ERROR_BAD_REQUEST_4_00) { //remove this observer from resource in case of non OK Code (see RFC7641, 3.2., 3rd paragraph) 377 | pObserver = pObserver->next; //next statement will free current observer so save its ancestor node right now 378 | newIA->pObserver = NULL; 379 | CoAP_RemoveObserverFromResource(&(newIA->pRes->pListObservers), newIA->socketHandle, &(pIA->RemoteEp), newIA->pRespMsg->Token); 380 | continue; 381 | } else { 382 | AddObserveOptionToMsg(newIA->pRespMsg, pRes->UpdateCnt); // Only 2.xx responses do include an Observe Option. 383 | } 384 | 385 | if (newIA->pRespMsg->Type == NON && pRes->UpdateCnt % 20 == 0) { //send every 20th message as CON even if notify handler defines the send out as NON to support "lazy" cancelation 386 | newIA->pRespMsg->Type = CON; 387 | } 388 | 389 | CoAP_AppendInteractionToList(&(CoAP.pInteractions), newIA); 390 | 391 | } else { 392 | CoAP_FreeInteraction(&newIA); //revert IA creation above 393 | } 394 | 395 | pObserver = pObserver->next; 396 | } 397 | return COAP_OK; 398 | } 399 | 400 | //we act as a CoAP Server (receiving requests) in this interaction 401 | CoAP_Result_t _rom CoAP_StartNewServerInteraction(CoAP_Message_t* pMsgReq, CoAP_Res_t* pRes, SocketHandle_t socketHandle, NetPacket_t* pPacket) { 402 | if (!CoAP_MsgIsRequest(pMsgReq)) 403 | return COAP_ERR_ARGUMENT; 404 | 405 | //NON or CON request 406 | NetEp_t* pReqEp = &(pPacket->remoteEp); 407 | CoAP_Interaction_t* pIA; 408 | 409 | //Set the CoAP resource to the requst message 410 | pMsgReq->pResource = pRes; 411 | 412 | //duplicate detection: 413 | //same request already received before? 414 | //iterate over all interactions 415 | for (pIA = CoAP.pInteractions; pIA != NULL; pIA = pIA->next) { 416 | if (pIA->Role == COAP_ROLE_SERVER && pIA->socketHandle == socketHandle && pIA->pReqMsg->MessageID == pMsgReq->MessageID && EpAreEqual(&(pIA->RemoteEp), pReqEp)) { 417 | //implements 4.5. "SHOULD"s 418 | if (pIA->pReqMsg->Type == CON && pIA->State == COAP_STATE_RESOURCE_POSTPONE_EMPTY_ACK_SENT) { //=> must be postponed resource with empty ack already sent, send it again 419 | CoAP_SendEmptyAck(pIA->pReqMsg->MessageID, pIA->socketHandle, pPacket->remoteEp); //send another empty ack 420 | } 421 | 422 | //pIA->ReqReliabilityState 423 | 424 | return COAP_ERR_EXISTING; 425 | } 426 | } 427 | 428 | //no duplicate request found-> create a new interaction for this new request 429 | CoAP_Interaction_t* newIA = CoAP_AllocNewInteraction(); 430 | 431 | if (newIA == NULL) { 432 | ERROR("(!) can't create new interaction - out of memory!\r\n"); 433 | return COAP_ERR_OUT_OF_MEMORY; 434 | } 435 | 436 | newIA->socketHandle = socketHandle; 437 | newIA->pRes = pRes; 438 | newIA->Role = COAP_ROLE_SERVER; 439 | newIA->State = COAP_STATE_HANDLE_REQUEST; 440 | newIA->ReqMetaInfo = pPacket->metaInfo; 441 | newIA->pReqMsg = pMsgReq; 442 | 443 | CopyEndpoints(&(newIA->RemoteEp), pReqEp); 444 | CoAP_AppendInteractionToList(&(CoAP.pInteractions), newIA); 445 | return COAP_OK; 446 | } 447 | 448 | CoAP_Result_t _rom CoAP_RemoveInteractionsObserver(CoAP_Interaction_t* pIA, CoAP_Token_t token) { 449 | return CoAP_RemoveObserverFromResource(&((pIA->pRes)->pListObservers), pIA->socketHandle, &(pIA->RemoteEp), token); 450 | } 451 | 452 | CoAP_Result_t _rom CoAP_HandleObservationInReq(CoAP_Interaction_t* pIA) { 453 | if (pIA == NULL || pIA->pReqMsg == NULL) { 454 | return COAP_ERR_ARGUMENT; //safety checks 455 | } 456 | 457 | CoAP_Result_t res; 458 | uint32_t obsVal = 0; 459 | CoAP_Observer_t* pObserver = NULL; 460 | CoAP_Observer_t* pExistingObserver = (pIA->pRes)->pListObservers; 461 | 462 | if (pIA->pRes->Notifier == NULL) { 463 | return COAP_ERR_NOT_FOUND; //resource does not support observe 464 | } 465 | if ((res = GetObserveOptionFromMsg(pIA->pReqMsg, &obsVal)) != COAP_OK) { 466 | return res; //if no observe option in req function can't do anything 467 | } 468 | 469 | //Client registers 470 | if (obsVal == OBSERVE_OPT_REGISTER) { // val == 0 471 | 472 | //Alloc memory for new Observer 473 | pObserver = CoAP_AllocNewObserver(); 474 | if (pObserver == NULL) { 475 | return COAP_ERR_OUT_OF_MEMORY; 476 | } 477 | 478 | //Copy relevant information for observation from current interaction 479 | pObserver->Ep = pIA->RemoteEp; 480 | pObserver->socketHandle = pIA->socketHandle; 481 | pObserver->Token = pIA->pReqMsg->Token; 482 | pObserver->next = NULL; 483 | 484 | //Copy relevant Options from Request (uri-query, observe) 485 | //Note: uri-path is not relevant since observers are fixed to its resource 486 | CoAP_option_t* pOption = pIA->pReqMsg->pOptionsList; 487 | while (pOption != NULL) { 488 | if (pOption->Number == OPT_NUM_URI_QUERY || pOption->Number == OPT_NUM_OBSERVE) { 489 | //create copy from volatile Iinteraction msg options 490 | if (CoAP_AppendOptionToList(&(pObserver->pOptList), pOption->Number, pOption->Value, pOption->Length) != COAP_OK) { 491 | CoAP_FreeObserver(&pObserver); 492 | return COAP_ERR_OUT_OF_MEMORY; 493 | } 494 | } 495 | pOption = pOption->next; 496 | } 497 | 498 | //delete eventually existing same observer 499 | while (pExistingObserver != NULL) { //found right existing observation -> delete it 500 | // Changed: Do NOT check the token. if socket and EP are the same, it's the same observer 501 | // We do NOT allow to observe the same resource twice from the same observer 502 | // CoAP_TokenEqual(pIA->pReqMsg->Token, pExistingObserver->Token) && 503 | if (pIA->socketHandle == pExistingObserver->socketHandle && EpAreEqual(&(pIA->RemoteEp), &(pExistingObserver->Ep))) { 504 | CoAP_UnlinkObserverFromList(&((pIA->pRes)->pListObservers), pExistingObserver, true); 505 | break; 506 | } 507 | pExistingObserver = pExistingObserver->next; 508 | } 509 | 510 | //attach/update observer to resource 511 | return CoAP_AppendObserverToList(&((pIA->pRes)->pListObservers), pObserver); 512 | 513 | //Client cancels observation actively (this is an alternative to simply forget the req token and send rst on next notification) 514 | } else if (obsVal == OBSERVE_OPT_DEREGISTER) { // val == 1 515 | //find matching observer in resource observers-list 516 | CoAP_RemoveInteractionsObserver(pIA, pIA->pReqMsg->Token); //remove observer identified by token, socketHandle and remote EP from resource 517 | 518 | //delete/abort any pending notification interaction 519 | CoAP_Interaction_t* pIApending; 520 | for (pIApending = CoAP.pInteractions; pIApending != NULL; pIApending = pIApending->next) { 521 | if (pIApending->Role == COAP_ROLE_NOTIFICATION) { 522 | if (CoAP_TokenEqual(pIApending->pRespMsg->Token, pIA->pReqMsg->Token) 523 | && pIApending->socketHandle == pIA->socketHandle 524 | && EpAreEqual(&(pIApending->RemoteEp), &(pIA->RemoteEp))) { 525 | INFO("Abort of pending notification interaction\r\n"); 526 | CoAP_DeleteInteraction(pIApending); 527 | break; 528 | } 529 | } 530 | } 531 | 532 | } else { 533 | return COAP_BAD_OPTION_VAL; 534 | } 535 | 536 | return COAP_ERR_NOT_FOUND; 537 | } 538 | 539 | void _rom CoAP_ClearInteractions(CoAP_Interaction_t **pIA) { 540 | CoAP_Interaction_t *p = *pIA; 541 | *pIA = NULL; 542 | size_t cnt = 0; 543 | while (p != NULL) { 544 | cnt++; 545 | CoAP_Interaction_t *next = p->next; 546 | CoAP_FreeInteraction(&p); 547 | p = next; 548 | } 549 | INFO("CoAP Interactions dropped: %u\r\n", cnt); 550 | } 551 | -------------------------------------------------------------------------------- /src/coap_interaction.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #ifndef COAP_INTERACTIONS_H 23 | #define COAP_INTERACTIONS_H 24 | 25 | typedef enum { 26 | COAP_ROLE_NOT_SET, 27 | COAP_ROLE_SERVER, //[server] 28 | COAP_ROLE_NOTIFICATION, //[notificator] 29 | COAP_ROLE_CLIENT //[client] 30 | } CoAP_InteractionRole_t; 31 | 32 | typedef enum { 33 | COAP_STATE_NOT_SET, // set after memory allocation 34 | 35 | //[server] 36 | COAP_STATE_HANDLE_REQUEST, // Remote request received & parsed -> invoke resource handler 37 | COAP_STATE_RESOURCE_POSTPONE_EMPTY_ACK_SENT,// Wait some time for resource to become ready, meanwhile empty ack has been sent 38 | COAP_STATE_RESPONSE_SENT, // NON or piggy-ACK response has been sent -> wait some time then delete 39 | COAP_STATE_RESPONSE_WAITING_LEISURE, // CON response has been sent (+previous separate ACK of req) 40 | // -> wait for ACK or resent after some time 41 | 42 | //[notificator] 43 | COAP_STATE_READY_TO_NOTIFY, // Notification is ready to be send (triggered by resource event) 44 | COAP_STATE_NOTIFICATION_SENT, // NON response has been sent -> wait some time then delete 45 | // CON response has been sent -> wait for ACK or resent after some time 46 | 47 | //[client] 48 | COAP_STATE_READY_TO_REQUEST, // request is ready to send 49 | COAP_STATE_WAITING_RESPONSE, // resend request after some time if no response or ack has been received for some time 50 | COAP_STATE_HANDLE_RESPONSE, // call client callback and delete after some time, ack maybe send too 51 | 52 | //[server]+[notificator]+[client] 53 | COAP_STATE_FINISHED // can be deleted / freed 54 | } CoAP_InteractionState_t; 55 | 56 | // State of confirmable (CON) messages 57 | typedef enum { 58 | NOT_SET, 59 | ACK_SEND, 60 | RST_SEND 61 | } CoAP_ConfirmationState_t; 62 | 63 | typedef CoAP_Result_t ( * CoAP_RespHandler_fn_t )(CoAP_Message_t* pRespMsg, CoAP_Message_t* pReqMsg, NetEp_t* Sender); 64 | 65 | 66 | /* 67 | * There are 3 Types of interactions, defined by the Role: CLIENT, SERVER, NOTIFICATION 68 | * 69 | * CLIENT interactions are created by sending a request to a CoAP server when this lib is used as a client 70 | * this usecase is not well tested yet and need further refactoring 71 | * 72 | * SERVER interactions are created by receiving a request from a CoAP client 73 | * and end with the response from the server (this lib) which might be 74 | * a single ACK or ACK + CON/NON for postponed responses 75 | * 76 | * NOTIFICATION interactions are created by receiving a request with the notify option set to 0 (= register) 77 | * and can be canceled with a NAK or a new request matching the token with the notify option set to 1 (=unregister) 78 | * 79 | * 80 | */ 81 | typedef struct CoAP_Interaction { 82 | struct CoAP_Interaction* next; //4 byte pointer (linked list of interactions) 83 | 84 | CoAP_InteractionRole_t Role; //[client], [server] or [notification] 85 | CoAP_InteractionState_t State; 86 | 87 | // An interaction is bound to a resource based on the requested URL 88 | CoAP_Res_t* pRes; //Resource of IA 89 | CoAP_Observer_t* pObserver; //"NULL" or link to Observer (Role=COAP_ROLE_NOTIFICATION) 90 | 91 | //Network State 92 | NetEp_t RemoteEp; //[server]=>requesting EP, [client]=>serving EP 93 | SocketHandle_t socketHandle; // Handle to identify the underlying Socket 94 | 95 | //control vars 96 | bool UpdatePendingNotification; //control flag for Observe RFC7641 ("4.5.2. Advanced Transmission") 97 | uint8_t RetransCounter; 98 | uint32_t AckTimeout; 99 | uint32_t SleepUntil; 100 | 101 | //Request 102 | CoAP_Message_t* pReqMsg; 103 | CoAP_ConfirmationState_t ReqConfirmState; // The ACK/RST answer send to CON requests 104 | MetaInfo_t ReqMetaInfo; 105 | 106 | //Response 107 | CoAP_Message_t* pRespMsg; // corresponding Response Message 108 | CoAP_ConfirmationState_t ResConfirmState; // The ACK/RST answer received for CON request 109 | MetaInfo_t RespMetaInfo; 110 | 111 | CoAP_RespHandler_fn_t RespCB; //response callback (if client) 112 | } CoAP_Interaction_t; 113 | 114 | //called by incoming request 115 | //we act as a CoAP Server (receiving requests) in this interaction 116 | CoAP_Result_t CoAP_StartNewServerInteraction(CoAP_Message_t* pMsgReq, CoAP_Res_t* pRes, SocketHandle_t socketHandle, NetPacket_t* pPacket); 117 | //called by internal requests to external servers (client mode) 118 | //we act as a CoAP Client (sending requests) in this interaction 119 | CoAP_Result_t CoAP_StartNewClientInteraction(CoAP_Message_t* pMsgReq, SocketHandle_t socketHandle, NetEp_t* ServerEp, CoAP_RespHandler_fn_t cb); 120 | CoAP_Result_t CoAP_StartNewGetRequest(char* UriString, SocketHandle_t socketHandle, NetEp_t* ServerEp, CoAP_RespHandler_fn_t cb); 121 | CoAP_Result_t CoAP_StartNewRequest(CoAP_MessageCode_t type, const char* UriString, SocketHandle_t socketHandle, NetEp_t* ServerEp, CoAP_RespHandler_fn_t cb, uint8_t *buf, size_t size); 122 | CoAP_Result_t CoAP_RemoveInteractionsObserver(CoAP_Interaction_t* pIA, CoAP_Token_t token); 123 | CoAP_Result_t CoAP_HandleObservationInReq(CoAP_Interaction_t* pIA); 124 | CoAP_Result_t CoAP_StartNotifyInteractions(CoAP_Res_t* pRes); 125 | CoAP_Result_t CoAP_FreeInteraction(CoAP_Interaction_t** pInteraction); 126 | CoAP_Interaction_t* CoAP_GetLongestPendingInteraction(); 127 | CoAP_Result_t CoAP_DeleteInteraction(CoAP_Interaction_t* pInteractionToDelete); 128 | CoAP_Result_t CoAP_ResetInteractionByHandle(uint16_t MsgID, SocketHandle_t socketHandle, NetEp_t* RstEp); 129 | CoAP_Result_t CoAP_EnqueueLastInteraction(CoAP_Interaction_t* pInteractionToEnqueue); 130 | CoAP_Result_t CoAP_SetSleepInteraction(CoAP_Interaction_t* pIA, uint32_t seconds); 131 | CoAP_Result_t CoAP_EnableAckTimeout(CoAP_Interaction_t* pIA, uint8_t retryNum); 132 | void CoAP_ClearInteractions(CoAP_Interaction_t** pInteraction); 133 | 134 | //client 135 | CoAP_Interaction_t* CoAP_FindInteractionByMessageIdAndEp(CoAP_Interaction_t* pList, uint16_t mID, NetEp_t* fromEp); 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /src/coap_main.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #ifndef COAP_MAIN_H_ 23 | #define COAP_MAIN_H_ 24 | 25 | #include "liblobaro_coap.h" 26 | #include "coap_interaction.h" 27 | 28 | #define HOLDTIME_AFTER_NON_TRANSACTION_END (0) 29 | #define POSTPONE_WAIT_TIME_SEK (3) 30 | #define POSTPONE_MAX_WAIT_TIME (30) 31 | #define CLIENT_MAX_RESP_WAIT_TIME (45) 32 | 33 | #define USE_RFC7641_ADVANCED_TRANSMISSION (1) //Update representation of resource during retry of observe sendout 34 | 35 | #ifdef COAP_ACK_TIMEOUT 36 | #define ACK_TIMEOUT (COAP_ACK_TIMEOUT) 37 | #else 38 | #define ACK_TIMEOUT (8) 39 | #endif 40 | #define ACK_RANDOM_FACTOR (1.5) 41 | #ifdef COAP_MAX_RETRANSMIT 42 | #define MAX_RETRANSMIT (COAP_MAX_RETRANSMIT) 43 | #else 44 | #define MAX_RETRANSMIT (3) 45 | #endif 46 | #define NSTART (1) //todo implement 47 | #define DEFAULT_LEISURE (5) //todo implement 48 | #define PROBING_RATE (1) //[client] 49 | 50 | 51 | typedef struct { 52 | CoAP_Interaction_t *pInteractions; 53 | CoAP_API_t api; 54 | } CoAP_t; 55 | 56 | extern CoAP_t CoAP; //Stack global variables 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/coap_mem.c: -------------------------------------------------------------------------------- 1 | #include "coap.h" 2 | #include "coap_mem.h" 3 | 4 | void CoAP_free(void *a) { 5 | CoAP.api.free(a); 6 | } 7 | 8 | void *CoAP_malloc(size_t size) { 9 | return CoAP.api.malloc(size); 10 | } 11 | 12 | void *CoAP_malloc0(size_t size) { 13 | void *a = CoAP.api.malloc(size); 14 | if (a != NULL) { 15 | memset(a, 0, size); 16 | } 17 | return a; 18 | } 19 | -------------------------------------------------------------------------------- /src/coap_mem.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_COAP_COAP_MEM_H_ 2 | #define SRC_COAP_COAP_MEM_H_ 3 | 4 | #include 5 | 6 | void *CoAP_malloc(size_t size); 7 | void *CoAP_malloc0(size_t size); 8 | void CoAP_free(void *a); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/coap_message.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #include 23 | #include 24 | #include "coap.h" 25 | #include "liblobaro_coap.h" 26 | #include "coap_mem.h" 27 | 28 | static void _rom CoAP_InitToEmptyResetMsg(CoAP_Message_t* msg) { 29 | msg->Type = RST; 30 | msg->Code = EMPTY; 31 | msg->PayloadLength = 0; 32 | msg->PayloadBufSize = 0; 33 | msg->MessageID = 0; 34 | msg->pOptionsList = NULL; 35 | msg->Payload = NULL; 36 | CoAP_Token_t tok = {.Token= {0,0,0,0,0,0,0,0}, .Length = 0}; 37 | msg->Token = tok; 38 | msg->Timestamp = 0; 39 | } 40 | 41 | 42 | bool CoAP_TokenEqual(CoAP_Token_t a, CoAP_Token_t b) { 43 | if (a.Length != b.Length) { 44 | return false; 45 | } 46 | for (int i = 0; i < a.Length; i++) { 47 | if (a.Token[i] != b.Token[i]) { 48 | return false; 49 | } 50 | } 51 | return true; 52 | } 53 | 54 | void _rom CoAP_free_MsgPayload(CoAP_Message_t** Msg) { 55 | return; 56 | /* 57 | if ((*Msg)->Payload == NULL) 58 | return; 59 | 60 | // TODO: this will break us!!! 61 | CoAP.api.free((void*) (*Msg)->Payload); 62 | (*Msg)->Payload = NULL; 63 | (*Msg)->PayloadBufSize = 0; 64 | */ 65 | } 66 | 67 | bool _rom CoAP_MsgIsRequest(CoAP_Message_t* pMsg) { 68 | if (pMsg->Code != EMPTY && pMsg->Code <= REQ_LAST) 69 | return true; 70 | return false; 71 | } 72 | 73 | bool _rom CoAP_MsgIsResponse(CoAP_Message_t* pMsg) { 74 | if (pMsg->Code != EMPTY && pMsg->Code >= RESP_FIRST_2_00) 75 | return true; 76 | return false; 77 | } 78 | 79 | bool _rom CoAP_MsgIsOlderThan(CoAP_Message_t* pMsg, uint32_t timespan) { 80 | if (timeAfter(CoAP.api.rtc1HzCnt(), pMsg->Timestamp + timespan)) { 81 | return true; 82 | } 83 | else { 84 | return false; 85 | } 86 | } 87 | 88 | CoAP_Result_t _rom CoAP_free_Message(CoAP_Message_t** Msg) { 89 | DEBUG("Free message %p\n", *Msg); 90 | if (*Msg == NULL) { 91 | return COAP_OK; //nothing to free 92 | } 93 | 94 | if ((*Msg)->Type == CON) { 95 | DEBUG("- Message memory freed! (CON, MID: %d):\r\n", (*Msg)->MessageID); 96 | } 97 | else if ((*Msg)->Type == NON) { 98 | DEBUG("- Message memory freed! (NON, MID: %d):\r\n", (*Msg)->MessageID); 99 | } 100 | else if ((*Msg)->Type == ACK) { 101 | DEBUG("- Message memory freed! (ACK, MID: %d):\r\n", (*Msg)->MessageID); 102 | } 103 | else if ((*Msg)->Type == RST) { 104 | DEBUG("- Message memory freed! (RST, MID: %d):\r\n", (*Msg)->MessageID); 105 | } 106 | 107 | CoAP_FreeOptionList(&((*Msg)->pOptionsList)); 108 | CoAP_free_MsgPayload(Msg); 109 | 110 | //finally delete msg body 111 | CoAP_free((void*) (*Msg)); 112 | *Msg = NULL; 113 | 114 | return COAP_OK; 115 | } 116 | 117 | static CoAP_MessageType_t _rom CoAP_getRespMsgType(CoAP_Message_t* ReqMsg) { //todo inline it 118 | if (ReqMsg->Type == CON) 119 | return ACK; //for piggybacked responses 120 | else 121 | return NON; 122 | } 123 | 124 | static uint16_t _rom CoAP_getRespMsgID(CoAP_Message_t* ReqMsg) { 125 | if (ReqMsg->Type == CON) 126 | return ReqMsg->MessageID; //for piggybacked responses 127 | else 128 | return CoAP_GetNextMid(); 129 | } 130 | 131 | CoAP_Message_t* _rom CoAP_AllocRespMsg(CoAP_Message_t* ReqMsg, CoAP_MessageCode_t Code, uint16_t PayloadMaxSize) { 132 | return CoAP_CreateMessage(CoAP_getRespMsgType(ReqMsg), Code, CoAP_getRespMsgID(ReqMsg), NULL, 0, PayloadMaxSize, ReqMsg->Token); 133 | } 134 | 135 | CoAP_Message_t* _rom CoAP_CreateMessage(CoAP_MessageType_t Type, 136 | CoAP_MessageCode_t Code, 137 | uint16_t MessageID, 138 | const uint8_t* pPayloadInitialContent, 139 | uint16_t PayloadInitialContentLength, 140 | uint16_t PayloadMaxSize, 141 | CoAP_Token_t Token) { 142 | 143 | //safety checks 144 | if (PayloadInitialContentLength > PayloadMaxSize) { 145 | ERROR("Initial Content bigger than field size!"); 146 | return NULL; 147 | } 148 | 149 | CoAP_Message_t* pMsg = (CoAP_Message_t*) CoAP_malloc0(sizeof(CoAP_Message_t) + PayloadMaxSize); //malloc space 150 | if (pMsg == NULL) { 151 | return NULL; 152 | } 153 | INFO("Created message %p\n", pMsg); 154 | 155 | CoAP_InitToEmptyResetMsg(pMsg); //init 156 | 157 | pMsg->Type = Type; 158 | pMsg->Code = Code; 159 | pMsg->MessageID = MessageID; 160 | pMsg->Token = Token; 161 | pMsg->Timestamp = 0; 162 | 163 | pMsg->PayloadLength = PayloadInitialContentLength; 164 | if (PayloadMaxSize) { 165 | pMsg->PayloadBufSize = PayloadMaxSize; 166 | pMsg->Payload = ((uint8_t*) (pMsg)) + sizeof(CoAP_Message_t); //set pointer 167 | if (pPayloadInitialContent != NULL) { 168 | coap_memcpy((void*) ((pMsg)->Payload), (void*) pPayloadInitialContent, PayloadInitialContentLength); 169 | } 170 | } 171 | 172 | return pMsg; 173 | } 174 | 175 | CoAP_Result_t _rom CoAP_ParseMessageFromDatagram(uint8_t* srcArr, uint16_t srcArrLength, CoAP_Message_t** rxedMsg) { 176 | //we use local mem and copy afterwards because we dont know yet the size of payload buffer 177 | //but want to allocate one block for complete final "rxedMsg" memory without realloc the buf size later. 178 | static CoAP_Message_t Msg; 179 | 180 | uint8_t TokenLength = 0; 181 | 182 | *rxedMsg = NULL; 183 | 184 | if (srcArrLength < 4) 185 | return COAP_PARSE_DATAGRAM_TOO_SHORT; // Minimum Size of CoAP Message = 4 Bytes 186 | 187 | CoAP_InitToEmptyResetMsg(&Msg); 188 | 189 | //1st Header Byte 190 | uint8_t Version = srcArr[0] >> 6u; 191 | if (Version != COAP_VERSION) 192 | return COAP_PARSE_UNKOWN_COAP_VERSION; 193 | 194 | Msg.Type = (srcArr[0] & 0x30u) >> 4u; 195 | TokenLength = srcArr[0] & 0xFu; 196 | if (TokenLength > 8) { 197 | INFO("CoAP-Parse Byte1 Error\r\n"); 198 | return COAP_PARSE_MESSAGE_FORMAT_ERROR; 199 | } // return COAP_PARSE_MESSAGE_FORMAT_ERROR; 200 | 201 | //2nd & 3rd Header Byte 202 | Msg.Code = srcArr[1]; 203 | 204 | //"Hack" to support early version of "myCoAP" iOS app which sends malformed "CoAP-pings" containing a token... 205 | //if(Msg.Code == EMPTY && (TokenLength != 0 || srcArrLength != 4)) {INFO("err2\r\n");return COAP_PARSE_MESSAGE_FORMAT_ERROR;}// return COAP_PARSE_MESSAGE_FORMAT_ERROR; 206 | 207 | uint8_t codeClass = ((uint8_t) Msg.Code) >> 5u; 208 | if (codeClass == 1 || codeClass == 6 || codeClass == 7) { 209 | INFO("CoAP-Parse Byte2/3 Error\r\n"); 210 | return COAP_PARSE_MESSAGE_FORMAT_ERROR; 211 | } // return COAP_PARSE_MESSAGE_FORMAT_ERROR; //reserved classes 212 | 213 | //4th Header Byte 214 | Msg.MessageID = (uint16_t) srcArr[2] << 8u | srcArr[3]; 215 | 216 | //further parsing locations depend on parsed 4Byte CoAP Header -> use of offset addressing 217 | uint16_t offset = 4; 218 | if (srcArrLength == offset) //no more data -> maybe a CoAP Ping 219 | { 220 | goto START_MSG_COPY_LABEL; 221 | //quick end of parsing... 222 | } 223 | 224 | //Token (if any) 225 | CoAP_Token_t tok = {.Token = {0,0,0,0,0,0,0,0}, .Length = TokenLength}; 226 | Msg.Token = tok; 227 | int i; 228 | for (i = 0; i < TokenLength; i++) { 229 | Msg.Token.Token[i] = srcArr[offset + i]; 230 | } 231 | 232 | offset += TokenLength; 233 | if (srcArrLength == offset) 234 | goto START_MSG_COPY_LABEL; 235 | 236 | //Options (if any) 237 | uint8_t* pPayloadBegin = NULL; 238 | 239 | //this allocates memory for every option and puts it in die pOptionsList linked list 240 | //start address of payload also given back 241 | CoAP_Result_t ParseOptionsResult = parse_OptionsFromRaw(&(srcArr[offset]), srcArrLength - offset, &pPayloadBegin, &(Msg.pOptionsList)); 242 | 243 | if (ParseOptionsResult != COAP_OK) { 244 | CoAP_FreeOptionList(&(Msg.pOptionsList)); 245 | INFO("CoAP-Parse Options Error\r\n"); 246 | return ParseOptionsResult; 247 | } 248 | 249 | //Payload (if any) 250 | if (pPayloadBegin != NULL) { 251 | Msg.PayloadLength = srcArrLength - (pPayloadBegin - srcArr); 252 | if (Msg.PayloadLength > MAX_PAYLOAD_SIZE) { 253 | CoAP_FreeOptionList(&(Msg.pOptionsList)); 254 | return COAP_PARSE_TOO_MUCH_PAYLOAD; 255 | } 256 | } else 257 | Msg.PayloadLength = 0; 258 | 259 | Msg.PayloadBufSize = Msg.PayloadLength; 260 | 261 | //Get memory for total message data and copy parsed data 262 | //Payload Buffers MUST located at end of CoAP_Message_t to let this work! 263 | START_MSG_COPY_LABEL: 264 | *rxedMsg = (CoAP_Message_t*) CoAP_malloc(sizeof(CoAP_Message_t) + Msg.PayloadLength); 265 | 266 | if (*rxedMsg == NULL) //out of memory 267 | { 268 | CoAP_FreeOptionList(&(Msg.pOptionsList)); 269 | return COAP_ERR_OUT_OF_MEMORY; 270 | } 271 | 272 | coap_memcpy((void*) (*rxedMsg), (void*) &Msg, sizeof(CoAP_Message_t)); 273 | 274 | if (Msg.PayloadLength) { 275 | (*rxedMsg)->Payload = ((uint8_t*) (*rxedMsg)) + sizeof(CoAP_Message_t); 276 | coap_memcpy((void*) ((*rxedMsg)->Payload), (void*) pPayloadBegin, Msg.PayloadLength); 277 | } 278 | 279 | (*rxedMsg)->Timestamp = CoAP.api.rtc1HzCnt(); 280 | 281 | return COAP_OK; 282 | } 283 | 284 | int CoAP_GetRawSizeOfMessage(CoAP_Message_t* Msg) { 285 | int TotalMsgBytes = 0; 286 | 287 | TotalMsgBytes += 4; //Header 288 | 289 | TotalMsgBytes += CoAP_NeededMem4PackOptions(Msg->pOptionsList); 290 | 291 | if (Msg->Code != EMPTY) { 292 | 293 | if (Msg->PayloadLength) { 294 | TotalMsgBytes += Msg->PayloadLength + 1; //+1 = PayloadMarker 295 | } 296 | 297 | TotalMsgBytes += Msg->Token.Length; 298 | } 299 | 300 | return TotalMsgBytes; 301 | } 302 | 303 | static CoAP_Result_t _rom CoAP_BuildDatagram(uint8_t* destArr, uint16_t* pDestArrSize, CoAP_Message_t* Msg) { 304 | uint16_t offset = 0; 305 | uint8_t TokenLength; 306 | 307 | if (Msg->Code == EMPTY) { //must send only 4 byte header overwrite upper layer in any case! 308 | Msg->PayloadLength = 0; 309 | TokenLength = 0; 310 | } else { 311 | TokenLength = Msg->Token.Length; 312 | } 313 | 314 | // 4Byte Header (see p.16 RFC7252) 315 | destArr[0] = 0; 316 | destArr[0] |= (COAP_VERSION & 3u) << 6u; 317 | destArr[0] |= (Msg->Type & 3u) << 4u; 318 | destArr[0] |= (TokenLength & 15u); 319 | destArr[1] = (uint8_t) Msg->Code; 320 | destArr[2] = (uint8_t) (Msg->MessageID >> 8u); 321 | destArr[3] = (uint8_t) (Msg->MessageID & 0xffu); 322 | 323 | offset += 4; 324 | 325 | // Token (0 to 8 Bytes) 326 | int i; 327 | for (i = 0; i < TokenLength; i++) { 328 | destArr[offset + i] = Msg->Token.Token[i]; 329 | } 330 | offset += TokenLength; 331 | 332 | // Options 333 | if (Msg->pOptionsList != NULL) { 334 | uint16_t OptionsRawByteCount = 0; 335 | //iterates through (ascending sorted!) list of options and encodes them in CoAPs compact binary representation 336 | pack_OptionsFromList(&(destArr[offset]), &OptionsRawByteCount, Msg->pOptionsList); 337 | 338 | offset += OptionsRawByteCount; 339 | } 340 | 341 | //Payload 342 | if (Msg->PayloadLength != 0) { 343 | destArr[offset] = 0xff; //Payload Marker 344 | offset++; 345 | 346 | coap_memcpy((void*) &(destArr[offset]), (void*) (Msg->Payload), Msg->PayloadLength); 347 | 348 | offset += Msg->PayloadLength; 349 | } 350 | 351 | *pDestArrSize = offset; // => Size of Datagram array 352 | return COAP_OK; 353 | } 354 | 355 | //send minimal 4Byte header CoAP empty ACK message 356 | CoAP_Result_t _rom CoAP_SendEmptyAck(uint16_t MessageID, SocketHandle_t socketHandle, NetEp_t receiver) { 357 | CoAP_Message_t Msg; //put on stack (no need to free) 358 | 359 | CoAP_InitToEmptyResetMsg(&Msg); 360 | Msg.Type = ACK; 361 | Msg.MessageID = MessageID; 362 | return CoAP_SendMsg(&Msg, socketHandle, receiver); 363 | } 364 | 365 | //send short response 366 | CoAP_Result_t _rom CoAP_SendShortResp(CoAP_MessageType_t Type, CoAP_MessageCode_t Code, uint16_t MessageID, CoAP_Token_t token, SocketHandle_t socketHandle, NetEp_t receiver) { 367 | CoAP_Message_t Msg; //put on stack (no need to free) 368 | CoAP_InitToEmptyResetMsg(&Msg); 369 | Msg.Type = Type; 370 | Msg.MessageID = MessageID; 371 | Msg.Code = Code; 372 | Msg.Token = token; 373 | return CoAP_SendMsg(&Msg, socketHandle, receiver); 374 | } 375 | 376 | //send minimal 4Byte header CoAP empty RST message 377 | CoAP_Result_t _rom CoAP_SendEmptyRST(uint16_t MessageID, SocketHandle_t socketHandle, NetEp_t receiver) { 378 | CoAP_Message_t Msg; //put on stack (no need to free) 379 | CoAP_InitToEmptyResetMsg(&Msg); 380 | Msg.MessageID = MessageID; 381 | return CoAP_SendMsg(&Msg, socketHandle, receiver); 382 | } 383 | 384 | CoAP_Result_t _rom CoAP_SendMsg(CoAP_Message_t* Msg, SocketHandle_t socketHandle, NetEp_t receiver) { 385 | INFO("Sending CoAP msg\r\n"); 386 | int i; 387 | uint16_t bytesToSend = 0; 388 | CoAP_Socket_t* pSocket = RetrieveSocket(socketHandle); 389 | 390 | if (pSocket == NULL) { 391 | ERROR("Socket not found! handle: %p\r\n", socketHandle); 392 | return COAP_NOT_FOUND; 393 | } 394 | 395 | NetTransmit_fn SendPacket = pSocket->Tx; 396 | uint8_t quickBuf[16]; //speed up sending of tiny messages 397 | 398 | if (SendPacket == NULL) { 399 | ERROR("SendPacket function not found! handle: %p\r\n", socketHandle); 400 | return COAP_NOT_FOUND; 401 | } 402 | 403 | // build generic packet 404 | NetPacket_t pked; 405 | pked.remoteEp = receiver; 406 | 407 | //Alloc raw memory 408 | pked.size = CoAP_GetRawSizeOfMessage(Msg); 409 | if (pked.size <= 16) { //for small messages don't take overhead of mem allocation 410 | pked.pData = quickBuf; 411 | } else { 412 | pked.pData = (uint8_t*) CoAP_malloc(pked.size); 413 | if (pked.pData == NULL) 414 | return COAP_ERR_OUT_OF_MEMORY; 415 | } 416 | 417 | // serialize msg 418 | CoAP_BuildDatagram((pked.pData), &bytesToSend, Msg); 419 | 420 | if (bytesToSend != pked.size) { 421 | INFO("(!!!) Bytes to Send = %d estimated = %d\r\n", bytesToSend, CoAP_GetRawSizeOfMessage(Msg)); 422 | } 423 | 424 | INFO("\r\no>>>>>>>>>>>>>>>>>>>>>>\r\nSend Message [%d Bytes], Interface #%p\r\n", bytesToSend, socketHandle); 425 | INFO("Receiving Endpoint: "); 426 | PrintEndpoint(&(pked.remoteEp)); 427 | INFO("\n"); 428 | 429 | DEBUG("Hex: "); 430 | for (i = 0; i < pked.size; i++) { 431 | DEBUG("%02x ", pked.pData[i]); 432 | } 433 | DEBUG("\r\nRaw: \""); 434 | for (i = 0; i < pked.size; i++) { 435 | DEBUG("%c", CoAP_CharPrintable(pked.pData[i])); 436 | } 437 | DEBUG("\"\r\n"); 438 | 439 | bool sendResult; 440 | #if DEBUG_RANDOM_DROP_OUTGOING_PERCENTAGE > 0 441 | if (CoAP.api.rand() % 100 < DEBUG_RANDOM_DROP_OUTGOING_PERCENTAGE) { 442 | INFO("!!!FAIL!!! on purpose, dropping outgoing message (%d%% chance)\n", DEBUG_RANDOM_DROP_OUTGOING_PERCENTAGE); 443 | sendResult = true; // make stack think it sent message, to simulate loss of UDP packet in network 444 | } else { 445 | sendResult = SendPacket(socketHandle, &pked); 446 | } 447 | #else 448 | sendResult = SendPacket(socketHandle, &pked); 449 | #endif 450 | 451 | if (sendResult == true) { // send COAP_OK! 452 | Msg->Timestamp = CoAP.api.rtc1HzCnt(); 453 | CoAP_PrintMsg(Msg); 454 | INFO("o>>>>>>>>>>OK>>>>>>>>>>\r\n"); 455 | if (pked.pData != quickBuf) { 456 | CoAP_free(pked.pData); 457 | } 458 | return COAP_OK; 459 | } else { 460 | CoAP_PrintMsg(Msg); 461 | INFO("o>>>>>>>>>>FAIL>>>>>>>>>>\r\n"); 462 | if (pked.pData != quickBuf) { 463 | CoAP_free(pked.pData); 464 | } 465 | return COAP_ERR_NETWORK; 466 | } 467 | 468 | } 469 | 470 | static uint16_t MId = 0; 471 | static uint8_t currToken = 0; 472 | 473 | void _rom CoAP_InitIds() { 474 | // Initialise Message-ID and Token with random values: 475 | MId = CoAP.api.rand() & 0xffffu; 476 | currToken = CoAP.api.rand() & 0xffu; 477 | } 478 | 479 | uint16_t _rom CoAP_GetNextMid() { 480 | MId++; 481 | return MId; 482 | } 483 | 484 | // TODO: Improove generated tokens 485 | CoAP_Token_t _rom CoAP_GenerateToken() { 486 | currToken++; 487 | CoAP_Token_t tok = {.Token = {currToken, 0,0,0,0,0,0,0}, .Length = 1}; 488 | return tok; 489 | } 490 | 491 | CoAP_Result_t _rom CoAP_addNewPayloadToMessage(CoAP_Message_t* Msg, uint8_t* pData, uint16_t size) { 492 | if (size > MAX_PAYLOAD_SIZE) { 493 | ERROR("payload > MAX_PAYLOAD_SIZE"); 494 | return COAP_ERR_OUT_OF_MEMORY; 495 | } 496 | 497 | if (size) { 498 | if (Msg->PayloadBufSize >= size) { 499 | coap_memcpy(Msg->Payload, pData, size); //use existing buffer 500 | } else { // will move payload buf outside of msg memory frame! 501 | CoAP_free_MsgPayload(&Msg); //free old buffer 502 | Msg->Payload = (uint8_t*) CoAP.api.malloc(size); //alloc a different new buffer 503 | Msg->PayloadBufSize = size; 504 | 505 | coap_memcpy(Msg->Payload, pData, size); 506 | } 507 | } else { 508 | CoAP_free_MsgPayload(&Msg); 509 | } 510 | 511 | Msg->PayloadLength = size; 512 | 513 | return COAP_OK; 514 | } 515 | 516 | CoAP_Result_t _rom CoAP_addTextPayload(CoAP_Message_t* Msg, char* PayloadStr) { 517 | return CoAP_addNewPayloadToMessage(Msg, (uint8_t*) PayloadStr, (uint16_t) (strlen(PayloadStr))); 518 | } 519 | 520 | void _rom CoAP_PrintMsg(CoAP_Message_t* msg) { 521 | 522 | if(COAP_LOG_LEVEL < COAP_LOG_LEVEL_DEBUG) 523 | { 524 | // Short version 525 | LOG_INFO("CoAP msg: Type="); 526 | 527 | switch(msg->Type) 528 | { 529 | case CON: LOG_INFO("CON(0x%02x)", msg->Type); break; 530 | case NON: LOG_INFO("NON(0x%02x)", msg->Type); break; 531 | case ACK: LOG_INFO("ACK(0x%02x)", msg->Type); break; 532 | case RST: LOG_INFO("RST(0x%02x)", msg->Type); break; 533 | default: LOG_INFO("UNKNOWN (0x%02x)", msg->Type); break; 534 | } 535 | 536 | LOG_INFO(" Code=%s", CoAP_CodeName(msg->Code)); 537 | LOG_INFO(" MsgId=%"PRIu16, msg->MessageID); 538 | LOG_INFO(" Timestamp=%"PRIu32, msg->Timestamp); 539 | LOG_INFO(" PayloadLen=%"PRIu16, msg->PayloadLength); 540 | LOG_INFO("\n"); 541 | return; 542 | } 543 | 544 | INFO("---------CoAP msg--------\r\n"); 545 | 546 | if (msg->Type == CON) { 547 | LOG_DEBUG("*Type: CON (0x%02x)\r\n", msg->Type); 548 | } 549 | else if (msg->Type == NON) { 550 | LOG_DEBUG("*Type: NON (0x%02x)\r\n", msg->Type); 551 | } 552 | else if (msg->Type == ACK) { 553 | LOG_DEBUG("*Type: ACK (0x%02x)\r\n", msg->Type); 554 | } 555 | else if (msg->Type == RST) { 556 | LOG_DEBUG("*Type: RST (0x%02x)\r\n", msg->Type); 557 | } 558 | else { 559 | LOG_DEBUG("*Type: UNKNOWN! (0x%02x)\r\n", msg->Type); 560 | } 561 | 562 | uint8_t tokenBytes = msg->Token.Length; 563 | if (tokenBytes > 0) { 564 | LOG_DEBUG("*Token: %u Byte -> 0x", tokenBytes); 565 | int i; 566 | for (i = 0; i < tokenBytes; i++) { 567 | LOG_DEBUG("%02x", msg->Token.Token[i]); 568 | } 569 | } else { 570 | LOG_DEBUG("*Token: %u Byte -> 0", tokenBytes); 571 | } 572 | 573 | uint8_t code = msg->Code; 574 | LOG_DEBUG("\r\n*Code: %d.%02d (0x%02x) [%s]\r\n", code >> 5u, code & 31u, code, CoAP_CodeName(code)); 575 | 576 | LOG_DEBUG("*MessageId: %u\r\n", msg->MessageID); 577 | 578 | CoAP_printOptionsList(msg->pOptionsList); 579 | if (msg->PayloadLength) { 580 | LOG_DEBUG("*Payload (%u Byte): \r\n", msg->PayloadLength); 581 | if (msg->PayloadLength > MAX_PAYLOAD_SIZE) { 582 | LOG_DEBUG(" too much payload!\r\n"); 583 | } 584 | else { 585 | LOG_DEBUG(" Hex: "); 586 | for (int i = 0; i < msg->PayloadLength && i < MAX_PAYLOAD_SIZE; i++) { 587 | LOG_DEBUG("%02x ", msg->Payload[i]); 588 | } 589 | LOG_DEBUG("\"\r\n Raw: \""); 590 | for (int i = 0; i < msg->PayloadLength && i < MAX_PAYLOAD_SIZE; i++) { 591 | LOG_DEBUG("%c", CoAP_CharPrintable(msg->Payload[i])); 592 | } 593 | LOG_DEBUG("\"\r\n"); 594 | } 595 | } 596 | 597 | INFO("*Timestamp: %"PRIu32"\r\n", msg->Timestamp); 598 | INFO("----------------------------\r\n"); 599 | } 600 | 601 | const char _rom *CoAP_CodeName(CoAP_MessageCode_t code) { 602 | switch (code) { 603 | case EMPTY: 604 | return "EMPTY"; 605 | case REQ_GET: 606 | return "REQ_GET"; 607 | case REQ_POST: 608 | return "REQ_POST"; 609 | case REQ_PUT: 610 | return "REQ_PUT"; 611 | case REQ_DELETE: 612 | return "REQ_DELETE"; 613 | case REQ_FETCH: 614 | return "REQ_FETCH"; 615 | case REQ_PATCH: 616 | return "REQ_PATCH"; 617 | case REQ_IPATCH: 618 | // iPATCH and LAST both 0.07 619 | return "REQ_IPATCH/REQ_LAST"; 620 | case RESP_FIRST_2_00: 621 | return "RESP_FIRST_2_00"; 622 | case RESP_SUCCESS_CREATED_2_01: 623 | return "RESP_SUCCESS_CREATED_2_01"; 624 | case RESP_SUCCESS_DELETED_2_02: 625 | return "RESP_SUCCESS_DELETED_2_02"; 626 | case RESP_SUCCESS_VALID_2_03: 627 | return "RESP_SUCCESS_VALID_2_03"; 628 | case RESP_SUCCESS_CHANGED_2_04: 629 | return "RESP_SUCCESS_CHANGED_2_04"; 630 | case RESP_SUCCESS_CONTENT_2_05: 631 | return "RESP_SUCCESS_CONTENT_2_05"; 632 | case RESP_SUCCESS_CONTINUE_2_31: 633 | return "RESP_SUCCESS_CONTINUE_2_31"; 634 | case RESP_ERROR_BAD_REQUEST_4_00: 635 | return "RESP_ERROR_BAD_REQUEST_4_00"; 636 | case RESP_ERROR_UNAUTHORIZED_4_01: 637 | return "RESP_ERROR_UNAUTHORIZED_4_01"; 638 | case RESP_BAD_OPTION_4_02: 639 | return "RESP_BAD_OPTION_4_02"; 640 | case RESP_FORBIDDEN_4_03: 641 | return "RESP_FORBIDDEN_4_03"; 642 | case RESP_NOT_FOUND_4_04: 643 | return "RESP_NOT_FOUND_4_04"; 644 | case RESP_METHOD_NOT_ALLOWED_4_05: 645 | return "RESP_METHOD_NOT_ALLOWED_4_05"; 646 | case RESP_METHOD_NOT_ACCEPTABLE_4_06: 647 | return "RESP_METHOD_NOT_ACCEPTABLE_4_06"; 648 | case RESP_REQUEST_ENTITY_INCOMPLETE_4_08: 649 | return "RESP_REQUEST_ENTITY_INCOMPLETE_4_08"; 650 | case RESP_PRECONDITION_FAILED_4_12: 651 | return "RESP_PRECONDITION_FAILED_4_12"; 652 | case RESP_REQUEST_ENTITY_TOO_LARGE_4_13: 653 | return "RESP_REQUEST_ENTITY_TOO_LARGE_4_13"; 654 | case RESP_UNSUPPORTED_CONTENT_FORMAT_4_15: 655 | return "RESP_UNSUPPORTED_CONTENT_FORMAT_4_15"; 656 | case RESP_INTERNAL_SERVER_ERROR_5_00: 657 | return "RESP_INTERNAL_SERVER_ERROR_5_00"; 658 | case RESP_NOT_IMPLEMENTED_5_01: 659 | return "RESP_NOT_IMPLEMENTED_5_01"; 660 | case RESP_BAD_GATEWAY_5_02: 661 | return "RESP_BAD_GATEWAY_5_02"; 662 | case RESP_SERVICE_UNAVAILABLE_5_03: 663 | return "RESP_SERVICE_UNAVAILABLE_5_03"; 664 | case RESP_GATEWAY_TIMEOUT_5_04: 665 | return "RESP_GATEWAY_TIMEOUT_5_04"; 666 | case RESP_PROXYING_NOT_SUPPORTED_5_05: 667 | return "RESP_PROXYING_NOT_SUPPORTED_5_05"; 668 | default: 669 | return "UNKNOWN"; 670 | } 671 | } 672 | 673 | void _rom CoAP_PrintResultValue(CoAP_Result_t res) { 674 | if (res == COAP_OK) { 675 | INFO("COAP_OK\r\n"); 676 | } 677 | else if (res == COAP_PARSE_DATAGRAM_TOO_SHORT) { 678 | INFO("COAP_PARSE_DATAGRAM_TOO_SHORT\r\n"); 679 | } 680 | else if (res == COAP_PARSE_UNKOWN_COAP_VERSION) { 681 | INFO("COAP_PARSE_UNKOWN_COAP_VERSION\r\n"); 682 | } 683 | else if (res == COAP_PARSE_MESSAGE_FORMAT_ERROR) { 684 | INFO("COAP_PARSE_MESSAGE_FORMAT_ERROR\r\n"); 685 | } 686 | else if (res == COAP_PARSE_TOO_MANY_OPTIONS) { 687 | INFO("COAP_PARSE_TOO_MANY_OPTIONS\r\n"); 688 | } 689 | else if (res == COAP_PARSE_TOO_LONG_OPTION) { 690 | INFO("COAP_PARSE_TOO_LONG_OPTION\r\n"); 691 | } 692 | else if (res == COAP_PARSE_TOO_MUCH_PAYLOAD) { 693 | INFO("COAP_PARSE_TOO_MUCH_PAYLOAD\r\n"); 694 | } 695 | else if (res == COAP_ERR_OUT_OF_MEMORY) { 696 | INFO("COAP_ERR_OUT_OF_MEMORY\r\n"); 697 | } 698 | else { 699 | INFO("UNKNOWN RESULT\r\n"); 700 | } 701 | } 702 | 703 | bool _rom CoAP_CharIsPrintable(char c) { 704 | // according to: https://en.wikipedia.org/wiki/ASCII#Printable_characters 705 | return c >= 0x20 && c <= 0x7e; 706 | } 707 | 708 | char _rom CoAP_CharPrintable(char c) { 709 | if (CoAP_CharIsPrintable(c)) { 710 | return c; 711 | } else { 712 | return '.'; 713 | } 714 | } 715 | -------------------------------------------------------------------------------- /src/coap_message.h: -------------------------------------------------------------------------------- 1 | /**************************CoAP_Result_t CoAP_RespHandler_fn_t(CoAP_Message_t* pRespMsg, CoAP_Message_t* pReqMsg, NetEp_t* Sender); 2 | ***************************************************** 3 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | *******************************************************************************/ 23 | 24 | #ifndef COAP_MESSAGE_H_ 25 | #define COAP_MESSAGE_H_ 26 | 27 | #include "coap_options.h" 28 | #include "liblobaro_coap.h" 29 | 30 | #define TOKEN_BYTE(num, token) (((uint8_t*)(&token))[num]) 31 | #define TOKEN2STR(token) TOKEN_BYTE(0,token), \ 32 | TOKEN_BYTE(1,token), \ 33 | TOKEN_BYTE(2,token), \ 34 | TOKEN_BYTE(3,token), \ 35 | TOKEN_BYTE(4,token), \ 36 | TOKEN_BYTE(5,token), \ 37 | TOKEN_BYTE(6,token), \ 38 | TOKEN_BYTE(7,token) 39 | 40 | bool CoAP_TokenEqual(CoAP_Token_t a, CoAP_Token_t b); 41 | 42 | CoAP_Message_t* CoAP_CreateMessage(CoAP_MessageType_t Type, CoAP_MessageCode_t Code, 43 | uint16_t MessageID, const uint8_t* pPayloadInitialContent, uint16_t PayloadInitialContentLength, uint16_t PayloadMaxSize, CoAP_Token_t Token); 44 | 45 | CoAP_Result_t CoAP_ParseMessageFromDatagram(uint8_t* srcArr, uint16_t srcArrLength, CoAP_Message_t** rxedMsg); 46 | 47 | CoAP_Result_t CoAP_SendMsg(CoAP_Message_t* Msg, SocketHandle_t socketHandle, NetEp_t receiver); 48 | CoAP_Result_t CoAP_SendEmptyAck(uint16_t MessageID, SocketHandle_t socketHandle, NetEp_t receiver); 49 | CoAP_Result_t CoAP_SendEmptyRST(uint16_t MessageID, SocketHandle_t socketHandle, NetEp_t receiver); 50 | CoAP_Result_t CoAP_SendShortResp(CoAP_MessageType_t Type, CoAP_MessageCode_t Code, uint16_t MessageID, CoAP_Token_t token, SocketHandle_t socketHandle, NetEp_t receiver); 51 | CoAP_Message_t* CoAP_AllocRespMsg(CoAP_Message_t* ReqMsg, CoAP_MessageCode_t Code, uint16_t PayloadMaxSize); 52 | 53 | CoAP_Result_t CoAP_free_Message(CoAP_Message_t** Msg); 54 | void CoAP_free_MsgPayload(CoAP_Message_t** Msg); 55 | 56 | bool CoAP_MsgIsRequest(CoAP_Message_t* pMsg); 57 | bool CoAP_MsgIsResponse(CoAP_Message_t* pMsg); 58 | bool CoAP_MsgIsOlderThan(CoAP_Message_t* pMsg, uint32_t timespan); 59 | 60 | void CoAP_PrintMsg(CoAP_Message_t* msg); 61 | int CoAP_GetRawSizeOfMessage(CoAP_Message_t* Msg); 62 | void CoAP_PrintResultValue(CoAP_Result_t res); 63 | 64 | //note: payload should be set normally by CoAP_SetPayload(...) function, because this function performs 65 | //beside setting the payload also the blockwise transfer logic! 66 | CoAP_Result_t CoAP_addTextPayload(CoAP_Message_t* Msg, char* PayloadStr); 67 | CoAP_Result_t CoAP_addNewPayloadToMessage(CoAP_Message_t* Msg, uint8_t* pData, uint16_t size); 68 | 69 | void CoAP_InitIds(); 70 | uint16_t CoAP_GetNextMid(); 71 | CoAP_Token_t CoAP_GenerateToken(); 72 | const char *CoAP_CodeName(CoAP_MessageCode_t code); 73 | 74 | bool CoAP_CharIsPrintable(char c); 75 | char CoAP_CharPrintable(char c); 76 | 77 | #endif /* COAP_MESSAGE_H_ */ 78 | -------------------------------------------------------------------------------- /src/coap_options.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef COAP_OPTIONS_H_ 24 | #define COAP_OPTIONS_H_ 25 | 26 | 27 | 28 | //on addition of an option also change coap_options.C (array initializer) and count 29 | //Proxy-Uri 30 | extern uint16_t KNOWN_OPTIONS[]; 31 | 32 | #define OPT_FLAG_CRITICAL (1u<<0u) 33 | #define OPT_FLAG_UNSAFE (1u<<1u) 34 | #define OPT_FLAG_NOCACHEKEY (1u<<2u) 35 | #define OPT_FLAG_REPEATABLE (1u<<3u) 36 | 37 | #define MAX_OPTION_VALUE_SIZE (1034) //used in option "Proxy-Uri" 38 | 39 | #define OPTION_PAYLOAD_MARKER (0xFF) 40 | 41 | CoAP_Result_t parse_OptionsFromRaw(uint8_t* srcArr, uint16_t srcLength, uint8_t** pPayloadBeginInSrc, CoAP_option_t** pOptionsListBegin); 42 | CoAP_Result_t pack_OptionsFromList(uint8_t* pDestArr, uint16_t* pBytesWritten, CoAP_option_t* pOptionsListBegin); 43 | uint16_t CoAP_NeededMem4PackOptions(CoAP_option_t* pOptionsListBegin); 44 | 45 | CoAP_option_t* CoAP_FindOptionByNumber(CoAP_Message_t* msg, uint16_t number); 46 | CoAP_Result_t CoAP_AppendUintOptionToList(CoAP_option_t** pOptionsListBegin, uint16_t OptNumber, uint32_t val); 47 | CoAP_Result_t CoAP_AppendOptionToList(CoAP_option_t** pOptionsListBegin, uint16_t OptNumber, const uint8_t* buf, uint16_t length); 48 | CoAP_Result_t CoAP_CopyOptionToList(CoAP_option_t** pOptionsListBegin, CoAP_option_t* OptToCopy); 49 | CoAP_Result_t CoAP_RemoveOptionFromList(CoAP_option_t** pOptionListStart, CoAP_option_t* pOptionToRemove); 50 | CoAP_Result_t CoAP_FreeOptionList(CoAP_option_t** pOptionsListBegin); 51 | 52 | CoAP_Result_t CoAP_GetUintFromOption(const CoAP_option_t* pOption, uint32_t* value); 53 | 54 | uint32_t CoAP_PackBlockParameter(uint32_t num, bool m, uint8_t szx); 55 | void CoAP_UnpackBlockParameter(uint32_t v, uint32_t *num, bool *m, uint8_t *szx); 56 | uint16_t CoAP_DecodeSzx(uint8_t szx); 57 | uint8_t CoAP_EncodeSzx(uint16_t blocksize); 58 | 59 | bool CoAP_OptionsAreEqual(CoAP_option_t* OptA, CoAP_option_t* OptB); 60 | uint16_t CoAP_CheckForUnknownCriticalOption(CoAP_option_t* pOptionsListBegin); 61 | 62 | void CoAP_printOptionsList(CoAP_option_t* pOptListBegin); 63 | 64 | #endif /* COAP_OPTIONS_H_ */ 65 | -------------------------------------------------------------------------------- /src/coap_resource.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #include "coap.h" 23 | #include "liblobaro_coap.h" 24 | #include 25 | 26 | static CoAP_Res_t* pResList = NULL; 27 | static uint32_t ResListMembers = 0; 28 | 29 | uint8_t TempPage[2048]; 30 | 31 | 32 | /** 33 | * Save resource and observe information as options to non volatile storage 34 | * 35 | * @param writeBufFn a function that can write bytes to non volatile memory 36 | * @return 37 | */ 38 | CoAP_Result_t _rom CoAP_NVsaveObservers(WriteBuf_fn writeBufFn) { 39 | CoAP_Res_t* pList = pResList; //List of internal resources 40 | CoAP_option_t* pOptList = NULL; 41 | uint8_t* pTempPage = TempPage; 42 | uint32_t TotalPageBytes = 0; 43 | bool UriPathHasBeenEncoded = false; //uri path options are only encoded one time for each resource and not for every observer 44 | 45 | while (pList != NULL) { //iterate over all resources 46 | UriPathHasBeenEncoded = false; //reset for next resource 47 | 48 | if (pList->pListObservers != NULL) { //Resource has active observers 49 | 50 | CoAP_Observer_t* pObserverList = pList->pListObservers; 51 | 52 | while (pObserverList != NULL) { //iterate over all observers of this resource 53 | 54 | //Store Uri of external Oberserver as option 55 | CoAP_AppendOptionToList(&pOptList, OPT_NUM_URI_HOST, (uint8_t*) &(pObserverList->Ep), sizeof(NetEp_t)); //IP+Port of external Observer 56 | CoAP_AppendOptionToList(&pOptList, OPT_NUM_URI_PORT, (uint8_t*) &(pObserverList->socketHandle), sizeof(SocketHandle_t)); //socketHandle as pseudo "Port" 57 | 58 | CoAP_option_t* pOptionsTemp = pList->pUri; 59 | 60 | //Copy uri-path option of resource 61 | if (UriPathHasBeenEncoded == false) { 62 | while (pOptionsTemp != NULL) { 63 | CoAP_CopyOptionToList(&pOptList, pOptionsTemp); //todo check for sufficent memory or implement cross linking to save memory 64 | pOptionsTemp = pOptionsTemp->next; 65 | } 66 | UriPathHasBeenEncoded = true; //store uri path of resource only once 67 | } 68 | 69 | //Also copy any other options of observe structure (e.g. uri-query) 70 | pOptionsTemp = pObserverList->pOptList; 71 | while (pOptionsTemp != NULL) { 72 | CoAP_CopyOptionToList(&pOptList, pOptionsTemp); //todo check for sufficent memory or implement cross linking to save memory 73 | pOptionsTemp = pOptionsTemp->next; 74 | } 75 | 76 | CoAP_AppendOptionToList(&pOptList, OPT_NUM_LOBARO_TOKEN_SAVE, (uint8_t*) &(pObserverList->Token), 8); 77 | 78 | uint16_t BytesWritten = 0; 79 | pack_OptionsFromList(pTempPage, &BytesWritten, pOptList); 80 | 81 | pTempPage[BytesWritten] = 0xff; //add pseudo "payload"-marker to make reparsing easier 82 | TotalPageBytes += BytesWritten + 1; 83 | pTempPage = pTempPage + TotalPageBytes; 84 | 85 | INFO("Wrote Observer Option List:\r\n"); 86 | CoAP_printOptionsList(pOptList); 87 | 88 | CoAP_FreeOptionList(&pOptList); //free options 89 | pObserverList = pObserverList->next; 90 | } 91 | } 92 | 93 | pList = pList->next; 94 | } 95 | 96 | pTempPage = TempPage; 97 | INFO("writing: %ld bytes to flash\r\n", TotalPageBytes); 98 | writeBufFn(pTempPage, TotalPageBytes); 99 | return COAP_OK; 100 | } 101 | 102 | /** 103 | * Load and attach observers 104 | * @param pRawPage pointer to the non volatile memory to load the observer from 105 | * @return 106 | */ 107 | CoAP_Result_t _rom CoAP_NVloadObservers(uint8_t* pRawPage) { 108 | CoAP_option_t* pOptList = NULL; 109 | CoAP_Res_t* pRes = NULL; 110 | CoAP_Res_t* pResTemp = NULL; 111 | 112 | while (parse_OptionsFromRaw(pRawPage, 2048, &pRawPage, &pOptList) == COAP_OK) { //finds "payload-marker" and sets pointer to its beginning. in this context this is the next stored observe dataset 113 | INFO("found flash stored options:\r\n"); 114 | CoAP_printOptionsList(pOptList); 115 | 116 | //Map Stored option to resource 117 | pResTemp = CoAP_FindResourceByUri(pResList, pOptList); 118 | if (pResTemp != NULL) pRes = pResTemp; 119 | else { 120 | INFO("- Observed Resource not found!\r\n"); 121 | //todo: del observe res? 122 | continue; 123 | } 124 | 125 | 126 | //(re)create observer for resource 127 | CoAP_Observer_t* pNewObserver = CoAP_AllocNewObserver(); 128 | 129 | if (pNewObserver == NULL) { 130 | INFO("pNewObserver out of Mem!\r\n"); 131 | //todo: do anything different to simple continue 132 | continue; 133 | } 134 | 135 | CoAP_option_t* pOpt = pOptList; 136 | while (pOpt != NULL) { 137 | INFO("."); 138 | 139 | switch (pOpt->Number) { 140 | case OPT_NUM_URI_HOST: 141 | coap_memcpy((void*) &(pNewObserver->Ep), pOpt->Value, sizeof(NetEp_t)); 142 | break; 143 | case OPT_NUM_URI_PORT: //"hack" netID, remove if netID removal cleanup done! 144 | coap_memcpy((void*) &(pNewObserver->socketHandle), pOpt->Value, sizeof(SocketHandle_t)); 145 | break; 146 | case OPT_NUM_URI_PATH: 147 | break; //dont copy path to observe struct (it's connected to its resource anyway!) 148 | case OPT_NUM_LOBARO_TOKEN_SAVE: 149 | coap_memcpy((void*) &(pNewObserver->Token), pOpt->Value, 8); 150 | break; 151 | default: 152 | CoAP_CopyOptionToList(&(pNewObserver->pOptList), pOpt); 153 | break; 154 | } 155 | 156 | pOpt = pOpt->next; 157 | } 158 | 159 | //attach observer to resource 160 | CoAP_AppendObserverToList(&(pRes->pListObservers), pNewObserver); 161 | 162 | CoAP_FreeOptionList(&pOptList); //free temp options 163 | } 164 | 165 | CoAP_PrintAllResources(); 166 | //CoAP_FindResourceByUri 167 | return COAP_OK; 168 | } 169 | 170 | 171 | CoAP_HandlerResult_t _rom WellKnown_GetHandler(CoAP_Message_t* pReq, CoAP_Message_t* pResp) { 172 | // static uint8_t wellknownStr[500]; 173 | // uint8_t* pWr = wellknownStr; 174 | 175 | if (pReq->Code != REQ_GET) { 176 | uint8_t errMsg[] = {"CoAP GET only!"}; 177 | pResp->Code = RESP_ERROR_BAD_REQUEST_4_00; 178 | CoAP_SetPayload(pResp, errMsg, (uint16_t) (sizeof(errMsg)-1), true); 179 | return HANDLER_ERROR; 180 | } 181 | 182 | CoAP_Res_t* pList = pResList; //List of internal resources 183 | uint8_t* pStr = (uint8_t*) CoAP.api.malloc((ResListMembers + 1) * 64); //first estimation of needed memory 184 | uint8_t* pStrStart = pStr; 185 | 186 | if (pStr == NULL) { 187 | INFO("- WellKnown_GetHandler(): Ouf memory error!\r\n"); 188 | return HANDLER_ERROR; 189 | } 190 | memset(pStr, 0, (ResListMembers + 1) * 64); 191 | 192 | INFO("- WellKnown_GetHandler(): res cnt:%u temp alloc:%u\r\n", (unsigned int) ResListMembers, (unsigned int) (ResListMembers + 2) * 64); 193 | 194 | //TODO: Implement non ram version, e.g. write to memory to eeprom 195 | while (pList != NULL) { 196 | CoAP_option_t* pUriOpt = pList->pUri; 197 | 198 | *pStr++ = '<'; 199 | while (pUriOpt != NULL) { 200 | *pStr++ = '/'; 201 | coap_memcpy(pStr, pUriOpt->Value, pUriOpt->Length); 202 | pStr += pUriOpt->Length; 203 | pUriOpt = pUriOpt->next; 204 | } 205 | *pStr++ = '>'; 206 | if (pList->Options.Cf != COAP_CF_LINK_FORMAT) { 207 | if( pList->pDescription != NULL ) { 208 | pStr += coap_sprintf((char *) pStr, ";title=\"%s\"", pList->pDescription); 209 | } 210 | 211 | pStr += coap_sprintf((char *) pStr, ";ct=%d", pList->Options.Cf); 212 | 213 | if (pList->Notifier != NULL) { 214 | pStr += coap_sprintf((char*) pStr, ";obs"); 215 | } 216 | } 217 | *pStr++ = ','; 218 | 219 | pList = pList->next; 220 | 221 | //TODO: implement growing of buf/overwrite check 222 | } 223 | 224 | CoAP_SetPayload(pResp, pStrStart, (uint16_t) coap_strlen((char*) pStrStart), true); 225 | CoAP.api.free(pStrStart); 226 | 227 | CoAP_AddCfOptionToMsg(pResp, COAP_CF_LINK_FORMAT); 228 | 229 | return HANDLER_OK; 230 | } 231 | 232 | void _rom CoAP_InitResources() { 233 | CoAP_ResOpts_t Options = {.Cf = COAP_CF_LINK_FORMAT, .AllowedMethods = RES_OPT_GET}; 234 | CoAP_CreateResource("/.well-known/core", "\0", Options, WellKnown_GetHandler, NULL); 235 | } 236 | 237 | static CoAP_Result_t _rom CoAP_AppendResourceToList(CoAP_Res_t** pListStart, CoAP_Res_t* pResToAdd) { 238 | if (pResToAdd == NULL) return COAP_ERR_ARGUMENT; 239 | 240 | if (*pListStart == NULL) //List empty? create new first element 241 | { 242 | *pListStart = pResToAdd; 243 | (*pListStart)->next = NULL; 244 | } else //append new element at end 245 | { 246 | CoAP_Res_t* pRes = *pListStart; 247 | while (pRes->next != NULL) pRes = pRes->next; 248 | 249 | pRes->next = pResToAdd; 250 | pRes = pRes->next; 251 | pRes->next = NULL; 252 | } 253 | return COAP_OK; 254 | } 255 | 256 | CoAP_Result_t _rom CoAP_FreeResource(CoAP_Res_t** pResource) { 257 | CoAP_FreeOptionList(&(*pResource)->pUri); 258 | 259 | CoAP.api.free((*pResource)->pDescription); 260 | CoAP.api.free((void*) (*pResource)); 261 | *pResource = NULL; 262 | return COAP_OK; 263 | } 264 | 265 | //static CoAp_Result_t CoAP_UnlinkResourceFromList(CoAP_Res_t** pListStart, CoAP_Res_t* pResToRemove, bool FreeUnlinked) 266 | //{ 267 | // CoAP_Res_t* currP; 268 | // CoAP_Res_t* prevP; 269 | // 270 | // // For 1st node, indicate there is no previous. 271 | // prevP = NULL; 272 | // 273 | // //Visit each node, maintaining a pointer to 274 | // //the previous node we just visited. 275 | // for (currP = *pListStart; currP != NULL; 276 | // prevP = currP, currP = currP->next) { 277 | // 278 | // if (currP == pResToRemove) { // Found it. 279 | // if (prevP == NULL) { 280 | // //Fix beginning pointer. 281 | // *pListStart = currP->next; 282 | // } else { 283 | // //Fix previous node's next to 284 | // //skip over the removed node. 285 | // prevP->next = currP->next; 286 | // } 287 | // 288 | // // Deallocate the node. 289 | // if(FreeUnlinked) CoAP_FreeResource(&currP); 290 | // //Done searching. 291 | // ResListMembers--; 292 | // return COAP_OK; 293 | // } 294 | // } 295 | // return COAP_OK; 296 | //} 297 | 298 | CoAP_Res_t* _rom CoAP_FindResourceByUri(CoAP_Res_t* pResListToSearchIn, CoAP_option_t* pOptionsToMatch) { 299 | CoAP_Res_t* pList = pResList; 300 | if (pResListToSearchIn != NULL) { 301 | pList = pResListToSearchIn; 302 | } 303 | 304 | for (; pList != NULL; pList = pList->next) { 305 | if (CoAP_UriOptionsAreEqual(pList->pUri, pOptionsToMatch)) { 306 | return pList; 307 | } 308 | } 309 | 310 | return NULL; 311 | } 312 | 313 | CoAP_Res_t* _rom CoAP_CreateResource(char* Uri, char* Descr, CoAP_ResOpts_t Options, CoAP_ResourceHandler_fPtr_t pHandlerFkt, CoAP_ResourceNotifier_fPtr_t pNotifierFkt) { 314 | INFO("Creating resource %s (%s) AllowedMethods: %x%x%x%x\r\n", Uri, Descr == NULL ? "" : Descr, 315 | !!(Options.AllowedMethods & RES_OPT_GET), 316 | !!(Options.AllowedMethods & RES_OPT_POST), 317 | !!(Options.AllowedMethods & RES_OPT_PUT), 318 | !!(Options.AllowedMethods & RES_OPT_DELETE)); 319 | 320 | if (Options.AllowedMethods == 0) { 321 | ERROR("Can not create Resource that does not allow any method!"); 322 | return NULL; 323 | } 324 | 325 | CoAP_Res_t* pRes = (CoAP_Res_t*) (CoAP.api.malloc(sizeof(CoAP_Res_t))); 326 | if (pRes == NULL) { 327 | return NULL; 328 | } 329 | memset(pRes, 0, sizeof(CoAP_Res_t)); 330 | 331 | pRes->pListObservers = NULL; 332 | pRes->pUri = NULL; 333 | pRes->next = NULL; 334 | 335 | pRes->Options = Options; 336 | 337 | if (Descr != NULL && *Descr != '\0') { 338 | pRes->pDescription = (char*) (CoAP.api.malloc(sizeof(char) * (coap_strlen(Descr) + 1))); 339 | coap_strcpy(pRes->pDescription, Descr); 340 | } else { 341 | pRes->pDescription = NULL; 342 | } 343 | 344 | CoAP_AppendUriOptionsFromString(&(pRes->pUri), Uri); 345 | 346 | pRes->Handler = pHandlerFkt; 347 | pRes->Notifier = pNotifierFkt; 348 | 349 | CoAP_AppendResourceToList(&pResList, pRes); 350 | 351 | ResListMembers++; 352 | 353 | return pRes; 354 | } 355 | 356 | 357 | CoAP_Result_t _rom CoAP_NotifyResourceObservers(CoAP_Res_t* pRes) { 358 | pRes->UpdateCnt++; 359 | CoAP_StartNotifyInteractions(pRes); //async start of update interaction 360 | return COAP_OK; 361 | } 362 | 363 | 364 | void _rom CoAP_PrintResource(CoAP_Res_t* pRes) { 365 | CoAP_printUriOptionsList(pRes->pUri); 366 | INFO("Observers:\r\n"); 367 | CoAP_Observer_t* pOpserver = pRes->pListObservers; //point to ListStart 368 | while (pOpserver != NULL) { 369 | INFO("Token (%"PRIu8"): %016"PRIx64" - ", pOpserver->Token.Length, (uint64_t)pOpserver->Token.Token[0]); 370 | PrintEndpoint(&(pOpserver->Ep)); 371 | INFO("\n"); 372 | CoAP_printUriOptionsList(pOpserver->pOptList); 373 | CoAP_printOptionsList(pOpserver->pOptList); 374 | INFO("---\r\n"); 375 | pOpserver = pOpserver->next; 376 | 377 | } 378 | INFO("\r\n"); 379 | } 380 | 381 | void _rom CoAP_PrintAllResources() { 382 | CoAP_Res_t* pRes = pResList; 383 | while (pRes != NULL) { 384 | CoAP_PrintResource(pRes); 385 | pRes = pRes->next; 386 | } 387 | } 388 | 389 | 390 | CoAP_Result_t _rom CoAP_RemoveObserverFromResource(CoAP_Observer_t** pObserverList, SocketHandle_t socketHandle, NetEp_t* pRemoteEP, CoAP_Token_t token) { 391 | CoAP_Observer_t* pObserver = *pObserverList; 392 | 393 | while (pObserver != NULL) { //found right existing observation -> delete it 394 | 395 | if (CoAP_TokenEqual(token, pObserver->Token) && socketHandle == pObserver->socketHandle && EpAreEqual(pRemoteEP, &(pObserver->Ep))) { 396 | 397 | INFO("- (!) Unlinking observer from resource\r\n"); 398 | CoAP_UnlinkObserverFromList(pObserverList, pObserver, true); 399 | return COAP_REMOVED; 400 | 401 | } 402 | pObserver = pObserver->next; 403 | } 404 | return COAP_ERR_NOT_FOUND; 405 | } 406 | -------------------------------------------------------------------------------- /src/coap_resource.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #ifndef SRC_COAP_COAP_RES_H_ 23 | #define SRC_COAP_COAP_RES_H_ 24 | 25 | typedef uint32_t (* CoAP_ResourceGetETag_fPtr_t)(); 26 | 27 | typedef bool (* WriteBuf_fn)(uint8_t* data, uint32_t len); 28 | 29 | 30 | CoAP_Res_t* CoAP_FindResourceByUri(CoAP_Res_t* pResListToSearchIn, CoAP_option_t* pOptionsToMatch); 31 | CoAP_Result_t CoAP_NotifyResourceObservers(CoAP_Res_t* pRes); 32 | CoAP_Result_t CoAP_FreeResource(CoAP_Res_t** pResource); 33 | 34 | void CoAP_PrintResource(CoAP_Res_t* pRes); 35 | void CoAP_PrintAllResources(); 36 | 37 | void CoAP_InitResources(); 38 | 39 | CoAP_Result_t CoAP_NVsaveObservers(WriteBuf_fn writeBufFn); 40 | CoAP_Result_t CoAP_NVloadObservers(uint8_t* pRawPage); 41 | 42 | CoAP_Result_t CoAP_RemoveObserverFromResource(CoAP_Observer_t** pObserverList, SocketHandle_t socketHandle, NetEp_t* pRemoteEP, CoAP_Token_t token); 43 | 44 | #endif /* SRC_COAP_COAP_RESOURCES_H_ */ 45 | -------------------------------------------------------------------------------- /src/diagnostic.c: -------------------------------------------------------------------------------- 1 | #include "coap.h" 2 | #include 3 | 4 | char* InteractionRoleToString(CoAP_InteractionRole_t role) { 5 | switch (role) { 6 | case COAP_ROLE_NOT_SET: 7 | return "NOT_SET"; 8 | case COAP_ROLE_SERVER: 9 | return "SERVER"; 10 | case COAP_ROLE_NOTIFICATION: 11 | return "NOTIFICATION"; 12 | case COAP_ROLE_CLIENT: 13 | return "CLIENT"; 14 | default: 15 | return "UNKNOWN_ROLE"; 16 | } 17 | } 18 | 19 | char* InteractionStateToString(CoAP_InteractionState_t state) { 20 | switch (state) { 21 | case COAP_STATE_NOT_SET: 22 | return "NOT_SET"; 23 | case COAP_STATE_HANDLE_REQUEST: 24 | return "HANDLE_REQUEST"; 25 | case COAP_STATE_RESOURCE_POSTPONE_EMPTY_ACK_SENT: 26 | return "RESOURCE_POSTPONE_EMPTY_ACK_SENT"; 27 | case COAP_STATE_RESPONSE_SENT: 28 | return "RESPONSE_SENT"; 29 | case COAP_STATE_RESPONSE_WAITING_LEISURE: 30 | return "RESPONSE_WAITING_LEISURE"; 31 | case COAP_STATE_READY_TO_NOTIFY: 32 | return "READY_TO_NOTIFY"; 33 | case COAP_STATE_NOTIFICATION_SENT: 34 | return "NOTIFICATION_SENT"; 35 | case COAP_STATE_READY_TO_REQUEST: 36 | return "READY_TO_REQUEST"; 37 | case COAP_STATE_WAITING_RESPONSE: 38 | return "WAITING_RESPONSE"; 39 | case COAP_STATE_HANDLE_RESPONSE: 40 | return "HANDLE_RESPONSE"; 41 | case COAP_STATE_FINISHED: 42 | return "FINISHED"; 43 | default: 44 | return "UNKNOWN_STATE"; 45 | } 46 | } 47 | 48 | char* ResultToString(CoAP_Result_t res) { 49 | switch (res) { 50 | case COAP_OK: 51 | return "COAP_OK"; 52 | case COAP_NOT_FOUND: 53 | return "COAP_NOT_FOUND"; 54 | case COAP_PARSE_DATAGRAM_TOO_SHORT: 55 | return "COAP_PARSE_DATAGRAM_TOO_SHORT"; 56 | case COAP_PARSE_UNKOWN_COAP_VERSION: 57 | return "COAP_PARSE_UNKOWN_COAP_VERSION"; 58 | case COAP_PARSE_MESSAGE_FORMAT_ERROR: 59 | return "COAP_PARSE_MESSAGE_FORMAT_ERROR"; 60 | case COAP_PARSE_TOO_MANY_OPTIONS: 61 | return "COAP_PARSE_TOO_MANY_OPTIONS"; 62 | case COAP_PARSE_TOO_LONG_OPTION: 63 | return "COAP_PARSE_TOO_LONG_OPTION"; 64 | case COAP_PARSE_TOO_MUCH_PAYLOAD: 65 | return "COAP_PARSE_TOO_MUCH_PAYLOAD"; 66 | case COAP_PACK_TOO_MANY_OPTIONS: 67 | return "COAP_PACK_TOO_MANY_OPTIONS"; 68 | case COAP_PACK_TOO_LONG_OPTION: 69 | return "COAP_PACK_TOO_LONG_OPTION"; 70 | case COAP_ERR_ARGUMENT: 71 | return "COAP_ERR_ARGUMENT"; 72 | case COAP_ERR_SOCKET: 73 | return "COAP_ERR_SOCKET"; 74 | case COAP_ERR_NETWORK: 75 | return "COAP_ERR_NETWORK"; 76 | case COAP_ERR_OUT_OF_MEMORY: 77 | return "COAP_ERR_OUT_OF_MEMORY"; 78 | case COAP_ERR_TOO_LONG_URI_PATH: 79 | return "COAP_ERR_TOO_LONG_URI_PATH"; 80 | case COAP_ERR_NOT_FOUND: 81 | return "COAP_ERR_NOT_FOUND"; 82 | case COAP_ERR_WRONG_OPTION: 83 | return "COAP_ERR_WRONG_OPTION"; 84 | case COAP_ERR_EXISTING: 85 | return "COAP_ERR_EXISTING"; 86 | case COAP_TRUE: 87 | return "COAP_TRUE"; 88 | case COAP_FALSE: 89 | return "COAP_FALSE"; 90 | case COAP_ERR_WRONG_REQUEST: 91 | return "COAP_ERR_WRONG_REQUEST"; 92 | case COAP_BAD_OPTION_VAL: 93 | return "COAP_BAD_OPTION_VAL"; 94 | case COAP_BAD_OPTION_LEN: 95 | return "COAP_BAD_OPTION_LEN"; 96 | case COAP_REMOVED: 97 | return "COAP_REMOVED"; 98 | case COAP_ERR_UNKNOWN: 99 | return "COAP_ERR_UNKNOWN"; 100 | case COAP_ERR_REMOTE_RST: 101 | return "COAP_ERR_REMOTE_RST"; 102 | case COAP_ERR_OUT_OF_ATTEMPTS: 103 | return "COAP_ERR_OUT_OF_ATTEMPTS"; 104 | case COAP_ERR_TIMEOUT: 105 | return "COAP_ERR_TIMEOUT"; 106 | case COAP_WAITING: 107 | return "COAP_WAITING"; 108 | case COAP_HOLDING_BACK: 109 | return "COAP_HOLDING_BACK"; 110 | case COAP_RETRY: 111 | return "COAP_RETRY"; 112 | default: 113 | return "UNKNOWN_RESULT"; 114 | } 115 | } 116 | 117 | char* ReliabilityStateToString(CoAP_ConfirmationState_t state) { 118 | switch (state) { 119 | case NOT_SET: 120 | return "NOT_SET"; 121 | case ACK_SEND: 122 | return "ACK_SET"; 123 | case RST_SEND: 124 | return "RST_SET"; 125 | default: 126 | return "UNKNOWN_STATE"; 127 | } 128 | } 129 | 130 | void _rom PrintEndpoint(const NetEp_t* ep) { 131 | switch (ep->NetType) { 132 | case EP_NONE: 133 | INFO("NONE"); 134 | break; 135 | case IPV6: 136 | INFO("IPv6, ["); 137 | PRINT_IPV6(ep->NetAddr.IPv6); 138 | INFO("]: %u", ep->NetPort); 139 | break; 140 | case IPV4: 141 | INFO("IPv4, %d.%d.%d.%d:%d", 142 | ep->NetAddr.IPv4.u8[0], ep->NetAddr.IPv4.u8[1], ep->NetAddr.IPv4.u8[2], ep->NetAddr.IPv4.u8[3], 143 | ep->NetPort); 144 | break; 145 | case BTLE: 146 | INFO("BTLE"); 147 | break; 148 | case UART: 149 | INFO("UART, COM%d", ep->NetAddr.Uart.ComPortID); 150 | break; 151 | default: 152 | INFO("UNKNOWN_EP (%d)", ep->NetType); 153 | } 154 | } 155 | 156 | void PrintToken(CoAP_Token_t* token) { 157 | uint8_t tokenBytes = token->Length; 158 | if (tokenBytes > 0) { 159 | INFO("%u Byte -> 0x", tokenBytes); 160 | int i; 161 | for (i = 0; i < tokenBytes; i++) { 162 | INFO("%02x", token->Token[i]); 163 | } 164 | } else { 165 | INFO("%u Byte -> 0x0", tokenBytes); 166 | } 167 | } 168 | 169 | void _rom PrintInteractions(CoAP_Interaction_t *pInteractions) { 170 | (void)pInteractions; 171 | int cnt = 0; 172 | for (CoAP_Interaction_t* pIA = CoAP.pInteractions; pIA != NULL; pIA = pIA->next) { 173 | cnt++; 174 | } 175 | 176 | INFO("Interactions: %d\n", cnt); 177 | INFO("-------------\n"); 178 | for (CoAP_Interaction_t* pIA = CoAP.pInteractions; pIA != NULL; pIA = pIA->next) { 179 | INFO("- Role: %s, State: %s\n", 180 | InteractionRoleToString(pIA->Role), InteractionStateToString(pIA->State)); 181 | INFO("RetransCnt: %u, SleepUntil %"PRIu32", AckTimeout: %"PRIu32"\n", pIA->RetransCounter, pIA->SleepUntil, pIA->AckTimeout); 182 | INFO("Socket: %p, RemoteEp: ", pIA->socketHandle); 183 | PrintEndpoint(&pIA->RemoteEp); 184 | INFO("\n"); 185 | INFO("ReqReliabilityState: %s\n", ReliabilityStateToString(pIA->ReqConfirmState)); 186 | INFO("RespReliabilityState: %s\n", ReliabilityStateToString(pIA->ResConfirmState)); 187 | 188 | INFO("Observer: "); 189 | if (pIA->pObserver != NULL) { 190 | PrintEndpoint(&pIA->pObserver->Ep); 191 | INFO(", Socket: %p, FailCnt: %d, Token: ", pIA->pObserver->socketHandle, pIA->pObserver->FailCount); 192 | PrintToken(&pIA->pObserver->Token); 193 | INFO("\n"); 194 | } else { 195 | INFO("NONE\n"); 196 | } 197 | 198 | if (pIA->UpdatePendingNotification) { 199 | INFO("Update Pending Notifications\n"); 200 | } 201 | 202 | // TODO: Consider: Resource description and observers 203 | } 204 | INFO("-------------\n\n"); 205 | } 206 | -------------------------------------------------------------------------------- /src/diagnostic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * diagnostic.h 3 | * 4 | * Created on: 11.05.2017 5 | * Author: Tobias 6 | */ 7 | 8 | #ifndef LOBARO_COAP_SRC_DIAGNOSTIC_H_ 9 | #define LOBARO_COAP_SRC_DIAGNOSTIC_H_ 10 | 11 | /* 12 | * Methods for debugging the stack 13 | */ 14 | 15 | 16 | void PrintInteractions(CoAP_Interaction_t *pInteractions); 17 | void PrintEndpoint(const NetEp_t* ep); 18 | char* ResultToString(CoAP_Result_t res); 19 | 20 | #endif /* LOBARO_COAP_SRC_DIAGNOSTIC_H_ */ 21 | -------------------------------------------------------------------------------- /src/interface/coap_interface.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef USER_LOBARO_COAP_INTERFACE_COAP_INTERFACE_H_ 24 | #define USER_LOBARO_COAP_INTERFACE_COAP_INTERFACE_H_ 25 | 26 | //------------------------------------------------------------------------- 27 | // lobaro-coap needs some std C language functions/headers 28 | //------------------------------------------------------------------------- 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #ifdef LOBARO_HAL_ESP8266 //can be defined as "-D" compiler option 36 | //ESP8266 specific glue (because the sdk has no complete std c lib) 37 | //ESP8266 with partial libC from SDK 38 | #include 39 | #include 40 | 41 | #define coap_sprintf os_sprintf 42 | #define coap_printf ets_uart_printf 43 | #define coap_memcpy os_memcpy 44 | #define coap_memset os_memset 45 | #define coap_memmove os_memmove 46 | #define coap_strlen os_strlen 47 | #define coap_strstr os_strstr 48 | #define coap_strcpy os_strcpy 49 | #define coap_memcmp os_memcmp 50 | 51 | #define _rom ICACHE_FLASH_ATTR 52 | #define _ram 53 | #else //in most cases a full standard C lib will be available and used by the stack 54 | #include 55 | #include 56 | 57 | #define coap_sprintf sprintf 58 | #define coap_printf printf 59 | #define coap_memcpy memcpy 60 | #define coap_memset memset 61 | #define coap_memmove memmove 62 | #define coap_strlen strlen 63 | #define coap_strstr strstr 64 | #define coap_strcpy strcpy 65 | #define coap_memcmp memcmp 66 | 67 | #define _rom 68 | #define _ram 69 | #endif 70 | 71 | //Debug/Uart/Terminal Output 72 | #include "debug/coap_debug.h" 73 | 74 | //---------------------------------------------------------------------------------- 75 | //Interface "glue" to surrounding project/software 76 | //Lobaro CoAP manages external network send and receive function in a pool of 77 | //interfaces addressed by a socketHandle. To port the lib you must do the following: 78 | //1) Get a free interface buffer from the CoAP stack by calling "CoAP_Socket_t* CoAP_NewSocket(SocketHandle_t handle)" 79 | // This will give you a prefilled structure to work with. 80 | //2) Attach your Send/TX data function/callback following the "NetTransmit_fn" function signature 81 | //In the rx function you must determinate (or remember) your socket handle, because the stack uses only this as key 82 | //3) Listen to socket 83 | //---------------------------------------------------------------------------------- 84 | #include "network/net_Endpoint.h" 85 | #include "network/net_Packet.h" 86 | #include "network/net_Socket.h" 87 | 88 | //------------------------------------------------------------------------- 89 | //Implementation for these function prototypes must be provided externally: 90 | //------------------------------------------------------------------------- 91 | //Uart/Display function to print debug/status messages to 92 | void hal_debug_puts(char *s); 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/interface/debug/coap_debug.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #include "../coap_interface.h" 23 | 24 | 25 | char dbgBuf[DEBUG_BUF_SIZE]; 26 | -------------------------------------------------------------------------------- /src/interface/debug/coap_debug.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #ifndef COM_DEBUG_H 23 | #define COM_DEBUG_H 24 | 25 | #define COAP_LOG_LEVEL_DEBUG 3 26 | #define COAP_LOG_LEVEL_INFO 2 27 | #define COAP_LOG_LEVEL_ERROR 1 28 | #define COAP_LOG_LEVEL_NONE 0 29 | 30 | #define COAP_LOG_LEVEL COAP_LOG_LEVEL_INFO 31 | 32 | #define DEBUG_BUF_SIZE (500) 33 | extern char dbgBuf[DEBUG_BUF_SIZE]; 34 | 35 | #define UART_PUTS hal_debug_puts 36 | 37 | 38 | #define PUTS(...) \ 39 | do { \ 40 | UART_PUTS(__VA_ARGS__); \ 41 | } while(0) 42 | 43 | #define PRINTF(...) \ 44 | do { \ 45 | coap_sprintf(dbgBuf,__VA_ARGS__);\ 46 | PUTS(dbgBuf); \ 47 | } while(0) 48 | 49 | 50 | #if (COAP_LOG_LEVEL >= COAP_LOG_LEVEL_ERROR) 51 | #define ERROR(...) \ 52 | do { \ 53 | PRINTF("[ERROR] "); \ 54 | PRINTF(__VA_ARGS__); \ 55 | } while(0) 56 | #else 57 | #define ERROR(...) do{}while(0) 58 | #endif 59 | 60 | #if (COAP_LOG_LEVEL >= COAP_LOG_LEVEL_INFO) 61 | #define INFO(...) \ 62 | do { \ 63 | PRINTF(__VA_ARGS__); \ 64 | } while(0) 65 | #else 66 | #define INFO(...) do{}while(0) 67 | #endif 68 | 69 | #if (COAP_LOG_LEVEL >= COAP_LOG_LEVEL_DEBUG) 70 | #define DEBUG(...) \ 71 | do { \ 72 | PRINTF(__VA_ARGS__); \ 73 | } while(0) 74 | #else 75 | #define DEBUG(...) do{}while(0) 76 | #endif 77 | 78 | #define assert_coap(VAL) \ 79 | do { \ 80 | if(!(VAL)) { PRINTF("!!! ASSERT FAILED [line: %d at %s]!!!\r\n", __LINE__, __FILE__); } \ 81 | } while(0) 82 | 83 | 84 | #define PRINT_IPV6(IP) \ 85 | do { \ 86 | PRINTF("%02x%02x:%02x%02x:%02x%02x" \ 87 | ":%02x%02x:%02x%02x:%02x%02x" \ 88 | ":%02x%02x:%02x%02x", \ 89 | ( IP ) . u8 [ 0 ], ( IP ) . u8 [ 1 ], \ 90 | ( IP ) . u8 [ 2 ], ( IP ) . u8 [ 3 ], \ 91 | ( IP ) . u8 [ 4 ], ( IP ) . u8 [ 5 ], \ 92 | ( IP ) . u8 [ 6 ], ( IP ) . u8 [ 7 ], \ 93 | ( IP ) . u8 [ 8 ], ( IP ) . u8 [ 9 ], \ 94 | ( IP ) . u8 [ 10 ], ( IP ) . u8 [ 11 ], \ 95 | ( IP ) . u8 [ 12 ], ( IP ) . u8 [ 13 ], \ 96 | ( IP ) . u8 [ 14 ], ( IP ) . u8 [ 15 ] ); \ 97 | } while(0) 98 | 99 | #define LOG_INFO(...) INFO(__VA_ARGS__) 100 | #define LOG_ERROR(...) ERROR(__VA_ARGS__) 101 | #define LOG_DEBUG(...) DEBUG(__VA_ARGS__) 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /src/interface/network/net_Endpoint.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include 25 | #include "../../coap.h" 26 | 27 | const NetAddr_IPv6_t NetAddr_IPv6_unspecified = {.u8 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}; 28 | const NetAddr_IPv6_t NetAddr_IPv6_mulitcast = {.u8 = {0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }}; 29 | const NetAddr_IPv4_t NetAddr_IPv4_unspecified = { .u8 = { 0, 0, 0, 0 } }; 30 | const NetAddr_IPv4_t NetAddr_IPv4_mulitcast = { .u8 = { 224, 0, 1, 187 } }; 31 | 32 | const NetEp_t NetEp_IPv6_mulitcast = { .NetType = IPV6, .NetPort = 5683, .NetAddr = { .IPv6 = {.u8 = {0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } } } }; 33 | const NetEp_t NetEp_IPv4_mulitcast = { .NetType = IPV4, .NetPort = 5683, .NetAddr = { .IPv4 = { .u8 = { 224, 0, 1, 187 } } } }; 34 | 35 | bool _rom EpAreEqual(const NetEp_t* ep_A, const NetEp_t* ep_B) 36 | { 37 | if (!ep_A || !ep_B) { return false; } 38 | if (ep_A->NetType != ep_B->NetType) { return false; } 39 | if (ep_A->NetPort != ep_B->NetPort) { return false; } 40 | 41 | if (ep_A->NetType == IPV6) { 42 | if (ep_A->NetAddr.IPv6.u32[0] != ep_B->NetAddr.IPv6.u32[0] 43 | || ep_A->NetAddr.IPv6.u32[1] != ep_B->NetAddr.IPv6.u32[1] 44 | || ep_A->NetAddr.IPv6.u32[2] != ep_B->NetAddr.IPv6.u32[2] 45 | || ep_A->NetAddr.IPv6.u32[3] != ep_B->NetAddr.IPv6.u32[3]) { return false; } 46 | } else if (ep_A->NetType == IPV4) { 47 | if (ep_A->NetAddr.IPv4.u8[0] != ep_B->NetAddr.IPv4.u8[0] 48 | || ep_A->NetAddr.IPv4.u8[1] != ep_B->NetAddr.IPv4.u8[1] 49 | || ep_A->NetAddr.IPv4.u8[2] != ep_B->NetAddr.IPv4.u8[2] 50 | || ep_A->NetAddr.IPv4.u8[3] != ep_B->NetAddr.IPv4.u8[3]) { return false; } 51 | } else { 52 | int i; 53 | for (i = 0; i < NetAddr_MAX_LENGTH; i++) { 54 | if (ep_A->NetAddr.mem[i] != ep_B->NetAddr.mem[i]) { return false; } 55 | } 56 | } 57 | 58 | return true; 59 | } 60 | 61 | void _rom CopyEndpoints(NetEp_t* Destination, const NetEp_t* Source) { 62 | memmove((void*) Destination, (const void*) Source, sizeof(NetEp_t)); 63 | } 64 | 65 | NetInterfaceType_t _rom CoAP_ParseNetAddress(NetAddr_t *addr, const char *s) { 66 | if (addr == NULL) { 67 | return EP_NONE; 68 | } 69 | // try IPv4: 70 | int i0=-1, i1=-1, i2=-1, i3=-1; 71 | int got = sscanf(s, "%d.%d.%d.%d", &i0, &i1, &i2, &i3); 72 | // Log("PARSE: %d\n", got); 73 | if (got==4) { 74 | if (0<=i0 && i0<=255 && 0<=i1 && i1<=255 && 0<=i2 && i2<=255 && 0<=i3 && i3<=255) { 75 | addr->IPv4.u8[0] = i0; 76 | addr->IPv4.u8[1] = i1; 77 | addr->IPv4.u8[2] = i2; 78 | addr->IPv4.u8[3] = i3; 79 | return IPV4; 80 | } 81 | } 82 | // try IPv6: 83 | // TODO: 84 | return EP_NONE; 85 | } 86 | -------------------------------------------------------------------------------- /src/interface/network/net_Endpoint.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #ifndef COM_NET_EP_H 23 | #define COM_NET_EP_H 24 | 25 | #include 26 | #include 27 | #include "../../liblobaro_coap.h" 28 | 29 | #define IPv6_IP(A3, A2, A1, A0) \ 30 | { (A3 >> 24 & 0xff), (A3 >> 16 & 0xff), (A3 >> 8 & 0xff), (A3 >> 0 & 0xff), \ 31 | (A2 >> 24 & 0xff), (A2 >> 16 & 0xff), (A2 >> 8 & 0xff), (A2 >> 0 & 0xff), \ 32 | (A1 >> 24 & 0xff), (A1 >> 16 & 0xff), (A1 >> 8 & 0xff), (A1 >> 0 & 0xff), \ 33 | (A0 >> 24 & 0xff), (A0 >> 16 & 0xff), (A0 >> 8 & 0xff), (A0 >> 0 & 0xff)} 34 | 35 | extern const NetAddr_IPv6_t NetAddr_IPv6_unspecified; 36 | extern const NetAddr_IPv6_t NetAddr_IPv6_mulitcast; 37 | extern const NetAddr_IPv4_t NetAddr_IPv4_unspecified; 38 | extern const NetAddr_IPv4_t NetAddr_IPv4_mulitcast; 39 | 40 | extern const NetEp_t NetEp_IPv6_mulitcast; 41 | extern const NetEp_t NetEp_IPv4_mulitcast; 42 | 43 | bool EpAreEqual(const NetEp_t* ep_A, const NetEp_t* ep_B); 44 | void CopyEndpoints(NetEp_t* Destination, const NetEp_t* Source); 45 | 46 | NetInterfaceType_t CoAP_ParseNetAddress(NetAddr_t *addr, const char *s); 47 | #endif 48 | -------------------------------------------------------------------------------- /src/interface/network/net_Packet.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #include 23 | #include "../../coap.h" 24 | 25 | void _rom PrintRawPacket(NetPacket_t* pckt) 26 | { 27 | 28 | INFO("Packet size:%d rssi:%" PRIi32 " hops: %d \r\nRawData (hex) :", pckt->size, pckt->metaInfo.Dat.RfPath.RSSI, pckt->metaInfo.Dat.RfPath.HopCount); 29 | int i; 30 | for(i=0; isize; i++) 31 | { 32 | INFO("0x%02x, ", pckt->pData[i]); 33 | } 34 | INFO("\r\nRawData (char):"); 35 | for(i=0; isize; i++) 36 | { 37 | INFO("%c", pckt->pData[i]); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/interface/network/net_Packet.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #ifndef COM_NET_PACKET_H 23 | #define COM_NET_PACKET_H 24 | 25 | #include "../../liblobaro_coap.h" 26 | 27 | void PrintRawPacket(NetPacket_t* pckt); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/interface/network/net_Socket.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #include "../../coap.h" 23 | 24 | typedef struct { 25 | CoAP_Socket_t SocketMemory[MAX_ACTIVE_SOCKETS]; 26 | bool initDone; 27 | } CoAP_SocketCtrl_t; 28 | 29 | static CoAP_SocketCtrl_t SocketCtrl = {.initDone = false}; 30 | 31 | CoAP_Socket_t* _rom AllocSocket() { 32 | int i; 33 | if (!SocketCtrl.initDone) { 34 | for (i = 0; i < MAX_ACTIVE_SOCKETS; i++) { 35 | SocketCtrl.SocketMemory[i].Alive = false; 36 | } 37 | SocketCtrl.initDone = true; 38 | } 39 | 40 | for (i = 0; i < MAX_ACTIVE_SOCKETS; i++) { 41 | CoAP_Socket_t* socket = &(SocketCtrl.SocketMemory[i]); 42 | if (socket->Alive == false) { 43 | memset(socket, 0, sizeof(*socket)); 44 | socket->Alive = true; 45 | return socket; 46 | } 47 | } 48 | 49 | return NULL; //no free memory 50 | } 51 | 52 | CoAP_Socket_t* _rom RetrieveSocket(SocketHandle_t handle) { 53 | int i; 54 | for (i = 0; i < MAX_ACTIVE_SOCKETS; i++) { 55 | if (SocketCtrl.SocketMemory[i].Alive && 56 | SocketCtrl.SocketMemory[i].Handle == handle) //corresponding socket found! 57 | { 58 | return &(SocketCtrl.SocketMemory[i]); 59 | } 60 | 61 | } 62 | return NULL; //not found 63 | } -------------------------------------------------------------------------------- /src/interface/network/net_Socket.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #ifndef COM_NET_SOCKET_H 23 | #define COM_NET_SOCKET_H 24 | 25 | #include 26 | #include 27 | #include "../../liblobaro_coap.h" 28 | 29 | //function pointer typedefs, used below in struct 30 | 31 | #define MAX_ACTIVE_SOCKETS (5) 32 | 33 | CoAP_Socket_t *AllocSocket(); 34 | CoAP_Socket_t *RetrieveSocket(SocketHandle_t handle); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/liblobaro_coap.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include "liblobaro_coap.h" 24 | #include "coap.h" 25 | #include "coap_main.h" 26 | 27 | void debugPuts_Empty(const char* s) { 28 | (void) s; // unused 29 | } 30 | 31 | void CoAP_Init(CoAP_API_t api) { 32 | CoAP.api = api; 33 | 34 | CoAP_InitIds(); 35 | 36 | // To make the tests stable, we should provide proper log functions in future 37 | if (CoAP.api.debugPuts == NULL) { 38 | CoAP.api.debugPuts = debugPuts_Empty; 39 | } 40 | 41 | #if DEBUG_RANDOM_DROP_INCOMING_PERCENTAGE != 0 42 | INFO("\n\nWARNING!!!\n\n DEBUG FEATURE, DROPPING %d%% INCOMING MESSAGES ON PURPOSE!\n\n", DEBUG_RANDOM_DROP_INCOMING_PERCENTAGE); 43 | #endif 44 | #if DEBUG_RANDOM_DROP_OUTGOING_PERCENTAGE != 0 45 | INFO("\n\nWARNING!!!\n\n DEBUG FEATURE, DROPPING %d%% OUTGOING MESSAGES ON PURPOSE!\n\n", DEBUG_RANDOM_DROP_OUTGOING_PERCENTAGE); 46 | #endif 47 | 48 | INFO("CoAP_init!\r\n"); 49 | INFO("CoAP Interaction size: %zu byte\r\n", sizeof(CoAP_Interaction_t)); 50 | INFO("CoAP_Res_t size: %zu byte\r\n", sizeof(CoAP_Res_t)); 51 | INFO("CoAP_Message_t size: %zu byte\r\n", sizeof(CoAP_Message_t)); 52 | INFO("CoAP_option_t size: %zu byte\r\n", sizeof(CoAP_option_t)); 53 | INFO("CoAP_Observer_t size: %zu byte\r\n", sizeof(CoAP_Observer_t)); 54 | 55 | CoAP_InitResources(); 56 | } 57 | -------------------------------------------------------------------------------- /src/liblobaro_coap.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #ifndef LIBLOBARO_COAP_H 24 | #define LIBLOBARO_COAP_H 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | //################################ 35 | // Function Results 36 | //################################ 37 | 38 | typedef enum { 39 | COAP_OK = 0, 40 | COAP_NOT_FOUND, //not found but no error 41 | COAP_PARSE_DATAGRAM_TOO_SHORT, 42 | COAP_PARSE_UNKOWN_COAP_VERSION, 43 | COAP_PARSE_MESSAGE_FORMAT_ERROR, 44 | COAP_PARSE_TOO_MANY_OPTIONS, 45 | COAP_PARSE_TOO_LONG_OPTION, 46 | COAP_PARSE_TOO_MUCH_PAYLOAD, 47 | COAP_PACK_TOO_MANY_OPTIONS, 48 | COAP_PACK_TOO_LONG_OPTION, 49 | COAP_ERR_ARGUMENT, 50 | COAP_ERR_SOCKET, 51 | COAP_ERR_NETWORK, 52 | COAP_ERR_OUT_OF_MEMORY, 53 | COAP_ERR_TOO_LONG_URI_PATH, 54 | COAP_ERR_NOT_FOUND, 55 | COAP_ERR_WRONG_OPTION, 56 | COAP_ERR_EXISTING, 57 | COAP_TRUE, 58 | COAP_FALSE, 59 | COAP_ERR_WRONG_REQUEST, 60 | COAP_BAD_OPTION_VAL, 61 | COAP_BAD_OPTION_LEN, 62 | COAP_REMOVED, 63 | COAP_ERR_UNKNOWN, 64 | COAP_ERR_REMOTE_RST, 65 | COAP_ERR_OUT_OF_ATTEMPTS, 66 | COAP_ERR_TIMEOUT, 67 | COAP_WAITING, 68 | COAP_HOLDING_BACK, 69 | COAP_RETRY 70 | } CoAP_Result_t; 71 | 72 | //################################ 73 | // Endpoints 74 | //################################ 75 | 76 | //IPv6 address 77 | typedef union { 78 | uint8_t u8[16]; 79 | uint16_t u16[8]; 80 | uint32_t u32[4]; 81 | } NetAddr_IPv6_t; 82 | 83 | //IPv4 address 84 | typedef union { 85 | uint8_t u8[4]; 86 | uint16_t u16[2]; 87 | uint32_t u32[1]; 88 | } NetAddr_IPv4_t; 89 | 90 | //UART address 91 | typedef struct { 92 | uint8_t ComPortID; 93 | } NetAddr_Uart_t; 94 | 95 | //general address 96 | #define NetAddr_MAX_LENGTH (16) 97 | typedef union { 98 | NetAddr_IPv6_t IPv6; 99 | NetAddr_IPv4_t IPv4; 100 | NetAddr_Uart_t Uart; 101 | uint8_t mem[NetAddr_MAX_LENGTH]; //used for init and comparisons of addresses 102 | } NetAddr_t; 103 | 104 | typedef enum { 105 | EP_NONE, IPV6, IPV4, BTLE, UART 106 | } NetInterfaceType_t; 107 | 108 | // general network endpoint 109 | // used by drivers and network libs 110 | typedef struct { 111 | NetInterfaceType_t NetType; 112 | NetAddr_t NetAddr; 113 | uint16_t NetPort; 114 | } NetEp_t; 115 | 116 | //################################ 117 | // Packets 118 | //################################ 119 | 120 | typedef enum { 121 | META_INFO_NONE, 122 | META_INFO_RF_PATH, 123 | META_INFO_MULTICAST 124 | } MetaInfoType_t; 125 | 126 | typedef struct { 127 | uint8_t HopCount; 128 | int32_t RSSI; 129 | } MetaInfo_RfPath_t; 130 | 131 | typedef union { 132 | MetaInfo_RfPath_t RfPath; 133 | } MetaInfoUnion_t; 134 | 135 | typedef struct { 136 | MetaInfoType_t Type; 137 | MetaInfoUnion_t Dat; 138 | } MetaInfo_t; 139 | 140 | // general network packet 141 | // received in callbacks and send out 142 | // in network send routines 143 | typedef struct { 144 | uint8_t *pData; 145 | uint16_t size; 146 | // The remote EndPoint is either the sender for incoming packets or the receiver for outgoing packets 147 | NetEp_t remoteEp; 148 | // Optional meta info that will be translated into in options 149 | MetaInfo_t metaInfo; 150 | } NetPacket_t; 151 | 152 | //################################ 153 | // Sockets 154 | //################################ 155 | typedef void *SocketHandle_t; 156 | 157 | typedef void (*NetReceiveCallback_fn)(SocketHandle_t socketHandle, NetPacket_t *pckt); 158 | typedef bool (*NetTransmit_fn)(SocketHandle_t socketHandle, NetPacket_t *pckt); 159 | 160 | typedef struct { 161 | SocketHandle_t Handle; // Handle to identify the socket 162 | 163 | NetTransmit_fn Tx; // ext. function called by coap stack to send data after finding socket by socketHandle (internally) 164 | bool Alive; // We can only deal with sockets that are alive 165 | } CoAP_Socket_t; 166 | 167 | //################################ 168 | // Options 169 | //################################ 170 | 171 | typedef enum { 172 | // Core Options 173 | OPT_NUM_URI_PATH = 11, 174 | OPT_NUM_URI_HOST = 3, 175 | OPT_NUM_ETAG = 4, 176 | OPT_NUM_OBSERVE = 6, 177 | OPT_NUM_URI_PORT = 7, 178 | OPT_NUM_CONTENT_FORMAT = 12, 179 | OPT_NUM_URI_QUERY = 15, 180 | OPT_NUM_ACCEPT = 17, 181 | // Blockwise transfers 182 | OPT_NUM_BLOCK2 = 23, 183 | OPT_NUM_BLOCK1 = 27, 184 | OPT_NUM_SIZE2 = 28, 185 | OPT_NUM_SIZE1 = 60, 186 | OPT_NUM_LOBARO_TOKEN_SAVE = 350 187 | } CoAP_KnownOptionNumbers_t; 188 | 189 | typedef struct CoAP_option { 190 | struct CoAP_option *next; //4 byte pointer (linked list) 191 | 192 | uint16_t Number; //2 byte 193 | uint16_t Length; //2 byte 194 | uint8_t *Value; //4 byte (should be last in struct!) 195 | } CoAP_option_t; 196 | 197 | //################################ 198 | // Message 199 | //################################ 200 | 201 | typedef enum { 202 | CON = 0, // Confirmable Message 203 | NON = 1, // Non-confirmable Message 204 | ACK = 2, // Acknowlegment Message 205 | RST = 3 // Reset Message 206 | } CoAP_MessageType_t; 207 | 208 | #define CODE(CLASS, CODE) ( (CLASS <<5u) | CODE ) 209 | typedef enum { 210 | EMPTY = CODE(0u, 0u), 211 | REQ_GET = CODE(0u, 1u), 212 | REQ_POST = CODE(0u, 2u), 213 | REQ_PUT = CODE(0u, 3u), 214 | REQ_DELETE = CODE(0u, 4u), 215 | REQ_FETCH = CODE(0u, 5u), 216 | REQ_PATCH = CODE(0u, 6u), 217 | REQ_IPATCH = CODE(0u, 7u), 218 | REQ_LAST = CODE(0u, 7u), 219 | RESP_FIRST_2_00 = CODE(2u, 0u), 220 | RESP_SUCCESS_CREATED_2_01 = CODE(2u, 1u), // only used on response to "POST" and "PUT" like HTTP 201 221 | RESP_SUCCESS_DELETED_2_02 = CODE(2u, 2u), // only used on response to "DELETE" and "POST" like HTTP 204 222 | RESP_SUCCESS_VALID_2_03 = CODE(2u, 3u), 223 | RESP_SUCCESS_CHANGED_2_04 = CODE(2u, 4u), // only used on response to "POST" and "PUT" like HTTP 204 224 | RESP_SUCCESS_CONTENT_2_05 = CODE(2u, 5u), // only used on response to "GET" like HTTP 200 (OK) 225 | RESP_SUCCESS_CONTINUE_2_31 = CODE(2u, 31u), // used for blockwise, see RFC 7959 226 | RESP_ERROR_BAD_REQUEST_4_00 = CODE(4u, 0u), // like HTTP 400 227 | RESP_ERROR_UNAUTHORIZED_4_01 = CODE(4u, 1u), 228 | RESP_BAD_OPTION_4_02 = CODE(4u, 2u), 229 | RESP_FORBIDDEN_4_03 = CODE(4u, 3u), 230 | RESP_NOT_FOUND_4_04 = CODE(4u, 4u), 231 | RESP_METHOD_NOT_ALLOWED_4_05 = CODE(4u, 5u), 232 | RESP_METHOD_NOT_ACCEPTABLE_4_06 = CODE(4u, 6u), 233 | RESP_REQUEST_ENTITY_INCOMPLETE_4_08 = CODE(4u, 8u), 234 | RESP_PRECONDITION_FAILED_4_12 = CODE(4u, 12u), 235 | RESP_REQUEST_ENTITY_TOO_LARGE_4_13 = CODE(4u, 13u), 236 | RESP_UNSUPPORTED_CONTENT_FORMAT_4_15 = CODE(4u, 15u), 237 | RESP_INTERNAL_SERVER_ERROR_5_00 = CODE(5u, 0u), 238 | RESP_NOT_IMPLEMENTED_5_01 = CODE(5u, 1u), 239 | RESP_BAD_GATEWAY_5_02 = CODE(5u, 2u), 240 | RESP_SERVICE_UNAVAILABLE_5_03 = CODE(5u, 3u), 241 | RESP_GATEWAY_TIMEOUT_5_04 = CODE(5u, 4u), 242 | RESP_PROXYING_NOT_SUPPORTED_5_05 = CODE(5u, 5u) 243 | } CoAP_MessageCode_t; 244 | 245 | typedef struct { 246 | uint8_t Length; 247 | uint8_t Token[8]; 248 | } CoAP_Token_t; 249 | 250 | // Delcare CoAP_Res so it can be used in CoAP_Message_t 251 | struct CoAP_Res; 252 | 253 | typedef struct { 254 | uint32_t Timestamp; //set by parse/send network routines 255 | //VER is implicit = 1 256 | //TKL (Token Length) is calculated dynamically 257 | CoAP_MessageType_t Type; // [1] T 258 | CoAP_MessageCode_t Code; // [1] Code 259 | uint16_t MessageID; // [2] Message ID (maps ACK msg to coresponding CON msg) 260 | uint16_t PayloadLength; // [2] 261 | uint16_t PayloadBufSize; // [2] size of allocated msg payload buffer 262 | CoAP_Token_t Token; // [9] Token (1 byte Length + up to 8 Byte for the token content) 263 | CoAP_option_t *pOptionsList; // [4] linked list of Options 264 | uint8_t *Payload; // [4] MUST be last in struct! Because of mem allocation scheme which tries to allocate message mem and payload mem in ONE big data chunk 265 | 266 | struct CoAP_Res *pResource; // Pointer the the resource this message is intended for. 267 | } CoAP_Message_t; //total of 25 Bytes 268 | 269 | //################################ 270 | // Observer 271 | //################################ 272 | 273 | typedef struct CoAP_Observer { 274 | NetEp_t Ep; // [16B] 275 | SocketHandle_t socketHandle; // [4B] 276 | uint8_t FailCount; // [1B] 277 | CoAP_Token_t Token; // [9B] 278 | CoAP_option_t *pOptList; // [xxB](uri-host) <- will be removed if attached, uri-query, observe (for seq number) 279 | 280 | struct CoAP_Observer *next; // [4B] pointer (linked list) (not saved while sleeping) 281 | } CoAP_Observer_t; 282 | 283 | //################################ 284 | // Resources 285 | //################################ 286 | 287 | //Bitfields for resource BitOpts 288 | #define RES_OPT_GET (1 << REQ_GET) // 1<<1 289 | #define RES_OPT_POST (1 << REQ_POST) // 1<<2 290 | #define RES_OPT_PUT (1 << REQ_PUT) // 1<<3 291 | #define RES_OPT_DELETE (1 << REQ_DELETE) // 1<<4 292 | #define RES_OPT_FETCH (1 << REQ_FETCH) // 1<<5 293 | #define RES_OPT_PATCH (1 << REQ_PATCH) // 1<<6 294 | #define RES_OPT_IPATCH (1 << REQ_IPATCH) // 1<<7 295 | 296 | typedef enum { 297 | HANDLER_OK = 0, 298 | HANDLER_POSTPONE = 1, 299 | HANDLER_ERROR = 2 300 | } CoAP_HandlerResult_t; 301 | 302 | typedef CoAP_HandlerResult_t (*CoAP_ResourceHandler_fPtr_t)(CoAP_Message_t *pReq, CoAP_Message_t *pResp); 303 | // TODO: Can we use the CoAP_ResourceHandler_fPtr_t signature also for notifiers? 304 | typedef CoAP_HandlerResult_t (*CoAP_ResourceNotifier_fPtr_t)(CoAP_Observer_t *pObserver, CoAP_Message_t *pResp); 305 | 306 | typedef struct { 307 | uint16_t Cf; // Content-Format 308 | uint16_t AllowedMethods; // Bitwise resource options //todo: Send Response as CON or NON 309 | uint16_t ETag; 310 | } CoAP_ResOpts_t; 311 | 312 | typedef struct CoAP_Res { 313 | struct CoAP_Res *next; //4 byte pointer (linked list) 314 | char *pDescription; 315 | uint32_t UpdateCnt; // Used as value for the Observe option 316 | CoAP_ResOpts_t Options; 317 | CoAP_option_t *pUri; //linked list of this resource URI options 318 | CoAP_Observer_t *pListObservers; //linked list of this resource observers 319 | CoAP_ResourceHandler_fPtr_t Handler; 320 | CoAP_ResourceNotifier_fPtr_t Notifier; //maybe "NULL" if resource not observable 321 | } CoAP_Res_t; 322 | 323 | //################################ 324 | // Initialization 325 | //################################ 326 | 327 | 328 | /* 329 | * API functions used by the CoAP stack. All fields need to be initialized. 330 | * 331 | * Fields marked as optional can be initialized with NULL 332 | */ 333 | typedef struct { 334 | // 1Hz Clock used by timeout logic 335 | uint32_t (*rtc1HzCnt)(); 336 | // Uart/Display function to print debug/status messages 337 | void (*debugPuts)(const char *s); 338 | // Memory management: 339 | void *(*malloc)(size_t size); 340 | void (*free)(void *p); 341 | int (*rand)(); 342 | } CoAP_API_t; 343 | 344 | //################################ 345 | // Public API (setup) 346 | //################################ 347 | 348 | /** 349 | * Initialize the CoAP stack with a set of API functions used by the stack and a config struct. 350 | * @param api Struct with API functions that need to be defined for the stack to work 351 | * @param cfg Configuration values to setup the stack 352 | * @return A result code 353 | */ 354 | void CoAP_Init(CoAP_API_t api); 355 | 356 | /** 357 | * Each CoAP implementation 358 | * @param handle 359 | * @return 360 | */ 361 | CoAP_Socket_t *CoAP_NewSocket(SocketHandle_t handle); 362 | 363 | /** 364 | * All resources must be created explicitly. 365 | * One reason is that the stack handles observer state per resource. 366 | * @param Uri 367 | * @param Descr 368 | * @param Options 369 | * @param pHandlerFkt 370 | * @param pNotifierFkt 371 | * @return 372 | */ 373 | CoAP_Res_t *CoAP_CreateResource(char *Uri, char *Descr, CoAP_ResOpts_t Options, CoAP_ResourceHandler_fPtr_t pHandlerFkt, 374 | CoAP_ResourceNotifier_fPtr_t pNotifierFkt); 375 | 376 | //##################### 377 | // Message API 378 | //##################### 379 | 380 | // Set payload in resource handlers 381 | // pMsgReq: The request message 382 | // pMsgResp: The response message 383 | // pPayload: The payload to be send 384 | // payloadTotalSize: The size of pPayload 385 | // payloadIsVolatile: Set to true for volatile memory. 386 | // If false, pPayload MUST point to static memory that is not freed before the interaction ends 387 | // which is hard to detect. 388 | CoAP_Result_t 389 | CoAP_SetPayload(CoAP_Message_t *pMsgResp, uint8_t *pPayload, size_t payloadTotalSize, bool payloadIsVolatile); 390 | 391 | // Adds an option to the CoAP message 392 | CoAP_Result_t CoAP_AddOption(CoAP_Message_t *pMsg, uint16_t OptNumber, uint8_t *buf, uint16_t length); 393 | 394 | //######################################## 395 | // Interaction processing API 396 | //######################################## 397 | 398 | // This function must be called by network drivers 399 | // on reception of a new network packets which 400 | // should be passed to the CoAP stack. 401 | // "socketHandle" can be chosen arbitrary by calling network driver, 402 | // but can be considered constant over runtime. 403 | void CoAP_HandleIncomingPacket(SocketHandle_t socketHandle, NetPacket_t *pPacket); 404 | 405 | // doWork must be called regularly to process pending interactions 406 | void CoAP_doWork(); 407 | 408 | // drop all unfinished work 409 | void CoAP_ClearPendingInteractions(); 410 | 411 | // Endpoint api 412 | NetInterfaceType_t CoAP_ParseNetAddress(NetAddr_t *addr, const char *s); 413 | #ifdef __cplusplus 414 | } 415 | #endif 416 | 417 | 418 | #endif //LIBLOBARO_COAP_H 419 | -------------------------------------------------------------------------------- /src/option-types/coap_option_ETag.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #include "../coap.h" 23 | 24 | 25 | 26 | 27 | CoAP_Result_t _rom AddETagOptionToMsg(CoAP_Message_t* msg, uint8_t* pData, uint32_t size) 28 | { 29 | uint32_t hash=0; 30 | uint32_t i; 31 | 32 | //TODO use a hash function 33 | for(i=0; i < size; i++) 34 | { 35 | hash+= pData[i]; 36 | } 37 | 38 | uint8_t wBuf[4]; 39 | /* 40 | wBuf[0]=hash & 0xff; 41 | wBuf[1] = (hash >> 8) & 0xff; 42 | wBuf[2] = (hash >> 16) & 0xff; 43 | wBuf[3] = (hash >> 24) & 0xff; 44 | */ 45 | 46 | wBuf[0]=1; 47 | wBuf[1] = 2; 48 | wBuf[2] = 3; 49 | wBuf[3] = 4; 50 | 51 | return CoAP_AppendOptionToList(&(msg->pOptionsList), OPT_NUM_ETAG,wBuf, 4); 52 | } 53 | 54 | CoAP_Result_t _rom GetETagOptionFromMsg(CoAP_Message_t* msg, uint8_t* val, uint8_t* pLen) { //len [1..8] 55 | 56 | 57 | CoAP_option_t* pOpts = msg->pOptionsList; 58 | *val = 0; 59 | 60 | while(pOpts != NULL) { 61 | if(pOpts->Number == OPT_NUM_ETAG) { 62 | 63 | if(pOpts->Length == 0 || pOpts->Length > 8) { //implicit val = 0 (register) 64 | return COAP_BAD_OPTION_LEN; 65 | } else { 66 | *pLen = (uint8_t)pOpts->Length; 67 | int i; 68 | for(i=0; i< pOpts->Length; i++) { 69 | val[i]=pOpts->Value[i]; 70 | } 71 | } 72 | return COAP_OK; 73 | } 74 | 75 | pOpts = pOpts->next; 76 | } 77 | return COAP_NOT_FOUND; 78 | } 79 | 80 | CoAP_Result_t _rom Add64BitETagOptionToMsg(CoAP_Message_t* msg, uint64_t val) { 81 | uint8_t wBuf[8]; 82 | uint8_t i = 0; 83 | 84 | for(i=0; i<8; i++){ 85 | wBuf[i]= val & 0xff; 86 | val = val >> 8; 87 | if(!val) break; 88 | } 89 | 90 | return CoAP_AppendOptionToList(&(msg->pOptionsList), OPT_NUM_ETAG, wBuf, i+1); 91 | } 92 | 93 | CoAP_Result_t _rom Get64BitETagOptionFromMsg(CoAP_Message_t* msg, uint64_t* pVal) { 94 | CoAP_Result_t res; 95 | uint8_t wBuf[8]; 96 | uint8_t pLen = 0; 97 | 98 | res = GetETagOptionFromMsg(msg, wBuf, &pLen); 99 | 100 | if(res != COAP_OK) return res; 101 | 102 | *pVal = 0; 103 | int i; 104 | for(i= 0; i 24 | 25 | //blockwise transfers options 26 | 27 | CoAP_Result_t _rom dbgBlkOption(CoAP_blockwise_option_t* blkOption) 28 | { 29 | if (blkOption->Type == BLOCK_1) 30 | { 31 | INFO("\r\n>>Block1 (#%d) Option found\r\n", blkOption->Type); 32 | } 33 | 34 | if (blkOption->Type == BLOCK_2) 35 | { 36 | INFO("\r\n>>Block2 (#%d) Option found\r\n", blkOption->Type); 37 | } 38 | 39 | INFO("- BlockSize: %ud\r\n", blkOption->BlockSize); 40 | INFO("- BlockNum: %"PRIu32"\r\n", blkOption->BlockNum); 41 | INFO("- MoreFlag: %d\r\n", blkOption->MoreFlag); 42 | 43 | return COAP_OK; 44 | } 45 | 46 | CoAP_Result_t _rom AddBlkOptionToMsg(CoAP_Message_t* msg, CoAP_blockwise_option_t* blkOption) 47 | { 48 | if (blkOption->Type != BLOCK_1 && blkOption->Type != BLOCK_2) 49 | return COAP_ERR_WRONG_OPTION; 50 | 51 | if (blkOption->BlockNum == 0 && blkOption->BlockSize == BLOCK_SIZE_16 && blkOption->MoreFlag == false) //=> NUM, |M| and SZX are all coded to zero -> send zero-byte integer 52 | { 53 | return CoAP_AppendOptionToList(&(msg->pOptionsList), (uint16_t) blkOption->Type, NULL, 0); 54 | } 55 | 56 | uint32_t OptionValue = 0; 57 | 58 | //Block Size 59 | uint8_t szxCalc = (uint8_t) ((blkOption->BlockSize) >> 4u); // divide by 16 60 | int i; 61 | for (i = 6; i >= 0; i--) //find highest bit, e.g. calc log2, i=7 forbidden 62 | { 63 | if (szxCalc & 1u << i) 64 | { 65 | OptionValue |= i; 66 | break; 67 | } 68 | } 69 | 70 | //More Flag 71 | if (blkOption->MoreFlag) 72 | OptionValue |= 1u << 3u; 73 | 74 | //Num Value 75 | OptionValue |= (blkOption->BlockNum) << 4u; 76 | 77 | uint8_t wBuf[3]; 78 | 79 | if (blkOption->BlockNum < 16) 80 | { 81 | //msg->Options[msg->OptionCount].Length = 1; 82 | //wBuf[0]=msg->Options[msg->OptionCount].Value[0] = (uint8_t)OptionValue; 83 | wBuf[0] = (uint8_t) OptionValue; 84 | return CoAP_AppendOptionToList(&(msg->pOptionsList), (uint16_t) blkOption->Type, wBuf, 1); 85 | } 86 | else if (blkOption->BlockNum < 4096u) 87 | { 88 | //msg->Options[msg->OptionCount].Length = 2; 89 | wBuf[0] = (uint8_t) (OptionValue >> 8u); 90 | wBuf[1] = (uint8_t) (OptionValue & 0xffu); 91 | return CoAP_AppendOptionToList(&(msg->pOptionsList), (uint16_t) blkOption->Type, wBuf, 2); 92 | } 93 | else 94 | { 95 | //msg->Options[msg->OptionCount].Length = 3; 96 | wBuf[0] = (uint8_t) (OptionValue >> 16u); 97 | wBuf[1] = (uint8_t) (OptionValue >> 8u); 98 | wBuf[2] = (uint8_t) (OptionValue & 0xffu); 99 | return CoAP_AppendOptionToList(&(msg->pOptionsList), (uint16_t) blkOption->Type, wBuf, 3); 100 | } 101 | } 102 | 103 | CoAP_Result_t _rom GetBlockOptionFromMsg(CoAP_Message_t* msg, CoAP_blockwise_option_type_t Type, CoAP_blockwise_option_t* BlkOption) 104 | { 105 | CoAP_option_t* pOption = msg->pOptionsList; 106 | 107 | if (pOption == NULL) 108 | return COAP_ERR_NOT_FOUND; 109 | 110 | do 111 | { 112 | if (pOption->Number == (uint16_t) Type) 113 | { 114 | uint16_t ValLength = pOption->Length; 115 | uint32_t OptionValue = 0; 116 | uint8_t* pVals = pOption->Value; 117 | 118 | BlkOption->Type = Type; 119 | 120 | if (pOption->Length > 3) 121 | return COAP_PARSE_MESSAGE_FORMAT_ERROR; 122 | 123 | if (ValLength == 0) 124 | { 125 | BlkOption->BlockNum = 0; 126 | BlkOption->BlockSize = BLOCK_SIZE_16; 127 | BlkOption->MoreFlag = false; 128 | return COAP_OK; 129 | } 130 | else if (ValLength == 1) 131 | { 132 | OptionValue = (uint32_t) pVals[0]; 133 | } 134 | else if (ValLength == 2) 135 | { 136 | OptionValue = (((uint32_t) pVals[0]) << 8u) | ((uint32_t) pVals[1]); 137 | } 138 | else if (ValLength == 3) 139 | { 140 | OptionValue = (((uint32_t) pVals[0]) << 16u) | (((uint32_t) pVals[1]) << 8u) | ((uint32_t) pVals[2]); 141 | } 142 | 143 | uint8_t SZX = OptionValue & 7u; 144 | if (SZX == 7) 145 | return COAP_PARSE_MESSAGE_FORMAT_ERROR; //must lead to 4.00 Bad Request! 146 | BlkOption->BlockSize = 1u << (SZX + 4u); 147 | 148 | if (OptionValue & 8u) 149 | BlkOption->MoreFlag = true; 150 | else 151 | BlkOption->MoreFlag = false; 152 | 153 | BlkOption->BlockNum = OptionValue >> 4u; 154 | 155 | return COAP_OK; 156 | } 157 | 158 | if (pOption->next == NULL) 159 | return COAP_ERR_NOT_FOUND; 160 | pOption = pOption->next; 161 | } while (1); 162 | 163 | return COAP_ERR_NOT_FOUND; 164 | } 165 | 166 | CoAP_Result_t _rom GetBlock1OptionFromMsg(CoAP_Message_t* msg, CoAP_blockwise_option_t* BlkOption) 167 | { 168 | return GetBlockOptionFromMsg(msg, BLOCK_1, BlkOption); 169 | } 170 | 171 | CoAP_Result_t _rom GetBlock2OptionFromMsg(CoAP_Message_t* msg, CoAP_blockwise_option_t* BlkOption) 172 | { 173 | return GetBlockOptionFromMsg(msg, BLOCK_2, BlkOption); 174 | } 175 | 176 | CoAP_Result_t _rom RemoveAllBlockOptionsFromMsg(CoAP_Message_t* msg, CoAP_blockwise_option_type_t Type) 177 | { 178 | CoAP_option_t* pOption = NULL; 179 | bool found = false; 180 | 181 | RESTART: 182 | for (pOption = msg->pOptionsList; pOption != NULL; pOption = pOption->next) { 183 | if (pOption->Number == Type) { 184 | CoAP_RemoveOptionFromList(&(msg->pOptionsList), pOption); 185 | found = true; 186 | goto RESTART; 187 | } 188 | } 189 | 190 | if (found) 191 | return COAP_OK; 192 | else 193 | return COAP_NOT_FOUND; 194 | } 195 | 196 | // Copies the given payload to the message. Respects the MAX_PAYLOAD_SIZE 197 | // If the content does not fit into the response a Block options is added 198 | CoAP_Result_t _rom CoAP_SetPayload(CoAP_Message_t* pMsgResp, uint8_t* pPayload, size_t payloadTotalSize, bool payloadIsVolatile) 199 | { 200 | CoAP_blockwise_option_t B2opt = { .Type = BLOCK_2 }; 201 | int32_t BytesToSend = 0; 202 | 203 | if (payloadTotalSize == 0) { 204 | pMsgResp->PayloadLength = 0; 205 | return COAP_OK; 206 | } 207 | 208 | if (payloadTotalSize > MAX_PAYLOAD_SIZE) { //must use blockwise transfer? 209 | B2opt.BlockSize = MAX_PAYLOAD_SIZE; 210 | B2opt.BlockNum = 0; 211 | B2opt.MoreFlag = true; 212 | AddBlkOptionToMsg(pMsgResp, &B2opt); 213 | BytesToSend = MAX_PAYLOAD_SIZE; 214 | } else { 215 | BytesToSend = payloadTotalSize; 216 | } 217 | 218 | if (pPayload != pMsgResp->Payload) { 219 | //set payload to beginning of given external payload buf 220 | if (payloadIsVolatile) { 221 | if (pMsgResp->PayloadBufSize < BytesToSend) { 222 | CoAP_free_MsgPayload(&pMsgResp); //this is save in any case because free routine checks location 223 | pMsgResp->Payload = (uint8_t*) CoAP.api.malloc(BytesToSend); //alloc new buffer to copy data to send to 224 | pMsgResp->PayloadBufSize = BytesToSend; 225 | } 226 | coap_memcpy(pMsgResp->Payload, pPayload, BytesToSend); 227 | pMsgResp->PayloadBufSize = BytesToSend; 228 | } else { 229 | pMsgResp->Payload = pPayload; //use external set buffer (will not be freed, MUST be static!!!) 230 | pMsgResp->PayloadBufSize = 0; //protect external buf from unwanted overwrite 231 | } 232 | } // [else] => no need to alter payload buf beside change payload length before return 233 | 234 | pMsgResp->PayloadLength = BytesToSend; 235 | return COAP_OK; 236 | } 237 | 238 | //Copy from external payload buffer to response msg payload, and grow buffer if needed 239 | //1) pPayload is static (-> payloadIsVolatile = false) -> pPayload pointer will be directly used 240 | //2) pPayload is volatile (-> payloadIsVolatile = true) -> pPayload will be copied into new buffer 241 | //3) pPayload equals pMsgResp->Payload only blockwise memove ops are performed (-> copyPl = no meaning) -> a memory shunk within pPayload will be directly used 242 | CoAP_Result_t _rom CoAP_SetPayload_CheckBlockOpt(CoAP_Message_t* pMsgReq, CoAP_Message_t* pMsgResp, uint8_t* pPayload, uint16_t payloadTotalSize, bool payloadIsVolatile) 243 | { 244 | CoAP_blockwise_option_t B2opt = { .Type = BLOCK_2 }; 245 | int32_t BytesToSend = 0; 246 | 247 | if (payloadTotalSize == 0) { 248 | pMsgResp->PayloadLength = 0; 249 | return COAP_OK; 250 | } 251 | 252 | //is block2 option included in request (control usage)? 253 | if (pMsgReq != NULL && GetBlock2OptionFromMsg(pMsgReq, &B2opt) == COAP_OK) //=found 254 | { 255 | //clients blocksize wish too big for us? 256 | if (B2opt.BlockSize > MAX_PAYLOAD_SIZE) { 257 | B2opt.BlockSize = MAX_PAYLOAD_SIZE; //choose a smaller value (our maximum) 258 | } 259 | 260 | int32_t TotalBytesLeft = ((int32_t) (payloadTotalSize)) - (int32_t) ((B2opt.BlockSize) * (B2opt.BlockNum)); 261 | if (TotalBytesLeft <= 0) { 262 | CoAP_addTextPayload(pMsgResp, "block not existing"); 263 | pMsgResp->Code = RESP_BAD_OPTION_4_02; 264 | return COAP_ERR_WRONG_OPTION; 265 | } 266 | 267 | if (TotalBytesLeft > B2opt.BlockSize) { 268 | BytesToSend = B2opt.BlockSize; 269 | B2opt.MoreFlag = true; 270 | } 271 | else 272 | { 273 | BytesToSend = TotalBytesLeft; 274 | B2opt.MoreFlag = false; 275 | } 276 | 277 | //Add Block2 278 | RemoveAllBlockOptionsFromMsg(pMsgResp, BLOCK_2); //if called more than one a block 2 can already be present, clean up 279 | AddBlkOptionToMsg(pMsgResp, &B2opt); 280 | 281 | if (pPayload == pMsgResp->Payload) { //no need to alter payload buf beside move contents 282 | coap_memmove(pMsgResp->Payload, &(pMsgResp->Payload[(B2opt.BlockSize) * (B2opt.BlockNum)]), BytesToSend); 283 | } else { 284 | //set payload to calculated position of given external payload buf 285 | 286 | if (payloadIsVolatile) { 287 | if (pMsgResp->PayloadBufSize < BytesToSend) { 288 | CoAP_free_MsgPayload(&pMsgResp); //this is save in any case because free routine checks location 289 | pMsgResp->Payload = (uint8_t*) CoAP.api.malloc(BytesToSend); //alloc new buffer to copy data to send to 290 | pMsgResp->PayloadBufSize = BytesToSend; 291 | } 292 | coap_memcpy(pMsgResp->Payload, &(pPayload[(B2opt.BlockSize) * (B2opt.BlockNum)]), BytesToSend); 293 | pMsgResp->PayloadBufSize = BytesToSend; 294 | } else { 295 | pMsgResp->Payload = &(pPayload[(B2opt.BlockSize) * (B2opt.BlockNum)]); //use external set buffer (will not be freed, MUST be static!) 296 | pMsgResp->PayloadBufSize = 0; //protect "external to msg" buffer 297 | } 298 | } 299 | 300 | } else { //no block2 in request 301 | 302 | if (payloadTotalSize > MAX_PAYLOAD_SIZE) { //must use blockwise transfer? 303 | 304 | B2opt.BlockSize = MAX_PAYLOAD_SIZE; 305 | B2opt.BlockNum = 0; 306 | B2opt.MoreFlag = true; 307 | AddBlkOptionToMsg(pMsgResp, &B2opt); 308 | BytesToSend = MAX_PAYLOAD_SIZE; 309 | } else { 310 | BytesToSend = payloadTotalSize; 311 | } 312 | 313 | if (pPayload != pMsgResp->Payload) { 314 | //set payload to beginning of given external payload buf 315 | if (payloadIsVolatile) { 316 | if (pMsgResp->PayloadBufSize < BytesToSend) { 317 | CoAP_free_MsgPayload(&pMsgResp); //this is save in any case because free routine checks location 318 | pMsgResp->Payload = (uint8_t*) CoAP.api.malloc(BytesToSend); //alloc new buffer to copy data to send to 319 | pMsgResp->PayloadBufSize = BytesToSend; 320 | } 321 | coap_memcpy(pMsgResp->Payload, &(pPayload[0]), BytesToSend); 322 | pMsgResp->PayloadBufSize = BytesToSend; 323 | } else { 324 | pMsgResp->Payload = &(pPayload[0]); //use external set buffer (will not be freed, MUST be static!!!) 325 | pMsgResp->PayloadBufSize = 0; //protect external buf from unwanted overwrite 326 | } 327 | } // [else] => no need to alter payload buf beside change payload length before return 328 | } 329 | 330 | pMsgResp->PayloadLength = BytesToSend; 331 | return COAP_OK; 332 | } 333 | -------------------------------------------------------------------------------- /src/option-types/coap_option_blockwise.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | /* 23 | * coap_blockwise.h 24 | * 25 | * Created on: 17.11.2014 26 | * Author: Tobias 27 | */ 28 | 29 | #ifndef SRC_COAP_COAP_BLOCKWISE_H_ 30 | #define SRC_COAP_COAP_BLOCKWISE_H_ 31 | 32 | typedef enum 33 | { 34 | BLOCK_1=27, //pertains to request payload 35 | BLOCK_2=23 //pertains to response payload 36 | }CoAP_blockwise_option_type_t; 37 | 38 | typedef enum 39 | { 40 | BLOCK_SIZE_NOT_USED = 0, 41 | BLOCK_SIZE_16 = 16, 42 | BLOCK_SIZE_32 = 32, 43 | BLOCK_SIZE_64 = 64, 44 | BLOCK_SIZE_128 = 128, 45 | BLOCK_SIZE_256 = 256, 46 | BLOCK_SIZE_512 = 512, 47 | BLOCK_SIZE_1024 = 1024 48 | }CoAP_blockwise_blockSize_t; 49 | 50 | 51 | #define DEFAULT_SERVER_BLOCK_SIZE BLOCK_SIZE_64 52 | 53 | //Blockwise transfers 54 | typedef struct 55 | { 56 | CoAP_blockwise_option_type_t Type; 57 | CoAP_blockwise_blockSize_t BlockSize; 58 | bool MoreFlag; 59 | uint32_t BlockNum; 60 | }CoAP_blockwise_option_t; 61 | 62 | 63 | CoAP_Result_t _rom CoAP_SetPayload_CheckBlockOpt(CoAP_Message_t* pMsgReq, CoAP_Message_t* pMsgResp, uint8_t* pPayload, uint16_t payloadTotalSize, bool payloadIsVolatile); 64 | 65 | CoAP_Result_t AddBlkOptionToMsg(CoAP_Message_t* msg, CoAP_blockwise_option_t* blkOption); 66 | CoAP_Result_t GetBlock1OptionFromMsg(CoAP_Message_t* msg, CoAP_blockwise_option_t* BlkOption); 67 | CoAP_Result_t GetBlock2OptionFromMsg(CoAP_Message_t* msg, CoAP_blockwise_option_t* BlkOption); 68 | 69 | CoAP_Result_t _rom RemoveAllBlockOptionsFromMsg(CoAP_Message_t* msg, CoAP_blockwise_option_type_t Type); 70 | CoAP_Result_t dbgBlkOption(CoAP_blockwise_option_t* blkOption); 71 | 72 | int32_t getBlockwiseOffset(uint16_t TotalPayloadSize, CoAP_blockwise_option_t* blkOpt); 73 | 74 | #endif /* SRC_COAP_COAP_BLOCKWISE_H_ */ 75 | -------------------------------------------------------------------------------- /src/option-types/coap_option_cf.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #include "../coap.h" 23 | 24 | CoAP_Result_t _rom CoAP_AddCfOptionToMsg(CoAP_Message_t* msg, uint16_t contentFormat) 25 | { 26 | return CoAP_AppendUintOptionToList(&(msg->pOptionsList), OPT_NUM_CONTENT_FORMAT, contentFormat); 27 | } 28 | 29 | CoAP_Result_t _rom CoAP_AddAcceptOptionToMsg(CoAP_Message_t* msg, uint16_t contentFormat) 30 | { 31 | return CoAP_AppendUintOptionToList(&(msg->pOptionsList), OPT_NUM_ACCEPT, contentFormat); 32 | } 33 | 34 | uint16_t _rom CoAP_GetAcceptOptionVal(CoAP_option_t* pAcceptOpt) 35 | { 36 | if(pAcceptOpt == NULL) return 0; 37 | if(pAcceptOpt->Number != OPT_NUM_ACCEPT) return 0; 38 | if(pAcceptOpt->Length == 0 || pAcceptOpt->Length > 2) return 0; 39 | 40 | uint16_t retVal = 0; 41 | if(pAcceptOpt->Length == 1) 42 | { 43 | retVal = pAcceptOpt->Value[0]; 44 | } 45 | else if(pAcceptOpt->Length == 2) 46 | { 47 | retVal = pAcceptOpt->Value[0]; 48 | retVal |= pAcceptOpt->Value[1] << 8; 49 | } 50 | 51 | return retVal; 52 | } 53 | 54 | uint16_t _rom CoAP_GetAcceptOptionValFromMsg(CoAP_Message_t* pMsg) 55 | { 56 | uint16_t retVal = 0; 57 | for(CoAP_option_t* pOpt =pMsg->pOptionsList ; pOpt != NULL; pOpt = pOpt->next) { 58 | if(pOpt->Number != OPT_NUM_ACCEPT) 59 | continue; 60 | retVal = CoAP_GetAcceptOptionVal(pOpt); 61 | } 62 | return retVal; 63 | } -------------------------------------------------------------------------------- /src/option-types/coap_option_cf.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #ifndef COAP_CF_OPTION 23 | #define COAP_CF_OPTION 24 | 25 | CoAP_Result_t CoAP_AddCfOptionToMsg(CoAP_Message_t* msg, uint16_t cf); 26 | CoAP_Result_t CoAP_AddAcceptOptionToMsg(CoAP_Message_t* msg, uint16_t contentFormat); 27 | 28 | uint16_t CoAP_GetAcceptOptionVal(CoAP_option_t* pAcceptOpt); 29 | uint16_t CoAP_GetAcceptOptionValFromMsg(CoAP_Message_t* pMsg); 30 | typedef enum 31 | { 32 | COAP_CF_TEXT_PLAIN = 0, 33 | COAP_CF_LINK_FORMAT = 40, 34 | COAP_CF_APP_XML = 41, 35 | COAP_CF_OCTET_STREAM = 42, 36 | COAP_CF_EXI = 47, 37 | COAP_CF_JSON = 50, 38 | COAP_CF_CBOR = 60 39 | }CoAP_ContentFormat_t; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/option-types/coap_option_observe.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #include "../coap_mem.h" 23 | #include "../coap.h" 24 | 25 | CoAP_Result_t _rom AddObserveOptionToMsg(CoAP_Message_t* msg, uint32_t val) 26 | { 27 | uint8_t wBuf[3]; 28 | wBuf[2] = val & 0xffu; 29 | wBuf[1] = (val >> 8u) & 0xffu; 30 | wBuf[0] = (val >> 16u) & 0xffu; 31 | 32 | if (val > 0xffff) { 33 | wBuf[0] = (val >> 16u) & 0xffu; 34 | wBuf[1] = (val >> 8u) & 0xffu; 35 | wBuf[2] = val & 0xffu; 36 | return CoAP_AppendOptionToList(&(msg->pOptionsList), OPT_NUM_OBSERVE, wBuf, 3); 37 | } else if (val > 0xff) { 38 | wBuf[0] = (val >> 8u) & 0xffu; 39 | wBuf[1] = val & 0xffu; 40 | return CoAP_AppendOptionToList(&(msg->pOptionsList), OPT_NUM_OBSERVE, wBuf, 2); 41 | } else if (val > 0) { 42 | wBuf[0] = val & 0xffu; 43 | return CoAP_AppendOptionToList(&(msg->pOptionsList), OPT_NUM_OBSERVE, wBuf, 1); 44 | } else { //val == 0 45 | return CoAP_AppendOptionToList(&(msg->pOptionsList), OPT_NUM_OBSERVE, NULL, 0); 46 | } 47 | } 48 | 49 | CoAP_Result_t _rom RemoveObserveOptionFromMsg(CoAP_Message_t* msg) { 50 | CoAP_option_t* pOpt; 51 | for (pOpt = msg->pOptionsList; pOpt != NULL; pOpt = pOpt->next) { 52 | if (pOpt->Number == OPT_NUM_OBSERVE) { 53 | CoAP_RemoveOptionFromList(&(msg->pOptionsList), pOpt); 54 | return COAP_OK; 55 | } 56 | } 57 | return COAP_NOT_FOUND; 58 | } 59 | 60 | CoAP_Result_t _rom UpdateObserveOptionInMsg(CoAP_Message_t* msg, uint32_t val) 61 | { 62 | RemoveObserveOptionFromMsg(msg); 63 | return AddObserveOptionToMsg(msg, val); 64 | } 65 | 66 | CoAP_Result_t _rom GetObserveOptionFromMsg(CoAP_Message_t* msg, uint32_t* val) { 67 | 68 | CoAP_option_t* pOpts = msg->pOptionsList; 69 | *val = 0; 70 | 71 | while (pOpts != NULL) { 72 | if (pOpts->Number == OPT_NUM_OBSERVE) { 73 | 74 | if (pOpts->Length == 0) { //implicit val = 0 (register) 75 | return COAP_OK; 76 | } else { 77 | if (pOpts->Length == 1) { 78 | *val |= (uint32_t) (pOpts->Value[0]); 79 | } else if (pOpts->Length == 2) { 80 | *val |= (((uint32_t) pOpts->Value[0]) << 8u) | ((uint32_t) pOpts->Value[1]); 81 | } else if (pOpts->Length == 3) { 82 | *val |= (((uint32_t) pOpts->Value[0]) << 16u) | (((uint32_t) pOpts->Value[1]) << 8u) | ((uint32_t) pOpts->Value[2]); 83 | } else { 84 | return COAP_BAD_OPTION_VAL; 85 | } 86 | } 87 | return COAP_OK; 88 | } 89 | 90 | pOpts = pOpts->next; 91 | } 92 | return COAP_NOT_FOUND; 93 | } 94 | 95 | CoAP_Observer_t* _rom CoAP_AllocNewObserver() 96 | { 97 | CoAP_Observer_t* newObserver = (CoAP_Observer_t*) (CoAP_malloc0(sizeof(CoAP_Observer_t))); 98 | if (newObserver == NULL) { 99 | return NULL; 100 | } 101 | 102 | memset(newObserver, 0, sizeof(CoAP_Observer_t)); 103 | return newObserver; 104 | } 105 | 106 | CoAP_Result_t _rom CoAP_FreeObserver(CoAP_Observer_t** pObserver) 107 | { 108 | INFO("Releasing pObserver\r\n"); 109 | //coap_mem_stats(); 110 | 111 | CoAP_FreeOptionList(&((*pObserver)->pOptList)); 112 | CoAP_free((void*) (*pObserver)); 113 | *pObserver = NULL; 114 | 115 | return COAP_OK; 116 | } 117 | 118 | //does not copy! 119 | CoAP_Result_t _rom CoAP_AppendObserverToList(CoAP_Observer_t** pListStart, CoAP_Observer_t* pObserverToAdd) 120 | { 121 | if (pObserverToAdd == NULL) 122 | return COAP_ERR_ARGUMENT; 123 | 124 | if (*pListStart == NULL) //List empty? create new first element 125 | { 126 | *pListStart = pObserverToAdd; 127 | (*pListStart)->next = NULL; 128 | } 129 | else //append new element at end 130 | { 131 | CoAP_Observer_t* pObs = *pListStart; 132 | while (pObs->next != NULL) { 133 | pObs = pObs->next; 134 | } 135 | 136 | pObs->next = pObserverToAdd; 137 | pObs = pObs->next; 138 | pObs->next = NULL; 139 | } 140 | return COAP_OK; 141 | } 142 | 143 | CoAP_Result_t _rom CoAP_UnlinkObserverFromList(CoAP_Observer_t** pListStart, CoAP_Observer_t* pObserverToRemove, bool FreeUnlinked) 144 | { 145 | CoAP_Observer_t* currP; 146 | CoAP_Observer_t* prevP; 147 | 148 | // For 1st node, indicate there is no previous. 149 | prevP = NULL; 150 | 151 | //Visit each node, maintaining a pointer to 152 | //the previous node we just visited. 153 | for (currP = *pListStart; currP != NULL; prevP = currP, currP = currP->next) { 154 | 155 | if (currP == pObserverToRemove) { // Found it. 156 | if (prevP == NULL) { 157 | //Fix beginning pointer. 158 | *pListStart = currP->next; 159 | } else { 160 | //Fix previous node's next to 161 | //skip over the removed node. 162 | prevP->next = currP->next; 163 | } 164 | 165 | // Deallocate the node. 166 | if (FreeUnlinked) { 167 | CoAP_FreeObserver(&currP); 168 | } 169 | //Done searching. 170 | return COAP_OK; 171 | } 172 | } 173 | return COAP_OK; 174 | } 175 | 176 | -------------------------------------------------------------------------------- /src/option-types/coap_option_observe.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #ifndef COAP_OBSERVE_OPTION 23 | #define COAP_OBSERVE_OPTION 24 | 25 | #define OBSERVE_OPT_REGISTER (0) 26 | #define OBSERVE_OPT_DEREGISTER (1) 27 | 28 | CoAP_Result_t AddObserveOptionToMsg(CoAP_Message_t *msg, uint32_t val); 29 | CoAP_Result_t GetObserveOptionFromMsg(CoAP_Message_t *msg, uint32_t *val); 30 | CoAP_Result_t RemoveObserveOptionFromMsg(CoAP_Message_t *msg); 31 | CoAP_Result_t UpdateObserveOptionInMsg(CoAP_Message_t *msg, uint32_t val); 32 | CoAP_Observer_t *CoAP_AllocNewObserver(); 33 | CoAP_Result_t CoAP_FreeObserver(CoAP_Observer_t **pObserver); 34 | CoAP_Result_t CoAP_AppendObserverToList(CoAP_Observer_t **pListStart, CoAP_Observer_t *pObserverToAdd); 35 | CoAP_Result_t CoAP_UnlinkObserverFromList(CoAP_Observer_t **pListStart, CoAP_Observer_t *pObserverToRemove, bool FreeUnlinked); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/option-types/coap_option_uri.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #include "../coap.h" 23 | 24 | 25 | //internal function 26 | //QueryStr points to char AFTER ? in a uri query string e.g: 'O' in [...]myuri/bla?Option1=Val1&Option2=Val2 27 | //todo: support percent encoding 28 | static CoAP_Result_t _rom ParseUriQueryFromStringToOption(CoAP_option_t** pUriOptionsListBegin, const char* QueryStr) { 29 | INFO("Query string coming:\n"); 30 | INFO("Query string: '%s'\n", QueryStr); 31 | const char* pCurUriPartBegin = QueryStr; 32 | uint32_t cnt = 0; 33 | 34 | while (*QueryStr != 0) { //str end 35 | if (*QueryStr == '&') {//query delimeter found 36 | if (cnt == 0) { //part begins with (another) delimiter -> skip it 37 | pCurUriPartBegin++; 38 | QueryStr++; 39 | continue; 40 | } 41 | 42 | CoAP_AppendOptionToList(pUriOptionsListBegin, OPT_NUM_URI_QUERY, (const uint8_t *) pCurUriPartBegin, cnt); //copy & alloc mem 43 | 44 | pCurUriPartBegin = pCurUriPartBegin + cnt + 1;//points to char following delimiter '&' 45 | cnt = 0; 46 | } else cnt++; 47 | 48 | QueryStr++; 49 | } 50 | 51 | //last uri part which is not a query string 52 | if (cnt != 0) { 53 | CoAP_AppendOptionToList(pUriOptionsListBegin, OPT_NUM_URI_QUERY, (const uint8_t *) pCurUriPartBegin, cnt); //copy & alloc last uri part 54 | } 55 | 56 | return COAP_OK; 57 | } 58 | 59 | 60 | // Appends to list of coap options uri-path and uri-query options from uri-string 61 | CoAP_Result_t _rom CoAP_AppendUriOptionsFromString(CoAP_option_t** pUriOptionsListBegin, const char* UriStr) { 62 | // Tested against following URI parts: 63 | // "halloWelt/wiegehts/dir?var1=val1&var2=val2&" 64 | // "halloWelt/wiegehts/dir" 65 | // "/halloWelt/wiegehts/dir?bla&bla&bla&bla" 66 | // "halloWelt/wiegehts/dir/?bla&bla=n&bla&bla" 67 | 68 | if (UriStr == NULL) { 69 | return COAP_ERR_ARGUMENT; 70 | } 71 | 72 | const char* pCurUriPartBegin = UriStr; 73 | uint32_t cnt = 0; 74 | 75 | while (*UriStr != 0) { //str end 76 | if (*UriStr == '/' || *UriStr == ' ' || *UriStr == '?') { //uri delimeter found - do not count 77 | 78 | if (cnt == 0) { //part begins with (another) delimiter -> skip it 79 | if (*UriStr == '?') { 80 | return ParseUriQueryFromStringToOption(pUriOptionsListBegin, UriStr + 1); 81 | } 82 | 83 | pCurUriPartBegin++; 84 | UriStr++; 85 | continue; 86 | } 87 | CoAP_AppendOptionToList(pUriOptionsListBegin, OPT_NUM_URI_PATH, (const uint8_t *) pCurUriPartBegin, cnt); //copy & alloc mem 88 | 89 | pCurUriPartBegin = pCurUriPartBegin + cnt + 1;//points to char following delimiter '/', ' ' or '?' 90 | 91 | if (*UriStr == '?') { //case /dir?var1 -> "dir" = path, "var1"=query begin (path component only MAY end with '\') 92 | return ParseUriQueryFromStringToOption(pUriOptionsListBegin, UriStr + 1); 93 | } 94 | 95 | cnt = 0; 96 | } else cnt++; 97 | 98 | UriStr++; 99 | } 100 | 101 | //last uri part which is not a query string 102 | if (cnt != 0) { 103 | CoAP_AppendOptionToList(pUriOptionsListBegin, OPT_NUM_URI_PATH, (const uint8_t *) pCurUriPartBegin, cnt); //copy & alloc last uri part 104 | } 105 | 106 | return COAP_OK; 107 | } 108 | 109 | CoAP_Result_t _rom CoAP_AddUriOptionsToMsgFromString(CoAP_Message_t* msg, char* UriStr) { 110 | return CoAP_AppendUriOptionsFromString(&(msg->pOptionsList), UriStr); 111 | } 112 | 113 | // Iterates over all URI_PATH options and match them one by one 114 | // if any part of the URI does not match, return false 115 | // uses implicit ordering of uri options! 116 | bool _rom CoAP_UriOptionsAreEqual(CoAP_option_t* OptListA, CoAP_option_t* OptListB) { 117 | 118 | CoAP_option_t* CurOptA = OptListA; 119 | CoAP_option_t* CurOptB = OptListB; 120 | 121 | while (!(CurOptA == NULL && CurOptB == NULL)) { 122 | while (CurOptA != NULL) { 123 | if (CurOptA->Number == OPT_NUM_URI_PATH) { 124 | break; 125 | } 126 | CurOptA = CurOptA->next; 127 | } 128 | 129 | while (CurOptB != NULL) { 130 | if (CurOptB->Number == OPT_NUM_URI_PATH) { 131 | break; 132 | } 133 | CurOptB = CurOptB->next; 134 | } 135 | 136 | if (!CoAP_OptionsAreEqual(CurOptA, CurOptB)) { //returns also true if both NULL! (implicit URI:"/") 137 | return false; 138 | } 139 | 140 | if (CurOptB != NULL) { 141 | CurOptB = CurOptB->next; 142 | } 143 | if (CurOptA != NULL) { 144 | CurOptA = CurOptA->next; 145 | } 146 | } 147 | return true; 148 | } 149 | 150 | 151 | void _rom CoAP_printUriOptionsList(CoAP_option_t* pOptListBegin) { 152 | bool queryPos = false; 153 | int j; 154 | while (pOptListBegin != NULL) { 155 | if (pOptListBegin->Number == OPT_NUM_URI_PATH) { 156 | for (j = 0; j < pOptListBegin->Length; j++) { 157 | INFO("%c", pOptListBegin->Value[j]); 158 | } 159 | INFO("\\"); 160 | } else if (pOptListBegin->Number == OPT_NUM_URI_QUERY) { 161 | if (!queryPos) { 162 | INFO("?"); 163 | queryPos = true; 164 | } else 165 | INFO("&"); 166 | 167 | for (j = 0; j < pOptListBegin->Length; j++) { 168 | INFO("%c", pOptListBegin->Value[j]); 169 | } 170 | } 171 | 172 | pOptListBegin = pOptListBegin->next; 173 | } 174 | INFO("\r\n"); 175 | } 176 | 177 | // Return the value of 178 | uint8_t* CoAP_GetUriQueryVal(CoAP_option_t* pUriOpt, const char* prefixStr, uint8_t* pValueLen) { 179 | if (pUriOpt == NULL) return NULL; 180 | if (pUriOpt->Number != OPT_NUM_URI_QUERY) return NULL; 181 | if (pUriOpt->Length == 0 || pUriOpt->Length > 255) return NULL; 182 | 183 | int prefixLen = coap_strlen(prefixStr); 184 | if (prefixLen >= pUriOpt->Length) return NULL; 185 | 186 | int i = 0; 187 | for (; i < prefixLen; i++) { 188 | if (pUriOpt->Value[i] != prefixStr[i]) return NULL; 189 | } 190 | 191 | //prefix found 192 | if (pValueLen != NULL) { 193 | // Strip possible equal sign after prefix 194 | if (pUriOpt->Length >= prefixLen + 1 && pUriOpt->Value[prefixLen] == '=') { 195 | prefixLen++; 196 | } 197 | *pValueLen = (pUriOpt->Length) - prefixLen; 198 | } 199 | 200 | return &(pUriOpt->Value[prefixLen]); 201 | } 202 | 203 | 204 | //if((pVal=CoAP_GetUriQueryValFromMsg(pReq, "cmd=", &valLen)) && valLen) { 205 | // for(int i=0; ipOptionsList ; pOpt != NULL; pOpt = pOpt->next) { 219 | retVal = CoAP_GetUriQueryVal(pOpt, prefixStr, pValueLen); 220 | if (retVal) break; 221 | } 222 | return retVal; 223 | } 224 | 225 | // Find by Value 226 | int8_t CoAP_FindUriQueryVal(CoAP_option_t* pUriOpt, const char* prefixStr, int CmpStrCnt, ...) { 227 | va_list ap; //compare string pointer 228 | int i, j; 229 | char* pStr = NULL; 230 | bool Match = false; 231 | uint8_t* pUriQueryVal; 232 | uint8_t ValLen; 233 | 234 | pUriQueryVal = CoAP_GetUriQueryVal(pUriOpt, prefixStr, &ValLen); 235 | if (pUriQueryVal == NULL) return 0;//-1; //prefix not found, no uri-query 236 | 237 | va_start (ap, CmpStrCnt); // Initialize the argument list. 238 | for (i = 1; i < CmpStrCnt + 1; i++) { //loop over all string arguments to compare the found uri query against 239 | pStr = va_arg(ap, char*); 240 | 241 | if (coap_strlen(pStr) != ValLen) continue; //already length does not match -> try next given string 242 | 243 | Match = true; 244 | for (j = 0; j < ValLen; j++) { 245 | if (pStr[j] != pUriQueryVal[j]) { 246 | Match = false; 247 | break; 248 | } 249 | } 250 | if (Match == false) continue; 251 | //found argument string matching to uri-query value 252 | va_end (ap); 253 | return i; //return argument number of match 254 | } 255 | 256 | va_end (ap); 257 | return 0; //not found 258 | } 259 | 260 | uint32_t CoAP_atoi(const uint8_t* Str, uint8_t Len) { 261 | 262 | uint32_t num = 0; 263 | uint32_t mul = 1;//multiplier 264 | 265 | if(Len > 10) return 0; //max 32bit num 2,147,483,647 266 | int i; 267 | for(i=Len-1; i>=0; i--) { 268 | if(Str[i]<'0' || Str[i]>'9') return 0; //no [0..9] character found -> return 0 as error indicator (as standard atoi does) 269 | num+=(Str[i]-'0')*mul; 270 | mul *=10; 271 | } 272 | 273 | return num; 274 | } 275 | 276 | 277 | -------------------------------------------------------------------------------- /src/option-types/coap_option_uri.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #ifndef COAP_URIPATH_OPTION 23 | #define COAP_URIPATH_OPTION 24 | 25 | 26 | CoAP_Result_t CoAP_AppendUriOptionsFromString(CoAP_option_t** pUriOptionsListBegin, const char* UriStr); 27 | 28 | CoAP_Result_t CoAP_AddUriOptionsToMsgFromString(CoAP_Message_t* msg, char* UriStr); 29 | 30 | bool CoAP_UriOptionsAreEqual(CoAP_option_t* OptListA, CoAP_option_t* OptListB); 31 | 32 | uint8_t* CoAP_GetUriQueryVal(CoAP_option_t* pUriOpt, const char* prefixStr, uint8_t* pValueLen); //searches only option in parameter 33 | uint8_t* CoAP_GetUriQueryValFromMsg(CoAP_Message_t* pMsg, const char* prefixStr, uint8_t* pValueLen); //searches all options in message 34 | int8_t CoAP_FindUriQueryVal(CoAP_option_t* pUriOpt, const char* prefixStr, int CmpStrCnt, ...); //searches only option in parameter 35 | 36 | void CoAP_printUriOptionsList(CoAP_option_t* pOptListBegin); 37 | uint32_t CoAP_atoi(const uint8_t* Str, uint8_t Len); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /test/ArduinoBuildTest/ArduinoBuildTest.ino: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | // For the moment, we just test if the library compiles fine for an 3 | // arduino environment. 4 | //----------------------------------------------------------------------- 5 | #include 6 | #include "about_res.h" 7 | 8 | // Generic Arduino implementations for coap_interface.h 9 | extern "C" void hal_uart_puts(char *s) { 10 | Serial.println(s); 11 | } 12 | extern "C" void hal_uart_putc(char c) { 13 | Serial.print(c); 14 | } 15 | 16 | //1Hz Clock used by timeout logic 17 | extern "C" uint32_t hal_rtc_1Hz_Cnt(void) { 18 | return millis()/1000; 19 | } 20 | 21 | //Non volatile memory e.g. flash/sd-card/eeprom 22 | //used to store observers during deepsleep of server 23 | extern "C" uint8_t* hal_nonVolatile_GetBufPtr() { 24 | return 0; 25 | } 26 | 27 | extern "C" bool hal_nonVolatile_WriteBuf(uint8_t* data, uint32_t len) { 28 | return false; 29 | } 30 | 31 | void setup() { 32 | Serial.begin(115200); 33 | static uint8_t CoAP_WorkMemory[512]; //Working memory of CoAPs internal memory allocator 34 | 35 | CoAP_API_t coapApi; 36 | coapApi.debugPutc = hal_uart_putc; 37 | coapApi.debugPuts = hal_uart_puts; 38 | coapApi.rtc1HzCnt = hal_rtc_1Hz_Cnt; 39 | 40 | CoAP_Config_t coapCfg; 41 | coapCfg.Memory = CoAP_WorkMemory; 42 | coapCfg.MemorySize = sizeof(CoAP_WorkMemory); 43 | 44 | CoAP_Init(coapApi, coapCfg); 45 | 46 | SocketHandle_t handle = (SocketHandle_t)1; 47 | uint16_t LocalPort = 1234; 48 | 49 | NetSocket_t* pSocket; 50 | pSocket=RetrieveSocket(handle); 51 | if(pSocket != nullptr) { 52 | ERROR("CoAP_ESP8266_CreateInterfaceSocket(): interface ID already in use!\r\n"); 53 | return; 54 | } 55 | 56 | pSocket = AllocSocket(); 57 | if(pSocket == nullptr){ 58 | ERROR("CoAP_ESP8266_CreateInterfaceSocket(): failed socket allocation\r\n"); 59 | return; 60 | } 61 | 62 | //local side of socket 63 | pSocket->EpLocal.NetType = IPV4; 64 | pSocket->EpLocal.NetPort = LocalPort; 65 | 66 | //remote side of socket 67 | pSocket->EpRemote.NetType = IPV4; 68 | pSocket->EpRemote.NetPort = LocalPort; 69 | 70 | // No real implementation 71 | pSocket->Handle = nullptr; 72 | pSocket->RxCB = nullptr; 73 | pSocket->Tx = nullptr; 74 | pSocket->Alive = true; 75 | 76 | //pSocket->ifID = ifID; 77 | 78 | //example of large resource (blockwise transfers) 79 | Create_About_Resource(); 80 | 81 | coap_mem_determinateStaticMem(); 82 | coap_mem_stats(); 83 | } 84 | 85 | void loop() { 86 | CoAP_doWork(); 87 | } -------------------------------------------------------------------------------- /test/ArduinoBuildTest/about_res.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | 23 | #include 24 | #include 25 | #include "about_res.h" 26 | 27 | static const char CoapInfoStringInFlash[] = {"\ 28 | The Constrained Application Protocol (CoAP) is a specialized web \ 29 | transfer protocol for use with constrained nodes and constrained \ 30 | (e.g., low-power, lossy) networks."}; 31 | 32 | CoAP_Res_t* pAbout_Res = NULL; 33 | 34 | static CoAP_HandlerResult_t RequestHandler(CoAP_Message_t* pReq, CoAP_Message_t* pResp) { 35 | static uint16_t payloadSize = sizeof(CoapInfoStringInFlash)-1; 36 | 37 | CoAP_SetPayload(pResp, (uint8_t*)&(CoapInfoStringInFlash[0]), payloadSize, false); 38 | 39 | return HANDLER_OK; 40 | } 41 | 42 | CoAP_Res_t* Create_About_Resource() { 43 | CoAP_ResOpts_t Options = {COAP_CF_TEXT_PLAIN, RES_OPT_GET, 0}; 44 | return (pAbout_Res=CoAP_CreateResource((char*)"about/coap", (char*)"CoAP Description",Options, RequestHandler, NULL)); 45 | } 46 | -------------------------------------------------------------------------------- /test/ArduinoBuildTest/about_res.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Dipl.-Ing. Tobias Rohde, http://www.lobaro.com 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | *******************************************************************************/ 22 | #ifndef USER_RESOURCES_ABOUT_RES_H_ 23 | #define USER_RESOURCES_ABOUT_RES_H_ 24 | 25 | CoAP_Res_t* Create_About_Resource(); 26 | extern CoAP_Res_t* pAbout_Res; 27 | 28 | #endif /* USER_RESOURCES_ABOUT_RES_H_ */ -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | add_subdirectory("../" "LobaroCoapLib") 4 | 5 | ###### Compile test executable ###### 6 | project(LobaroCoapTests) 7 | 8 | ## Prepare cmake for testing 9 | enable_testing() 10 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib) 11 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib) 12 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) 13 | 14 | ## Prepare gtest 15 | set(GTEST_DIR $ENV{GTEST_DIR} CACHE PATH "") 16 | set(GTEST_FILES ${GTEST_DIR}/src/gtest-all.cc ${GTEST_DIR}/src/gtest_main.cc) 17 | include_directories( ${GTEST_DIR} ${GTEST_DIR}/include) 18 | add_definitions(-DGTEST_HAS_PTHREAD=0) 19 | 20 | ## Prepare for coverage measuring 21 | if(${COVERAGE}) 22 | set(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") 23 | endif() 24 | 25 | # All cpp files in this directory are considered testcase files. 26 | file(GLOB_RECURSE TESTS_FILES ${CMAKE_CURRENT_LIST_DIR}/*.cpp) 27 | 28 | ## Create executable, set c++11 standard, link to library 29 | add_executable(${PROJECT_NAME} ${TESTS_FILES} ${GTEST_FILES}) 30 | target_compile_features(${PROJECT_NAME} PRIVATE cxx_range_for) 31 | target_link_libraries(${PROJECT_NAME} lobaro_coap) 32 | 33 | ## We actually do not use the cmake test system (we would add each test case with add_test) 34 | ## but the gtest framework. We therefore add only "one" test case here, which internally 35 | ## performs many gtest testcases. 36 | add_test(${PROJECT_NAME} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}) 37 | -------------------------------------------------------------------------------- /test/arduino.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eux 2 | 3 | echo "Tests disabled until fixed." 4 | exit 0 5 | 6 | /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16 7 | sleep 3 8 | export DISPLAY=:1.0 9 | 10 | mkdir -p /tmp/arduino 11 | curl -sS http://downloads.arduino.cc/arduino-$VERSION-linux64.tar.xz | tar xJ -C /tmp/arduino --strip 1 || 12 | curl -sS http://downloads.arduino.cc/arduino-$VERSION-linux64.tgz | tar xz -C /tmp/arduino --strip 1 13 | export PATH=$PATH:/tmp/arduino/ 14 | 15 | ln -s $PWD /tmp/arduino/libraries/lobaro-coap 16 | 17 | for TESTNAME in ArduinoBuildTest 18 | do 19 | arduino --verify --board $BOARD $PWD/test/$TESTNAME/$TESTNAME.ino 20 | done 21 | -------------------------------------------------------------------------------- /test/basic_tests.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2016 MSc. David Graeff 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | typedef int16_t bufsize; 20 | extern "C" void bstats(bufsize *curalloc, bufsize *totfree, bufsize *maxfree, long *nget, long *nrel); 21 | 22 | // Test fixture class. For these very basic tests, we only allocate some memory in the fixture. 23 | class BasicTest : public testing::Test { 24 | protected: 25 | virtual void SetUp() { 26 | // CoAP_Init can only be called once with the same memory address, 27 | // because internally bpool is called, which does not check if the same 28 | // address range is already in the managed memory pool. 29 | // Leads to an endless loop otherwise. 30 | static bool initialized = false; 31 | if (!initialized) { 32 | 33 | CoAP_API_t coapApi; 34 | coapApi.debugPuts = NULL; 35 | coapApi.rtc1HzCnt = NULL; 36 | 37 | CoAP_Config_t coapCfg; 38 | coapCfg.Memory = CoAP_WorkMemory; 39 | coapCfg.MemorySize = sizeof(CoAP_WorkMemory); 40 | 41 | CoAP_Init(coapApi, coapCfg); 42 | initialized = true; 43 | } 44 | } 45 | 46 | uint8_t CoAP_WorkMemory[4096] = { 0 }; 47 | }; 48 | 49 | TEST_F(BasicTest, MemoryTest) { 50 | bufsize curalloc, curalloc2, totfree, maxfree; 51 | long nget,nrel; 52 | 53 | bstats(&curalloc, &totfree, &maxfree, &nget, &nrel); 54 | 55 | void* buf = coap_mem_get(16); 56 | ASSERT_NE(buf, nullptr) << "Failed to allocate memory"; 57 | 58 | int i = coap_mem_size((uint8_t*)buf); 59 | ASSERT_EQ(i, 16) << "Buffer should be 16 bytes"; 60 | 61 | coap_mem_release(buf); 62 | bstats(&curalloc2, &totfree, &maxfree, &nget, &nrel); 63 | ASSERT_EQ(curalloc, curalloc2) << "Available memory should be the same after get/release"; 64 | } 65 | 66 | TEST_F(BasicTest, AllocSocketTest) { 67 | SocketHandle_t socketHandle = (SocketHandle_t) 0; 68 | uint16_t LocalPort = 1234; 69 | 70 | CoAP_Socket_t* pSocket; 71 | pSocket=RetrieveSocket(socketHandle); 72 | ASSERT_EQ(pSocket, nullptr) << "Interface id should not be in use"; 73 | 74 | pSocket = AllocSocket(); 75 | ASSERT_NE(pSocket, nullptr) << "Failed to allocate a socket"; 76 | 77 | 78 | // No real implementation, just for type check during test yet 79 | pSocket->Handle = nullptr; 80 | pSocket->Tx = nullptr; 81 | pSocket->Alive = true; 82 | } -------------------------------------------------------------------------------- /test/cmake.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eux 2 | 3 | # Download newer version of cmake. We need 3.1 at least, travis provides 2.8. 4 | CMAKE_URL="http://cmake.org/files/v3.3/cmake-3.3.1-Linux-x86_64.tar.gz" 5 | mkdir cmake 6 | curl -L ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake 7 | export PATH=${TRAVIS_BUILD_DIR}/cmake/bin:${PATH} 8 | 9 | # GTest files 10 | mkdir ${GTEST_DIR} 11 | cp -r /usr/src/gtest/* ${GTEST_DIR} 12 | 13 | # Create a build dir and change to that. 14 | mkdir ${TRAVIS_BUILD_DIR}/buildtest 15 | cd ${TRAVIS_BUILD_DIR}/buildtest 16 | 17 | # Execute cmake and build and test. 18 | cmake -DCMAKE_CXX_COMPILER=$CMAKE_CXX_COMPILER ${TRAVIS_BUILD_DIR}/test/ 19 | make 20 | 21 | echo "Tests disabled until fixed." 22 | # make test 23 | cd ${TRAVIS_BUILD_DIR} -------------------------------------------------------------------------------- /test/coap_interface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | extern "C" { 3 | #include 4 | } 5 | 6 | time_t millis(void) 7 | { 8 | struct timeb start; 9 | ftime(&start); 10 | return (start.time * 1000 + start.millitm); 11 | } 12 | 13 | // Generic implementations for coap_interface.h 14 | extern "C" void hal_uart_puts(char *s) { 15 | std::cout << s << std::endl; 16 | } 17 | extern "C" void hal_uart_putc(char c) { 18 | std::cout << c; 19 | } 20 | 21 | //1Hz Clock used by timeout logic 22 | extern "C" uint32_t hal_rtc_1Hz_Cnt(void) { 23 | return (uint32_t) millis()/1000; 24 | } 25 | 26 | //Non volatile memory e.g. flash/sd-card/eeprom 27 | //used to store observers during deepsleep of server 28 | extern "C" uint8_t* hal_nonVolatile_GetBufPtr() { 29 | return 0; 30 | } 31 | 32 | extern "C" bool hal_nonVolatile_WriteBuf(uint8_t* data, uint32_t len) { 33 | return false; 34 | } -------------------------------------------------------------------------------- /test/platformio.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eux 2 | 3 | echo "Tests disabled until fixed." 4 | exit 0 5 | 6 | pip install --user platformio 7 | 8 | for TESTNAME in ArduinoBuildTest 9 | do 10 | echo $(pwd) 11 | cd ${TRAVIS_BUILD_DIR}/test/$TESTNAME/ 12 | platformio ci $TESTNAME.ino -l . -l ../../ -b $BOARD 13 | done 14 | cd ${TRAVIS_BUILD_DIR} --------------------------------------------------------------------------------