coap_server_misc.c
Go to the documentation of this file.
1 /**
2  * @file coap_server_misc.c
3  * @brief Helper functions for CoAP server
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2026 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.6.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL COAP_TRACE_LEVEL
33 
34 //Dependencies
35 #include <stdlib.h>
36 #include "coap/coap_server.h"
39 #include "coap/coap_server_misc.h"
40 #include "coap/coap_common.h"
41 #include "coap/coap_debug.h"
42 #include "debug.h"
43 
44 //Check TCP/IP stack configuration
45 #if (COAP_SERVER_SUPPORT == ENABLED)
46 
47 
48 /**
49  * @brief Handle periodic operations
50  * @param[in] context Pointer to the CoAP server context
51  **/
52 
54 {
55 #if (COAP_SERVER_DTLS_SUPPORT == ENABLED)
56  error_t error;
57  uint_t i;
59  TlsState state;
60  CoapDtlsSession *session;
61 
62  //Get current time
64 
65  //Loop through DTLS sessions
66  for(i = 0; i < context->numSessions; i++)
67  {
68  //Point to the current DTLS session
69  session = &context->sessions[i];
70 
71  //Valid DTLS session?
72  if(session->dtlsContext != NULL)
73  {
74  //Implementations should limit the lifetime of established sessions
75  if((time - session->timestamp) >= COAP_SERVER_SESSION_TIMEOUT)
76  {
77  //Debug message
78  TRACE_INFO("CoAP Server: DTLS session timeout!\r\n");
79 
80  //Send a close notify alert to the client
81  tlsShutdown(session->dtlsContext);
82  //Release DTLS session
83  coapServerDeleteSession(session);
84  }
85  else
86  {
87  //Retrieve current state
88  state = tlsGetState(session->dtlsContext);
89 
90  //DTLS handshake in progress?
91  if(state < TLS_STATE_APPLICATION_DATA)
92  {
93  //Continue DTLS handshake
94  error = tlsConnect(session->dtlsContext);
95  }
96  else
97  {
98  //Handle periodic operations
99  error = tlsTick(session->dtlsContext);
100  }
101 
102  //Any error to report?
103  if(error != NO_ERROR &&
104  error != ERROR_TIMEOUT &&
105  error != ERROR_WOULD_BLOCK)
106  {
107  //Debug message
108  TRACE_INFO("CoAP Server: DTLS handshake failed!\r\n");
109 
110  //Release DTLS session
111  coapServerDeleteSession(session);
112  }
113  }
114  }
115  }
116 #endif
117 
118 #if (COAP_SERVER_OBSERVE_SUPPORT == ENABLED)
119  //Process observe-related events
121 #endif
122 }
123 
124 
125 /**
126  * @brief Process CoAP message
127  * @param[in] context Pointer to the CoAP server context
128  * @param[in] data Pointer to the incoming CoAP message
129  * @param[in] length Length of the CoAP message, in bytes
130  * @return Error code
131  **/
132 
134  const uint8_t *data, size_t length)
135 {
136  error_t error;
138 
139  //Check the length of the CoAP message
141  return ERROR_INVALID_LENGTH;
142 
143  //Copy the request message
144  osMemcpy(context->request.buffer, data, length);
145 
146  //Save the length of the request message
147  context->request.length = length;
148  context->request.pos = 0;
149 
150  //Parse the received message
151  error = coapParseMessage(&context->request);
152 
153  //Valid CoAP message?
154  if(error == NO_ERROR)
155  {
156  //Terminate the payload with a NULL character
157  context->request.buffer[context->request.length] = '\0';
158 
159  //Debug message
160  TRACE_INFO("CoAP Server: CoAP message received (%" PRIuSIZE " bytes)...\r\n",
161  context->request.length);
162 
163  //Dump the contents of the message for debugging purpose
164  coapDumpMessage(context->request.buffer, context->request.length);
165 
166  //Retrieve message type
167  coapGetType(&context->request, &type);
168 
169  //Check the type of the message
170  if(type == COAP_TYPE_CON || type == COAP_TYPE_NON)
171  {
172  //CoAP requests are carried in Confirmable or non-confirmable messages
173  error = coapServerProcessRequest(context);
174  }
175  else if(type == COAP_TYPE_ACK)
176  {
177 #if (COAP_SERVER_OBSERVE_SUPPORT == ENABLED)
178  //An Acknowledgement message acknowledges that a specific Confirmable
179  //message arrived
180  error = coapServerProcessAck(context);
181 #else
182  //Recipients of Acknowledgement and Reset messages must not respond
183  //with either Acknowledgement or Reset messages
184  error = ERROR_INVALID_MESSAGE;
185 #endif
186  }
187  else if(type == COAP_TYPE_RST)
188  {
189 #if (COAP_SERVER_OBSERVE_SUPPORT == ENABLED)
190  //A Reset message indicates that a specific message (Confirmable or
191  //Non-confirmable) was received, but some context is missing to
192  //properly process it
193  error = coapServerProcessReset(context);
194 #else
195  //Recipients of Acknowledgement and Reset messages must not respond
196  //with either Acknowledgement or Reset messages
197  error = ERROR_INVALID_MESSAGE;
198 #endif
199  }
200  else
201  {
202  //Invalid message type
203  error = ERROR_INVALID_MESSAGE;
204  }
205  }
206  else if(error == ERROR_INVALID_HEADER || error == ERROR_INVALID_VERSION)
207  {
208  //Messages with a size smaller than four bytes or with unknown version
209  //numbers must be silently ignored (refer to RFC 7252, section 3)
210  }
211  else
212  {
213  //Other message format errors, such as an incomplete datagram or the
214  //usage of reserved values, are rejected with a Reset message
215  error = coapServerRejectRequest(context);
216  }
217 
218  //Check status code
219  if(!error)
220  {
221  //Any response?
222  if(context->response.length > 0)
223  {
224  //Debug message
225  TRACE_INFO("CoAP Server: Sending CoAP message (%" PRIuSIZE " bytes)...\r\n",
226  context->response.length);
227 
228  //Dump the contents of the message for debugging purpose
229  coapDumpMessage(context->response.buffer, context->response.length);
230 
231  //Send CoAP response message
232  error = coapServerSendResponse(context, context->response.buffer,
233  context->response.length);
234  }
235  }
236 
237  //Return status code
238  return error;
239 }
240 
241 
242 /**
243  * @brief Process CoAP request
244  * @param[in] context Pointer to the CoAP server context
245  * @return Error code
246  **/
247 
249 {
250  error_t error;
251  CoapCode code;
252 
253  //Initialize CoAP response
254  coapServerInitResponse(context);
255 
256  //The Code field 8-bit unsigned integer, split into a 3-bit class (most
257  //significant bits) and a 5-bit detail (least significant bits)
258  coapGetCode(&context->request, &code);
259 
260  //In case of a request, the Code field indicates the request method
261  if(code == COAP_CODE_GET ||
262  code == COAP_CODE_POST ||
263  code == COAP_CODE_PUT ||
264  code == COAP_CODE_DELETE ||
265  code == COAP_CODE_FETCH ||
266  code == COAP_CODE_PATCH ||
268  {
269  //Reconstruct the path component from Uri-Path options
270  coapJoinRepeatableOption(&context->request, COAP_OPT_URI_PATH,
271  context->uri, COAP_SERVER_MAX_URI_LEN, '/');
272 
273  //If the resource name is the empty string, set it to a single "/"
274  //character (refer to RFC 7252, section 6.5)
275  if(context->uri[0] == '\0')
276  {
277  osStrcpy(context->uri, "/");
278  }
279 
280  //Initialize status code
281  error = ERROR_NOT_FOUND;
282 
283 #if (COAP_SERVER_OBSERVE_SUPPORT == ENABLED)
284  //GET request?
285  if(code == COAP_CODE_GET)
286  {
287  //A client registers its interest in a resource by initiating an
288  //extended GET request to the server. In addition to returning a
289  //representation of the target resource, this request causes the
290  //server to add the client to the list of observers of the resource
291  error = coapServerProcessRegistrationRequest(context);
292  }
293 #endif
294 
295  //Check status code
296  if(error == ERROR_NOT_FOUND)
297  {
298  //Any registered callback?
299  if(context->requestCallback != NULL)
300  {
301  //Invoke user callback function
302  error = context->requestCallback(context, code, context->uri);
303  }
304  }
305 
306  //Check status code
307  if(error == ERROR_NOT_FOUND)
308  {
309  //Generate a 4.04 piggybacked response
310  error = coapSetCode(&context->response, COAP_CODE_NOT_FOUND);
311  }
312  }
313  else if(code == COAP_CODE_EMPTY)
314  {
315  //Provoking a Reset message by sending an Empty Confirmable message
316  //can be used to check of the liveness of an endpoint (refer to
317  //RFC 7252, section 4.3)
318  error = coapServerRejectRequest(context);
319  }
320  else
321  {
322  //A request with an unrecognized or unsupported method code must
323  //generate a 4.05 piggybacked response (refer to RFC 7252, section
324  //5.8)
325  error = coapSetCode(&context->response, COAP_CODE_METHOD_NOT_ALLOWED);
326  }
327 
328  //Return status code
329  return error;
330 }
331 
332 
333 /**
334  * @brief Reject a CoAP request
335  * @param[in] context Pointer to the CoAP server context
336  * @return Error code
337  **/
338 
340 {
341  error_t error;
342  const CoapMessageHeader *header;
343 
344  //Initialize status code
345  error = NO_ERROR;
346 
347  //Debug message
348  TRACE_INFO("CoAP Server: Rejecting CoAP message...\r\n");
349 
350  //Point to the CoAP message header
351  header = (CoapMessageHeader *) &context->request.buffer;
352 
353  //Check the type of the request
354  if(header->type == COAP_TYPE_CON)
355  {
356  //Rejecting a Confirmable message is effected by sending a matching
357  //Reset message
358  error = coapServerFormatReset(context, ntohs(header->mid));
359  }
360  else if(header->type == COAP_TYPE_NON)
361  {
362  //Rejecting a Non-confirmable message may involve sending a matching
363  //Reset message, and apart from the Reset message the rejected message
364  //must be silently ignored (refer to RFC 7252, section 4.3)
365  error = coapServerFormatReset(context, ntohs(header->mid));
366  }
367  else
368  {
369  //Rejecting an Acknowledgment or Reset message is effected by silently
370  //ignoring it (refer to RFC 7252, section 4.2)
371  context->response.length = 0;
372  }
373 
374  //Return status code
375  return error;
376 }
377 
378 
379 /**
380  * @brief Initialize CoAP response
381  * @param[in] context Pointer to the CoAP server context
382  * @return Error code
383  **/
384 
386 {
387  CoapMessageHeader *requestHeader;
388  CoapMessageHeader *responseHeader;
389 
390  //Point to the CoAP request header
391  requestHeader = (CoapMessageHeader *) context->request.buffer;
392  //Point to the CoAP response header
393  responseHeader = (CoapMessageHeader *) context->response.buffer;
394 
395  //Format message header
396  responseHeader->version = COAP_VERSION_1;
397  responseHeader->tokenLen = requestHeader->tokenLen;
398  responseHeader->code = COAP_CODE_INTERNAL_SERVER;
399  responseHeader->mid = requestHeader->mid;
400 
401  //If immediately available, the response to a request carried in a
402  //Confirmable message is carried in an Acknowledgement (ACK) message
403  if(requestHeader->type == COAP_TYPE_CON)
404  {
405  responseHeader->type = COAP_TYPE_ACK;
406  }
407  else
408  {
409  responseHeader->type = COAP_TYPE_NON;
410  }
411 
412  //The token is used to match a response with a request
413  osMemcpy(responseHeader->token, requestHeader->token,
414  requestHeader->tokenLen);
415 
416  //Set the length of the CoAP message
417  context->response.length = sizeof(CoapMessageHeader) + responseHeader->tokenLen;
418  context->response.pos = 0;
419 
420  //Successful processing
421  return NO_ERROR;
422 }
423 
424 
425 /**
426  * @brief Send CoAP response
427  * @param[in] context Pointer to the CoAP server context
428  * @param[in] data Pointer to a buffer containing the response message
429  * @param[in] length Length of the response message, in bytes
430  * @return Error code
431  **/
432 
434  const void *data, size_t length)
435 {
436  error_t error;
437 
438 #if (COAP_SERVER_DTLS_SUPPORT == ENABLED)
439  //DTLS-secured communication?
440  if(context->dtlsInitCallback != NULL)
441  {
442  uint_t i;
443  CoapDtlsSession *session;
444 
445  //Loop through DTLS sessions
446  for(i = 0; i < context->numSessions; i++)
447  {
448  //Point to the current DTLS session
449  session = &context->sessions[i];
450 
451  //Valid DTLS session?
452  if(session->dtlsContext != NULL)
453  {
454  //Matching DTLS session?
455  if(session->interface == context->localInterface &&
456  ipCompAddr(&session->serverIpAddr, &context->localIpAddr) &&
457  ipCompAddr(&session->clientIpAddr, &context->remoteIpAddr) &&
458  session->clientPort == context->remotePort)
459  {
460  break;
461  }
462  }
463  }
464 
465  //Any matching DTLS session?
466  if(i < context->numSessions)
467  {
468  //Send DTLS datagram
469  error = tlsWrite(session->dtlsContext, data, length, NULL, 0);
470  }
471  else
472  {
473  //Report an error
474  error = ERROR_FAILURE;
475  }
476  }
477  else
478 #endif
479  {
480  SocketMsg msg;
481 
482  //Point to the send buffer
483  msg = SOCKET_DEFAULT_MSG;
484  msg.data = (void *) data;
485  msg.length = length;
486 
487  //Set the source and destination IP addresses
488  msg.interface = context->localInterface;
489  msg.srcIpAddr = context->localIpAddr;
490  msg.destIpAddr = context->remoteIpAddr;
491  msg.destPort = context->remotePort;
492 
493  //Send datagram
494  error = socketSendMsg(context->socket, &msg, 0);
495  }
496 
497  //Return status code
498  return error;
499 }
500 
501 
502 /**
503  * @brief Format Reset message
504  * @param[in] context Pointer to the CoAP server context
505  * @param[in] mid Message ID
506  * @return Error code
507  **/
508 
510 {
511  CoapMessageHeader *header;
512 
513  //Point to the CoAP response header
514  header = (CoapMessageHeader *) context->response.buffer;
515 
516  //Format Reset message
517  header->version = COAP_VERSION_1;
518  header->type = COAP_TYPE_RST;
519  header->tokenLen = 0;
520  header->code = COAP_CODE_EMPTY;
521 
522  //The Reset message message must echo the message ID of the confirmable
523  //message and must be empty (refer to RFC 7252, section 4.2)
524  header->mid = htons(mid);
525 
526  //Set the length of the CoAP message
527  context->response.length = sizeof(CoapMessageHeader);
528 
529  //Successful processing
530  return NO_ERROR;
531 }
532 
533 #endif
error_t coapServerFormatReset(CoapServerContext *context, uint16_t mid)
Format Reset message.
error_t coapSetCode(CoapMessage *message, CoapCode code)
Set method or response code.
Definition: coap_message.c:201
#define htons(value)
Definition: cpu_endian.h:413
#define COAP_MAX_MSG_SIZE
Definition: coap_message.h:40
uint8_t code
Definition: coap_common.h:179
@ ERROR_NOT_FOUND
Definition: error.h:148
@ ERROR_WOULD_BLOCK
Definition: error.h:96
void coapServerProcessObserveEvents(CoapServerContext *context)
Process observe-related events.
@ COAP_CODE_METHOD_NOT_ALLOWED
Definition: coap_common.h:133
error_t coapServerRejectRequest(CoapServerContext *context)
Reject a CoAP request.
TlsState
TLS FSM states.
Definition: tls.h:1582
uint8_t data[]
Definition: ethernet.h:224
Message and ancillary data.
Definition: socket.h:241
error_t coapServerSendResponse(CoapServerContext *context, const void *data, size_t length)
Send CoAP response.
@ ERROR_INVALID_HEADER
Definition: error.h:87
@ TLS_STATE_APPLICATION_DATA
Definition: tls.h:1614
error_t coapJoinRepeatableOption(const CoapMessage *message, uint16_t optionNum, char_t *optionValue, size_t maxLen, char_t separator)
Decode a path or query component from multiple repeatable options.
Definition: coap_option.c:877
void * data
Pointer to the payload.
Definition: socket.h:242
uint8_t type
Definition: coap_common.h:176
Helper functions for CoAP server.
@ ERROR_INVALID_MESSAGE
Definition: error.h:105
@ COAP_CODE_POST
Definition: coap_common.h:116
#define COAP_SERVER_SESSION_TIMEOUT
Definition: coap_server.h:77
error_t coapServerProcessMessage(CoapServerContext *context, const uint8_t *data, size_t length)
Process CoAP message.
@ ERROR_INVALID_VERSION
Definition: error.h:118
error_t coapServerProcessReset(CoapServerContext *context)
Process Reset message.
uint16_t destPort
Destination port.
Definition: socket.h:252
@ COAP_VERSION_1
CoAP version 1.
Definition: coap_common.h:69
error_t socketSendMsg(Socket *socket, const SocketMsg *message, uint_t flags)
Send a message to a connectionless socket.
Definition: socket.c:1666
NetInterface * interface
Underlying network interface.
Definition: socket.h:248
error_t coapServerProcessRegistrationRequest(CoapServerContext *context)
Process registration request.
error_t tlsShutdown(TlsContext *context)
Gracefully close TLS session.
Definition: tls.c:2621
const SocketMsg SOCKET_DEFAULT_MSG
Definition: socket.c:52
size_t length
Actual length of the payload, in bytes.
Definition: socket.h:244
void coapServerTick(CoapServerContext *context)
Handle periodic operations.
#define osMemcpy(dest, src, length)
Definition: os_port.h:147
error_t coapGetType(const CoapMessage *message, CoapMessageType *type)
Get message type.
Definition: coap_message.c:176
uint16_t mid
Definition: coap_common.h:180
error_t
Error codes.
Definition: error.h:43
bool_t ipCompAddr(const IpAddr *ipAddr1, const IpAddr *ipAddr2)
Compare IP addresses.
Definition: ip.c:318
@ COAP_CODE_PUT
Definition: coap_common.h:117
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
void coapServerDeleteSession(CoapDtlsSession *session)
Delete DTLS session.
IpAddr srcIpAddr
Source IP address.
Definition: socket.h:249
@ ERROR_INVALID_LENGTH
Definition: error.h:111
@ COAP_TYPE_ACK
Acknowledgment message.
Definition: coap_common.h:91
@ COAP_CODE_NOT_FOUND
Definition: coap_common.h:132
#define TRACE_INFO(...)
Definition: debug.h:105
uint8_t length
Definition: tcp.h:375
@ COAP_TYPE_CON
Confirmable message.
Definition: coap_common.h:89
@ COAP_OPT_URI_PATH
Definition: coap_option.h:99
#define CoapDtlsSession
Definition: coap_server.h:168
@ COAP_TYPE_RST
Reset message.
Definition: coap_common.h:92
uint32_t systime_t
System time.
CoAP server.
#define ntohs(value)
Definition: cpu_endian.h:421
IpAddr destIpAddr
Destination IP address.
Definition: socket.h:251
@ COAP_CODE_FETCH
Definition: coap_common.h:119
@ ERROR_TIMEOUT
Definition: error.h:95
error_t coapGetCode(const CoapMessage *message, CoapCode *code)
Get method or response code.
Definition: coap_message.c:226
error_t coapServerProcessAck(CoapServerContext *context)
Process Acknowledgement message.
uint32_t time
@ COAP_CODE_GET
Definition: coap_common.h:115
CoapMessageType
CoAP message types.
Definition: coap_common.h:88
Data logging functions for debugging purpose (CoAP)
#define COAP_SERVER_MAX_URI_LEN
Definition: coap_server.h:140
TlsState tlsGetState(TlsContext *context)
Retrieve current TLS state.
Definition: tls.c:220
@ COAP_CODE_INTERNAL_SERVER
Definition: coap_common.h:141
#define CoapServerContext
Definition: coap_server.h:164
Transport protocol abstraction layer.
@ COAP_CODE_DELETE
Definition: coap_common.h:118
error_t tlsWrite(TlsContext *context, const void *data, size_t length, size_t *written, uint_t flags)
Send application data to the remote host using TLS.
Definition: tls.c:2145
@ COAP_CODE_EMPTY
Definition: coap_common.h:114
error_t coapParseMessage(const CoapMessage *message)
Parse CoAP message.
Definition: coap_message.c:51
error_t tlsTick(TlsContext *context)
Handle periodic operations.
Definition: tls.c:2787
@ COAP_CODE_IPATCH
Definition: coap_common.h:121
error_t coapServerProcessRequest(CoapServerContext *context)
Process CoAP request.
@ COAP_CODE_PATCH
Definition: coap_common.h:120
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:57
@ COAP_TYPE_NON
Non-confirmable message.
Definition: coap_common.h:90
#define osStrcpy(s1, s2)
Definition: os_port.h:213
CoapCode
CoAP method and response codes.
Definition: coap_common.h:113
error_t tlsConnect(TlsContext *context)
Initiate the TLS handshake.
Definition: tls.c:1805
Definitions common to CoAP client and server.
error_t coapServerInitResponse(CoapServerContext *context)
Initialize CoAP response.
@ NO_ERROR
Success.
Definition: error.h:44
CoapMessageHeader
Definition: coap_common.h:182
Debugging facilities.
error_t coapDumpMessage(const void *message, size_t length)
Dump CoAP message for debugging purpose.
Definition: coap_debug.c:122
systime_t osGetSystemTime(void)
Retrieve system time.