1 /* session.c -- Session management for libcoap
2 *
3 * Copyright (C) 2017 Jean-Claue Michelou <jcm@spinetix.com>
4 * Copyright (c) 2021 Huawei Device Co., Ltd. All rights reserved.
5 *
6 * This file is part of the CoAP library libcoap. Please see
7 * README for terms of use.
8 */
9
10 #include "coap_internal.h"
11
12 #ifndef COAP_SESSION_C_
13 #define COAP_SESSION_C_
14
15 #include <stdio.h>
16
17 #ifdef COAP_EPOLL_SUPPORT
18 #include <sys/epoll.h>
19 #include <sys/timerfd.h>
20 #endif /* COAP_EPOLL_SUPPORT */
21 #include <errno.h>
22 #include <unistd.h>
23 #include <net/if.h>
24 #include <sys/ioctl.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27
28 void
coap_session_set_max_retransmit(coap_session_t * session,unsigned int value)29 coap_session_set_max_retransmit (coap_session_t *session, unsigned int value) {
30 if (value > 0)
31 session->max_retransmit = value;
32 coap_log(LOG_DEBUG, "***%s: session max_retransmit set to %d\n",
33 coap_session_str(session), session->max_retransmit);
34 return;
35 }
36
37 void
coap_session_set_ack_timeout(coap_session_t * session,coap_fixed_point_t value)38 coap_session_set_ack_timeout (coap_session_t *session, coap_fixed_point_t value) {
39 if (value.integer_part > 0 && value.fractional_part < 1000)
40 session->ack_timeout = value;
41 coap_log(LOG_DEBUG, "***%s: session ack_timeout set to %d.%03d\n",
42 coap_session_str(session), session->ack_timeout.integer_part,
43 session->ack_timeout.fractional_part);
44 return;
45 }
46
47 void
coap_session_set_ack_random_factor(coap_session_t * session,coap_fixed_point_t value)48 coap_session_set_ack_random_factor (coap_session_t *session,
49 coap_fixed_point_t value) {
50 if (value.integer_part > 0 && value.fractional_part < 1000)
51 session->ack_random_factor = value;
52 coap_log(LOG_DEBUG, "***%s: session ack_random_factor set to %d.%03d\n",
53 coap_session_str(session), session->ack_random_factor.integer_part,
54 session->ack_random_factor.fractional_part);
55 return;
56 }
57
58 unsigned int
coap_session_get_max_transmit(coap_session_t * session)59 coap_session_get_max_transmit (coap_session_t *session) {
60 return session->max_retransmit;
61 }
62
63 coap_fixed_point_t
coap_session_get_ack_timeout(coap_session_t * session)64 coap_session_get_ack_timeout (coap_session_t *session) {
65 return session->ack_timeout;
66 }
67
68 coap_fixed_point_t
coap_session_get_ack_random_factor(coap_session_t * session)69 coap_session_get_ack_random_factor (coap_session_t *session) {
70 return session->ack_random_factor;
71 }
72
73 coap_session_t *
coap_session_reference(coap_session_t * session)74 coap_session_reference(coap_session_t *session) {
75 ++session->ref;
76 return session;
77 }
78
79 void
coap_session_release(coap_session_t * session)80 coap_session_release(coap_session_t *session) {
81 if (session) {
82 assert(session->ref > 0);
83 if (session->ref > 0)
84 --session->ref;
85 if (session->ref == 0 && session->type == COAP_SESSION_TYPE_CLIENT)
86 coap_session_free(session);
87 }
88 }
89
90 void
coap_session_set_app_data(coap_session_t * session,void * app_data)91 coap_session_set_app_data(coap_session_t *session, void *app_data) {
92 assert(session);
93 session->app = app_data;
94 }
95
96 void *
coap_session_get_app_data(const coap_session_t * session)97 coap_session_get_app_data(const coap_session_t *session) {
98 assert(session);
99 return session->app;
100 }
101
102 static coap_session_t *
coap_make_session(coap_proto_t proto,coap_session_type_t type,const coap_address_t * local_if,const coap_address_t * local_addr,const coap_address_t * remote_addr,int ifindex,coap_context_t * context,coap_endpoint_t * endpoint)103 coap_make_session(coap_proto_t proto, coap_session_type_t type,
104 const coap_address_t *local_if, const coap_address_t *local_addr,
105 const coap_address_t *remote_addr, int ifindex, coap_context_t *context,
106 coap_endpoint_t *endpoint) {
107 coap_session_t *session = (coap_session_t*)coap_malloc_type(COAP_SESSION, sizeof(coap_session_t));
108 if (!session)
109 return NULL;
110 memset(session, 0, sizeof(*session));
111 session->proto = proto;
112 session->type = type;
113 if (local_if)
114 coap_address_copy(&session->local_if, local_if);
115 else
116 coap_address_init(&session->local_if);
117 if (local_addr)
118 coap_address_copy(&session->addr_info.local, local_addr);
119 else
120 coap_address_init(&session->addr_info.local);
121 if (remote_addr)
122 coap_address_copy(&session->addr_info.remote, remote_addr);
123 else
124 coap_address_init(&session->addr_info.remote);
125 session->ifindex = ifindex;
126 session->context = context;
127 session->endpoint = endpoint;
128 if (endpoint)
129 session->mtu = endpoint->default_mtu;
130 else
131 session->mtu = COAP_DEFAULT_MTU;
132 if (proto == COAP_PROTO_DTLS) {
133 session->tls_overhead = 29;
134 if (session->tls_overhead >= session->mtu) {
135 session->tls_overhead = session->mtu;
136 coap_log(LOG_ERR, "DTLS overhead exceeds MTU\n");
137 }
138 }
139 session->max_retransmit = COAP_DEFAULT_MAX_RETRANSMIT;
140 session->ack_timeout = COAP_DEFAULT_ACK_TIMEOUT;
141 session->ack_random_factor = COAP_DEFAULT_ACK_RANDOM_FACTOR;
142 session->dtls_event = -1;
143 session->last_ping_mid = COAP_INVALID_TID;
144
145 /* initialize message id */
146 prng((unsigned char *)&session->tx_mid, sizeof(session->tx_mid));
147
148 return session;
149 }
150
coap_session_mfree(coap_session_t * session)151 void coap_session_mfree(coap_session_t *session) {
152 coap_queue_t *q, *tmp;
153
154 if (session->partial_pdu)
155 coap_delete_pdu(session->partial_pdu);
156 if (session->proto == COAP_PROTO_DTLS)
157 coap_dtls_free_session(session);
158 else if (session->proto == COAP_PROTO_TLS)
159 coap_tls_free_session(session);
160 if (session->sock.flags != COAP_SOCKET_EMPTY)
161 coap_socket_close(&session->sock);
162 if (session->psk_identity)
163 coap_free(session->psk_identity);
164 if (session->psk_key)
165 coap_free(session->psk_key);
166
167 LL_FOREACH_SAFE(session->delayqueue, q, tmp) {
168 if (q->pdu->type==COAP_MESSAGE_CON && session->context && session->context->nack_handler)
169 session->context->nack_handler(session->context, session, q->pdu, session->proto == COAP_PROTO_DTLS ? COAP_NACK_TLS_FAILED : COAP_NACK_NOT_DELIVERABLE, q->id);
170 coap_delete_node(q);
171 }
172 }
173
coap_session_free(coap_session_t * session)174 void coap_session_free(coap_session_t *session) {
175 if (!session)
176 return;
177 assert(session->ref == 0);
178 if (session->ref)
179 return;
180 coap_session_mfree(session);
181 if (session->endpoint) {
182 if (session->endpoint->sessions)
183 SESSIONS_DELETE(session->endpoint->sessions, session);
184 } else if (session->context) {
185 if (session->context->sessions)
186 SESSIONS_DELETE(session->context->sessions, session);
187 }
188 coap_log(LOG_DEBUG, "***%s: session closed\n", coap_session_str(session));
189
190 coap_free_type(COAP_SESSION, session);
191 }
192
coap_session_max_pdu_size(const coap_session_t * session)193 size_t coap_session_max_pdu_size(const coap_session_t *session) {
194 size_t max_with_header = (size_t)(session->mtu - session->tls_overhead);
195 if (COAP_PROTO_NOT_RELIABLE(session->proto))
196 return max_with_header > 4 ? max_with_header - 4 : 0;
197 /* we must assume there is no token to be on the safe side */
198 if (max_with_header <= 2)
199 return 0;
200 else if (max_with_header <= COAP_MAX_MESSAGE_SIZE_TCP0 + 2)
201 return max_with_header - 2;
202 else if (max_with_header <= COAP_MAX_MESSAGE_SIZE_TCP8 + 3)
203 return max_with_header - 3;
204 else if (max_with_header <= COAP_MAX_MESSAGE_SIZE_TCP16 + 4)
205 return max_with_header - 4;
206 else
207 return max_with_header - 6;
208 }
209
coap_session_set_mtu(coap_session_t * session,unsigned mtu)210 void coap_session_set_mtu(coap_session_t *session, unsigned mtu) {
211 #if defined(WITH_CONTIKI) || defined(WITH_LWIP)
212 if (mtu > COAP_MAX_MESSAGE_SIZE_TCP16 + 4)
213 mtu = COAP_MAX_MESSAGE_SIZE_TCP16 + 4;
214 #endif
215 session->mtu = mtu;
216 if (session->tls_overhead >= session->mtu) {
217 session->tls_overhead = session->mtu;
218 coap_log(LOG_ERR, "DTLS overhead exceeds MTU\n");
219 }
220 }
221
coap_session_send(coap_session_t * session,const uint8_t * data,size_t datalen)222 ssize_t coap_session_send(coap_session_t *session, const uint8_t *data, size_t datalen) {
223 ssize_t bytes_written;
224
225 coap_socket_t *sock = &session->sock;
226 if (sock->flags == COAP_SOCKET_EMPTY) {
227 assert(session->endpoint != NULL);
228 sock = &session->endpoint->sock;
229 }
230
231 bytes_written = coap_socket_send(sock, session, data, datalen);
232 if (bytes_written == (ssize_t)datalen) {
233 coap_ticks(&session->last_rx_tx);
234 coap_log(LOG_DEBUG, "* %s: sent %zd bytes\n",
235 coap_session_str(session), datalen);
236 } else {
237 coap_log(LOG_DEBUG, "* %s: failed to send %zd bytes\n",
238 coap_session_str(session), datalen);
239 }
240 return bytes_written;
241 }
242
coap_session_write(coap_session_t * session,const uint8_t * data,size_t datalen)243 ssize_t coap_session_write(coap_session_t *session, const uint8_t *data, size_t datalen) {
244 ssize_t bytes_written = coap_socket_write(&session->sock, data, datalen);
245 if (bytes_written > 0) {
246 coap_ticks(&session->last_rx_tx);
247 coap_log(LOG_DEBUG, "* %s: sent %zd bytes\n",
248 coap_session_str(session), datalen);
249 } else if (bytes_written < 0) {
250 coap_log(LOG_DEBUG, "* %s: failed to send %zd bytes\n",
251 coap_session_str(session), datalen );
252 }
253 return bytes_written;
254 }
255
256 ssize_t
coap_session_delay_pdu(coap_session_t * session,coap_pdu_t * pdu,coap_queue_t * node)257 coap_session_delay_pdu(coap_session_t *session, coap_pdu_t *pdu,
258 coap_queue_t *node)
259 {
260 if ( node ) {
261 coap_queue_t *removed = NULL;
262 coap_remove_from_queue(&session->context->sendqueue, session, node->id, &removed);
263 assert(removed == node);
264 coap_session_release(node->session);
265 node->session = NULL;
266 node->t = 0;
267 } else {
268 coap_queue_t *q = NULL;
269 /* Check that the same tid is not getting re-used in violation of RFC7252 */
270 LL_FOREACH(session->delayqueue, q) {
271 if (q->id == pdu->tid) {
272 coap_log(LOG_ERR, "** %s: tid=%d: already in-use - dropped\n", coap_session_str(session), pdu->tid);
273 return COAP_INVALID_TID;
274 }
275 }
276 node = coap_new_node();
277 if (node == NULL)
278 return COAP_INVALID_TID;
279 node->id = pdu->tid;
280 node->pdu = pdu;
281 if (pdu->type == COAP_MESSAGE_CON && COAP_PROTO_NOT_RELIABLE(session->proto)) {
282 uint8_t r;
283 prng(&r, sizeof(r));
284 /* add timeout in range [ACK_TIMEOUT...ACK_TIMEOUT * ACK_RANDOM_FACTOR] */
285 node->timeout = coap_calc_timeout(session, r);
286 }
287 }
288 LL_APPEND(session->delayqueue, node);
289 coap_log(LOG_DEBUG, "** %s: tid=%d: delayed\n",
290 coap_session_str(session), node->id);
291 return COAP_PDU_DELAYED;
292 }
293
coap_session_send_csm(coap_session_t * session)294 void coap_session_send_csm(coap_session_t *session) {
295 coap_pdu_t *pdu;
296 uint8_t buf[4];
297 assert(COAP_PROTO_RELIABLE(session->proto));
298 coap_log(LOG_DEBUG, "***%s: sending CSM\n", coap_session_str(session));
299 session->state = COAP_SESSION_STATE_CSM;
300 session->partial_write = 0;
301 if (session->mtu == 0)
302 session->mtu = COAP_DEFAULT_MTU; /* base value */
303 pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_SIGNALING_CSM, 0, 16);
304 if ( pdu == NULL
305 || coap_add_option(pdu, COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE,
306 coap_encode_var_safe(buf, sizeof(buf),
307 COAP_DEFAULT_MAX_PDU_RX_SIZE), buf) == 0
308 || coap_pdu_encode_header(pdu, session->proto) == 0
309 ) {
310 coap_session_disconnected(session, COAP_NACK_NOT_DELIVERABLE);
311 } else {
312 ssize_t bytes_written = coap_session_send_pdu(session, pdu);
313 if (bytes_written != (ssize_t)pdu->used_size + pdu->hdr_size)
314 coap_session_disconnected(session, COAP_NACK_NOT_DELIVERABLE);
315 }
316 if (pdu)
317 coap_delete_pdu(pdu);
318 }
319
coap_session_send_ping(coap_session_t * session)320 coap_tid_t coap_session_send_ping(coap_session_t *session) {
321 coap_pdu_t *ping;
322 if (session->state != COAP_SESSION_STATE_ESTABLISHED)
323 return COAP_INVALID_TID;
324 if (COAP_PROTO_NOT_RELIABLE(session->proto)) {
325 uint16_t tid = coap_new_message_id (session);
326 ping = coap_pdu_init(COAP_MESSAGE_CON, 0, tid, 0);
327 }
328 else {
329 ping = coap_pdu_init(COAP_MESSAGE_CON, COAP_SIGNALING_PING, 0, 1);
330 }
331 if (!ping)
332 return COAP_INVALID_TID;
333 return coap_send(session, ping);
334 }
335
coap_session_connected(coap_session_t * session)336 void coap_session_connected(coap_session_t *session) {
337 if (session->state != COAP_SESSION_STATE_ESTABLISHED) {
338 coap_log(LOG_DEBUG, "***%s: session connected\n",
339 coap_session_str(session));
340 if (session->state == COAP_SESSION_STATE_CSM)
341 coap_handle_event(session->context, COAP_EVENT_SESSION_CONNECTED, session);
342 }
343
344 session->state = COAP_SESSION_STATE_ESTABLISHED;
345 session->partial_write = 0;
346
347 if ( session->proto==COAP_PROTO_DTLS) {
348 session->tls_overhead = coap_dtls_get_overhead(session);
349 if (session->tls_overhead >= session->mtu) {
350 session->tls_overhead = session->mtu;
351 coap_log(LOG_ERR, "DTLS overhead exceeds MTU\n");
352 }
353 }
354
355 while (session->delayqueue && session->state == COAP_SESSION_STATE_ESTABLISHED) {
356 ssize_t bytes_written;
357 coap_queue_t *q = session->delayqueue;
358 if (q->pdu->type == COAP_MESSAGE_CON && COAP_PROTO_NOT_RELIABLE(session->proto)) {
359 if (session->con_active >= COAP_DEFAULT_NSTART)
360 break;
361 session->con_active++;
362 }
363 /* Take entry off the queue */
364 session->delayqueue = q->next;
365 q->next = NULL;
366
367 coap_log(LOG_DEBUG, "** %s: tid=%d: transmitted after delay\n",
368 coap_session_str(session), (int)q->pdu->tid);
369 bytes_written = coap_session_send_pdu(session, q->pdu);
370 if (q->pdu->type == COAP_MESSAGE_CON && COAP_PROTO_NOT_RELIABLE(session->proto)) {
371 if (coap_wait_ack(session->context, session, q) >= 0)
372 q = NULL;
373 }
374 if (COAP_PROTO_NOT_RELIABLE(session->proto)) {
375 if (q)
376 coap_delete_node(q);
377 if (bytes_written < 0)
378 break;
379 } else {
380 if (bytes_written <= 0 || (size_t)bytes_written < q->pdu->used_size + q->pdu->hdr_size) {
381 q->next = session->delayqueue;
382 session->delayqueue = q;
383 if (bytes_written > 0)
384 session->partial_write = (size_t)bytes_written;
385 break;
386 } else {
387 coap_delete_node(q);
388 }
389 }
390 }
391 }
392
coap_session_disconnected(coap_session_t * session,coap_nack_reason_t reason)393 void coap_session_disconnected(coap_session_t *session, coap_nack_reason_t reason) {
394 (void)reason;
395 coap_session_state_t state = session->state;
396
397 coap_log(LOG_DEBUG, "***%s: session disconnected (reason %d)\n",
398 coap_session_str(session), reason);
399 #ifndef WITHOUT_OBSERVE
400 coap_delete_observers( session->context, session );
401 #endif
402
403 if ( session->tls) {
404 if (session->proto == COAP_PROTO_DTLS)
405 coap_dtls_free_session(session);
406 else if (session->proto == COAP_PROTO_TLS)
407 coap_tls_free_session(session);
408 session->tls = NULL;
409 }
410
411 if (session->proto == COAP_PROTO_UDP)
412 session->state = COAP_SESSION_STATE_ESTABLISHED;
413 else
414 session->state = COAP_SESSION_STATE_NONE;
415
416 session->con_active = 0;
417
418 if (session->partial_pdu) {
419 coap_delete_pdu(session->partial_pdu);
420 session->partial_pdu = NULL;
421 }
422 session->partial_read = 0;
423
424 while (session->delayqueue) {
425 coap_queue_t *q = session->delayqueue;
426 session->delayqueue = q->next;
427 q->next = NULL;
428 coap_log(LOG_DEBUG, "** %s: tid=%d: not transmitted after disconnect\n",
429 coap_session_str(session), q->id);
430 if (q->pdu->type==COAP_MESSAGE_CON
431 && COAP_PROTO_NOT_RELIABLE(session->proto)
432 && reason == COAP_NACK_ICMP_ISSUE)
433 {
434 /* Make sure that we try a re-transmit later on ICMP error */
435 if (coap_wait_ack(session->context, session, q) >= 0)
436 q = NULL;
437 }
438 if (q && q->pdu->type == COAP_MESSAGE_CON
439 && session->context->nack_handler)
440 {
441 session->context->nack_handler(session->context, session, q->pdu,
442 reason, q->id);
443 }
444 if (q)
445 coap_delete_node(q);
446 }
447 if (reason != COAP_NACK_ICMP_ISSUE)
448 coap_cancel_session_messages(session->context, session, reason);
449
450 if ( COAP_PROTO_RELIABLE(session->proto) ) {
451 if (session->sock.flags != COAP_SOCKET_EMPTY) {
452 coap_socket_close(&session->sock);
453 coap_handle_event(session->context,
454 state == COAP_SESSION_STATE_CONNECTING ?
455 COAP_EVENT_TCP_FAILED : COAP_EVENT_TCP_CLOSED, session);
456 }
457 if (state != COAP_SESSION_STATE_NONE) {
458 coap_handle_event(session->context,
459 state == COAP_SESSION_STATE_ESTABLISHED ?
460 COAP_EVENT_SESSION_CLOSED : COAP_EVENT_SESSION_FAILED, session);
461 }
462 }
463 }
464
loopback_packet_check(const char * srcbuf)465 static int32_t loopback_packet_check(const char *srcbuf) {
466 const char addr_perface[] = "::ffff:";
467 char tmpbuf[INET6_ADDRSTRLEN] = {0};
468 int ret;
469 void *tmp_addr_ptr = NULL;
470 struct ifreq buf[COAP_INTERFACE_MAX];
471 struct ifconf ifc = {0};
472
473 int fd = socket(AF_INET, SOCK_DGRAM, 0);
474 if (fd < 0) {
475 coap_log(LOG_DEBUG, "socket() fail\n");
476 return 1;
477 }
478 ifc.ifc_len = sizeof(buf);
479 ifc.ifc_buf = (char *)buf;
480 if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0) {
481 coap_log(LOG_DEBUG, "ioctl fail, errno = %d\n", errno);
482 goto L_IS_LOOPBACK;
483 }
484 int interfaceNum = ifc.ifc_len / sizeof(struct ifreq);
485 for (int i = 0; i < interfaceNum && i < COAP_INTERFACE_MAX; i++) {
486 coap_log(LOG_DEBUG, "interface name: %s\n", buf[i].ifr_name);
487 /* get IP of this interface */
488 if (ioctl(fd, SIOCGIFADDR, (char *)&buf[i]) < 0) {
489 coap_log(LOG_DEBUG, "ioctl fail, errno = %d\n", errno);
490 goto L_IS_LOOPBACK;
491 }
492 tmp_addr_ptr = &((struct sockaddr_in *)&(buf[i].ifr_addr))->sin_addr;
493 inet_ntop(AF_INET, tmp_addr_ptr, tmpbuf, INET_ADDRSTRLEN);
494 if (memcmp(srcbuf, addr_perface, strlen(addr_perface)) == 0 &&
495 strlen(srcbuf) == strlen(tmpbuf) + strlen(addr_perface)) {
496 ret = memcmp(srcbuf + strlen(addr_perface), tmpbuf, strlen(srcbuf) - strlen(addr_perface));
497 } else {
498 if (strlen(srcbuf) == strlen(tmpbuf)) {
499 ret = memcmp(srcbuf, tmpbuf, strlen(srcbuf));
500 } else {
501 ret = 1;
502 }
503 }
504 if (ret == 0) {
505 goto L_IS_LOOPBACK;
506 }
507 }
508 close(fd);
509 return 0;
510 L_IS_LOOPBACK:
511 close(fd);
512 return 1;
513 }
514
is_loopback_packet(const coap_packet_t * packet)515 static int32_t is_loopback_packet(const coap_packet_t *packet) {
516 char srcbuf[INET6_ADDRSTRLEN] = {0};
517
518 coap_address_ntop(&(packet->addr_info.remote), srcbuf, INET6_ADDRSTRLEN);
519
520 return loopback_packet_check(srcbuf);
521 }
522
523 coap_session_t *
coap_endpoint_get_session(coap_endpoint_t * endpoint,const coap_packet_t * packet,coap_tick_t now)524 coap_endpoint_get_session(coap_endpoint_t *endpoint,
525 const coap_packet_t *packet, coap_tick_t now) {
526 coap_session_t *session;
527 coap_session_t *rtmp;
528 unsigned int num_idle = 0;
529 unsigned int num_hs = 0;
530 coap_session_t *oldest = NULL;
531 coap_session_t *oldest_hs = NULL;
532
533 if (is_loopback_packet(packet) == 1) {
534 coap_log(LOG_DEBUG, "drop loopback packet.");
535 return NULL;
536 }
537
538 SESSIONS_FIND(endpoint->sessions, packet->addr_info, session);
539 if (session) {
540 session->last_rx_tx = now;
541 return session;
542 }
543
544 SESSIONS_ITER(endpoint->sessions, session, rtmp) {
545 if (session->ref == 0 && session->delayqueue == NULL) {
546 if (session->type == COAP_SESSION_TYPE_SERVER) {
547 ++num_idle;
548 if (oldest==NULL || session->last_rx_tx < oldest->last_rx_tx)
549 oldest = session;
550
551 if (session->state == COAP_SESSION_STATE_HANDSHAKE) {
552 ++num_hs;
553 /* See if this is a partial (D)TLS session set up
554 which needs to be cleared down to prevent DOS */
555 if ((session->last_rx_tx + COAP_PARTIAL_SESSION_TIMEOUT_TICKS) < now) {
556 if (oldest_hs == NULL ||
557 session->last_rx_tx < oldest_hs->last_rx_tx)
558 oldest_hs = session;
559 }
560 }
561 }
562 else if (session->type == COAP_SESSION_TYPE_HELLO) {
563 ++num_hs;
564 /* See if this is a partial (D)TLS session set up for Client Hello
565 which needs to be cleared down to prevent DOS */
566 if ((session->last_rx_tx + COAP_PARTIAL_SESSION_TIMEOUT_TICKS) < now) {
567 if (oldest_hs == NULL ||
568 session->last_rx_tx < oldest_hs->last_rx_tx)
569 oldest_hs = session;
570 }
571 }
572 }
573 }
574
575 if (endpoint->context->max_idle_sessions > 0 &&
576 num_idle >= endpoint->context->max_idle_sessions) {
577 coap_session_free(oldest);
578 }
579 else if (oldest_hs) {
580 coap_log(LOG_WARNING, "***%s: Incomplete session timed out\n",
581 coap_session_str(oldest_hs));
582 coap_session_free(oldest_hs);
583 }
584
585 if (num_hs > (endpoint->context->max_handshake_sessions ?
586 endpoint->context->max_handshake_sessions :
587 COAP_DEFAULT_MAX_HANDSHAKE_SESSIONS)) {
588 /* Maxed out on number of sessions in (D)TLS negotiation state */
589 coap_log(LOG_DEBUG,
590 "Oustanding sessions in COAP_SESSION_STATE_HANDSHAKE too "
591 "large. New request ignored\n");
592 return NULL;
593 }
594
595 if (endpoint->proto == COAP_PROTO_DTLS) {
596 /*
597 * Need to check that this actually is a Client Hello before wasting
598 * time allocating and then freeing off session.
599 */
600
601 /*
602 * Generic header structure of the DTLS record layer.
603 * typedef struct __attribute__((__packed__)) {
604 * uint8_t content_type; content type of the included message
605 * uint16_t version; Protocol version
606 * uint16_t epoch; counter for cipher state changes
607 * uint8_t sequence_number[6]; sequence number
608 * uint16_t length; length of the following fragment
609 * uint8_t handshake; If content_type == DTLS_CT_HANDSHAKE
610 * } dtls_record_handshake_t;
611 */
612 #define OFF_CONTENT_TYPE 0 /* offset of content_type in dtls_record_handshake_t */
613 #define DTLS_CT_ALERT 21 /* Content Type Alert */
614 #define DTLS_CT_HANDSHAKE 22 /* Content Type Handshake */
615 #define OFF_HANDSHAKE_TYPE 13 /* offset of handshake in dtls_record_handshake_t */
616 #define DTLS_HT_CLIENT_HELLO 1 /* Client Hello handshake type */
617
618 #ifdef WITH_LWIP
619 const uint8_t *payload = (const uint8_t*)packet->pbuf->payload;
620 size_t length = packet->pbuf->len;
621 #else /* ! WITH_LWIP */
622 const uint8_t *payload = (const uint8_t*)packet->payload;
623 size_t length = packet->length;
624 #endif /* ! WITH_LWIP */
625 if (length < (OFF_HANDSHAKE_TYPE + 1)) {
626 coap_log(LOG_DEBUG,
627 "coap_dtls_hello: ContentType %d Short Packet (%zu < %d) dropped\n",
628 payload[OFF_CONTENT_TYPE], length,
629 OFF_HANDSHAKE_TYPE + 1);
630 return NULL;
631 }
632 if (payload[OFF_CONTENT_TYPE] != DTLS_CT_HANDSHAKE ||
633 payload[OFF_HANDSHAKE_TYPE] != DTLS_HT_CLIENT_HELLO) {
634 /* only log if not a late alert */
635 if (payload[OFF_CONTENT_TYPE] != DTLS_CT_ALERT)
636 coap_log(LOG_DEBUG,
637 "coap_dtls_hello: ContentType %d Handshake %d dropped\n",
638 payload[OFF_CONTENT_TYPE], payload[OFF_HANDSHAKE_TYPE]);
639 return NULL;
640 }
641 }
642 session = coap_make_session(endpoint->proto, COAP_SESSION_TYPE_SERVER,
643 NULL, &packet->addr_info.local,
644 &packet->addr_info.remote,
645 packet->ifindex, endpoint->context, endpoint);
646 if (session) {
647 session->last_rx_tx = now;
648 if (endpoint->proto == COAP_PROTO_UDP)
649 session->state = COAP_SESSION_STATE_ESTABLISHED;
650 else if (endpoint->proto == COAP_PROTO_DTLS) {
651 session->type = COAP_SESSION_TYPE_HELLO;
652 }
653 SESSIONS_ADD(endpoint->sessions, session);
654 coap_log(LOG_DEBUG, "***%s: new incoming session\n",
655 coap_session_str(session));
656 }
657 return session;
658 }
659
660 coap_session_t *
coap_session_new_dtls_session(coap_session_t * session,coap_tick_t now)661 coap_session_new_dtls_session(coap_session_t *session,
662 coap_tick_t now) {
663 if (session) {
664 session->last_rx_tx = now;
665 session->type = COAP_SESSION_TYPE_SERVER;
666 session->tls = coap_dtls_new_server_session(session);
667 if (session->tls) {
668 session->state = COAP_SESSION_STATE_HANDSHAKE;
669 } else {
670 coap_session_free(session);
671 session = NULL;
672 }
673 }
674 return session;
675 }
676
677 #ifdef COAP_EPOLL_SUPPORT
678 static void
coap_epoll_ctl_add(coap_socket_t * sock,uint32_t events,const char * func)679 coap_epoll_ctl_add(coap_socket_t *sock,
680 uint32_t events,
681 const char *func
682 ) {
683 int ret;
684 struct epoll_event event;
685 coap_context_t *context;
686
687 if (sock == NULL)
688 return;
689
690 context = sock->session ? sock->session->context :
691 sock->endpoint ? sock->endpoint->context : NULL;
692 if (context == NULL)
693 return;
694
695 /* Needed if running 32bit as ptr is only 32bit */
696 memset(&event, 0, sizeof(event));
697 event.events = events;
698 event.data.ptr = sock;
699
700 ret = epoll_ctl(context->epfd, EPOLL_CTL_ADD, sock->fd, &event);
701 if (ret == -1) {
702 coap_log(LOG_ERR,
703 "%s: epoll_ctl ADD failed: %s (%d)\n",
704 func,
705 coap_socket_strerror(), errno);
706 }
707 }
708 #endif /* COAP_EPOLL_SUPPORT */
709
710 static coap_session_t *
coap_session_create_client(coap_context_t * ctx,const coap_address_t * local_if,const coap_address_t * server,coap_proto_t proto)711 coap_session_create_client(
712 coap_context_t *ctx,
713 const coap_address_t *local_if,
714 const coap_address_t *server,
715 coap_proto_t proto
716 ) {
717 coap_session_t *session = NULL;
718
719 assert(server);
720 assert(proto != COAP_PROTO_NONE);
721
722 session = coap_make_session(proto, COAP_SESSION_TYPE_CLIENT, local_if,
723 local_if, server, 0, ctx, NULL);
724 if (!session)
725 goto error;
726
727 coap_session_reference(session);
728
729 if (proto == COAP_PROTO_UDP || proto == COAP_PROTO_DTLS) {
730 if (!coap_socket_connect_udp(&session->sock, &session->local_if, server,
731 proto == COAP_PROTO_DTLS ? COAPS_DEFAULT_PORT : COAP_DEFAULT_PORT,
732 &session->addr_info.local, &session->addr_info.remote)) {
733 goto error;
734 }
735 } else if (proto == COAP_PROTO_TCP || proto == COAP_PROTO_TLS) {
736 if (!coap_socket_connect_tcp1(&session->sock, &session->local_if, server,
737 proto == COAP_PROTO_TLS ? COAPS_DEFAULT_PORT : COAP_DEFAULT_PORT,
738 &session->addr_info.local, &session->addr_info.remote)) {
739 goto error;
740 }
741 }
742
743 session->sock.session = session;
744 #ifdef COAP_EPOLL_SUPPORT
745 coap_epoll_ctl_add(&session->sock,
746 EPOLLIN |
747 ((session->sock.flags & COAP_SOCKET_WANT_CONNECT) ?
748 EPOLLOUT : 0),
749 __func__);
750 #endif /* COAP_EPOLL_SUPPORT */
751
752 session->sock.flags |= COAP_SOCKET_NOT_EMPTY | COAP_SOCKET_WANT_READ;
753 if (local_if)
754 session->sock.flags |= COAP_SOCKET_BOUND;
755 SESSIONS_ADD(ctx->sessions, session);
756 return session;
757
758 error:
759 coap_session_release(session);
760 return NULL;
761 }
762
763 static coap_session_t *
coap_session_connect(coap_session_t * session)764 coap_session_connect(coap_session_t *session) {
765 if (session->proto == COAP_PROTO_UDP) {
766 session->state = COAP_SESSION_STATE_ESTABLISHED;
767 } else if (session->proto == COAP_PROTO_DTLS) {
768 session->tls = coap_dtls_new_client_session(session);
769 if (session->tls) {
770 session->state = COAP_SESSION_STATE_HANDSHAKE;
771 } else {
772 /* Need to free session object. As a new session may not yet
773 * have been referenced, we call coap_session_reference() first
774 * before trying to release the object.
775 */
776 coap_session_reference(session);
777 coap_session_release(session);
778 return NULL;
779 }
780 } else if (session->proto == COAP_PROTO_TCP || session->proto == COAP_PROTO_TLS) {
781 if (session->sock.flags & COAP_SOCKET_WANT_CONNECT) {
782 session->state = COAP_SESSION_STATE_CONNECTING;
783 } else if (session->proto == COAP_PROTO_TLS) {
784 int connected = 0;
785 session->tls = coap_tls_new_client_session(session, &connected);
786 if (session->tls) {
787 session->state = COAP_SESSION_STATE_HANDSHAKE;
788 if (connected)
789 coap_session_send_csm(session);
790 } else {
791 /* Need to free session object. As a new session may not yet
792 * have been referenced, we call coap_session_reference()
793 * first before trying to release the object.
794 */
795 coap_session_reference(session);
796 coap_session_release(session);
797 return NULL;
798 }
799 } else {
800 coap_session_send_csm(session);
801 }
802 }
803 coap_ticks(&session->last_rx_tx);
804 return session;
805 }
806
807 static coap_session_t *
coap_session_accept(coap_session_t * session)808 coap_session_accept(coap_session_t *session) {
809 if (session->proto == COAP_PROTO_TCP || session->proto == COAP_PROTO_TLS)
810 coap_handle_event(session->context, COAP_EVENT_TCP_CONNECTED, session);
811 if (session->proto == COAP_PROTO_TCP) {
812 coap_session_send_csm(session);
813 } else if (session->proto == COAP_PROTO_TLS) {
814 int connected = 0;
815 session->tls = coap_tls_new_server_session(session, &connected);
816 if (session->tls) {
817 session->state = COAP_SESSION_STATE_HANDSHAKE;
818 if (connected) {
819 coap_handle_event(session->context, COAP_EVENT_DTLS_CONNECTED, session);
820 coap_session_send_csm(session);
821 }
822 } else {
823 /* Need to free session object. As a new session may not yet
824 * have been referenced, we call coap_session_reference() first
825 * before trying to release the object.
826 */
827 coap_session_reference(session);
828 coap_session_release(session);
829 session = NULL;
830 }
831 }
832 return session;
833 }
834
coap_new_client_session(struct coap_context_t * ctx,const coap_address_t * local_if,const coap_address_t * server,coap_proto_t proto)835 coap_session_t *coap_new_client_session(
836 struct coap_context_t *ctx,
837 const coap_address_t *local_if,
838 const coap_address_t *server,
839 coap_proto_t proto
840 ) {
841 coap_session_t *session = coap_session_create_client(ctx, local_if, server, proto);
842 if (session) {
843 coap_log(LOG_DEBUG, "***%s: new outgoing session\n",
844 coap_session_str(session));
845 session = coap_session_connect(session);
846 }
847 return session;
848 }
849
coap_new_client_session_psk(struct coap_context_t * ctx,const coap_address_t * local_if,const coap_address_t * server,coap_proto_t proto,const char * identity,const uint8_t * key,unsigned key_len)850 coap_session_t *coap_new_client_session_psk(
851 struct coap_context_t *ctx,
852 const coap_address_t *local_if,
853 const coap_address_t *server,
854 coap_proto_t proto,
855 const char *identity,
856 const uint8_t *key,
857 unsigned key_len
858 ) {
859 coap_session_t *session = coap_session_create_client(ctx, local_if, server, proto);
860
861 if (!session)
862 return NULL;
863
864 if (identity && (strlen(identity) > 0)) {
865 size_t identity_len = strlen(identity);
866 session->psk_identity = (uint8_t*)coap_malloc(identity_len);
867 if (session->psk_identity) {
868 memcpy(session->psk_identity, identity, identity_len);
869 session->psk_identity_len = identity_len;
870 } else {
871 coap_log(LOG_WARNING, "Cannot store session PSK identity\n");
872 coap_session_release(session);
873 return NULL;
874 }
875 }
876 else if (coap_dtls_is_supported()) {
877 coap_log(LOG_WARNING, "PSK identity not defined\n");
878 coap_session_release(session);
879 return NULL;
880 }
881
882 if (key && key_len > 0) {
883 session->psk_key = (uint8_t*)coap_malloc(key_len);
884 if (session->psk_key) {
885 memcpy(session->psk_key, key, key_len);
886 session->psk_key_len = key_len;
887 } else {
888 coap_log(LOG_WARNING, "Cannot store session PSK key\n");
889 coap_session_release(session);
890 return NULL;
891 }
892 }
893 else if (coap_dtls_is_supported()) {
894 coap_log(LOG_WARNING, "PSK key not defined\n");
895 coap_session_release(session);
896 return NULL;
897 }
898
899 if (coap_dtls_is_supported()) {
900 if (!coap_dtls_context_set_psk(ctx, NULL, COAP_DTLS_ROLE_CLIENT)) {
901 coap_session_release(session);
902 return NULL;
903 }
904 }
905 coap_log(LOG_DEBUG, "***%s: new outgoing session\n",
906 coap_session_str(session));
907 return coap_session_connect(session);
908 }
909
coap_new_client_session_pki(struct coap_context_t * ctx,const coap_address_t * local_if,const coap_address_t * server,coap_proto_t proto,coap_dtls_pki_t * setup_data)910 coap_session_t *coap_new_client_session_pki(
911 struct coap_context_t *ctx,
912 const coap_address_t *local_if,
913 const coap_address_t *server,
914 coap_proto_t proto,
915 coap_dtls_pki_t* setup_data
916 ) {
917 coap_session_t *session;
918
919 if (coap_dtls_is_supported()) {
920 if (!setup_data) {
921 return NULL;
922 } else {
923 if (setup_data->version != COAP_DTLS_PKI_SETUP_VERSION) {
924 coap_log(LOG_ERR,
925 "coap_new_client_session_pki: Wrong version of setup_data\n");
926 return NULL;
927 }
928 }
929
930 }
931 session = coap_session_create_client(ctx, local_if, server, proto);
932
933 if (!session) {
934 return NULL;
935 }
936
937 if (coap_dtls_is_supported()) {
938 /* we know that setup_data is not NULL */
939 if (!coap_dtls_context_set_pki(ctx, setup_data, COAP_DTLS_ROLE_CLIENT)) {
940 coap_session_release(session);
941 return NULL;
942 }
943 }
944 coap_log(LOG_DEBUG, "***%s: new outgoing session\n",
945 coap_session_str(session));
946 return coap_session_connect(session);
947 }
948
949
coap_new_server_session(struct coap_context_t * ctx,coap_endpoint_t * ep)950 coap_session_t *coap_new_server_session(
951 struct coap_context_t *ctx,
952 coap_endpoint_t *ep
953 ) {
954 coap_session_t *session;
955 session = coap_make_session( ep->proto, COAP_SESSION_TYPE_SERVER,
956 &ep->bind_addr, NULL, NULL, 0, ctx, ep );
957 if (!session)
958 goto error;
959
960 if (!coap_socket_accept_tcp(&ep->sock, &session->sock,
961 &session->addr_info.local,
962 &session->addr_info.remote))
963 goto error;
964 session->sock.flags |= COAP_SOCKET_NOT_EMPTY | COAP_SOCKET_CONNECTED
965 | COAP_SOCKET_WANT_READ;
966 session->sock.session = session;
967 #ifdef COAP_EPOLL_SUPPORT
968 coap_epoll_ctl_add(&session->sock,
969 EPOLLIN,
970 __func__);
971 #endif /* COAP_EPOLL_SUPPORT */
972 SESSIONS_ADD(ep->sessions, session);
973 if (session) {
974 coap_log(LOG_DEBUG, "***%s: new incoming session\n",
975 coap_session_str(session));
976 session = coap_session_accept(session);
977 }
978 return session;
979
980 error:
981 coap_session_free(session);
982 return NULL;
983 }
984
985 #ifndef WITH_LWIP
986 coap_endpoint_t *
coap_new_endpoint(coap_context_t * context,const coap_address_t * listen_addr,coap_proto_t proto)987 coap_new_endpoint(coap_context_t *context, const coap_address_t *listen_addr, coap_proto_t proto) {
988 struct coap_endpoint_t *ep = NULL;
989
990 assert(context);
991 assert(listen_addr);
992 assert(proto != COAP_PROTO_NONE);
993
994 if (proto == COAP_PROTO_DTLS && !coap_dtls_is_supported()) {
995 coap_log(LOG_CRIT, "coap_new_endpoint: DTLS not supported\n");
996 goto error;
997 }
998
999 if (proto == COAP_PROTO_TLS && !coap_tls_is_supported()) {
1000 coap_log(LOG_CRIT, "coap_new_endpoint: TLS not supported\n");
1001 goto error;
1002 }
1003
1004 if (proto == COAP_PROTO_DTLS || proto == COAP_PROTO_TLS) {
1005 if (!coap_dtls_context_check_keys_enabled(context)) {
1006 coap_log(LOG_INFO,
1007 "coap_new_endpoint: one of coap_context_set_psk() or "
1008 "coap_context_set_pki() not called\n");
1009 goto error;
1010 }
1011 }
1012
1013 ep = coap_malloc_endpoint();
1014 if (!ep) {
1015 coap_log(LOG_WARNING, "coap_new_endpoint: malloc");
1016 goto error;
1017 }
1018
1019 memset(ep, 0, sizeof(struct coap_endpoint_t));
1020 ep->context = context;
1021 ep->proto = proto;
1022
1023 if (proto==COAP_PROTO_TCP || proto==COAP_PROTO_TLS) {
1024 if (!coap_socket_bind_tcp(&ep->sock, listen_addr, &ep->bind_addr))
1025 goto error;
1026 ep->sock.flags |= COAP_SOCKET_WANT_ACCEPT;
1027 } else if (proto==COAP_PROTO_UDP || proto==COAP_PROTO_DTLS) {
1028 if (!coap_socket_bind_udp(&ep->sock, listen_addr, &ep->bind_addr))
1029 goto error;
1030 ep->sock.flags |= COAP_SOCKET_WANT_READ;
1031 } else {
1032 coap_log(LOG_CRIT, "coap_new_endpoint: protocol not supported\n");
1033 goto error;
1034 }
1035
1036 if (LOG_DEBUG <= coap_get_log_level()) {
1037 #ifndef INET6_ADDRSTRLEN
1038 #define INET6_ADDRSTRLEN 40
1039 #endif
1040 unsigned char addr_str[INET6_ADDRSTRLEN + 8];
1041
1042 if (coap_print_addr(&ep->bind_addr, addr_str, INET6_ADDRSTRLEN + 8)) {
1043 coap_log(LOG_DEBUG, "created %s endpoint %s\n",
1044 ep->proto == COAP_PROTO_TLS ? "TLS "
1045 : ep->proto == COAP_PROTO_TCP ? "TCP "
1046 : ep->proto == COAP_PROTO_DTLS ? "DTLS" : "UDP ",
1047 addr_str);
1048 }
1049 }
1050
1051 ep->sock.flags |= COAP_SOCKET_NOT_EMPTY | COAP_SOCKET_BOUND;
1052
1053 ep->default_mtu = COAP_DEFAULT_MTU;
1054
1055 ep->sock.endpoint = ep;
1056 #ifdef COAP_EPOLL_SUPPORT
1057 coap_epoll_ctl_add(&ep->sock,
1058 EPOLLIN,
1059 __func__);
1060 #endif /* COAP_EPOLL_SUPPORT */
1061
1062 LL_PREPEND(context->endpoint, ep);
1063 return ep;
1064
1065 error:
1066 coap_free_endpoint(ep);
1067 return NULL;
1068 }
1069
coap_endpoint_set_default_mtu(coap_endpoint_t * ep,unsigned mtu)1070 void coap_endpoint_set_default_mtu(coap_endpoint_t *ep, unsigned mtu) {
1071 ep->default_mtu = (uint16_t)mtu;
1072 }
1073
1074 void
coap_free_endpoint(coap_endpoint_t * ep)1075 coap_free_endpoint(coap_endpoint_t *ep) {
1076 if (ep) {
1077 coap_session_t *session, *rtmp;
1078
1079 if (ep->sock.flags != COAP_SOCKET_EMPTY)
1080 coap_socket_close(&ep->sock);
1081
1082 SESSIONS_ITER_SAFE(ep->sessions, session, rtmp) {
1083 assert(session->ref == 0);
1084 if (session->ref == 0) {
1085 coap_session_free(session);
1086 }
1087 }
1088
1089 if (ep->context && ep->context->endpoint) {
1090 LL_DELETE(ep->context->endpoint, ep);
1091 }
1092 coap_mfree_endpoint(ep);
1093 }
1094 }
1095 #endif /* WITH_LWIP */
1096
1097 coap_session_t *
coap_session_get_by_peer(coap_context_t * ctx,const coap_address_t * remote_addr,int ifindex)1098 coap_session_get_by_peer(coap_context_t *ctx,
1099 const coap_address_t *remote_addr,
1100 int ifindex) {
1101 coap_session_t *s, *rtmp;
1102 coap_endpoint_t *ep;
1103 SESSIONS_ITER(ctx->sessions, s, rtmp) {
1104 if (s->ifindex == ifindex && coap_address_equals(&s->addr_info.remote,
1105 remote_addr))
1106 return s;
1107 }
1108 LL_FOREACH(ctx->endpoint, ep) {
1109 SESSIONS_ITER(ep->sessions, s, rtmp) {
1110 if (s->ifindex == ifindex && coap_address_equals(&s->addr_info.remote,
1111 remote_addr))
1112 return s;
1113 }
1114 }
1115 return NULL;
1116 }
1117
coap_session_str(const coap_session_t * session)1118 const char *coap_session_str(const coap_session_t *session) {
1119 static char szSession[256];
1120 char *p = szSession, *end = szSession + sizeof(szSession);
1121 if (coap_print_addr(&session->addr_info.local,
1122 (unsigned char*)p, end - p) > 0)
1123 p += strlen(p);
1124 if (p + 6 < end) {
1125 strcpy(p, " <-> ");
1126 p += 5;
1127 }
1128 if (p + 1 < end) {
1129 if (coap_print_addr(&session->addr_info.remote,
1130 (unsigned char*)p, end - p) > 0)
1131 p += strlen(p);
1132 }
1133 if (session->ifindex > 0 && p + 1 < end)
1134 p += snprintf(p, end - p, " (if%d)", session->ifindex);
1135 if (p + 6 < end) {
1136 if (session->proto == COAP_PROTO_UDP) {
1137 strcpy(p, " UDP ");
1138 p += 4;
1139 } else if (session->proto == COAP_PROTO_DTLS) {
1140 strcpy(p, " DTLS");
1141 p += 5;
1142 } else if (session->proto == COAP_PROTO_TCP) {
1143 strcpy(p, " TCP ");
1144 p += 4;
1145 } else if (session->proto == COAP_PROTO_TLS) {
1146 strcpy(p, " TLS ");
1147 p += 4;
1148 } else {
1149 strcpy(p, " NONE");
1150 p += 5;
1151 }
1152 }
1153
1154 return szSession;
1155 }
1156
coap_endpoint_str(const coap_endpoint_t * endpoint)1157 const char *coap_endpoint_str(const coap_endpoint_t *endpoint) {
1158 static char szEndpoint[128];
1159 char *p = szEndpoint, *end = szEndpoint + sizeof(szEndpoint);
1160 if (coap_print_addr(&endpoint->bind_addr, (unsigned char*)p, end - p) > 0)
1161 p += strlen(p);
1162 if (p + 6 < end) {
1163 if (endpoint->proto == COAP_PROTO_UDP) {
1164 strcpy(p, " UDP");
1165 p += 4;
1166 } else if (endpoint->proto == COAP_PROTO_DTLS) {
1167 strcpy(p, " DTLS");
1168 p += 5;
1169 } else {
1170 strcpy(p, " NONE");
1171 p += 5;
1172 }
1173 }
1174
1175 return szEndpoint;
1176 }
1177
1178 #endif /* COAP_SESSION_C_ */
1179