diff options
-rw-r--r-- | res/res_http_websocket.c | 23 | ||||
-rw-r--r-- | res/res_pjsip_session.c | 5 | ||||
-rw-r--r-- | res/res_pjsip_t38.c | 22 | ||||
-rw-r--r-- | res/res_rtp_asterisk.c | 207 |
4 files changed, 163 insertions, 94 deletions
diff --git a/res/res_http_websocket.c b/res/res_http_websocket.c index 40aedff72..ecae03919 100644 --- a/res/res_http_websocket.c +++ b/res/res_http_websocket.c @@ -781,13 +781,6 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan return 0; } - fprintf(ser->f, "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: %s\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: %s\r\n", - upgrade, - websocket_combine_key(key, base64, sizeof(base64))); - /* RFC 6455, Section 4.1: * * 6. If the response includes a |Sec-WebSocket-Protocol| header @@ -798,11 +791,23 @@ int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instan * Connection_. */ if (protocol) { - fprintf(ser->f, "Sec-WebSocket-Protocol: %s\r\n", + fprintf(ser->f, "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: %s\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n" + "Sec-WebSocket-Protocol: %s\r\n\r\n", + upgrade, + websocket_combine_key(key, base64, sizeof(base64)), protocol); + } else { + fprintf(ser->f, "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: %s\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n\r\n", + upgrade, + websocket_combine_key(key, base64, sizeof(base64))); } - fprintf(ser->f, "\r\n"); fflush(ser->f); } else { diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index c72959400..84c343d3c 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -1039,7 +1039,10 @@ void ast_sip_session_resume_reinvite(struct ast_sip_session *session) return; } - pjsip_endpt_process_rx_data(ast_sip_get_pjsip_endpoint(), session->deferred_reinvite, NULL, NULL); + if (session->channel) { + pjsip_endpt_process_rx_data(ast_sip_get_pjsip_endpoint(), + session->deferred_reinvite, NULL, NULL); + } pjsip_rx_data_free_cloned(session->deferred_reinvite); session->deferred_reinvite = NULL; } diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c index addfd5861..43b6e9231 100644 --- a/res/res_pjsip_t38.c +++ b/res/res_pjsip_t38.c @@ -135,10 +135,13 @@ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_ses } session->t38state = new_state; - ast_debug(2, "T.38 state changed to '%u' from '%u' on channel '%s'\n", new_state, old_state, ast_channel_name(session->channel)); + ast_debug(2, "T.38 state changed to '%u' from '%u' on channel '%s'\n", + new_state, old_state, + session->channel ? ast_channel_name(session->channel) : "<gone>"); if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &state->timer)) { - ast_debug(2, "Automatic T.38 rejection on channel '%s' terminated\n", ast_channel_name(session->channel)); + ast_debug(2, "Automatic T.38 rejection on channel '%s' terminated\n", + session->channel ? ast_channel_name(session->channel) : "<gone>"); ao2_ref(session, -1); } @@ -198,7 +201,8 @@ static int t38_automatic_reject(void *obj) return 0; } - ast_debug(2, "Automatically rejecting T.38 request on channel '%s'\n", ast_channel_name(session->channel)); + ast_debug(2, "Automatically rejecting T.38 request on channel '%s'\n", + session->channel ? ast_channel_name(session->channel) : "<gone>"); t38_change_state(session, session_media, datastore->data, T38_REJECTED); ast_sip_session_resume_reinvite(session); @@ -227,9 +231,9 @@ static struct t38_state *t38_state_get_or_alloc(struct ast_sip_session *session) return datastore->data; } - if (!(datastore = ast_sip_session_alloc_datastore(&t38_datastore, "t38")) || - !(datastore->data = ast_calloc(1, sizeof(struct t38_state))) || - ast_sip_session_add_datastore(session, datastore)) { + if (!(datastore = ast_sip_session_alloc_datastore(&t38_datastore, "t38")) + || !(datastore->data = ast_calloc(1, sizeof(struct t38_state))) + || ast_sip_session_add_datastore(session, datastore)) { return NULL; } @@ -324,9 +328,13 @@ static int t38_interpret_parameters(void *obj) case AST_T38_REQUEST_NEGOTIATE: /* Request T38 */ /* Negotiation can not take place without a valid max_ifp value. */ if (!parameters->max_ifp) { - t38_change_state(data->session, session_media, state, T38_REJECTED); if (data->session->t38state == T38_PEER_REINVITE) { + t38_change_state(data->session, session_media, state, T38_REJECTED); ast_sip_session_resume_reinvite(data->session); + } else if (data->session->t38state == T38_ENABLED) { + t38_change_state(data->session, session_media, state, T38_DISABLED); + ast_sip_session_refresh(data->session, NULL, NULL, NULL, + AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1); } break; } else if (data->session->t38state == T38_PEER_REINVITE) { diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 2e9d7e35e..c9270ed18 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -204,11 +204,13 @@ struct rtp_learning_info { #ifdef HAVE_OPENSSL_SRTP struct dtls_details { + ast_mutex_t lock; /*!< Lock for timeout timer synchronization */ SSL *ssl; /*!< SSL session */ BIO *read_bio; /*!< Memory buffer for reading */ BIO *write_bio; /*!< Memory buffer for writing */ enum ast_rtp_dtls_setup dtls_setup; /*!< Current setup state */ enum ast_rtp_dtls_connection connection; /*!< Whether this is a new or existing connection */ + int timeout_timer; /*!< Scheduler id for timeout timer */ }; #endif @@ -317,7 +319,6 @@ struct ast_rtp { #ifdef HAVE_OPENSSL_SRTP SSL_CTX *ssl_ctx; /*!< SSL context */ - ast_mutex_t dtls_timer_lock; /*!< Lock for synchronization purposes */ enum ast_rtp_dtls_verify dtls_verify; /*!< What to verify */ enum ast_srtp_suite suite; /*!< SRTP crypto suite */ enum ast_rtp_dtls_hash local_hash; /*!< Local hash used for the fingerprint */ @@ -326,7 +327,6 @@ struct ast_rtp { unsigned char remote_fingerprint[EVP_MAX_MD_SIZE]; /*!< Fingerprint of the peer certificate */ unsigned int rekey; /*!< Interval at which to renegotiate and rekey */ int rekeyid; /*!< Scheduled item id for rekeying */ - int dtlstimerid; /*!< Scheduled item id for DTLS retransmission for RTP */ struct dtls_details dtls; /*!< DTLS state information */ #endif }; @@ -444,6 +444,8 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level); #ifdef HAVE_OPENSSL_SRTP static int ast_rtp_activate(struct ast_rtp_instance *instance); static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp); +static void dtls_srtp_start_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp); +static void dtls_srtp_stop_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp); #endif static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp); @@ -1229,6 +1231,9 @@ static int dtls_details_initialize(struct dtls_details *dtls, SSL_CTX *ssl_ctx, } dtls->connection = AST_RTP_DTLS_CONNECTION_NEW; + ast_mutex_init(&dtls->lock); + dtls->timeout_timer = -1; + return 0; error: @@ -1397,6 +1402,8 @@ static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + dtls_srtp_stop_timeout_timer(instance, rtp, 0); + if (rtp->ssl_ctx) { SSL_CTX_free(rtp->ssl_ctx); rtp->ssl_ctx = NULL; @@ -1405,11 +1412,17 @@ static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance) if (rtp->dtls.ssl) { SSL_free(rtp->dtls.ssl); rtp->dtls.ssl = NULL; + ast_mutex_destroy(&rtp->dtls.lock); } - if (rtp->rtcp && rtp->rtcp->dtls.ssl) { - SSL_free(rtp->rtcp->dtls.ssl); - rtp->rtcp->dtls.ssl = NULL; + if (rtp->rtcp) { + dtls_srtp_stop_timeout_timer(instance, rtp, 1); + + if (rtp->rtcp->dtls.ssl) { + SSL_free(rtp->rtcp->dtls.ssl); + rtp->rtcp->dtls.ssl = NULL; + ast_mutex_destroy(&rtp->rtcp->dtls.lock); + } } } @@ -1586,21 +1599,25 @@ static void dtls_perform_handshake(struct ast_rtp_instance *instance, struct dtl { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - if (!dtls->ssl) { + /* If we are not acting as a client connecting to the remote side then + * don't start the handshake as it will accomplish nothing and would conflict + * with the handshake we receive from the remote side. + */ + if (!dtls->ssl || (dtls->dtls_setup != AST_RTP_DTLS_SETUP_ACTIVE)) { return; } - if (SSL_is_init_finished(dtls->ssl)) { - SSL_clear(dtls->ssl); - if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) { - SSL_set_accept_state(dtls->ssl); - } else { - SSL_set_connect_state(dtls->ssl); - } - dtls->connection = AST_RTP_DTLS_CONNECTION_NEW; - } SSL_do_handshake(dtls->ssl); + + /* Since the handshake is started in a thread outside of the channel thread it's possible + * for the response to be handled in the channel thread before we start the timeout timer. + * To ensure this doesn't actually happen we hold the DTLS lock. The channel thread will + * block until we're done at which point the timeout timer will be immediately stopped. + */ + ast_mutex_lock(&dtls->lock); dtls_srtp_check_pending(instance, rtp, rtcp); + dtls_srtp_start_timeout_timer(instance, rtp, rtcp); + ast_mutex_unlock(&dtls->lock); } #endif @@ -1754,48 +1771,83 @@ static inline int rtcp_debug_test_addr(struct ast_sockaddr *addr) } #ifdef HAVE_OPENSSL_SRTP - -static int dtls_srtp_handle_timeout(const void *data) +static int dtls_srtp_handle_timeout(struct ast_rtp_instance *instance, int rtcp) { - struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data; struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls; + struct timeval dtls_timeout; + + DTLSv1_handle_timeout(dtls->ssl); + dtls_srtp_check_pending(instance, rtp, rtcp); - if (!rtp) - { + /* If a timeout can't be retrieved then this recurring scheduled item must stop */ + if (!DTLSv1_get_timeout(dtls->ssl, &dtls_timeout)) { + dtls->timeout_timer = -1; return 0; } - ast_mutex_lock(&rtp->dtls_timer_lock); - if (rtp->dtlstimerid == -1) - { - ast_mutex_unlock(&rtp->dtls_timer_lock); + return dtls_timeout.tv_sec * 1000 + dtls_timeout.tv_usec / 1000; +} + +static int dtls_srtp_handle_rtp_timeout(const void *data) +{ + struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data; + int reschedule; + + reschedule = dtls_srtp_handle_timeout(instance, 0); + + if (!reschedule) { ao2_ref(instance, -1); - return 0; } - rtp->dtlstimerid = -1; - ast_mutex_unlock(&rtp->dtls_timer_lock); + return reschedule; +} + +static int dtls_srtp_handle_rtcp_timeout(const void *data) +{ + struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data; + int reschedule; + + reschedule = dtls_srtp_handle_timeout(instance, 1); - if (rtp->dtls.ssl && !SSL_is_init_finished(rtp->dtls.ssl)) { - DTLSv1_handle_timeout(rtp->dtls.ssl); + if (!reschedule) { + ao2_ref(instance, -1); } - dtls_srtp_check_pending(instance, rtp, 0); - if (rtp->rtcp && rtp->rtcp->dtls.ssl && !SSL_is_init_finished(rtp->rtcp->dtls.ssl)) { - DTLSv1_handle_timeout(rtp->rtcp->dtls.ssl); + return reschedule; +} + +static void dtls_srtp_start_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp) +{ + struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls; + struct timeval dtls_timeout; + + if (DTLSv1_get_timeout(dtls->ssl, &dtls_timeout)) { + int timeout = dtls_timeout.tv_sec * 1000 + dtls_timeout.tv_usec / 1000; + + ast_assert(dtls->timeout_timer == -1); + + ao2_ref(instance, +1); + if ((dtls->timeout_timer = ast_sched_add(rtp->sched, timeout, + !rtcp ? dtls_srtp_handle_rtp_timeout : dtls_srtp_handle_rtcp_timeout, instance)) < 0) { + ao2_ref(instance, -1); + ast_log(LOG_WARNING, "Scheduling '%s' DTLS retransmission for RTP instance [%p] failed.\n", + !rtcp ? "RTP" : "RTCP", instance); + } } - dtls_srtp_check_pending(instance, rtp, 1); +} - ao2_ref(instance, -1); +static void dtls_srtp_stop_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp) +{ + struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls; - return 0; + AST_SCHED_DEL_UNREF(rtp->sched, dtls->timeout_timer, ao2_ref(instance, -1)); } static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp) { struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls; size_t pending; - struct timeval dtls_timeout; /* timeout on DTLS */ if (!dtls->ssl || !dtls->write_bio) { return; @@ -1821,24 +1873,6 @@ static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct as } out = BIO_read(dtls->write_bio, outgoing, sizeof(outgoing)); - - /* Stop existing DTLS timer if running */ - ast_mutex_lock(&rtp->dtls_timer_lock); - if (rtp->dtlstimerid > -1) { - AST_SCHED_DEL_UNREF(rtp->sched, rtp->dtlstimerid, ao2_ref(instance, -1)); - rtp->dtlstimerid = -1; - } - - if (DTLSv1_get_timeout(dtls->ssl, &dtls_timeout)) { - int timeout = dtls_timeout.tv_sec * 1000 + dtls_timeout.tv_usec / 1000; - ao2_ref(instance, +1); - if ((rtp->dtlstimerid = ast_sched_add(rtp->sched, timeout, dtls_srtp_handle_timeout, instance)) < 0) { - ao2_ref(instance, -1); - ast_log(LOG_WARNING, "scheduling DTLS retransmission for RTP instance [%p] failed.\n", instance); - } - } - ast_mutex_unlock(&rtp->dtls_timer_lock); - __rtp_sendto(instance, outgoing, out, 0, &remote_address, rtcp, &ice, 0); } } @@ -2014,8 +2048,6 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s } #ifdef HAVE_OPENSSL_SRTP - dtls_srtp_check_pending(instance, rtp, rtcp); - /* If this is an SSL packet pass it to OpenSSL for processing. RFC section for first byte value: * https://tools.ietf.org/html/rfc5764#section-5.1.2 */ if ((*in >= 20) && (*in <= 63)) { @@ -2029,6 +2061,15 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s return -1; } + /* This mutex is locked so that this thread blocks until the dtls_perform_handshake function + * completes. + */ + ast_mutex_lock(&dtls->lock); + ast_mutex_unlock(&dtls->lock); + + /* Before we feed data into OpenSSL ensure that the timeout timer is either stopped or completed */ + dtls_srtp_stop_timeout_timer(instance, rtp, rtcp); + /* If we don't yet know if we are active or passive and we receive a packet... we are obviously passive */ if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) { dtls->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE; @@ -2057,6 +2098,9 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s /* Use the keying material to set up key/salt information */ res = dtls_srtp_setup(rtp, srtp, instance); } + } else { + /* Since we've sent additional traffic start the timeout timer for retransmission */ + dtls_srtp_start_timeout_timer(instance, rtp, rtcp); } return res; @@ -2479,7 +2523,6 @@ static int ast_rtp_new(struct ast_rtp_instance *instance, #ifdef HAVE_OPENSSL_SRTP rtp->rekeyid = -1; - rtp->dtlstimerid = -1; #endif rtp->f.subclass.format = ao2_bump(ast_format_none); @@ -2497,6 +2540,10 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance) struct timespec ts = { .tv_sec = wait.tv_sec, .tv_nsec = wait.tv_usec * 1000, }; #endif +#ifdef HAVE_OPENSSL_SRTP + ast_rtp_dtls_stop(instance); +#endif + /* Destroy the smoother that was smoothing out audio if present */ if (rtp->smoother) { ast_smoother_free(rtp->smoother); @@ -2515,11 +2562,6 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance) * RTP instance while it's active. */ close(rtp->rtcp->s); -#ifdef HAVE_OPENSSL_SRTP - if (rtp->rtcp->dtls.ssl) { - SSL_free(rtp->rtcp->dtls.ssl); - } -#endif ast_free(rtp->rtcp); } @@ -2571,18 +2613,6 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance) } #endif -#ifdef HAVE_OPENSSL_SRTP - /* Destroy the SSL context if present */ - if (rtp->ssl_ctx) { - SSL_CTX_free(rtp->ssl_ctx); - } - - /* Destroy the SSL session if present */ - if (rtp->dtls.ssl) { - SSL_free(rtp->dtls.ssl); - } -#endif - ao2_cleanup(rtp->lasttxformat); ao2_cleanup(rtp->lastrxformat); ao2_cleanup(rtp->f.subclass.format); @@ -4909,9 +4939,11 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance) #ifdef HAVE_OPENSSL_SRTP AST_SCHED_DEL_UNREF(rtp->sched, rtp->rekeyid, ao2_ref(instance, -1)); - ast_mutex_lock(&rtp->dtls_timer_lock); - AST_SCHED_DEL_UNREF(rtp->sched, rtp->dtlstimerid, ao2_ref(instance, -1)); - ast_mutex_unlock(&rtp->dtls_timer_lock); + + dtls_srtp_stop_timeout_timer(instance, rtp, 0); + if (rtp->rtcp) { + dtls_srtp_stop_timeout_timer(instance, rtp, 1); + } #endif if (rtp->rtcp && rtp->rtcp->schedid > 0) { @@ -4993,10 +5025,31 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level) } #ifdef HAVE_OPENSSL_SRTP +static void dtls_perform_setup(struct dtls_details *dtls) +{ + if (!dtls->ssl || !SSL_is_init_finished(dtls->ssl)) { + return; + } + + SSL_clear(dtls->ssl); + if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) { + SSL_set_accept_state(dtls->ssl); + } else { + SSL_set_connect_state(dtls->ssl); + } + dtls->connection = AST_RTP_DTLS_CONNECTION_NEW; +} + static int ast_rtp_activate(struct ast_rtp_instance *instance) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + dtls_perform_setup(&rtp->dtls); + + if (rtp->rtcp) { + dtls_perform_setup(&rtp->rtcp->dtls); + } + /* If ICE negotiation is enabled the DTLS Handshake will be performed upon completion of it */ #ifdef HAVE_PJPROJECT if (rtp->ice) { |