From 25732fc8e68a2238b5966c1b0ae9ab7665b52677 Mon Sep 17 00:00:00 2001 From: jcmichelou Date: Sat, 2 Sep 2017 16:21:42 +0200 Subject: [PATCH] coap_session.[ch], net.c: Add handler for lost CON messages. Add a slot for storing application-specifc data in session object. Make the library resilient against client session released in a callback. --- include/coap/coap_io.h | 7 ++++++ include/coap/coap_session.h | 16 +++++++++++- include/coap/net.h | 50 ++++++++++++++++++++----------------- libcoap-1.sym | 2 ++ src/coap_openssl.c | 8 +++--- src/coap_session.c | 25 ++++++++++++++++--- src/coap_tinydtls.c | 4 +-- src/net.c | 23 ++++++++++++++--- 8 files changed, 97 insertions(+), 38 deletions(-) diff --git a/include/coap/coap_io.h b/include/coap/coap_io.h index 43da609a..30666f36 100644 --- a/include/coap/coap_io.h +++ b/include/coap/coap_io.h @@ -166,4 +166,11 @@ struct coap_packet_t { #endif typedef struct coap_packet_t coap_packet_t; +typedef enum { + COAP_NACK_TOO_MANY_RETRIES, + COAP_NACK_NOT_DELIVERABLE, + COAP_NACK_RST, + COAP_NACK_TLS_FAILED +} coap_nack_reason_t; + #endif /* _COAP_IO_H_ */ diff --git a/include/coap/coap_session.h b/include/coap/coap_session.h index b40552b1..640ca082 100644 --- a/include/coap/coap_session.h +++ b/include/coap/coap_session.h @@ -67,6 +67,7 @@ typedef struct coap_session_t { size_t psk_identity_len; uint8_t *psk_key; size_t psk_key_len; + void *app; /**< application-specific data */ } coap_session_t; /** @@ -86,12 +87,25 @@ coap_session_t *coap_session_reference(coap_session_t *session); */ void coap_session_release(coap_session_t *session); +/** +* Stores @p data with the given session. This function overwrites any value +* that has previously been stored with @p session. +*/ +void coap_session_set_app_data(coap_session_t *session, void *data); + +/** +* Returns any application-specific data that has been stored with @p +* session using the function coap_session_set_app_data(). This function will +* return @c NULL if no data has been stored. +*/ +void *coap_session_get_app_data(const coap_session_t *session); + /** * Notify session that it has failed connecting or has been disconnected. * * @param session The CoAP session. */ -void coap_session_disconnected(coap_session_t *session); +void coap_session_disconnected(coap_session_t *session, coap_nack_reason_t reason); /** * Notify session that the remote peer is no longer listening. diff --git a/include/coap/net.h b/include/coap/net.h index f8f1088f..cbe2dce4 100644 --- a/include/coap/net.h +++ b/include/coap/net.h @@ -69,6 +69,13 @@ typedef void (*coap_response_handler_t)(struct coap_context_t *, coap_pdu_t *received, const coap_tid_t id); +/** Message handler that is used as call-back in coap_context_t */ +typedef void (*coap_nack_handler_t)(struct coap_context_t *, + coap_session_t *session, + coap_pdu_t *sent, + coap_nack_reason_t reason, + const coap_tid_t id); + #define COAP_MID_CACHE_SIZE 3 typedef struct { unsigned char flags[COAP_MID_CACHE_SIZE]; @@ -120,6 +127,7 @@ typedef struct coap_context_t { unsigned int observe; coap_response_handler_t response_handler; + coap_nack_handler_t nack_handler; /** * Callback function that is used to signal events to the @@ -161,6 +169,22 @@ coap_register_response_handler(coap_context_t *context, context->response_handler = handler; } +/** + * Registers a new message handler that is called whenever a confirmable + * message (request or response) is dropped after all retries have been + * exhausted, or a rst message was received, or a network or TLS level + * event was received that indicates delivering the message is not possible. + * + * @param context The context to register the handler for. + * @param handler The nack handler to register. + */ +COAP_STATIC_INLINE void +coap_register_nack_handler(coap_context_t *context, + coap_nack_handler_t handler) { + context->nack_handler = handler; +} + + /** * Registers the option type @p type with the given context object @p ctx. * @@ -245,7 +269,7 @@ void coap_set_app_data(coap_context_t *context, void *data); * context using the function coap_set_app_data(). This function will * return @c NULL if no data has been stored. */ -void *coap_get_app_data(coap_context_t *context); +void *coap_get_app_data(const coap_context_t *context); /** * Creates a new ACK PDU with specified error @p code. The options specified by @@ -439,27 +463,6 @@ int coap_remove_from_queue(coap_queue_t **queue, coap_tid_t id, coap_queue_t **node); -/** - * Removes the transaction identified by @p id from given @p queue. This is a - * convenience function for coap_remove_from_queue() with automatic deletion of - * the removed node. - * - * @param queue The queue to search for @p id. - * @param session The session to look for. - * @param id The transaction id. - * - * @return @c 1 if node was found, removed and destroyed, @c 0 otherwise. - */ -COAP_STATIC_INLINE int -coap_remove_transaction(coap_queue_t **queue, coap_session_t *session, coap_tid_t id) { - coap_queue_t *node; - if (!coap_remove_from_queue(queue, session, id, &node)) - return 0; - - coap_delete_node(node); - return 1; -} - coap_tid_t coap_wait_ack( coap_context_t *context, coap_session_t *session, coap_queue_t *node); @@ -497,7 +500,8 @@ void coap_cancel_all_messages(coap_context_t *context, */ void coap_cancel_session_messages(coap_context_t *context, - coap_session_t *session); + coap_session_t *session, + coap_nack_reason_t reason); /** * Dispatches the PDUs from the receive queue in given context. diff --git a/libcoap-1.sym b/libcoap-1.sym index d893ad5c..6512e115 100644 --- a/libcoap-1.sym +++ b/libcoap-1.sym @@ -135,12 +135,14 @@ coap_session_connected coap_session_delay_pdu coap_session_disconnected coap_session_free +coap_session_get_app_data coap_session_get_by_peer coap_session_max_pdu_size coap_session_reference coap_session_release coap_session_reset coap_session_send +coap_session_set_app_data coap_session_set_mtu coap_session_str coap_set_app_data diff --git a/src/coap_openssl.c b/src/coap_openssl.c index 32a2f060..1aae0266 100644 --- a/src/coap_openssl.c +++ b/src/coap_openssl.c @@ -509,7 +509,7 @@ int coap_dtls_send(coap_session_t *session, if (dtls_event >= 0) { coap_handle_event(session->context, dtls_event, session); if (dtls_event == COAP_EVENT_DTLS_ERROR || dtls_event == COAP_EVENT_DTLS_CLOSED) { - coap_session_disconnected(session); + coap_session_disconnected(session, COAP_NACK_TLS_FAILED); r = -1; } } @@ -540,8 +540,8 @@ void coap_dtls_handle_timeout(coap_session_t *session) { assert(ssl != NULL); if (DTLSv1_handle_timeout(ssl) < 0) { - /* Too may retries */ - coap_session_disconnected(session); + /* Too many retries */ + coap_session_disconnected(session, COAP_NACK_TLS_FAILED); } } @@ -607,7 +607,7 @@ int coap_dtls_receive(coap_session_t *session, if (dtls_event >= 0) { coap_handle_event(session->context, dtls_event, session); if (dtls_event == COAP_EVENT_DTLS_ERROR || dtls_event == COAP_EVENT_DTLS_CLOSED) { - coap_session_disconnected(session); + coap_session_disconnected(session, COAP_NACK_TLS_FAILED); r = -1; } } diff --git a/src/coap_session.c b/src/coap_session.c index 44135155..35b61866 100644 --- a/src/coap_session.c +++ b/src/coap_session.c @@ -38,6 +38,18 @@ coap_session_release(coap_session_t *session) { } } +void +coap_session_set_app_data(coap_session_t *session, void *app_data) { + assert(session); + session->app = app_data; +} + +void * +coap_session_get_app_data(const coap_session_t *session) { + assert(session); + return session->app; +} + static coap_session_t * coap_make_session(coap_proto_t proto, coap_session_type_t type, const coap_address_t *local, const coap_address_t *remote, int ifindex, coap_context_t *context, coap_endpoint_t *endpoint) { coap_session_t *session = (coap_session_t*)coap_malloc_type(COAP_SESSION, sizeof(coap_session_t)); @@ -99,8 +111,11 @@ void coap_session_free(coap_session_t *session) { if (session->psk_key) coap_free(session->psk_key); - LL_FOREACH_SAFE(session->sendqueue, q, tmp) + LL_FOREACH_SAFE(session->sendqueue, q, tmp) { + if (q->pdu->hdr->type==COAP_MESSAGE_CON && session->context->nack_handler) + session->context->nack_handler(session->context, session, q->pdu, session->proto == COAP_PROTO_DTLS ? COAP_NACK_TLS_FAILED : COAP_NACK_NOT_DELIVERABLE, q->id); coap_delete_node(q); + } debug("*** %s: session closed\n", coap_session_str(session)); @@ -190,7 +205,7 @@ void coap_session_connected(coap_session_t *session) { bytes_written = coap_dtls_send(session, (const uint8_t*)q->pdu->hdr, q->pdu->length); else bytes_written = coap_session_send(session, (const uint8_t*)q->pdu->hdr, q->pdu->length); - if (bytes_written > 0 && q->pdu->hdr->type == COAP_MESSAGE_CON) { + if (q->pdu->hdr->type == COAP_MESSAGE_CON) { if (coap_wait_ack(session->context, session, q) >= 0) q = NULL; } @@ -201,7 +216,7 @@ void coap_session_connected(coap_session_t *session) { } } -void coap_session_disconnected(coap_session_t *session) { +void coap_session_disconnected(coap_session_t *session, coap_nack_reason_t reason) { debug("*** %s: session disconnected\n", coap_session_str(session)); if (session->proto == COAP_PROTO_DTLS && session->tls) { coap_dtls_free_session(session); @@ -227,7 +242,7 @@ void coap_session_reset(coap_session_t *session) { #ifndef WITHOUT_OBSERVE coap_delete_observers(session->context, session); #endif - coap_cancel_session_messages(session->context, session); + coap_cancel_session_messages(session->context, session, COAP_NACK_NOT_DELIVERABLE); if (session->proto == COAP_PROTO_DTLS && session->tls) { coap_dtls_free_session(session); session->tls = NULL; @@ -238,6 +253,8 @@ void coap_session_reset(coap_session_t *session) { session->sendqueue = q->next; q->next = NULL; debug("** %s tid=%d: not transmitted after delay\n", coap_session_str(session), (int)q->id); + if (q->pdu->hdr->type == COAP_MESSAGE_CON && session->context->nack_handler) + session->context->nack_handler(session->context, session, q->pdu, COAP_NACK_NOT_DELIVERABLE, q->id); coap_delete_node(q); } } diff --git a/src/coap_tinydtls.c b/src/coap_tinydtls.c index fe46e53b..ab581cda 100644 --- a/src/coap_tinydtls.c +++ b/src/coap_tinydtls.c @@ -354,7 +354,7 @@ coap_dtls_send(coap_session_t *session, if (coap_event_dtls == COAP_EVENT_DTLS_CONNECTED) coap_session_connected(session); else if (coap_event_dtls == DTLS_ALERT_CLOSE_NOTIFY || coap_event_dtls == COAP_EVENT_DTLS_ERROR) - coap_session_disconnected(session); + coap_session_disconnected(session, COAP_NACK_TLS_FAILED); } return res; @@ -400,7 +400,7 @@ coap_dtls_receive(coap_session_t *session, if (coap_event_dtls == COAP_EVENT_DTLS_CONNECTED) coap_session_connected(session); else if (coap_event_dtls == DTLS_ALERT_CLOSE_NOTIFY || coap_event_dtls == COAP_EVENT_DTLS_ERROR) - coap_session_disconnected(session); + coap_session_disconnected(session, COAP_NACK_TLS_FAILED); } return res; diff --git a/src/net.c b/src/net.c index ba734f2e..e19b7af8 100644 --- a/src/net.c +++ b/src/net.c @@ -520,7 +520,7 @@ coap_set_app_data(coap_context_t *ctx, void *app_data) { } void * -coap_get_app_data(coap_context_t *ctx) { +coap_get_app_data(const coap_context_t *ctx) { assert(ctx); return ctx->app; } @@ -896,6 +896,8 @@ coap_retransmit(coap_context_t *context, coap_queue_t *node) { #endif /* WITHOUT_OBSERVE */ /* And finally delete the node */ + if (node->pdu->hdr->type == COAP_MESSAGE_CON && context->nack_handler) + context->nack_handler(context, node->session, node->pdu, COAP_NACK_TOO_MANY_RETRIES, node->id); coap_delete_node(node); return COAP_INVALID_TID; } @@ -1019,8 +1021,12 @@ coap_read(coap_context_t *ctx, coap_tick_t now) { } LL_FOREACH_SAFE(ctx->sessions, s, tmp_s) { - if ((s->sock.flags & COAP_SOCKET_HAS_DATA) != 0) + if ((s->sock.flags & COAP_SOCKET_HAS_DATA) != 0) { + /* Make sure the session object is not deleted in one of the callbacks */ + coap_session_reference(s); coap_read_session(ctx, s, now); + coap_session_release(s); + } } } @@ -1152,13 +1158,16 @@ token_match(const unsigned char *a, size_t alen, } void -coap_cancel_session_messages(coap_context_t *context, coap_session_t *session) { +coap_cancel_session_messages(coap_context_t *context, coap_session_t *session, + coap_nack_reason_t reason) { coap_queue_t *p, *q; while (context->sendqueue && context->sendqueue->session == session) { q = context->sendqueue; context->sendqueue = q->next; debug("** %s tid=%d: removed\n", coap_session_str(session), q->id); + if (q->pdu->hdr->type == COAP_MESSAGE_CON && context->nack_handler) + context->nack_handler(context, session, q->pdu, reason, q->id); coap_delete_node(q); } @@ -1172,6 +1181,8 @@ coap_cancel_session_messages(coap_context_t *context, coap_session_t *session) { if (q->session == session) { p->next = q->next; debug("** %s tid=%d: removed\n", coap_session_str(session), q->id); + if (q->pdu->hdr->type == COAP_MESSAGE_CON && context->nack_handler) + context->nack_handler(context, session, q->pdu, reason, q->id); coap_delete_node(q); q = p->next; } else { @@ -1184,7 +1195,7 @@ coap_cancel_session_messages(coap_context_t *context, coap_session_t *session) { void coap_cancel_all_messages(coap_context_t *context, coap_session_t *session, const unsigned char *token, size_t token_length) { - /* cancel all messages in sendqueue that are for dst + /* cancel all messages in sendqueue that belong to session * and use the specified token */ coap_queue_t *p, *q; @@ -1810,6 +1821,10 @@ coap_dispatch(coap_context_t *context, coap_queue_t *rcvd) { if (sent) coap_cancel(context, sent); + + if(sent->pdu->hdr->type==COAP_MESSAGE_CON && context->nack_handler) + context->nack_handler(context, sent->session, sent->pdu, COAP_NACK_RST, sent->id); + goto cleanup; case COAP_MESSAGE_NON: /* check for unknown critical options */