diff --git a/include/coap2/coap_session.h b/include/coap2/coap_session.h index ef152315..f42b206a 100644 --- a/include/coap2/coap_session.h +++ b/include/coap2/coap_session.h @@ -29,6 +29,8 @@ typedef struct coap_fixed_point_t { } coap_fixed_point_t; #define COAP_DEFAULT_SESSION_TIMEOUT 300 +#define COAP_PARTIAL_SESSION_TIMEOUT_TICKS (30 * COAP_TICKS_PER_SECOND) +#define COAP_DEFAULT_MAX_HANDSHAKE_SESSIONS 100 #define COAP_PROTO_NOT_RELIABLE(p) ((p)==COAP_PROTO_UDP || (p)==COAP_PROTO_DTLS) #define COAP_PROTO_RELIABLE(p) ((p)==COAP_PROTO_TCP || (p)==COAP_PROTO_TLS) diff --git a/include/coap2/net.h b/include/coap2/net.h index 1b89c0f1..b7ee023c 100644 --- a/include/coap2/net.h +++ b/include/coap2/net.h @@ -212,6 +212,7 @@ typedef struct coap_context_t { unsigned int session_timeout; /**< Number of seconds of inactivity after which an unused session will be closed. 0 means use default. */ unsigned int max_idle_sessions; /**< Maximum number of simultaneous unused sessions per endpoint. 0 means no maximum. */ + unsigned int max_handshake_sessions; /**< Maximum number of simultaneous negotating sessions per endpoint. 0 means use default. */ unsigned int ping_timeout; /**< Minimum inactivity time before sending a ping message. 0 means disabled. */ unsigned int csm_timeout; /**< Timeout for waiting for a CSM from the remote side. 0 means disabled. */ diff --git a/src/coap_session.c b/src/coap_session.c index ce5a762c..aeee47ac 100644 --- a/src/coap_session.c +++ b/src/coap_session.c @@ -448,7 +448,9 @@ coap_endpoint_get_session(coap_endpoint_t *endpoint, const coap_packet_t *packet, coap_tick_t now) { coap_session_t *session = NULL; unsigned int num_idle = 0; + unsigned int num_hs = 0; coap_session_t *oldest = NULL; + coap_session_t *oldest_hs = NULL; endpoint->hello.ifindex = -1; @@ -460,15 +462,44 @@ coap_endpoint_get_session(coap_endpoint_t *endpoint, session->last_rx_tx = now; return session; } - if (session->ref == 0 && session->delayqueue == NULL && session->type == COAP_SESSION_TYPE_SERVER) { + if (session->ref == 0 && session->delayqueue == NULL && + session->type == COAP_SESSION_TYPE_SERVER) { ++num_idle; if (oldest==NULL || session->last_rx_tx < oldest->last_rx_tx) oldest = session; + + if (session->state == COAP_SESSION_STATE_HANDSHAKE) { + ++num_hs; + /* See if this is a partial SSL session set up + which needs to be cleared down to prevent DOS */ + if ((session->last_rx_tx + COAP_PARTIAL_SESSION_TIMEOUT_TICKS) < now) { + if (oldest_hs == NULL || + session->last_rx_tx < oldest_hs->last_rx_tx) + oldest_hs = session; + } + } } } - if (endpoint->context->max_idle_sessions > 0 && num_idle >= endpoint->context->max_idle_sessions) + if (endpoint->context->max_idle_sessions > 0 && + num_idle >= endpoint->context->max_idle_sessions) { coap_session_free(oldest); + } + else if (oldest_hs) { + coap_log(LOG_WARNING, "***%s: Incomplete session timed out\n", + coap_session_str(oldest_hs)); + coap_session_free(oldest_hs); + } + + if (num_hs > (endpoint->context->max_handshake_sessions ? + endpoint->context->max_handshake_sessions : + COAP_DEFAULT_MAX_HANDSHAKE_SESSIONS)) { + /* Maxed out on number of session in SSL negotiation state */ + coap_log(LOG_DEBUG, + "Oustanding sessions in COAP_SESSION_STATE_HANDSHAKE too " + "large. New request ignored\n"); + return NULL; + } if (endpoint->proto == COAP_PROTO_DTLS) { session = &endpoint->hello;