├── .gitignore ├── LICENSE ├── README.md ├── tcoap.c ├── tcoap.h ├── tcoap_helpers.c ├── tcoap_helpers.h ├── tcoap_tcp.c ├── tcoap_tcp.h ├── tcoap_udp.c ├── tcoap_udp.h ├── tcoap_utils.c └── tcoap_utils.h /.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 | *.su 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2018, Serge Maslyakov 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### CoAP client library for embedded devices in C 2 | 3 | Allows to add the CoAP functionality for embedded device. 4 | 5 | #### Features 6 | 7 | - very small memory consumption (in the common case it may be about of 200 bytes for both rx/tx buffers, you can tune a PDU size) 8 | 9 | - implemented CoAP over UDP [rfc7252](https://tools.ietf.org/html/rfc7252) 10 | 11 | - implemented CoAP over TCP [draft-coap-tcp-tls-07](https://tools.ietf.org/html/draft-ietf-core-coap-tcp-tls-07) 12 | 13 | - retransmition/acknowledgment functionality 14 | 15 | - parsing of responses. Received data will be return to the user via callback. 16 | 17 | - helpers for block-wise mode. The block-wise mode is located at a higher level of abstraction than this implementation. 18 | See [wiki](https://github.com/Mozilla9/tiny-coap/wiki/Block-wise-mode-example) for example. 19 | 20 | 21 | #### How to send CoAP request to server 22 | 23 | 1) Include `tcoap.h` in your code. 24 | 25 | ``` 26 | #include "tcoap.h" 27 | 28 | ``` 29 | There are several functions in the `tcoap.h` which are declared how `external`. You should provide it implementation in your code. See [wiki](https://github.com/Mozilla9/tiny-coap/wiki) for common case of their implementation. 30 | 31 | ``` 32 | /** 33 | * @brief In this function user should implement a transmission given data via 34 | * hardware interface (e.g. serial port) 35 | * 36 | */ 37 | extern tcoap_error 38 | tcoap_tx_data(tcoap_handle * const handle, const uint8_t * buf, const uint32_t len); 39 | 40 | 41 | /** 42 | * @brief In this function user should implement a functionality of waiting response. 43 | * This function has to return a control when timeout will expired or 44 | * when response from server will be received. 45 | */ 46 | extern tcoap_error 47 | tcoap_wait_event(tcoap_handle * const handle, const uint32_t timeout_ms); 48 | 49 | 50 | /** 51 | * @brief Through this function the 'tcoap' lib will be notifying about events. 52 | * See possible events here 'tcoap_out_signal'. 53 | */ 54 | extern tcoap_error 55 | tcoap_tx_signal(tcoap_handle * const handle, const tcoap_out_signal signal); 56 | 57 | 58 | /** 59 | * @brief In this function user should implement a generating of message id. 60 | * 61 | */ 62 | extern uint16_t 63 | tcoap_get_message_id(tcoap_handle * const handle); 64 | 65 | 66 | /** 67 | * @brief In this function user should implement a generating of token. 68 | * 69 | */ 70 | extern tcoap_error 71 | tcoap_fill_token(tcoap_handle * const handle, uint8_t * token, const uint32_t tkl); 72 | 73 | 74 | /** 75 | * @brief These functions are using for debug purpose, if user will enable debug mode. 76 | * 77 | */ 78 | extern void tcoap_debug_print_packet(tcoap_handle * const handle, const char * msg, uint8_t * data, const uint32_t len); 79 | extern void tcoap_debug_print_options(tcoap_handle * const handle, const char * msg, const tcoap_option_data * options); 80 | extern void tcoap_debug_print_payload(tcoap_handle * const handle, const char * msg, const tcoap_data * const payload); 81 | 82 | 83 | /** 84 | * @brief In this function user should implement an allocating block of memory. 85 | * In the simple case it may be a static buffer. The 'TCOAP' will make 86 | * two calls of this function before starting work (for rx and tx buffer). 87 | * So, you should have minimum two separate blocks of memory. 88 | * 89 | */ 90 | extern tcoap_error tcoap_alloc_mem_block(uint8_t ** block, const uint32_t min_len); 91 | 92 | 93 | /** 94 | * @brief In this function user should implement a freeing block of memory. 95 | * 96 | */ 97 | extern tcoap_error tcoap_free_mem_block(uint8_t * block, const uint32_t min_len); 98 | 99 | extern void mem_copy(void * dst, const void * src, uint32_t cnt); 100 | extern bool mem_cmp(const void * dst, const void * src, uint32_t cnt); 101 | 102 | ``` 103 | 104 | 2) Define a `tcoap_handle` object, e.g. 105 | 106 | ``` 107 | tcoap_handle tc_handle = { 108 | .name = "coap_over_gsm", 109 | .transport = TCOAP_UDP 110 | }; 111 | 112 | ``` 113 | 114 | 115 | 3) Implement a transfer of incoming data from your hardware interface (e.g. serial port) to the `tcoap` either `tcoap_rx_byte` or `tcoap_rx_packet`. E.g. 116 | 117 | ``` 118 | void uart1_rx_irq_handler() 119 | { 120 | uint8_t byte = UART1->DR; 121 | tcoap_rx_byte(&tc_handle, byte); 122 | } 123 | 124 | void eth_rx_irq_handler(uint8_t * data, uint32_t len) 125 | { 126 | tcoap_rx_packet(&tc_handle, data, len); 127 | } 128 | 129 | ``` 130 | 131 | 132 | 4) Send a coap request and get back response data in the provided callback: 133 | 134 | ``` 135 | 136 | static void data_resource_response_callback(const struct tcoap_request_descriptor * const reqd, const tcoap_result_data * const result) 137 | { 138 | // ... check response 139 | } 140 | 141 | 142 | void send_request_to_data_resource(uint32_t *token_etag, uint8_t * data, uint32_t len) 143 | { 144 | tcoap_error err; 145 | tcoap_request_descriptor data_request; 146 | 147 | tcoap_option_data opt_etag; 148 | tcoap_option_data opt_path; 149 | tcoap_option_data opt_content; 150 | 151 | /* fill options - we should adhere an order of options */ 152 | opt_content.num = TCOAP_CONTENT_FORMAT_OPT; 153 | opt_content.value = (uint8_t *)"\x2A"; /* 42 = TCOAP_APPLICATION_OCTET_STREAM */ 154 | opt_content.len = 1; 155 | opt_content.next = NULL; 156 | 157 | opt_path.num = TCOAP_URI_PATH_OPT; 158 | opt_path.value = (uint8_t *)"data"; 159 | opt_path.len = 4; 160 | opt_path.next = &opt_content; 161 | 162 | opt_etag.num = TCOAP_ETAG_OPT; 163 | opt_etag.value = (uint8_t *)token_etag; 164 | opt_etag.len = 4; 165 | opt_etag.next = &opt_path; 166 | 167 | /* fill the request descriptor */ 168 | data_request.payload.buf = data; 169 | data_request.payload.len = len; 170 | 171 | data_request.code = TCOAP_REQ_POST; 172 | data_request.tkl = 2; 173 | data_request.type = tc_handle.transport == TCOAP_UDP ? TCOAP_MESSAGE_CON : TCOAP_MESSAGE_NON; 174 | data_request.options = &opt_etag; 175 | 176 | /* define the callback for response data */ 177 | data_request.response_callback = data_resource_response_callback; 178 | 179 | /* enable debug */ 180 | tcoap_debug(&tc_handle, true); 181 | 182 | /* send request */ 183 | err = tcoap_send_coap_request(&tc_handle, &data_request); 184 | 185 | if (err != TCOAP_OK) { 186 | // error handling 187 | } 188 | } 189 | 190 | ``` 191 | -------------------------------------------------------------------------------- /tcoap.c: -------------------------------------------------------------------------------- 1 | /** 2 | * tcoap.c 3 | * 4 | * Author: Serge Maslyakov, rusoil.9@gmail.com 5 | * Copyright 2017 Serge Maslyakov. All rights reserved. 6 | * 7 | */ 8 | 9 | 10 | #include "tcoap.h" 11 | 12 | #include "tcoap_udp.h" 13 | #include "tcoap_tcp.h" 14 | #include "tcoap_utils.h" 15 | 16 | 17 | 18 | static tcoap_error init_coap_driver(tcoap_handle * const handle, const tcoap_request_descriptor * const reqd); 19 | static void deinit_coap_driver(tcoap_handle * handle); 20 | 21 | 22 | 23 | /** 24 | * @brief See description in the header file. 25 | * 26 | */ 27 | void tcoap_debug(tcoap_handle * const handle, const bool enable) 28 | { 29 | if (enable) { 30 | TCOAP_SET_STATUS(handle, TCOAP_DEBUG_ON); 31 | } else { 32 | TCOAP_RESET_STATUS(handle, TCOAP_DEBUG_ON); 33 | } 34 | } 35 | 36 | 37 | /** 38 | * @brief See description in the header file. 39 | * 40 | */ 41 | tcoap_error tcoap_send_coap_request(tcoap_handle * const handle, const tcoap_request_descriptor * const reqd) 42 | { 43 | tcoap_error err; 44 | 45 | if (TCOAP_CHECK_STATUS(handle, TCOAP_SENDING_PACKET)) { 46 | return TCOAP_BUSY_ERROR; 47 | } 48 | 49 | TCOAP_SET_STATUS(handle, TCOAP_SENDING_PACKET); 50 | err = init_coap_driver(handle, reqd); 51 | 52 | if (err == TCOAP_OK) { 53 | 54 | switch (handle->transport) { 55 | case TCOAP_UDP: 56 | err = tcoap_send_coap_request_udp(handle, reqd); 57 | break; 58 | 59 | case TCOAP_TCP: 60 | err = tcoap_send_coap_request_tcp(handle, reqd); 61 | break; 62 | 63 | case TCOAP_SMS: 64 | default: 65 | /* not supported yet */ 66 | err = TCOAP_PARAM_ERROR; 67 | break; 68 | } 69 | } 70 | 71 | deinit_coap_driver(handle); 72 | 73 | TCOAP_RESET_STATUS(handle, TCOAP_SENDING_PACKET); 74 | tcoap_tx_signal(handle, TCOAP_ROUTINE_PACKET_DID_FINISH); 75 | 76 | return err; 77 | } 78 | 79 | 80 | /** 81 | * @brief See description in the header file. 82 | * 83 | */ 84 | tcoap_error tcoap_rx_byte(tcoap_handle * const handle, const uint8_t byte) 85 | { 86 | if (TCOAP_CHECK_STATUS(handle, TCOAP_WAITING_RESP)) { 87 | 88 | if (handle->response.len < TCOAP_MAX_PDU_SIZE) { 89 | handle->response.buf[handle->response.len++] = byte; 90 | 91 | tcoap_tx_signal(handle, TCOAP_RESPONSE_BYTE_DID_RECEIVE); 92 | return TCOAP_OK; 93 | } 94 | 95 | return TCOAP_RX_BUFF_FULL_ERROR; 96 | } 97 | 98 | return TCOAP_WRONG_STATE_ERROR; 99 | } 100 | 101 | 102 | /** 103 | * @brief See description in the header file. 104 | * 105 | */ 106 | tcoap_error tcoap_rx_packet(tcoap_handle * const handle, const uint8_t * buf, const uint32_t len) 107 | { 108 | if (TCOAP_CHECK_STATUS(handle, TCOAP_WAITING_RESP)) { 109 | 110 | mem_copy(handle->response.buf, buf, len < TCOAP_MAX_PDU_SIZE ? len : TCOAP_MAX_PDU_SIZE); 111 | handle->response.len = len; 112 | 113 | if (len < TCOAP_MAX_PDU_SIZE) { 114 | tcoap_tx_signal(handle, TCOAP_RESPONSE_DID_RECEIVE); 115 | return TCOAP_OK; 116 | } 117 | 118 | return TCOAP_RX_BUFF_FULL_ERROR; 119 | } 120 | 121 | return TCOAP_WRONG_STATE_ERROR; 122 | } 123 | 124 | 125 | /** 126 | * @brief Init CoAP driver 127 | * 128 | * @param handle - coap handle 129 | * @param reqd - descriptor of request 130 | * 131 | * @return status of operation 132 | */ 133 | static tcoap_error init_coap_driver(tcoap_handle * const handle, const tcoap_request_descriptor * const reqd) 134 | { 135 | tcoap_error err; 136 | 137 | err = TCOAP_OK; 138 | handle->request.len = 0; 139 | handle->response.len = 0; 140 | 141 | if (reqd->code == TCOAP_CODE_EMPTY_MSG && reqd->tkl) { 142 | return TCOAP_PARAM_ERROR; 143 | } 144 | 145 | if (handle->request.buf == NULL) { 146 | err = tcoap_alloc_mem_block(&handle->request.buf, TCOAP_MAX_PDU_SIZE); 147 | 148 | if (err != TCOAP_OK) { 149 | return err; 150 | } 151 | } 152 | 153 | if (reqd->type == TCOAP_MESSAGE_CON || reqd->response_callback != NULL) { 154 | if (handle->response.buf == NULL) { 155 | err = tcoap_alloc_mem_block(&handle->response.buf, TCOAP_MAX_PDU_SIZE); 156 | } 157 | } 158 | 159 | return err; 160 | } 161 | 162 | 163 | /** 164 | * @brief Deinit CoAP driver 165 | * 166 | * @param handle - coap handle 167 | * 168 | */ 169 | static void deinit_coap_driver(tcoap_handle * handle) 170 | { 171 | if (handle->response.buf != NULL) { 172 | tcoap_free_mem_block(handle->response.buf, TCOAP_MAX_PDU_SIZE); 173 | handle->response.buf = NULL; 174 | } 175 | 176 | if (handle->request.buf != NULL) { 177 | tcoap_free_mem_block(handle->request.buf, TCOAP_MAX_PDU_SIZE); 178 | handle->request.buf = NULL; 179 | } 180 | 181 | handle->request.len = 0; 182 | handle->response.len = 0; 183 | } 184 | 185 | 186 | -------------------------------------------------------------------------------- /tcoap.h: -------------------------------------------------------------------------------- 1 | /** 2 | * tcoap.h 3 | * 4 | * Author: Serge Maslyakov, rusoil.9@gmail.com 5 | * Copyright 2017 Serge Maslyakov. All rights reserved. 6 | * 7 | * 8 | * Acknowledgement: 9 | * 10 | * 1) californium https://github.com/eclipse/californium 11 | * 12 | * Aims: Implementation of CoAP client for mcu with ram 1-4 kB. 13 | * 14 | */ 15 | 16 | 17 | #ifndef __TCOAP_H 18 | #define __TCOAP_H 19 | 20 | 21 | #include 22 | #include 23 | 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | 30 | #ifndef NUUL 31 | #define NULL ((void *)0) 32 | #endif /* NUUL */ 33 | 34 | 35 | #define TCOAP_DEFAULT_VERSION 1 36 | #define TCOAP_CODE(CLASS,CODE) (int)((CLASS<<5)|CODE) 37 | #define TCOAP_EXTRACT_CLASS(c) (int)((c)>>5) 38 | 39 | #define TCOAP_TCP_URI_SCHEME "coap+tcp" 40 | #define TCOAP_TCP_SECURE_URI_SCHEME "coaps+tcp" 41 | #define TCOAP_UDP_URI_SCHEME "coap" 42 | #define TCOAP_UDP_SECURE_URI_SCHEME "coaps" 43 | 44 | #define TCOAP_TCP_DEFAULT_PORT 5683 45 | #define TCOAP_TCP_DEFAULT_SECURE_PORT 5684 46 | #define TCOAP_UDP_DEFAULT_PORT 5683 47 | #define TCOAP_UDP_DEFAULT_SECURE_PORT 5684 48 | 49 | 50 | #ifndef TCOAP_RESP_TIMEOUT_MS 51 | #define TCOAP_RESP_TIMEOUT_MS 9000 52 | #endif /* TCOAP_RESP_TIMEOUT_MS */ 53 | 54 | #ifndef TCOAP_ACK_TIMEOUT_MS 55 | #define TCOAP_ACK_TIMEOUT_MS 5000 56 | #endif /* TCOAP_ACK_TIMEOUT_MS */ 57 | 58 | #ifndef TCOAP_MAX_RETRANSMIT 59 | #define TCOAP_MAX_RETRANSMIT 3 60 | #endif /* TCOAP_MAX_RETRANSMIT */ 61 | 62 | #ifndef TCOAP_ACK_RANDOM_FACTOR 63 | #define TCOAP_ACK_RANDOM_FACTOR 130 /* 1.3 -> 130 to rid from float */ 64 | #endif /* TCOAP_ACK_RANDOM_FACTOR */ 65 | 66 | #ifndef TCOAP_MAX_PDU_SIZE 67 | #define TCOAP_MAX_PDU_SIZE 96 /* maximum size of a CoAP PDU */ 68 | #endif /* TCOAP_MAX_PDU_SIZE */ 69 | 70 | 71 | 72 | typedef enum { 73 | 74 | TCOAP_OK = 0, 75 | TCOAP_BUSY_ERROR, 76 | TCOAP_PARAM_ERROR, 77 | 78 | TCOAP_NO_FREE_MEM_ERROR, 79 | TCOAP_TIMEOUT_ERROR, 80 | TCOAP_NRST_ANSWER, 81 | TCOAP_NO_ACK_ERROR, 82 | TCOAP_NO_RESP_ERROR, 83 | 84 | TCOAP_RX_BUFF_FULL_ERROR, 85 | TCOAP_WRONG_STATE_ERROR, 86 | 87 | TCOAP_NO_OPTIONS_ERROR, 88 | TCOAP_WRONG_OPTIONS_ERROR 89 | 90 | } tcoap_error; 91 | 92 | 93 | typedef enum { 94 | 95 | TCOAP_ROUTINE_PACKET_WILL_START = 0, 96 | TCOAP_ROUTINE_PACKET_DID_FINISH, 97 | 98 | TCOAP_TX_RETR_PACKET, 99 | TCOAP_TX_ACK_PACKET, 100 | 101 | TCOAP_ACK_DID_RECEIVE, 102 | TCOAP_NRST_DID_RECEIVE, 103 | TCOAP_WRONG_PACKET_DID_RECEIVE, 104 | 105 | TCOAP_RESPONSE_BYTE_DID_RECEIVE, 106 | TCOAP_RESPONSE_TO_LONG_ERROR, 107 | TCOAP_RESPONSE_DID_RECEIVE 108 | 109 | } tcoap_out_signal; 110 | 111 | 112 | typedef enum { 113 | 114 | TCOAP_UDP = 0, 115 | TCOAP_TCP, 116 | TCOAP_SMS 117 | 118 | } tcoap_transport; 119 | 120 | 121 | typedef enum { 122 | 123 | TCOAP_MESSAGE_CON = 0, /* confirmable message (requires ACK/RST) */ 124 | TCOAP_MESSAGE_NON = 1, /* non-confirmable message (one-shot message) */ 125 | TCOAP_MESSAGE_ACK = 2, /* used to acknowledge confirmable messages */ 126 | TCOAP_MESSAGE_RST = 3 /* indicates error in received messages */ 127 | 128 | } tcoap_udp_message; 129 | 130 | 131 | typedef enum { 132 | 133 | TCOAP_REQUEST_CLASS = 0, 134 | TCOAP_SUCCESS_CLASS = 2, 135 | TCOAP_BAD_REQUEST_CLASS = 4, 136 | TCOAP_SERVER_ERR_CLASS = 5, 137 | 138 | TCOAP_TCP_SIGNAL_CLASS = 7 139 | 140 | } tcoap_class; 141 | 142 | 143 | typedef enum { 144 | 145 | TCOAP_CODE_EMPTY_MSG = TCOAP_CODE(0, 0), 146 | 147 | TCOAP_REQ_GET = TCOAP_CODE(TCOAP_REQUEST_CLASS, 1), 148 | TCOAP_REQ_POST = TCOAP_CODE(TCOAP_REQUEST_CLASS, 2), 149 | TCOAP_REQ_PUT = TCOAP_CODE(TCOAP_REQUEST_CLASS, 3), 150 | TCOAP_REQ_DEL = TCOAP_CODE(TCOAP_REQUEST_CLASS, 4), 151 | 152 | TCOAP_RESP_SUCCESS_OK_200 = TCOAP_CODE(TCOAP_SUCCESS_CLASS, 0), 153 | TCOAP_RESP_SUCCESS_CREATED_201 = TCOAP_CODE(TCOAP_SUCCESS_CLASS, 1), 154 | TCOAP_RESP_SUCCESS_DELETED_202 = TCOAP_CODE(TCOAP_SUCCESS_CLASS, 2), 155 | TCOAP_RESP_SUCCESS_VALID_203 = TCOAP_CODE(TCOAP_SUCCESS_CLASS, 3), 156 | TCOAP_RESP_SUCCESS_CHANGED_204 = TCOAP_CODE(TCOAP_SUCCESS_CLASS, 4), 157 | TCOAP_RESP_SUCCESS_CONTENT_205 = TCOAP_CODE(TCOAP_SUCCESS_CLASS, 5), 158 | 159 | TCOAP_RESP_ERROR_BAD_REQUEST_400 = TCOAP_CODE(TCOAP_BAD_REQUEST_CLASS, 0), 160 | TCOAP_RESP_ERROR_UNAUTHORIZED_401 = TCOAP_CODE(TCOAP_BAD_REQUEST_CLASS, 1), 161 | TCOAP_RESP_BAD_OPTION_402 = TCOAP_CODE(TCOAP_BAD_REQUEST_CLASS, 2), 162 | TCOAP_RESP_FORBIDDEN_403 = TCOAP_CODE(TCOAP_BAD_REQUEST_CLASS, 3), 163 | TCOAP_RESP_NOT_FOUND_404 = TCOAP_CODE(TCOAP_BAD_REQUEST_CLASS, 4), 164 | TCOAP_RESP_METHOD_NOT_ALLOWED_405 = TCOAP_CODE(TCOAP_BAD_REQUEST_CLASS, 5), 165 | TCOAP_RESP_METHOD_NOT_ACCEPTABLE_406 = TCOAP_CODE(TCOAP_BAD_REQUEST_CLASS, 6), 166 | TCOAP_RESP_PRECONDITION_FAILED_412 = TCOAP_CODE(TCOAP_BAD_REQUEST_CLASS, 12), 167 | TCOAP_RESP_REQUEST_ENTITY_TOO_LARGE_413 = TCOAP_CODE(TCOAP_BAD_REQUEST_CLASS, 13), 168 | TCOAP_RESP_UNSUPPORTED_CONTENT_FORMAT_415 = TCOAP_CODE(TCOAP_BAD_REQUEST_CLASS, 15), 169 | 170 | TCOAP_RESP_INTERNAL_SERVER_ERROR_500 = TCOAP_CODE(TCOAP_SERVER_ERR_CLASS, 0), 171 | TCOAP_RESP_NOT_IMPLEMENTED_501 = TCOAP_CODE(TCOAP_SERVER_ERR_CLASS, 1), 172 | TCOAP_RESP_BAD_GATEWAY_502 = TCOAP_CODE(TCOAP_SERVER_ERR_CLASS, 2), 173 | TCOAP_RESP_SERVICE_UNAVAILABLE_503 = TCOAP_CODE(TCOAP_SERVER_ERR_CLASS, 3), 174 | TCOAP_RESP_GATEWAY_TIMEOUT_504 = TCOAP_CODE(TCOAP_SERVER_ERR_CLASS, 4), 175 | TCOAP_RESP_PROXYING_NOT_SUPPORTED_505 = TCOAP_CODE(TCOAP_SERVER_ERR_CLASS, 5), 176 | 177 | TCOAP_TCP_SIGNAL_700 = TCOAP_CODE(TCOAP_TCP_SIGNAL_CLASS, 0), 178 | TCOAP_TCP_SIGNAL_CSM_701 = TCOAP_CODE(TCOAP_TCP_SIGNAL_CLASS, 1), 179 | TCOAP_TCP_SIGNAL_PING_702 = TCOAP_CODE(TCOAP_TCP_SIGNAL_CLASS, 2), 180 | TCOAP_TCP_SIGNAL_PONG_703 = TCOAP_CODE(TCOAP_TCP_SIGNAL_CLASS, 3), 181 | TCOAP_TCP_SIGNAL_RELEASE_704 = TCOAP_CODE(TCOAP_TCP_SIGNAL_CLASS, 4), 182 | TCOAP_TCP_SIGNAL_ABORT_705 = TCOAP_CODE(TCOAP_TCP_SIGNAL_CLASS, 5) 183 | 184 | } tcoap_packet_code; 185 | 186 | 187 | /** 188 | * Critical = (optnum & 1) 189 | * UnSafe = (optnum & 2) 190 | * NoCacheKey = (optnum & 0x1e) == 0x1c 191 | * 192 | */ 193 | typedef enum { 194 | 195 | TCOAP_IF_MATCH_OPT = 1, 196 | TCOAP_URI_HOST_OPT = 3, 197 | TCOAP_ETAG_OPT = 4, 198 | TCOAP_IF_NON_MATCH_OPT = 5, 199 | TCOAP_URI_PORT_OPT = 7, 200 | TCOAP_LOCATION_PATH_OPT = 8, 201 | TCOAP_URI_PATH_OPT = 11, 202 | TCOAP_CONTENT_FORMAT_OPT = 12, 203 | 204 | TCOAP_MAX_AGE_OPT = 14, 205 | TCOAP_URI_QUERY_OPT = 15, 206 | TCOAP_ACCEPT_OPT = 17, 207 | TCOAP_LOCATION_QUERY_OPT = 20, 208 | 209 | TCOAP_BLOCK2_OPT = 23, /* blockwise option for GET */ 210 | TCOAP_BLOCK1_OPT = 27, /* blockwise option for POST */ 211 | 212 | TCOAP_PROXY_URI_OPT = 35, 213 | TCOAP_PROXY_SCHEME_OPT = 39, 214 | TCOAP_SIZE1_OPT = 60 215 | 216 | } tcoap_option; 217 | 218 | 219 | typedef enum { 220 | 221 | TCOAP_TEXT_PLAIN = 0, /* default value */ 222 | TCOAP_TEXT_XML = 1, 223 | TCOAP_TEXT_CSV = 2, 224 | TCOAP_TEXT_HTML = 3, 225 | TCOAP_IMAGE_GIF = 21, 226 | TCOAP_IMAGE_JPEG = 22, 227 | TCOAP_IMAGE_PNG = 23, 228 | TCOAP_IMAGE_TIFF = 24, 229 | TCOAP_AUDIO_RAW = 25, 230 | TCOAP_VIDEO_RAW = 26, 231 | TCOAP_APPLICATION_LINK_FORMAT = 40, 232 | TCOAP_APPLICATION_XML = 41, 233 | TCOAP_APPLICATION_OCTET_STREAM = 42, 234 | TCOAP_APPLICATION_RDF_XML = 43, 235 | TCOAP_APPLICATION_SOAP_XML = 44, 236 | TCOAP_APPLICATION_ATOM_XML = 45, 237 | TCOAP_APPLICATION_XMPP_XML = 46, 238 | TCOAP_APPLICATION_EXI = 47, 239 | TCOAP_APPLICATION_FASTINFOSET = 48, 240 | TCOAP_APPLICATION_SOAP_FASTINFOSET = 49, 241 | TCOAP_APPLICATION_JSON = 50, 242 | TCOAP_APPLICATION_X_OBIX_BINARY = 51, 243 | TCOAP_APPLICATION_CBOR = 60 244 | 245 | } tcoap_media_type; 246 | 247 | 248 | typedef struct tcoap_option_data { 249 | 250 | uint16_t num; 251 | uint16_t len; 252 | uint8_t * value; /* may be string/int/long */ 253 | 254 | struct tcoap_option_data * next; 255 | 256 | } tcoap_option_data; 257 | 258 | 259 | typedef struct tcoap_data { 260 | 261 | uint8_t * buf; 262 | uint32_t len; 263 | 264 | } tcoap_data; 265 | 266 | 267 | typedef struct tcoap_result_data { 268 | 269 | uint8_t resp_code; 270 | tcoap_data payload; 271 | tcoap_option_data * options; /* NULL terminated linked list of options */ 272 | 273 | } tcoap_result_data; 274 | 275 | 276 | typedef struct tcoap_request_descriptor { 277 | 278 | uint8_t type; 279 | uint8_t code; 280 | uint16_t tkl; 281 | 282 | tcoap_data payload; /* should not be NULL */ 283 | tcoap_option_data * options; /* should be NULL if there are no options */ 284 | 285 | /** 286 | * @brief Callback with results of request 287 | * 288 | * @param reqd - pointer on the request data (struct 'tcoap_request_descriptor') 289 | * @param result - pointer on result data (struct 'tcoap_result_data') 290 | */ 291 | void (* response_callback) (const struct tcoap_request_descriptor * const reqd, const struct tcoap_result_data * const result); 292 | 293 | } tcoap_request_descriptor; 294 | 295 | 296 | typedef struct tcoap_handle { 297 | 298 | const char * name; 299 | uint16_t transport; 300 | 301 | uint16_t statuses_mask; 302 | 303 | tcoap_data request; 304 | tcoap_data response; 305 | 306 | } tcoap_handle; 307 | 308 | 309 | /** 310 | * @brief In this function user should implement a transmission given data via 311 | * hardware interface (e.g. serial port) 312 | * 313 | */ 314 | extern tcoap_error tcoap_tx_data(tcoap_handle * const handle, const uint8_t * buf, const uint32_t len); 315 | 316 | 317 | /** 318 | * @brief In this function user should implement a functionality of waiting response. 319 | * This function has to return a control when timeout will expired or 320 | * when response from server will be received. 321 | */ 322 | extern tcoap_error tcoap_wait_event(tcoap_handle * const handle, const uint32_t timeout_ms); 323 | 324 | 325 | /** 326 | * @brief Through this function the 'tcoap' lib will be notifing about events. 327 | * See possible events here 'tcoap_out_signal'. 328 | */ 329 | extern tcoap_error tcoap_tx_signal(tcoap_handle * const handle, const tcoap_out_signal signal); 330 | 331 | 332 | /** 333 | * @brief In this function user should implement a generating of message id. 334 | * 335 | */ 336 | extern uint16_t tcoap_get_message_id(tcoap_handle * const handle); 337 | 338 | 339 | /** 340 | * @brief In this function user should implement a generating of token. 341 | * 342 | */ 343 | extern tcoap_error tcoap_fill_token(tcoap_handle * const handle, uint8_t * token, const uint32_t tkl); 344 | 345 | 346 | /** 347 | * @brief These functions are using for debug purpose, if user will enable debug mode. 348 | * 349 | */ 350 | extern void tcoap_debug_print_packet(tcoap_handle * const handle, const char * msg, uint8_t * data, const uint32_t len); 351 | extern void tcoap_debug_print_options(tcoap_handle * const handle, const char * msg, const tcoap_option_data * options); 352 | extern void tcoap_debug_print_payload(tcoap_handle * const handle, const char * msg, const tcoap_data * const payload); 353 | 354 | 355 | /** 356 | * @brief In this function user should implement an allocating block of memory. 357 | * In simple case it may be a static buffer. The 'TCOAP' will make 358 | * two calls of this function before starting work (for rx and tx buffers). 359 | * So, you should have minimum two separate blocks of memory. 360 | * 361 | */ 362 | extern tcoap_error tcoap_alloc_mem_block(uint8_t ** block, const uint32_t min_len); 363 | 364 | 365 | /** 366 | * @brief In this function user should implement a freeing mem block. 367 | * 368 | */ 369 | extern tcoap_error tcoap_free_mem_block(uint8_t * block, const uint32_t min_len); 370 | 371 | 372 | /** 373 | * @brief In this function user should implement a copying mem block. 374 | * 375 | */ 376 | extern void mem_copy(void * dst, const void * src, uint32_t cnt); 377 | 378 | 379 | /** 380 | * @brief In this function user should implement a comparing two mem blocks. 381 | * 382 | */ 383 | extern bool mem_cmp(const void * dst, const void * src, uint32_t cnt); 384 | 385 | 386 | /** 387 | * @brief Enable/disable CoAP debug. 388 | * Also you should implement 'tcoap_debug_print..' methods in your code. 389 | * 390 | */ 391 | void tcoap_debug(tcoap_handle * const handle, const bool enable); 392 | 393 | 394 | /** 395 | * @brief Send CoAP request to the server 396 | * 397 | * @param handle - coap handle 398 | * @param reqd - descriptor of request 399 | * 400 | * @return status of operation 401 | * 402 | */ 403 | tcoap_error tcoap_send_coap_request(tcoap_handle * const handle, const tcoap_request_descriptor * const reqd); 404 | 405 | 406 | /** 407 | * @brief Receive a packet step-by-step (sequence of bytes). 408 | * You may to use it if you communicate with server over serial port 409 | * or you haven't a free mem for cumulative buffer. Detecting of the 410 | * end of packet is a user responsibility (through byte-timeout). 411 | * 412 | * @param handle - coap handle 413 | * @param byte - received byte 414 | * 415 | * @return status of operation 416 | * 417 | */ 418 | tcoap_error tcoap_rx_byte(tcoap_handle * const handle, const uint8_t byte); 419 | 420 | 421 | /** 422 | * @brief Receive whole packet 423 | * 424 | * @param handle - coap handle 425 | * @param buf - pointer on buffer with data 426 | * @param len - length of data 427 | * 428 | * @return status of operation 429 | * 430 | */ 431 | tcoap_error tcoap_rx_packet(tcoap_handle * const handle, const uint8_t * buf, const uint32_t len); 432 | 433 | 434 | #ifdef __cplusplus 435 | } 436 | #endif 437 | 438 | #endif /* __TCOAP_H */ 439 | -------------------------------------------------------------------------------- /tcoap_helpers.c: -------------------------------------------------------------------------------- 1 | /** 2 | * tcoap_helpers.c 3 | * 4 | * Author: Serge Maslyakov, rusoil.9@gmail.com 5 | * Copyright 2017 Serge Maslyakov. All rights reserved. 6 | * 7 | */ 8 | 9 | 10 | #include "tcoap_helpers.h" 11 | 12 | 13 | /** 14 | * @brief See description in the header file. 15 | * 16 | */ 17 | uint16_t tcoap_decode_szx_to_size(const uint8_t szx) 18 | { 19 | switch (szx) 20 | { 21 | case 0: 22 | return TCOAP_BLOCK_SZX_VAL_0; 23 | 24 | case 1: 25 | return TCOAP_BLOCK_SZX_VAL_1; 26 | 27 | case 2: 28 | return TCOAP_BLOCK_SZX_VAL_2; 29 | 30 | case 3: 31 | return TCOAP_BLOCK_SZX_VAL_3; 32 | 33 | case 4: 34 | return TCOAP_BLOCK_SZX_VAL_4; 35 | 36 | case 5: 37 | return TCOAP_BLOCK_SZX_VAL_5; 38 | 39 | case 6: 40 | return TCOAP_BLOCK_SZX_VAL_6; 41 | 42 | default: 43 | return TCOAP_BLOCK_SZX_VAL_7; 44 | } 45 | } 46 | 47 | 48 | /** 49 | * @brief See description in the header file. 50 | * 51 | */ 52 | void tcoap_fill_block2_opt(tcoap_option_data * const option, const tcoap_blockwise_data * const bw, uint8_t * const value) 53 | { 54 | /* 55 | * Block Option Value 56 | * 57 | * 0 58 | * 0 1 2 3 4 5 6 7 59 | * +-+-+-+-+-+-+-+-+ 60 | * | NUM |M| SZX | 61 | * +-+-+-+-+-+-+-+-+ 62 | * 63 | * 0 1 64 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 65 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 66 | * | NUM |M| SZX | 67 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 68 | * 69 | * 0 1 2 70 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 71 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 72 | * | NUM |M| SZX | 73 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 74 | * 75 | */ 76 | 77 | option->num = TCOAP_BLOCK2_OPT; 78 | option->value = value; 79 | option->len = 1; 80 | option->next = NULL; 81 | 82 | value[0] = (bw->arr[0] & 0x0F); 83 | value[0] <<= 4; 84 | value[0] |= bw->fld.block_szx; 85 | value[0] |= bw->fld.more ? 8 : 0; 86 | 87 | if (bw->fld.num > 15) { 88 | option->len = 2; 89 | 90 | value[1] = value[0]; 91 | 92 | value[0] = (bw->arr[0] >> 4); 93 | value[0] |= (bw->arr[1] & 0x0F); 94 | } 95 | 96 | if (bw->fld.num > 4095) { 97 | option->len = 3; 98 | 99 | value[2] = value[1]; 100 | value[1] = value[0]; 101 | 102 | value[0] = (bw->arr[1] >> 4); 103 | value[0] |= (bw->arr[2] & 0x0F); 104 | } 105 | } 106 | 107 | 108 | /** 109 | * @brief See description in the header file. 110 | * 111 | */ 112 | void tcoap_extract_block2_from_opt(const tcoap_option_data * const block2, tcoap_blockwise_data * const bw) 113 | { 114 | bw->fld.num = 0; 115 | bw->fld.block_szx = 0; 116 | bw->fld.more = 0; 117 | 118 | switch (block2->len) { 119 | case 0: 120 | break; 121 | 122 | case 1: 123 | bw->fld.num = (block2->value[0] >> 4); 124 | bw->arr[3] = (block2->value[0] & 0x0F); 125 | break; 126 | 127 | case 2: 128 | bw->arr[1] = (block2->value[0] >> 4); 129 | bw->arr[0] = (block2->value[0] << 4) | (block2->value[1] >> 4); 130 | 131 | bw->arr[3] = (block2->value[1] & 0x0F); 132 | break; 133 | 134 | case 3: 135 | bw->arr[2] = (block2->value[0] >> 4); 136 | bw->arr[1] = (block2->value[0] << 4) | (block2->value[1] >> 4); 137 | bw->arr[0] = (block2->value[1] << 4) | (block2->value[2] >> 4); 138 | 139 | bw->arr[3] = (block2->value[2] & 0x0F); 140 | break; 141 | 142 | default: 143 | break; 144 | } 145 | } 146 | 147 | 148 | /** 149 | * @brief See description in the header file. 150 | * 151 | */ 152 | const tcoap_option_data * tcoap_find_option_by_number(const tcoap_option_data * options, const uint16_t opt_num) 153 | { 154 | do { 155 | 156 | if (options->num > opt_num) { 157 | break; 158 | } 159 | 160 | if (options->num == opt_num) { 161 | return options; 162 | } 163 | 164 | options = options->next; 165 | 166 | } while (options != NULL); 167 | 168 | return NULL; 169 | } 170 | 171 | 172 | -------------------------------------------------------------------------------- /tcoap_helpers.h: -------------------------------------------------------------------------------- 1 | /** 2 | * tcoap_helpers.h 3 | * 4 | * Author: Serge Maslyakov, rusoil.9@gmail.com 5 | * Copyright 2017 Serge Maslyakov. All rights reserved. 6 | * 7 | */ 8 | 9 | 10 | #ifndef __TCOAP_HELPERS_H 11 | #define __TCOAP_HELPERS_H 12 | 13 | 14 | #include 15 | #include "tcoap.h" 16 | 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | 23 | typedef enum { 24 | 25 | TCOAP_BLOCK_SZX_VAL_0 = 16, 26 | TCOAP_BLOCK_SZX_VAL_1 = 32, 27 | TCOAP_BLOCK_SZX_VAL_2 = 64, 28 | TCOAP_BLOCK_SZX_VAL_3 = 128, 29 | TCOAP_BLOCK_SZX_VAL_4 = 256, 30 | TCOAP_BLOCK_SZX_VAL_5 = 512, 31 | TCOAP_BLOCK_SZX_VAL_6 = 1024, 32 | 33 | /** 34 | * Reserved, i.e., MUST NOT be sent and 35 | * MUST lead to a 4.00 Bad Request response 36 | * code upon reception in a request. 37 | */ 38 | TCOAP_BLOCK_SZX_VAL_7 = 0 39 | 40 | } tcoap_blockwise_szx; 41 | 42 | 43 | typedef union tcoap_blockwise_data { 44 | 45 | struct { 46 | uint32_t num : 24; 47 | uint32_t block_szx : 3; 48 | uint32_t more : 1; 49 | } fld; 50 | 51 | uint8_t arr[4]; 52 | 53 | } tcoap_blockwise_data; 54 | 55 | 56 | /** 57 | * @brief Get block size by SZX value 58 | * 59 | * @param szx - a three-bit unsigned integer indicating the size of a block to the power of two. 60 | * 61 | */ 62 | uint16_t tcoap_decode_szx_to_size(const uint8_t szx); 63 | 64 | 65 | /** 66 | * @brief Fill block2 option 67 | * 68 | * @param option - pointer on the 'tcoap_option_data' struct 69 | * @param bw - pointer on the block2 data 70 | * @param value - buffer for storing encoded value (max length is 3) 71 | * 72 | */ 73 | void tcoap_fill_block2_opt(tcoap_option_data * const option, const tcoap_blockwise_data * const bw, uint8_t * const value); 74 | 75 | 76 | /** 77 | * @brief Extract block2 value from option 78 | * 79 | * @param block2 - raw option contains block2 80 | * @param bw - pointer on the block2 data to store value 81 | * 82 | */ 83 | void tcoap_extract_block2_from_opt(const tcoap_option_data * const block2, tcoap_blockwise_data * const bw); 84 | 85 | 86 | /** 87 | * @brief Find an option by its number 88 | * 89 | * @param options - list of options 90 | * @param opt_num - number of option 91 | * 92 | * @return pointer to the found option or NULL if option is absent 93 | */ 94 | const tcoap_option_data * tcoap_find_option_by_number(const tcoap_option_data * options, const uint16_t opt_num); 95 | 96 | 97 | #ifdef __cplusplus 98 | } 99 | #endif 100 | 101 | #endif /* __TCOAP_HELPERS_H */ 102 | -------------------------------------------------------------------------------- /tcoap_tcp.c: -------------------------------------------------------------------------------- 1 | /** 2 | * tcoap_tcp.c 3 | * 4 | * Author: Serge Maslyakov, rusoil.9@gmail.com 5 | * Copyright 2017 Serge Maslyakov. All rights reserved. 6 | * 7 | */ 8 | 9 | 10 | #include "tcoap_tcp.h" 11 | #include "tcoap_utils.h" 12 | 13 | 14 | 15 | #define TCOAP_MIN_TCP_HEADER_LEN 2u 16 | 17 | #define TCOAP_TCP_LEN_1BYTE 13 18 | #define TCOAP_TCP_LEN_2BYTES 14 19 | #define TCOAP_TCP_LEN_4BYTES 15 20 | 21 | #define TCOAP_TCP_LEN_MIN 13 22 | #define TCOAP_TCP_LEN_MED 269 23 | #define TCOAP_TCP_LEN_MAX 65805 24 | 25 | 26 | /** 27 | * Auxiliary data structures 28 | * 29 | */ 30 | typedef union { 31 | 32 | struct { 33 | uint8_t tkl : 4; /* length of Token */ 34 | uint8_t len : 4; /* length of Options & Payload */ 35 | } fields; 36 | 37 | uint8_t byte; 38 | } tcoap_tcp_len_header; 39 | 40 | 41 | typedef struct { 42 | 43 | tcoap_tcp_len_header len_header; 44 | 45 | uint8_t code; 46 | uint32_t data_len; 47 | 48 | } tcoap_tcp_header; 49 | 50 | 51 | 52 | static void asemble_request(tcoap_handle * const handle, tcoap_data * const request, const tcoap_request_descriptor * const reqd); 53 | static uint32_t parse_response(const tcoap_data * const request, const tcoap_data * const response, uint32_t * const options_shift); 54 | static uint32_t extract_data_length(tcoap_tcp_header * const header, const uint8_t * const buf); 55 | static void shift_data(uint8_t * dst, const uint8_t * src, uint32_t len); 56 | 57 | 58 | 59 | /** 60 | * @brief See description in the header file. 61 | * 62 | */ 63 | tcoap_error tcoap_send_coap_request_tcp(tcoap_handle * const handle, const tcoap_request_descriptor * const reqd) 64 | { 65 | tcoap_error err; 66 | uint32_t resp_mask; 67 | uint32_t option_start_idx; 68 | tcoap_result_data result; 69 | 70 | /* assembling packet */ 71 | asemble_request(handle, &handle->request, reqd); 72 | 73 | /* debug support */ 74 | if (TCOAP_CHECK_STATUS(handle, TCOAP_DEBUG_ON)) { 75 | tcoap_debug_print_packet(handle, "coap >> ", handle->request.buf, handle->request.len); 76 | } 77 | 78 | /* sending packet */ 79 | tcoap_tx_signal(handle, TCOAP_ROUTINE_PACKET_WILL_START); 80 | 81 | err = tcoap_tx_data(handle, handle->request.buf, handle->request.len); 82 | 83 | if (err != TCOAP_OK) { 84 | return err; 85 | } 86 | 87 | /* waiting response if needed */ 88 | resp_mask = TCOAP_RESP_EMPTY; 89 | if (reqd->response_callback != NULL) { 90 | 91 | handle->response.len = 0; 92 | TCOAP_SET_STATUS(handle, TCOAP_WAITING_RESP); 93 | 94 | /* waiting either data arriving or timeout expiring */ 95 | err = tcoap_wait_event(handle, TCOAP_RESP_TIMEOUT_MS); 96 | 97 | TCOAP_RESET_STATUS(handle, TCOAP_WAITING_RESP); 98 | 99 | if (err != TCOAP_OK) { 100 | return err; 101 | } 102 | 103 | /* debug support */ 104 | if (TCOAP_CHECK_STATUS(handle, TCOAP_DEBUG_ON)) { 105 | tcoap_debug_print_packet(handle, "coap << ", handle->response.buf, handle->response.len); 106 | } 107 | 108 | /* parsing incoming packet */ 109 | resp_mask = parse_response(&handle->request, &handle->response, &option_start_idx); 110 | 111 | if (TCOAP_CHECK_RESP(resp_mask, TCOAP_RESP_INVALID_PACKET)) { 112 | 113 | tcoap_tx_signal(handle, TCOAP_WRONG_PACKET_DID_RECEIVE); 114 | err = TCOAP_NO_RESP_ERROR; 115 | 116 | return err; 117 | } else if (TCOAP_CHECK_RESP(resp_mask, TCOAP_RESP_NRST)) { 118 | 119 | tcoap_tx_signal(handle, TCOAP_NRST_DID_RECEIVE); 120 | err = TCOAP_NRST_ANSWER; 121 | 122 | return err; 123 | } 124 | 125 | /* We are using the same request buffer for storing incoming options, bcoz 126 | * outgoing packet is not needed already. It allows us to save ram-memory. 127 | */ 128 | err = decoding_options(&handle->response, 129 | (tcoap_option_data *)handle->request.buf, 130 | option_start_idx, 131 | &handle->request.len); 132 | 133 | if (err == TCOAP_WRONG_OPTIONS_ERROR) { 134 | return err; 135 | } 136 | 137 | /* check the payload len */ 138 | if (handle->response.len > handle->request.len) { 139 | result.payload.len = handle->response.len - handle->request.len; 140 | result.payload.buf = handle->response.buf + handle->request.len; 141 | } else { 142 | result.payload.len = 0; 143 | result.payload.buf = NULL; 144 | } 145 | 146 | /* response_code_idx = option_start_idx - (handle->response.buf[0] & 0x0f) - 1 */ 147 | result.resp_code = handle->response.buf[option_start_idx - (handle->response.buf[0] & 0x0f) - 1]; 148 | result.options = err == TCOAP_NO_OPTIONS_ERROR ? NULL : (tcoap_option_data *)handle->request.buf; 149 | 150 | reqd->response_callback(reqd, &result); 151 | 152 | /* debug support */ 153 | if (TCOAP_CHECK_STATUS(handle, TCOAP_DEBUG_ON)) { 154 | tcoap_debug_print_options(handle, "coap opt << ", result.options); 155 | tcoap_debug_print_payload(handle, "coap pld << ", &result.payload); 156 | } 157 | } 158 | 159 | return err; 160 | } 161 | 162 | 163 | /** 164 | * @brief Assemble CoAP over TCP request. 165 | * 166 | * @param handle - coap handle 167 | * @param request - data struct for storing request 168 | * @param reqd - descriptor of request 169 | * 170 | */ 171 | static void asemble_request(tcoap_handle * const handle, tcoap_data * const request, const tcoap_request_descriptor * const reqd) 172 | { 173 | uint32_t options_shift; 174 | uint32_t options_len; 175 | tcoap_tcp_len_header header; 176 | 177 | /** 178 | * CoAP over TCP has a header with variable length. Therefore we should calculate 179 | * length of Options & Payload before assembling the header. 180 | * At first we will try to predict length of header. 181 | * We should be shift a data, if we will predict wrong length of header. 182 | * 183 | */ 184 | options_len = 0; 185 | options_shift = TCOAP_MIN_TCP_HEADER_LEN + reqd->tkl; 186 | 187 | if (reqd->payload.len > 10) { 188 | options_shift += 1; 189 | } 190 | 191 | /* assemble options */ 192 | if (reqd->options != NULL) { 193 | options_len += encoding_options(request->buf + options_shift, reqd->options); 194 | } 195 | 196 | /* assemble header */ 197 | request->len = options_len + (reqd->payload.len ? reqd->payload.len + 1 : 0); 198 | header.fields.tkl = reqd->tkl; 199 | 200 | if (request->len < TCOAP_TCP_LEN_MIN) { 201 | 202 | header.fields.len = request->len; 203 | 204 | request->buf[0] = header.byte; 205 | request->buf[1] = reqd->code; 206 | 207 | /* check on shift data */ 208 | if (options_shift > (TCOAP_MIN_TCP_HEADER_LEN + reqd->tkl)) { 209 | shift_data(request->buf + reqd->tkl + TCOAP_MIN_TCP_HEADER_LEN, request->buf + options_shift, options_len); 210 | } 211 | 212 | request->len = 2; 213 | 214 | } else if (request->len < TCOAP_TCP_LEN_MED) { 215 | 216 | header.fields.len = TCOAP_TCP_LEN_1BYTE; 217 | 218 | request->buf[0] = header.byte; 219 | request->buf[1] = request->len - TCOAP_TCP_LEN_MIN; 220 | 221 | /* check on shift data */ 222 | if (options_shift > reqd->tkl + TCOAP_MIN_TCP_HEADER_LEN + 1) { 223 | 224 | shift_data(request->buf + reqd->tkl + TCOAP_MIN_TCP_HEADER_LEN + 1, 225 | request->buf + options_shift, options_len); 226 | 227 | } else if (options_shift < reqd->tkl + TCOAP_MIN_TCP_HEADER_LEN + 1) { 228 | 229 | shift_data(request->buf + options_len + reqd->tkl + TCOAP_MIN_TCP_HEADER_LEN, 230 | request->buf + options_shift + options_len - 1, options_len); 231 | } 232 | 233 | request->buf[2] = reqd->code; 234 | request->len = 3; 235 | 236 | } else if (request->len < TCOAP_TCP_LEN_MAX) { 237 | 238 | header.fields.len = TCOAP_TCP_LEN_2BYTES; 239 | request->buf[0] = header.byte; 240 | 241 | /* check on shift data */ 242 | if (options_shift > reqd->tkl + TCOAP_MIN_TCP_HEADER_LEN + 2) { 243 | 244 | shift_data(request->buf + reqd->tkl + TCOAP_MIN_TCP_HEADER_LEN + 2, 245 | request->buf + options_shift, options_len); 246 | 247 | } else if (options_shift < reqd->tkl + TCOAP_MIN_TCP_HEADER_LEN + 2) { 248 | 249 | shift_data(request->buf + options_len + reqd->tkl + TCOAP_MIN_TCP_HEADER_LEN + 1, 250 | request->buf + options_shift + options_len - 1, options_len); 251 | } 252 | 253 | request->buf[1] = (request->len - TCOAP_TCP_LEN_MED) >> 8; 254 | request->buf[2] = (request->len - TCOAP_TCP_LEN_MED); 255 | request->buf[3] = reqd->code; 256 | request->len = 4; 257 | 258 | } else { 259 | 260 | header.fields.len = TCOAP_TCP_LEN_4BYTES; 261 | request->buf[0] = header.byte; 262 | 263 | /* check on shift data */ 264 | if (options_shift > reqd->tkl + TCOAP_MIN_TCP_HEADER_LEN + 4) { 265 | 266 | shift_data(request->buf + reqd->tkl + TCOAP_MIN_TCP_HEADER_LEN + 1, 267 | request->buf + options_shift, options_len); 268 | 269 | } else if (options_shift < reqd->tkl + TCOAP_MIN_TCP_HEADER_LEN + 4) { 270 | 271 | shift_data(request->buf + options_len + reqd->tkl + TCOAP_MIN_TCP_HEADER_LEN + 3, 272 | request->buf + options_shift + options_len - 1, options_len); 273 | } 274 | 275 | request->buf[1] = (request->len - TCOAP_TCP_LEN_MAX) >> 24; 276 | request->buf[2] = (request->len - TCOAP_TCP_LEN_MAX) >> 16; 277 | request->buf[3] = (request->len - TCOAP_TCP_LEN_MAX) >> 8; 278 | request->buf[4] = (request->len - TCOAP_TCP_LEN_MAX); 279 | request->buf[5] = reqd->code; 280 | request->len = 6; 281 | } 282 | 283 | /* assemble token */ 284 | if (reqd->tkl) { 285 | tcoap_fill_token(handle, request->buf + request->len, reqd->tkl); 286 | request->len += reqd->tkl; 287 | } 288 | 289 | request->len += options_len; 290 | 291 | /* assemble payload */ 292 | if (reqd->payload.len) { 293 | request->len += fill_payload(request->buf + request->len, &reqd->payload); 294 | } 295 | } 296 | 297 | 298 | /** 299 | * @brief Parse CoAP response 300 | * 301 | * @param request - pointer on outgoing packet 302 | * @param response - pointer on incoming packet 303 | * @param options_shift - in this variable will be stored start of options index 304 | * 305 | * @return bit mask of parsing results, see 'tcoap_parsing_result' 306 | */ 307 | static uint32_t parse_response(const tcoap_data * const request, const tcoap_data * const response, uint32_t * const options_shift) 308 | { 309 | tcoap_tcp_header resp_header; 310 | tcoap_tcp_header req_header; 311 | 312 | uint32_t resp_mask; 313 | uint32_t resp_idx; 314 | uint32_t req_idx; 315 | 316 | /* checking header */ 317 | if (response->len > 1) { 318 | resp_mask = TCOAP_RESP_SEPARATE; 319 | resp_idx = 0; 320 | req_idx = 0; 321 | 322 | resp_header.len_header.byte = response->buf[resp_idx++]; 323 | req_header.len_header.byte = request->buf[req_idx++]; 324 | 325 | /* fast checking tkl */ 326 | if (resp_header.len_header.fields.tkl != req_header.len_header.fields.tkl) { 327 | goto return_err_label; 328 | } 329 | 330 | resp_idx += extract_data_length(&resp_header, response->buf + resp_idx); 331 | req_idx += extract_data_length(&req_header, request->buf + req_idx); 332 | 333 | /* check length */ 334 | if ((resp_header.data_len + resp_header.len_header.fields.tkl + resp_idx + 1) > response->len) { 335 | goto return_err_label; 336 | } 337 | 338 | /* get code */ 339 | resp_header.code = response->buf[resp_idx++]; 340 | 341 | /* check code */ 342 | if (TCOAP_EXTRACT_CLASS(resp_header.code) != TCOAP_SUCCESS_CLASS 343 | && TCOAP_EXTRACT_CLASS(resp_header.code) != TCOAP_BAD_REQUEST_CLASS 344 | && TCOAP_EXTRACT_CLASS(resp_header.code) != TCOAP_SERVER_ERR_CLASS 345 | && TCOAP_EXTRACT_CLASS(resp_header.code) != TCOAP_TCP_SIGNAL_CLASS) { 346 | goto return_err_label; 347 | } 348 | 349 | /* check token */ 350 | if (resp_header.len_header.fields.tkl) { 351 | if (!mem_cmp(response->buf + resp_idx, request->buf + req_idx + 1, resp_header.len_header.fields.tkl)) { 352 | goto return_err_label; 353 | } 354 | } 355 | 356 | if (TCOAP_EXTRACT_CLASS(resp_header.code) == TCOAP_SUCCESS_CLASS) { 357 | TCOAP_SET_RESP(resp_mask, TCOAP_RESP_SUCCESS_CODE); 358 | } else if (TCOAP_EXTRACT_CLASS(resp_header.code) == TCOAP_TCP_SIGNAL_CLASS) { 359 | TCOAP_SET_RESP(resp_mask, TCOAP_RESP_TCP_SIGNAL_CODE); 360 | } else { 361 | TCOAP_SET_RESP(resp_mask, TCOAP_RESP_FAILURE_CODE); 362 | } 363 | 364 | /* packet is valid */ 365 | *options_shift = response->len - resp_header.data_len; 366 | return resp_mask; 367 | } 368 | 369 | /***********/ 370 | return_err_label: 371 | /***********/ 372 | 373 | *options_shift = 0; 374 | resp_mask = TCOAP_RESP_INVALID_PACKET; 375 | return resp_mask; 376 | } 377 | 378 | 379 | /** 380 | * @brief Extract length of data from header for TCP packet (payload + options) 381 | * 382 | * @param header - pointer on 'tcoap_tcp_header' 383 | * @param buf - pointer on packet buffer 384 | * 385 | * @return shift for length 386 | */ 387 | static uint32_t extract_data_length(tcoap_tcp_header * const header, const uint8_t * const buf) 388 | { 389 | uint32_t idx; 390 | 391 | idx = 0; 392 | 393 | switch (header->len_header.fields.len) { 394 | case TCOAP_TCP_LEN_1BYTE: 395 | header->data_len = buf[idx++] + TCOAP_TCP_LEN_MIN; 396 | break; 397 | 398 | case TCOAP_TCP_LEN_2BYTES: 399 | header->data_len = buf[idx++]; 400 | header->data_len <<= 8; 401 | header->data_len |= buf[idx++]; 402 | header->data_len += TCOAP_TCP_LEN_MED; 403 | break; 404 | 405 | case TCOAP_TCP_LEN_4BYTES: 406 | header->data_len = buf[idx++]; 407 | header->data_len <<= 8; 408 | header->data_len |= buf[idx++]; 409 | header->data_len <<= 8; 410 | header->data_len |= buf[idx++]; 411 | header->data_len <<= 8; 412 | header->data_len |= buf[idx++]; 413 | header->data_len += TCOAP_TCP_LEN_MAX; 414 | break; 415 | 416 | default: 417 | header->data_len = header->len_header.fields.len; 418 | break; 419 | } 420 | 421 | return idx; 422 | } 423 | 424 | 425 | /** 426 | * @brief Shift the data in the packet if we did predict a wrong length 427 | * 428 | * @param dst - pointer on new position of data 429 | * @param src - pointer on current position of data 430 | * @param len - length of the shift 431 | */ 432 | static void shift_data(uint8_t * dst, const uint8_t * src, uint32_t len) 433 | { 434 | if (dst < src) { 435 | while (len--) *dst++ = *src++; 436 | } else { 437 | while (len--) *dst-- = *src--; 438 | } 439 | } 440 | 441 | -------------------------------------------------------------------------------- /tcoap_tcp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * tcoap_tcp.h 3 | * 4 | * Author: Serge Maslyakov, rusoil.9@gmail.com 5 | * Copyright 2017 Serge Maslyakov. All rights reserved. 6 | * 7 | */ 8 | 9 | 10 | #ifndef __TCOAP_TCP_H 11 | #define __TCOAP_TCP_H 12 | 13 | 14 | #include "tcoap.h" 15 | 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | 22 | /** 23 | * TCP CoAP header 24 | * 25 | * 0 1 2 3 26 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 27 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 28 | * |Len=15 | TKL | Extended Length (32 bits) 29 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 30 | * | Code | Token (if any, TKL bytes) ... 31 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 32 | * | Options (if any) ... 33 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 34 | * |1 1 1 1 1 1 1 1| Payload (if any) ... 35 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 36 | * 37 | */ 38 | 39 | 40 | /** 41 | * @brief Send a CoAP packet over TCP. Do not use it directly. 42 | * 43 | * @param handle - coap handle 44 | * @param reqd - descriptor of request 45 | * 46 | * @return status of operation 47 | */ 48 | tcoap_error tcoap_send_coap_request_tcp(tcoap_handle * const handle, const tcoap_request_descriptor * const reqd); 49 | 50 | 51 | #ifdef __cplusplus 52 | } 53 | #endif 54 | 55 | #endif /* __TCOAP_TCP_H */ 56 | -------------------------------------------------------------------------------- /tcoap_udp.c: -------------------------------------------------------------------------------- 1 | /** 2 | * tcoap_udp.c 3 | * 4 | * Author: Serge Maslyakov, rusoil.9@gmail.com 5 | * Copyright 2017 Serge Maslyakov. All rights reserved. 6 | * 7 | */ 8 | 9 | 10 | #include "tcoap_udp.h" 11 | #include "tcoap_utils.h" 12 | 13 | 14 | #define TCOAP_RESPONSE_CODE(buf) ((buf)[1]) 15 | 16 | 17 | /** 18 | * @brief CoAP header data struct 19 | */ 20 | typedef struct { 21 | 22 | uint8_t tkl : 4; /* length of Token */ 23 | uint8_t type : 2; /* type flag */ 24 | uint8_t vers : 2; /* protocol version */ 25 | 26 | uint8_t code : 8; /* request method (value 1--10) or response 27 | code (value 40-255) */ 28 | uint16_t mid; /* transaction id (network byte order!) */ 29 | 30 | } tcoap_udp_header; 31 | 32 | 33 | 34 | static void asemble_request(tcoap_handle * const handle, tcoap_data * const request, const tcoap_request_descriptor * const reqd); 35 | static uint32_t parse_response(const tcoap_data * const request, const tcoap_data * const response); 36 | static void asemble_ack(tcoap_data * const ack, const tcoap_data * const response); 37 | static tcoap_error waiting_ack(tcoap_handle * const handle, const tcoap_data * const request); 38 | 39 | 40 | 41 | /** 42 | * @brief See description in the header file. 43 | * 44 | */ 45 | tcoap_error tcoap_send_coap_request_udp(tcoap_handle * const handle, const tcoap_request_descriptor * const reqd) 46 | { 47 | tcoap_error err; 48 | uint32_t resp_mask; 49 | tcoap_result_data result; 50 | 51 | /* assembling packet */ 52 | asemble_request(handle, &handle->request, reqd); 53 | 54 | /* debug support */ 55 | if (TCOAP_CHECK_STATUS(handle, TCOAP_DEBUG_ON)) { 56 | tcoap_debug_print_packet(handle, "coap >> ", handle->request.buf, handle->request.len); 57 | } 58 | 59 | /* sending packet */ 60 | tcoap_tx_signal(handle, TCOAP_ROUTINE_PACKET_WILL_START); 61 | 62 | err = tcoap_tx_data(handle, handle->request.buf, handle->request.len); 63 | 64 | if (err != TCOAP_OK) { 65 | return err; 66 | } 67 | 68 | /* waiting ack if needed */ 69 | resp_mask = TCOAP_RESP_EMPTY; 70 | if (reqd->type == TCOAP_MESSAGE_CON) { 71 | 72 | TCOAP_SET_STATUS(handle, TCOAP_WAITING_RESP); 73 | 74 | err = waiting_ack(handle, &handle->request); 75 | 76 | TCOAP_RESET_STATUS(handle, TCOAP_WAITING_RESP); 77 | 78 | if (err != TCOAP_OK) { 79 | return err; 80 | } 81 | 82 | /* debug support */ 83 | if (TCOAP_CHECK_STATUS(handle, TCOAP_DEBUG_ON)) { 84 | tcoap_debug_print_packet(handle, "coap << ", handle->response.buf, handle->response.len); 85 | } 86 | 87 | /* parsing incoming ack packet */ 88 | resp_mask = parse_response(&handle->request, &handle->response); 89 | 90 | if (TCOAP_CHECK_RESP(resp_mask, TCOAP_RESP_ACK)) { 91 | 92 | tcoap_tx_signal(handle, TCOAP_ACK_DID_RECEIVE); 93 | 94 | } else if (TCOAP_CHECK_RESP(resp_mask, TCOAP_RESP_NRST)) { 95 | 96 | tcoap_tx_signal(handle, TCOAP_NRST_DID_RECEIVE); 97 | err = TCOAP_NRST_ANSWER; 98 | 99 | return err; 100 | } else if (TCOAP_CHECK_RESP(resp_mask, TCOAP_RESP_INVALID_PACKET)) { 101 | 102 | tcoap_tx_signal(handle, TCOAP_WRONG_PACKET_DID_RECEIVE); 103 | err = TCOAP_NO_ACK_ERROR; 104 | 105 | return err; 106 | } 107 | } 108 | 109 | /* waiting response if needed */ 110 | if (reqd->response_callback != NULL) { 111 | 112 | if (reqd->type != TCOAP_MESSAGE_CON || !TCOAP_CHECK_RESP(resp_mask, TCOAP_RESP_PIGGYBACKED)) { 113 | 114 | handle->response.len = 0; 115 | TCOAP_SET_STATUS(handle, TCOAP_WAITING_RESP); 116 | 117 | /* waiting either data arriving or timeout expiring */ 118 | err = tcoap_wait_event(handle, TCOAP_RESP_TIMEOUT_MS); 119 | 120 | TCOAP_RESET_STATUS(handle, TCOAP_WAITING_RESP); 121 | 122 | if (err != TCOAP_OK) { 123 | return err; 124 | } 125 | 126 | /* debug support */ 127 | if (TCOAP_CHECK_STATUS(handle, TCOAP_DEBUG_ON)) { 128 | tcoap_debug_print_packet(handle, "rcv coap << ", handle->response.buf, handle->response.len); 129 | } 130 | 131 | resp_mask = parse_response(&handle->request, &handle->response); 132 | 133 | if (TCOAP_CHECK_RESP(resp_mask, TCOAP_RESP_INVALID_PACKET)) { 134 | 135 | tcoap_tx_signal(handle, TCOAP_WRONG_PACKET_DID_RECEIVE); 136 | err = TCOAP_NO_RESP_ERROR; 137 | 138 | return err; 139 | } else if (TCOAP_CHECK_RESP(resp_mask, TCOAP_RESP_NRST)) { 140 | 141 | tcoap_tx_signal(handle, TCOAP_NRST_DID_RECEIVE); 142 | err = TCOAP_NRST_ANSWER; 143 | 144 | return err; 145 | } 146 | } 147 | 148 | /* We are using the same request buffer for storing incoming options, bcoz 149 | * outgoing packet is not needed already. It allows us to save ram-memory. 150 | */ 151 | err = decoding_options(&handle->response, 152 | (tcoap_option_data *)handle->request.buf, 153 | ((handle->response.buf[0] & 0x0F) + 4), 154 | &handle->request.len); 155 | 156 | if (err == TCOAP_WRONG_OPTIONS_ERROR) { 157 | return err; 158 | } 159 | 160 | /* check the payload len */ 161 | if (handle->response.len > handle->request.len) { 162 | result.payload.len = handle->response.len - handle->request.len; 163 | result.payload.buf = handle->response.buf + handle->request.len; 164 | } else { 165 | result.payload.len = 0; 166 | result.payload.buf = NULL; 167 | } 168 | 169 | result.resp_code = TCOAP_RESPONSE_CODE(handle->response.buf); 170 | result.options = err == TCOAP_NO_OPTIONS_ERROR ? NULL : (tcoap_option_data *)handle->request.buf; 171 | 172 | reqd->response_callback(reqd, &result); 173 | 174 | /* debug support */ 175 | if (TCOAP_CHECK_STATUS(handle, TCOAP_DEBUG_ON)) { 176 | tcoap_debug_print_options(handle, "coap opt << ", result.options); 177 | tcoap_debug_print_payload(handle, "coap pld << ", &result.payload); 178 | } 179 | 180 | /* send ACK back if needed */ 181 | if (TCOAP_CHECK_RESP(resp_mask, TCOAP_RESP_NEED_SEND_ACK)) { 182 | 183 | asemble_ack(&handle->request, &handle->response); 184 | tcoap_tx_signal(handle, TCOAP_TX_ACK_PACKET); 185 | 186 | err = tcoap_tx_data(handle, handle->request.buf, handle->request.len); 187 | } 188 | } 189 | 190 | return err; 191 | } 192 | 193 | 194 | /** 195 | * @brief Assemble CoAP over UDP request. 196 | * 197 | * @param handle - coap handle 198 | * @param request - data struct for storing request 199 | * @param reqd - descriptor of request 200 | * 201 | */ 202 | static void asemble_request(tcoap_handle * const handle, tcoap_data * const request, const tcoap_request_descriptor * const reqd) 203 | { 204 | tcoap_udp_header header; 205 | 206 | request->len = sizeof(tcoap_udp_header); 207 | 208 | /* assemble header */ 209 | header.vers = TCOAP_DEFAULT_VERSION; 210 | header.type = reqd->type; 211 | header.code = reqd->code; 212 | header.tkl = reqd->tkl; 213 | header.mid = tcoap_get_message_id(handle); 214 | 215 | /* assemble token */ 216 | if (reqd->tkl) { 217 | tcoap_fill_token(handle, request->buf + request->len, reqd->tkl); 218 | request->len += reqd->tkl; 219 | } 220 | 221 | /* assemble options */ 222 | if (reqd->options != NULL) { 223 | request->len += encoding_options(request->buf + request->len, reqd->options); 224 | } 225 | 226 | /* assemble payload */ 227 | if (reqd->payload.len) { 228 | request->len += fill_payload(request->buf + request->len, &reqd->payload); 229 | } 230 | 231 | /* copy header */ 232 | mem_copy(request->buf, &header, sizeof(tcoap_udp_header)); 233 | } 234 | 235 | 236 | /** 237 | * @brief Parse CoAP response (it may be either an ACK response or separate response) 238 | * 239 | * @param request - pointer on outgoing packet data 240 | * @param response - pointer on incoming packet data 241 | * 242 | * @return bit mask of results parsing, see 'tcoap_parsing_result_t' 243 | */ 244 | static uint32_t parse_response(const tcoap_data * const request, const tcoap_data * const response) 245 | { 246 | /** 247 | * 4.2. Messages Transmitted Reliably 248 | * ... 249 | * ... 250 | * The Acknowledgement message MUST echo the Message ID of 251 | * the Confirmable message and MUST carry a response or be Empty (see 252 | * Sections 5.2.1 and 5.2.2). The Reset message MUST echo the Message 253 | * ID of the Confirmable message and MUST be Empty. 254 | */ 255 | 256 | tcoap_udp_header resp_header; 257 | tcoap_udp_header req_header; 258 | uint32_t resp_mask; 259 | 260 | /* check on header */ 261 | if (response->len > 3) { 262 | 263 | resp_mask = TCOAP_RESP_EMPTY; 264 | mem_copy(&resp_header, response->buf, sizeof(tcoap_udp_header)); 265 | mem_copy(&req_header, request->buf, sizeof(tcoap_udp_header)); 266 | 267 | /* do fast checking */ 268 | if (resp_header.vers != req_header.vers) { 269 | goto return_err_label; 270 | } 271 | 272 | /* do checking on the type of message */ 273 | switch (resp_header.type) { 274 | 275 | case TCOAP_MESSAGE_ACK: 276 | TCOAP_SET_RESP(resp_mask, TCOAP_RESP_ACK); 277 | 278 | if (resp_header.mid != req_header.mid) { 279 | goto return_err_label; 280 | } 281 | 282 | if (resp_header.code != TCOAP_CODE_EMPTY_MSG) { 283 | TCOAP_SET_RESP(resp_mask, TCOAP_RESP_PIGGYBACKED); 284 | } else { 285 | if (!resp_header.tkl && response->len == 4) { 286 | return resp_mask; 287 | } else { 288 | goto return_err_label; 289 | } 290 | } 291 | break; 292 | 293 | case TCOAP_MESSAGE_CON: 294 | TCOAP_SET_RESP(resp_mask, TCOAP_RESP_SEPARATE); 295 | TCOAP_SET_RESP(resp_mask, TCOAP_RESP_NEED_SEND_ACK); 296 | break; 297 | 298 | case TCOAP_MESSAGE_NON: 299 | TCOAP_SET_RESP(resp_mask, TCOAP_RESP_SEPARATE); 300 | break; 301 | 302 | case TCOAP_MESSAGE_RST: 303 | if (resp_header.code == TCOAP_CODE_EMPTY_MSG && !resp_header.tkl && response->len == 4) { 304 | TCOAP_SET_RESP(resp_mask, TCOAP_RESP_NRST); 305 | return resp_mask; 306 | } else { 307 | goto return_err_label; 308 | } 309 | 310 | default: 311 | goto return_err_label; 312 | } 313 | 314 | /* if it is a separate response (msg id's should not be equals) */ 315 | if (!TCOAP_CHECK_RESP(resp_mask, TCOAP_RESP_ACK)) { 316 | if (resp_header.mid == req_header.mid) { 317 | goto return_err_label; 318 | } 319 | } 320 | 321 | /* tkl's should be equals */ 322 | if (resp_header.tkl != req_header.tkl) { 323 | goto return_err_label; 324 | } 325 | 326 | /* check length of msg */ 327 | if (response->len < (uint32_t)(4 + resp_header.tkl)) { 328 | goto return_err_label; 329 | } 330 | 331 | /* check tokens */ 332 | if (!mem_cmp(response->buf + 4, request->buf + 4, resp_header.tkl)) { 333 | goto return_err_label; 334 | } 335 | 336 | /* check code */ 337 | if (TCOAP_EXTRACT_CLASS(resp_header.code) != TCOAP_SUCCESS_CLASS 338 | && TCOAP_EXTRACT_CLASS(resp_header.code) != TCOAP_BAD_REQUEST_CLASS 339 | && TCOAP_EXTRACT_CLASS(resp_header.code) != TCOAP_SERVER_ERR_CLASS) { 340 | goto return_err_label; 341 | } 342 | 343 | if (TCOAP_EXTRACT_CLASS(resp_header.code) == TCOAP_SUCCESS_CLASS) { 344 | TCOAP_SET_RESP(resp_mask, TCOAP_RESP_SUCCESS_CODE); 345 | } else { 346 | TCOAP_SET_RESP(resp_mask, TCOAP_RESP_FAILURE_CODE); 347 | } 348 | 349 | /* packet is valid */ 350 | return resp_mask; 351 | } 352 | 353 | /***********/ 354 | return_err_label: 355 | /***********/ 356 | 357 | resp_mask = TCOAP_RESP_INVALID_PACKET; 358 | return resp_mask; 359 | } 360 | 361 | 362 | /** 363 | * @brief Assemble ACK packet 364 | * 365 | * @param ack - data where was stored ACK packet 366 | * @param response - the response on the basis of which will be assemble ACK packet 367 | */ 368 | static void asemble_ack(tcoap_data * const ack, const tcoap_data * const response) 369 | { 370 | tcoap_udp_header ack_header; 371 | 372 | /* get header from incoming packet */ 373 | mem_copy(&ack_header, response, sizeof(tcoap_udp_header)); 374 | 375 | /* assemble header */ 376 | ack_header.type = TCOAP_MESSAGE_ACK; 377 | ack_header.code = TCOAP_CODE_EMPTY_MSG; 378 | ack_header.tkl = 0; 379 | 380 | /* copy header */ 381 | mem_copy(ack->buf, &ack_header, sizeof(tcoap_udp_header)); 382 | ack->len = sizeof(tcoap_udp_header); 383 | } 384 | 385 | 386 | 387 | /** 388 | * @brief Waiting ACK functionality (retransmission and etc) 389 | * 390 | * @param handle - coap handle 391 | * @param request - outgoing packet 392 | * 393 | * @return result of operation 394 | */ 395 | static tcoap_error waiting_ack(tcoap_handle * const handle, const tcoap_data * const request) 396 | { 397 | tcoap_error err; 398 | uint32_t retransmition; 399 | 400 | retransmition = 0; 401 | 402 | do { 403 | 404 | err = tcoap_wait_event(handle, retransmition * ((TCOAP_ACK_TIMEOUT_MS * TCOAP_ACK_RANDOM_FACTOR) / 100) + TCOAP_ACK_TIMEOUT_MS); 405 | 406 | if (err == TCOAP_TIMEOUT_ERROR) { 407 | 408 | if (retransmition < TCOAP_MAX_RETRANSMIT) { 409 | /* retransmission */ 410 | tcoap_tx_signal(handle, TCOAP_TX_RETR_PACKET); 411 | 412 | /* debug support */ 413 | if (TCOAP_CHECK_STATUS(handle, TCOAP_DEBUG_ON)) { 414 | tcoap_debug_print_packet(handle, "coap retr >> ", handle->request.buf, handle->request.len); 415 | } 416 | 417 | retransmition++; 418 | err = tcoap_tx_data(handle, request->buf, request->len); 419 | 420 | if (err != TCOAP_OK) { 421 | break; 422 | } 423 | } else { 424 | break; 425 | } 426 | } else { 427 | break; 428 | } 429 | } while (1); 430 | 431 | return err; 432 | } 433 | 434 | -------------------------------------------------------------------------------- /tcoap_udp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * tcoap_udp.h 3 | * 4 | * Author: Serge Maslyakov, rusoil.9@gmail.com 5 | * Copyright 2017 Serge Maslyakov. All rights reserved. 6 | * 7 | */ 8 | 9 | 10 | #ifndef __TCOAP_UDP_H 11 | #define __TCOAP_UDP_H 12 | 13 | 14 | #include "tcoap.h" 15 | 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | 22 | /** 23 | * UDP CoAP header 24 | * 25 | * 0 1 2 3 26 | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 27 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 28 | * |Ver| T | TKL | Code | Message ID | 29 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 30 | * | Token (if any, TKL bytes) ... 31 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 32 | * | Options (if any) ... 33 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 34 | * |1 1 1 1 1 1 1 1| Payload (if any) ... 35 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 36 | * 37 | */ 38 | 39 | 40 | /** 41 | * @brief Send a CoAP packet over UDP. Do not use it directly. 42 | * 43 | * @param handle - coap handle 44 | * @param reqd - descriptor of request 45 | * 46 | * @return status of operation 47 | */ 48 | tcoap_error tcoap_send_coap_request_udp(tcoap_handle * const handle, const tcoap_request_descriptor * const reqd); 49 | 50 | 51 | #ifdef __cplusplus 52 | } 53 | #endif 54 | 55 | #endif /* __TCOAP_UDP_H */ 56 | -------------------------------------------------------------------------------- /tcoap_utils.c: -------------------------------------------------------------------------------- 1 | /** 2 | * tcoap_utils.c 3 | * 4 | * Author: Serge Maslyakov, rusoil.9@gmail.com 5 | * Copyright 2017 Serge Maslyakov. All rights reserved. 6 | * 7 | */ 8 | 9 | 10 | 11 | #include "tcoap_utils.h" 12 | 13 | 14 | #define TCOAP_OPT_MIN 13 15 | #define TCOAP_OPT_MED 269 16 | 17 | #define TCOAP_OPT_1BYTE 13 18 | #define TCOAP_OPT_2BYTE 14 19 | #define TCOAP_OPT_DIS 15 20 | 21 | #define TCOAP_PAYLOAD_PREFIX 0xff 22 | 23 | 24 | 25 | /** 26 | * @brief See description in the header file. 27 | * 28 | */ 29 | uint32_t encoding_options(uint8_t * const buf, const tcoap_option_data * options) 30 | { 31 | uint32_t idx; 32 | uint32_t local_idx; 33 | 34 | uint16_t delta; 35 | uint16_t delta_sum; 36 | 37 | delta_sum = 0; 38 | idx = 0; 39 | 40 | do { 41 | local_idx = idx; 42 | 43 | /* option */ 44 | delta = options->num - delta_sum; 45 | delta_sum += delta; 46 | 47 | if (delta < TCOAP_OPT_MIN) { 48 | 49 | buf[idx++] = (delta << 4); 50 | } else if (delta < TCOAP_OPT_MED) { 51 | 52 | buf[idx++] = (TCOAP_OPT_1BYTE << 4); 53 | buf[idx++] = delta - TCOAP_OPT_MIN; 54 | } else { 55 | 56 | buf[idx++] = (TCOAP_OPT_2BYTE << 4); 57 | buf[idx++] = (delta - TCOAP_OPT_MED) >> 8; 58 | buf[idx++] = (delta - TCOAP_OPT_MED) & 0x00FF; 59 | } 60 | 61 | /* length */ 62 | if (options->len < TCOAP_OPT_MIN) { 63 | 64 | buf[local_idx] |= options->len; 65 | } else if (options->len < TCOAP_OPT_MED) { 66 | 67 | buf[local_idx] |= TCOAP_OPT_1BYTE; 68 | buf[idx++] = options->len - TCOAP_OPT_MIN; 69 | } else { 70 | 71 | buf[local_idx] |= TCOAP_OPT_2BYTE; 72 | buf[idx++] = (options->len - TCOAP_OPT_MED) >> 8; 73 | buf[idx++] = (options->len - TCOAP_OPT_MED) & 0x00FF; 74 | } 75 | 76 | /* value */ 77 | mem_copy(buf + idx, options->value, options->len); 78 | idx += options->len; 79 | 80 | options = options->next; 81 | 82 | } while(options != NULL); 83 | 84 | 85 | return idx; 86 | } 87 | 88 | 89 | /** 90 | * @brief See description in the header file. 91 | * 92 | */ 93 | tcoap_error decoding_options(const tcoap_data * const response, 94 | tcoap_option_data * options, 95 | const uint32_t const opt_start_idx, 96 | uint32_t * const payload_start_idx) 97 | { 98 | tcoap_error err; 99 | uint32_t idx; 100 | 101 | uint8_t opt; 102 | uint16_t delta_sum; 103 | 104 | /* initialize */ 105 | err = TCOAP_NO_OPTIONS_ERROR; 106 | idx = opt_start_idx; 107 | opt = response->buf[idx++]; 108 | 109 | /* decoding */ 110 | if (response->len > idx && opt != TCOAP_PAYLOAD_PREFIX) { 111 | delta_sum = 0; 112 | options->next = NULL; 113 | 114 | do { 115 | 116 | if (options->next != NULL) { 117 | options = options->next; 118 | } 119 | 120 | /* option */ 121 | switch (opt >> 4) { 122 | case TCOAP_OPT_1BYTE: 123 | options->num = response->buf[idx++] + TCOAP_OPT_MIN + delta_sum; 124 | delta_sum = options->num; 125 | break; 126 | 127 | case TCOAP_OPT_2BYTE: 128 | options->num = response->buf[idx++]; 129 | options->num <<= 8; 130 | options->num |= response->buf[idx++]; 131 | options->num += delta_sum + TCOAP_OPT_MED; 132 | 133 | delta_sum = options->num; 134 | break; 135 | 136 | case TCOAP_OPT_DIS: 137 | err = TCOAP_WRONG_OPTIONS_ERROR; 138 | goto return_label; 139 | 140 | default: 141 | options->num = (opt >> 4) + delta_sum; 142 | delta_sum += (opt >> 4); 143 | break; 144 | } 145 | 146 | /* length */ 147 | switch (opt & 0x0F) { 148 | case TCOAP_OPT_1BYTE: 149 | options->len = response->buf[idx++] + TCOAP_OPT_MIN; 150 | break; 151 | 152 | case TCOAP_OPT_2BYTE: 153 | options->len = response->buf[idx++]; 154 | options->len <<= 8; 155 | options->len |= response->buf[idx++]; 156 | options->len += TCOAP_OPT_MED; 157 | break; 158 | 159 | case TCOAP_OPT_DIS: 160 | err = TCOAP_WRONG_OPTIONS_ERROR; 161 | goto return_label; 162 | 163 | default: 164 | options->len = (opt & 0x0F); 165 | break; 166 | } 167 | 168 | /* value */ 169 | options->value = response->buf + idx; 170 | 171 | /* shift counters */ 172 | idx += options->len; 173 | options->next = (options + 1); 174 | 175 | opt = response->buf[idx++]; 176 | 177 | } while (opt != TCOAP_PAYLOAD_PREFIX); 178 | 179 | options->next = NULL; 180 | err = TCOAP_OK; 181 | } 182 | 183 | /***********/ 184 | return_label: 185 | /***********/ 186 | 187 | *payload_start_idx = idx; 188 | return err; 189 | } 190 | 191 | 192 | /** 193 | * @brief See description in the header file. 194 | * 195 | */ 196 | uint32_t fill_payload(uint8_t * const buf, const tcoap_data * const payload) 197 | { 198 | *buf = TCOAP_PAYLOAD_PREFIX; 199 | 200 | mem_copy(buf + 1, payload->buf, payload->len); 201 | 202 | return payload->len + 1; 203 | } 204 | 205 | 206 | -------------------------------------------------------------------------------- /tcoap_utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * tcoap_utils.h 3 | * 4 | * Author: Serge Maslyakov, rusoil.9@gmail.com 5 | * Copyright 2017 Serge Maslyakov. All rights reserved. 6 | * 7 | */ 8 | 9 | 10 | #ifndef __TCOAP_UTILS_H 11 | #define __TCOAP_UTILS_H 12 | 13 | 14 | #include "tcoap.h" 15 | 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | 22 | #define TCOAP_CHECK_STATUS(h,s) ((h)->statuses_mask & (s)) 23 | #define TCOAP_SET_STATUS(h,s) ((h)->statuses_mask |= (s)) 24 | #define TCOAP_RESET_STATUS(h,s) ((h)->statuses_mask &= ~(s)) 25 | 26 | #define TCOAP_CHECK_RESP(m,s) ((m) & (s)) 27 | #define TCOAP_SET_RESP(m,s) ((m) |= (s)) 28 | #define TCOAP_RESET_RESP(m,s) ((m) = ~(s)) 29 | 30 | 31 | 32 | typedef enum { 33 | 34 | TCOAP_UNKNOWN = (int) 0x0000, 35 | TCOAP_ALL_STATUSES = (int) 0xffff, 36 | 37 | TCOAP_SENDING_PACKET = (int) 0x0001, 38 | TCOAP_WAITING_RESP = (int) 0x0002, 39 | 40 | TCOAP_DEBUG_ON = (int) 0x0080 41 | 42 | } tcoap_handle_status; 43 | 44 | 45 | typedef enum { 46 | 47 | TCOAP_RESP_EMPTY = (int) 0x00000000, 48 | 49 | TCOAP_RESP_ACK = (int) 0x00000001, 50 | TCOAP_RESP_PIGGYBACKED = (int) 0x00000002, 51 | TCOAP_RESP_NRST = (int) 0x00000004, 52 | TCOAP_RESP_SEPARATE = (int) 0x00000008, 53 | 54 | TCOAP_RESP_SUCCESS_CODE = (int) 0x00000010, 55 | TCOAP_RESP_FAILURE_CODE = (int) 0x00000020, 56 | TCOAP_RESP_TCP_SIGNAL_CODE = (int) 0x00000020, 57 | 58 | TCOAP_RESP_NEED_SEND_ACK = (int) 0x00000100, 59 | 60 | TCOAP_RESP_INVALID_PACKET = (int) 0x80000000 61 | 62 | } tcoap_parsing_result; 63 | 64 | 65 | 66 | /** 67 | * @brief Encoding options and add it to the packet 68 | * 69 | * @param buf - pointer on packet buffer 70 | * @param option - pointer on first element of linked list of options. Must not be NULL. 71 | * 72 | * @return length of data that was added to the buffer 73 | */ 74 | uint32_t encoding_options(uint8_t * const buf, const tcoap_option_data * option); 75 | 76 | 77 | /** 78 | * @brief Decoding options from response 79 | * 80 | * @param response - incoming packet 81 | * @param option - pointer on first element of linked list 82 | * @param opt_start_idx - index of options in the incoming packet 83 | * @param payload_start_idx - pointer on variable for storing idx of payload in the incoming packet 84 | * 85 | * @return status of operations 86 | */ 87 | tcoap_error decoding_options(const tcoap_data * const response, 88 | tcoap_option_data * option, 89 | const uint32_t const opt_start_idx, 90 | uint32_t * const payload_start_idx); 91 | 92 | 93 | /** 94 | * @brief Add payload to the packet 95 | * 96 | * @param buf - pointer on packet buffer 97 | * @param payload - data with payload 98 | * 99 | * @return length of data that was added to the buffer 100 | */ 101 | uint32_t fill_payload(uint8_t * const buf, const tcoap_data * const payload); 102 | 103 | 104 | #ifdef __cplusplus 105 | } 106 | #endif 107 | 108 | 109 | #endif /* __TCOAP_UTILS_H */ 110 | --------------------------------------------------------------------------------