coap_server_transport.c
Go to the documentation of this file.
1 /**
2  * @file coap_server_transport.c
3  * @brief Transport protocol abstraction layer
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.0
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"
38 #include "coap/coap_server_misc.h"
39 #include "debug.h"
40 
41 //Check TCP/IP stack configuration
42 #if (COAP_SERVER_SUPPORT == ENABLED && COAP_SERVER_DTLS_SUPPORT == ENABLED)
43 
44 //Forward declaration of functions
45 error_t coapServerSendCallback(void *handle, const void *data,
46  size_t length, size_t *written, uint_t flags);
47 
48 error_t coapServerReceiveCallback(void *handle, void *data,
49  size_t size, size_t *received, uint_t flags);
50 
52  const DtlsClientParameters *clientParams, uint8_t *cookie,
53  size_t *length, void *param);
54 
56  const DtlsClientParameters *clientParams, const uint8_t *cookie,
57  size_t length, void *param);
58 
59 
60 /**
61  * @brief Accept a new connection from a client
62  * @param[in] context Pointer to the CoAP server context
63  * @param[in] session Pointer to the DTLS session
64  * @param[in] remoteIpAddr Client IP address
65  * @param[in] remotePort Client port number
66  * @return Error code
67  **/
68 
70  CoapDtlsSession *session, const IpAddr *remoteIpAddr, uint16_t remotePort)
71 {
72  error_t error;
73  TlsState state;
74 
75  //Clear DTLS session
76  osMemset(session, 0, sizeof(CoapDtlsSession));
77 
78  //Initialize session parameters
79  session->context = context;
80  session->serverIpAddr = context->serverIpAddr;
81  session->clientIpAddr = context->clientIpAddr;
82  session->clientPort = context->clientPort;
83  session->timestamp = osGetSystemTime();
84 
85  //Allocate DTLS context
86  session->dtlsContext = tlsInit();
87 
88  //DTLS context successfully created?
89  if(session->dtlsContext != NULL)
90  {
91  //Start of exception handling block
92  do
93  {
94  //Select server operation mode
95  error = tlsSetConnectionEnd(session->dtlsContext,
97  //Any error to report?
98  if(error)
99  break;
100 
101  //Use datagram transport protocol
102  error = tlsSetTransportProtocol(session->dtlsContext,
104  //Any error to report?
105  if(error)
106  break;
107 
108  //Set send and receive callbacks (I/O abstraction layer)
109  error = tlsSetSocketCallbacks(session->dtlsContext, coapServerSendCallback,
111  //Any error to report?
112  if(error)
113  break;
114 
115  //Set cookie generation/verification callbacks
116  error = tlsSetCookieCallbacks(session->dtlsContext,
118  session);
119  //Any error to report?
120  if(error)
121  break;
122 
123  //Invoke user-defined callback, if any
124  if(context->dtlsInitCallback != NULL)
125  {
126  //Perform DTLS related initialization
127  error = context->dtlsInitCallback(context, session->dtlsContext);
128  //Any error to report?
129  if(error)
130  break;
131  }
132 
133  //Initiate DTLS handshake
134  error = tlsConnect(session->dtlsContext);
135  //Any error to report?
136  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
137  break;
138 
139  //Retrieve current state
140  state = tlsGetState(session->dtlsContext);
141 
142  //The DTLS server verifies the cookie and proceeds with the handshake
143  //only if it is valid
144  if(state == TLS_STATE_INIT ||
145  state == TLS_STATE_CLIENT_HELLO ||
146  state == TLS_STATE_CLOSED)
147  {
148  //Do not allocate connection state yet if the stateless cookie
149  //exchange is being performed
150  error = ERROR_WRONG_COOKIE;
151  break;
152  }
153 
154  //The DTLS implementation decides to continue with the connection
155  error = NO_ERROR;
156 
157  //Debug message
158  TRACE_INFO("CoAP Server: DTLS session established with client %s port %"
159  PRIu16 "...\r\n", ipAddrToString(remoteIpAddr, NULL), ntohs(remotePort));
160 
161  //End of exception handling block
162  } while(0);
163 
164  //Check status code
165  if(error)
166  {
167  //Release DTLS context
168  tlsFree(session->dtlsContext);
169  session->dtlsContext = NULL;
170  }
171  }
172  else
173  {
174  //Failed to allocate DTLS context
175  error = ERROR_OUT_OF_MEMORY;
176  }
177 
178  //Return status code
179  return error;
180 }
181 
182 
183 /**
184  * @brief DTLS session demultiplexing
185  * @param[in] context Pointer to the CoAP server context
186  * @return Error code
187  **/
188 
190 {
191  error_t error;
192  uint_t i;
193  size_t length;
194  systime_t time;
195  CoapDtlsSession *session;
196  CoapDtlsSession *firstFreeSession;
197  CoapDtlsSession *oldestSession;
198 
199  //Initialize status code
200  error = NO_ERROR;
201 
202  //Get current time
203  time = osGetSystemTime();
204 
205  //Keep track of the first free entry
206  firstFreeSession = NULL;
207  //Keep track of the oldest entry
208  oldestSession = NULL;
209 
210  //Demultiplexing of incoming datagrams into separate DTLS sessions
211  for(i = 0; i < COAP_SERVER_MAX_SESSIONS; i++)
212  {
213  //Point to the current DTLS session
214  session = &context->session[i];
215 
216  //Valid DTLS session?
217  if(session->dtlsContext != NULL)
218  {
219  //Determine if a DTLS session matches the incoming datagram
220  if(ipCompAddr(&session->serverIpAddr, &context->serverIpAddr) &&
221  ipCompAddr(&session->clientIpAddr, &context->clientIpAddr) &&
222  session->clientPort == context->clientPort)
223  {
224  //Save current time
225  session->timestamp = osGetSystemTime();
226 
227  //The UDP datagram is passed to the DTLS implementation
228  error = tlsRead(session->dtlsContext, context->buffer,
230 
231  //Check status code
232  if(!error)
233  {
234  //Process the received CoAP message
235  error = coapServerProcessRequest(context, context->buffer,
236  length);
237  }
238  else if(error == ERROR_TIMEOUT || error == ERROR_WOULD_BLOCK)
239  {
240  //The UDP datagram contains DTLS handshake messages
241  }
242  else
243  {
244  //Debug message
245  TRACE_INFO("CoAP Server: Failed to read DTLS datagram!\r\n");
246 
247  //Release DTLS session
248  coapServerDeleteSession(session);
249  }
250 
251  //We are done
252  break;
253  }
254  else
255  {
256  //Keep track of the oldest entry
257  if(oldestSession == NULL)
258  {
259  oldestSession = session;
260  }
261  else if((time - session->timestamp) > (time - oldestSession->timestamp))
262  {
263  oldestSession = session;
264  }
265  }
266  }
267  else
268  {
269  //Keep track of the first free entry
270  if(firstFreeSession == NULL)
271  {
272  firstFreeSession = session;
273  }
274  }
275  }
276 
277  //No matching DTLS session?
278  if(i >= COAP_SERVER_MAX_SESSIONS)
279  {
280  //Any DTLS session available for use in the table?
281  if(firstFreeSession != NULL)
282  {
283  session = firstFreeSession;
284  }
285  else
286  {
287  //The oldest DTLS session is closed whenever the table runs out of space
288  tlsShutdown(oldestSession->dtlsContext);
289  coapServerDeleteSession(oldestSession);
290 
291  //Point to the DTLS session to be reused
292  session = oldestSession;
293  }
294 
295  //Process the new connection attempt
296  error = coapServerAcceptSession(context, session, &context->clientIpAddr,
297  context->clientPort);
298  }
299 
300  //Return status code
301  return error;
302 }
303 
304 
305 /**
306  * @brief Delete DTLS session
307  * @param[in] session Pointer to the DTLS session
308  **/
309 
311 {
312  //Debug message
313  TRACE_INFO("CoAP Server: DTLS session closed...\r\n");
314 
315  //Valid DTLS context?
316  if(session->dtlsContext != NULL)
317  {
318  //Release DTLS context
319  tlsFree(session->dtlsContext);
320  session->dtlsContext = NULL;
321  }
322 }
323 
324 
325 /**
326  * @brief DTLS send callback
327  * @param[in] handle Handle referencing a client connection
328  * @param[in] data Pointer to a buffer containing the data to be transmitted
329  * @param[in] length Number of data bytes to send
330  * @param[out] written Number of bytes that have been transmitted
331  * @param[in] flags Unused parameter
332  * @return Error code
333  **/
334 
335 error_t coapServerSendCallback(void *handle, const void *data,
336  size_t length, size_t *written, uint_t flags)
337 {
338  error_t error;
339  CoapServerContext *context;
340  CoapDtlsSession *session;
341 
342  //Point to the DTLS session
343  session = handle;
344  //Point to the CoAP server context
345  context = session->context;
346 
347  //Send datagram
348  error = socketSendTo(context->socket, &session->clientIpAddr,
349  session->clientPort, data, length, written, flags);
350 
351  //Return status code
352  return error;
353 }
354 
355 
356 /**
357  * @brief DTLS receive callback
358  * @param[in] handle Handle referencing a client connection
359  * @param[out] data Buffer where to store the incoming data
360  * @param[in] size Maximum number of bytes that can be received
361  * @param[out] received Number of bytes that have been received
362  * @param[in] flags Unused parameter
363  * @return Error code
364  **/
365 
367  size_t size, size_t *received, uint_t flags)
368 {
369  error_t error;
370  CoapServerContext *context;
371  CoapDtlsSession *session;
372 
373  //Initialize status code
374  error = ERROR_WOULD_BLOCK;
375 
376  //Point to the DTLS session
377  session = (CoapDtlsSession *) handle;
378  //Point to the CoAP server context
379  context = session->context;
380 
381  //Any pending datagram?
382  if(context->bufferLen > 0)
383  {
384  //Pass incoming datagram to the proper connection
385  if(ipCompAddr(&context->serverIpAddr, &session->serverIpAddr) &&
386  ipCompAddr(&context->clientIpAddr, &session->clientIpAddr) &&
387  context->clientPort == session->clientPort)
388  {
389  //Make sure the length of the datagram is acceptable
390  if(context->bufferLen < size)
391  {
392  //Copy incoming datagram
393  osMemcpy(data, context->buffer, context->bufferLen);
394  //Return the length of the datagram
395  *received = context->bufferLen;
396 
397  //Successful processing
398  error = NO_ERROR;
399  }
400 
401  //Flush the receive buffer
402  context->bufferLen = 0;
403  }
404  }
405 
406  //Return status code
407  return error;
408 }
409 
410 
411 /**
412  * @brief DTLS cookie generation callback function
413  * @param[in] context Pointer to the DTLS context
414  * @param[in] clientParams Client's parameters
415  * @param[out] cookie Pointer to the first byte of the cookie
416  * @param[in,out] length Length of the cookie, in bytes
417  * @param[in] param Pointer to the DTLS session
418  * @return Error code
419  **/
420 
422  const DtlsClientParameters *clientParams, uint8_t *cookie,
423  size_t *length, void *param)
424 {
425  error_t error;
426  CoapDtlsSession *session;
427  HmacContext hmacContext;
428 
429  //Point to the DTLS session
430  session = (CoapDtlsSession *) param;
431 
432  //Debug message
433  TRACE_INFO("CoAP Server: DTLS cookie generation...\r\n");
434 
435  //Make sure the output buffer is large enough to hold the cookie
437  return ERROR_BUFFER_OVERFLOW;
438 
439  //Invalid cookie secret?
440  if(session->context->cookieSecretLen == 0)
441  {
442  //Generate a cookie secret
443  error = context->prngAlgo->generate(context->prngContext,
444  session->context->cookieSecret, COAP_SERVER_MAX_COOKIE_SECRET_SIZE);
445  //Any error to report?
446  if(error)
447  return error;
448 
449  //Save the length of the generated secret
450  session->context->cookieSecretLen = COAP_SERVER_MAX_COOKIE_SECRET_SIZE;
451  }
452 
453  //Initialize HMAC context
454  hmacInit(&hmacContext, SHA256_HASH_ALGO, session->context->cookieSecret,
455  session->context->cookieSecretLen);
456 
457  //Generate stateless cookie
458  hmacUpdate(&hmacContext, (uint8_t *) &session->clientIpAddr + sizeof(size_t),
459  session->clientIpAddr.length);
460 
461  //The server should use client parameters (version, random, session_id,
462  //cipher_suites, compression_method) to generate its cookie
463  hmacUpdate(&hmacContext, &clientParams->version, sizeof(uint16_t));
464  hmacUpdate(&hmacContext, clientParams->random, clientParams->randomLen);
465  hmacUpdate(&hmacContext, clientParams->sessionId, clientParams->sessionIdLen);
466  hmacUpdate(&hmacContext, clientParams->cipherSuites, clientParams->cipherSuitesLen);
467  hmacUpdate(&hmacContext, clientParams->compressMethods, clientParams->compressMethodsLen);
468 
469  //Finalize HMAC computation
470  hmacFinal(&hmacContext, cookie);
471 
472  //Return the length of the cookie
474 
475  //Successful processing
476  return NO_ERROR;
477 }
478 
479 
480 /**
481  * @brief DTLS cookie verification callback function
482  * @param[in] context Pointer to the DTLS context
483  * @param[in] clientParams Client's parameters
484  * @param[in] cookie Pointer to the first byte of the cookie
485  * @param[in] length Length of the cookie, in bytes
486  * @param[in] param Pointer to the DTLS session
487  * @return Error code
488  **/
489 
491  const DtlsClientParameters *clientParams, const uint8_t *cookie,
492  size_t length, void *param)
493 {
494  error_t error;
495  CoapDtlsSession *session;
496  HmacContext hmacContext;
497 
498  //Point to the DTLS session
499  session = (CoapDtlsSession *) param;
500 
501  //Debug message
502  TRACE_INFO("CoAP Server: DTLS cookie verification...\r\n");
503 
504  //Make sure the length of the cookie is acceptable
506  return ERROR_WRONG_COOKIE;
507 
508  //Invalid cookie secret?
509  if(session->context->cookieSecretLen == 0)
510  {
511  //Generate a cookie secret
512  error = context->prngAlgo->generate(context->prngContext,
513  session->context->cookieSecret, COAP_SERVER_MAX_COOKIE_SECRET_SIZE);
514  //Any error to report?
515  if(error)
516  return error;
517 
518  //Save the length of the generated secret
519  session->context->cookieSecretLen = COAP_SERVER_MAX_COOKIE_SECRET_SIZE;
520  }
521 
522  //Initialize HMAC context
523  hmacInit(&hmacContext, SHA256_HASH_ALGO, session->context->cookieSecret,
524  session->context->cookieSecretLen);
525 
526  //Generate stateless cookie
527  hmacUpdate(&hmacContext, (uint8_t *) &session->clientIpAddr + sizeof(size_t),
528  session->clientIpAddr.length);
529 
530  //The server should use client parameters (version, random, session_id,
531  //cipher_suites, compression_method) to generate its cookie
532  hmacUpdate(&hmacContext, &clientParams->version, sizeof(uint16_t));
533  hmacUpdate(&hmacContext, clientParams->random, clientParams->randomLen);
534  hmacUpdate(&hmacContext, clientParams->sessionId, clientParams->sessionIdLen);
535  hmacUpdate(&hmacContext, clientParams->cipherSuites, clientParams->cipherSuitesLen);
536  hmacUpdate(&hmacContext, clientParams->compressMethods, clientParams->compressMethodsLen);
537 
538  //Finalize HMAC computation
539  hmacFinal(&hmacContext, NULL);
540 
541  //Compare the received cookie against the expected value
542  if(osMemcmp(cookie, hmacContext.digest, length) == 0)
543  {
544  //The cookie is valid
545  error = NO_ERROR;
546  }
547  else
548  {
549  //The cookie is invalid
550  error = ERROR_WRONG_COOKIE;
551  }
552 
553  //Return status code
554  return error;
555 }
556 
557 #endif
TlsContext * tlsInit(void)
TLS context initialization.
Definition: tls.c:67
error_t coapServerAcceptSession(CoapServerContext *context, CoapDtlsSession *session, const IpAddr *remoteIpAddr, uint16_t remotePort)
Accept a new connection from a client.
const uint8_t * random
Definition: dtls_misc.h:225
error_t tlsSetConnectionEnd(TlsContext *context, TlsConnectionEnd entity)
Set operation mode (client or server)
Definition: tls.c:385
#define SHA256_HASH_ALGO
Definition: sha256.h:49
HMAC algorithm context.
Definition: hmac.h:59
error_t tlsSetTransportProtocol(TlsContext *context, TlsTransportProtocol transportProtocol)
Set the transport protocol to be used.
Definition: tls.c:341
@ ERROR_WOULD_BLOCK
Definition: error.h:96
IP network address.
Definition: ip.h:90
@ ERROR_BUFFER_OVERFLOW
Definition: error.h:143
TlsState
TLS FSM states.
Definition: tls.h:1540
uint8_t data[]
Definition: ethernet.h:224
error_t coapServerProcessRequest(CoapServerContext *context, const uint8_t *data, size_t length)
Process CoAP request.
@ TLS_TRANSPORT_PROTOCOL_DATAGRAM
Definition: tls.h:1000
char_t * ipAddrToString(const IpAddr *ipAddr, char_t *str)
Convert a binary IP address to a string representation.
Definition: ip.c:810
error_t coapServerCookieGenerateCallback(TlsContext *context, const DtlsClientParameters *clientParams, uint8_t *cookie, size_t *length, void *param)
DTLS cookie generation callback function.
#define osMemcmp(p1, p2, length)
Definition: os_port.h:156
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
Helper functions for CoAP server.
@ ERROR_WRONG_COOKIE
Definition: error.h:92
#define COAP_SERVER_MAX_SESSIONS
Definition: coap_server.h:63
@ TLS_STATE_CLIENT_HELLO
Definition: tls.h:1542
error_t coapServerCookieVerifyCallback(TlsContext *context, const DtlsClientParameters *clientParams, const uint8_t *cookie, size_t length, void *param)
DTLS cookie verification callback function.
error_t coapServerSendCallback(void *handle, const void *data, size_t length, size_t *written, uint_t flags)
DTLS send callback.
error_t tlsShutdown(TlsContext *context)
Gracefully close TLS session.
Definition: tls.c:2603
#define osMemcpy(dest, src, length)
Definition: os_port.h:144
#define TlsContext
Definition: tls.h:36
error_t
Error codes.
Definition: error.h:43
bool_t ipCompAddr(const IpAddr *ipAddr1, const IpAddr *ipAddr2)
Compare IP addresses.
Definition: ip.c:318
@ TLS_CONNECTION_END_SERVER
Definition: tls.h:1013
Client parameters.
Definition: dtls_misc.h:223
const uint8_t * cipherSuites
Definition: dtls_misc.h:229
void coapServerDeleteSession(CoapDtlsSession *session)
Delete DTLS session.
#define TRACE_INFO(...)
Definition: debug.h:105
uint8_t length
Definition: tcp.h:375
error_t tlsRead(TlsContext *context, void *data, size_t size, size_t *received, uint_t flags)
Receive application data from a the remote host using TLS.
Definition: tls.c:2285
#define CoapDtlsSession
Definition: coap_server.h:125
uint32_t systime_t
System time.
CoAP server.
#define ntohs(value)
Definition: cpu_endian.h:421
__weak_func void hmacUpdate(HmacContext *context, const void *data, size_t length)
Update the HMAC context with a portion of the message being hashed.
Definition: hmac.c:201
@ ERROR_TIMEOUT
Definition: error.h:95
uint8_t digest[MAX_HASH_DIGEST_SIZE]
Definition: hmac.h:63
uint32_t time
error_t coapServerDemultiplexSession(CoapServerContext *context)
DTLS session demultiplexing.
TlsState tlsGetState(TlsContext *context)
Retrieve current TLS state.
Definition: tls.c:213
__weak_func void hmacFinal(HmacContext *context, uint8_t *digest)
Finish the HMAC calculation.
Definition: hmac.c:218
#define CoapServerContext
Definition: coap_server.h:121
@ TLS_STATE_INIT
Definition: tls.h:1541
Transport protocol abstraction layer.
error_t tlsSetSocketCallbacks(TlsContext *context, TlsSocketSendCallback socketSendCallback, TlsSocketReceiveCallback socketReceiveCallback, TlsSocketHandle handle)
Set socket send and receive callbacks.
Definition: tls.c:263
const uint8_t * sessionId
Definition: dtls_misc.h:227
const uint8_t * compressMethods
Definition: dtls_misc.h:231
uint8_t cookie[]
Definition: dtls_misc.h:206
error_t socketSendTo(Socket *socket, const IpAddr *destIpAddr, uint16_t destPort, const void *data, size_t length, size_t *written, uint_t flags)
Send a datagram to a specific destination.
Definition: socket.c:1535
void tlsFree(TlsContext *context)
Release TLS context.
Definition: tls.c:2765
error_t coapServerReceiveCallback(void *handle, void *data, size_t size, size_t *received, uint_t flags)
DTLS receive callback.
size_t compressMethodsLen
Definition: dtls_misc.h:232
uint8_t flags
Definition: tcp.h:358
#define COAP_SERVER_MAX_COOKIE_SECRET_SIZE
Definition: coap_server.h:91
error_t tlsSetCookieCallbacks(TlsContext *context, DtlsCookieGenerateCallback cookieGenerateCallback, DtlsCookieVerifyCallback cookieVerifyCallback, void *param)
Set cookie generation/verification callbacks (for DTLS only)
Definition: tls.c:1661
unsigned int uint_t
Definition: compiler_port.h:57
#define osMemset(p, value, length)
Definition: os_port.h:138
__weak_func error_t hmacInit(HmacContext *context, const HashAlgo *hash, const void *key, size_t keyLen)
Initialize HMAC calculation.
Definition: hmac.c:140
#define SHA256_DIGEST_SIZE
Definition: sha256.h:45
#define COAP_SERVER_BUFFER_SIZE
Definition: coap_server.h:84
error_t tlsConnect(TlsContext *context)
Initiate the TLS handshake.
Definition: tls.c:1819
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
void * TlsSocketHandle
Socket handle.
Definition: tls.h:2027
systime_t osGetSystemTime(void)
Retrieve system time.
@ TLS_STATE_CLOSED
Definition: tls.h:1574