From b957318fa0b73d63c7c90d25067e7d101e2b02a3 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Thu, 14 Sep 2017 10:05:45 +0200 Subject: [PATCH] implement defered ICE response to reINVITE, as defined by RFC. This produces when the Controlled side hasn't finished yet with its check list processing, while the controlling side has finished and has sent a reINVITE with remote-candidates. In this case of the 200Ok of the reINVITE must be delayed to the point where the controlled side finishes its check list processing. --- coreapi/callbacks.c | 7 +++++++ coreapi/linphonecall.c | 5 ++++- coreapi/misc.c | 33 +++++++++++++++++++++++++++++++++ coreapi/private.h | 2 ++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 83aad97d3..a1add1c39 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -688,6 +688,13 @@ static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){ static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call){ linphone_core_notify_display_status(lc,_("Call is updated by remote.")); linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote"); + + if (call->ice_session && check_ice_reinvite_needs_defered_response(call)){ + call->defer_update = TRUE; + ms_message("LinphoneCall [%p]: Ice reinvite received, but one or more check list are not completed. Response will be sent later, once ICE has completed.", call); + call->incoming_ice_reinvite_pending = TRUE; + } + if (call->defer_update == FALSE){ if (call->state == LinphoneCallUpdatedByRemote){ linphone_call_accept_update(call, NULL); diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index d96735a64..19075ff10 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -4660,7 +4660,7 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ switch (ice_session_state(call->ice_session)) { case IS_Completed: case IS_Failed: - /* At least one ICE session has succeeded, so perform a call update. */ + /* At least one ICE check list has succeeded, so perform a call update. */ if (ice_session_has_completed_check_list(call->ice_session) == TRUE) { const LinphoneCallParams *current_param = linphone_call_get_current_params(call); if (ice_session_role(call->ice_session) == IR_Controlling && current_param->update_call_when_ice_completed ) { @@ -4668,6 +4668,9 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ params->internal_call_update = TRUE; linphone_call_update(call, params); linphone_call_params_unref(params); + }else if (ice_session_role(call->ice_session) == IR_Controlled && call->incoming_ice_reinvite_pending){ + linphone_call_accept_update(call, NULL); + call->incoming_ice_reinvite_pending = FALSE; } start_dtls_on_all_streams(call); } diff --git a/coreapi/misc.c b/coreapi/misc.c index fe10698aa..167e4044b 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -813,6 +813,7 @@ void linphone_call_stop_ice_for_inactive_streams(LinphoneCall *call, SalMediaDes linphone_call_update_ice_state_in_call_stats(call); } + void _update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session, bool_t use_nortpproxy) { IceCandidate *rtp_candidate = NULL; IceCandidate *rtcp_candidate = NULL; @@ -1684,6 +1685,38 @@ static bool_t _check_for_ice_restart_and_set_remote_credentials(IceSession *ice_ return ice_restarted; } +/*the purpose of this function is to detect a situation where a check list is still running while a reINVITE +with remote-candidates is received*/ +bool_t check_ice_reinvite_needs_defered_response(LinphoneCall *call){ + SalMediaDescription *md = sal_call_get_remote_media_description(call->op); + int i,j; + IceCheckList *cl; + + if (ice_session_state(call->ice_session) != IS_Running ) return FALSE; + + for (i = 0; i < md->nb_streams; i++) { + SalStreamDescription *stream = &md->streams[i]; + cl = ice_session_check_list(call->ice_session, i); + + if (cl==NULL) continue; + if (stream->ice_mismatch == TRUE) { + return FALSE; + } + if (stream->rtp_port == 0) { + continue; + } + + if (ice_check_list_state(cl) != ICL_Running) continue; + + for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) { + const SalIceRemoteCandidate *remote_candidate = &stream->ice_remote_candidates[j]; + if (remote_candidate->addr[0] != '\0') return TRUE; + + } + } + return FALSE; +} + static void _create_ice_check_lists_and_parse_ice_attributes(LinphoneCall *call, const SalMediaDescription *md, bool_t ice_restarted) { const SalStreamDescription *stream; IceCheckList *cl = NULL; diff --git a/coreapi/private.h b/coreapi/private.h index b0940a30d..306bd9810 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -405,6 +405,7 @@ struct _LinphoneCall{ bool_t reinvite_on_cancel_response_requested; bool_t non_op_error; /*set when the LinphoneErrorInfo was set at higher level than sal*/ + bool_t incoming_ice_reinvite_pending; bctbx_list_t *callbacks; /* A list of LinphoneCallCbs object */ LinphoneCallCbs *current_cbs; /* The current LinphoneCallCbs object used to call a callback */ @@ -1996,6 +1997,7 @@ char *linphone_presence_model_to_xml(LinphonePresenceModel *model) ; #define LINPHONE_SQLITE3_VFS "sqlite3bctbx_vfs" void linphone_call_check_ice_session(LinphoneCall *call, IceRole role, bool_t is_reinvite); +bool_t check_ice_reinvite_needs_defered_response(LinphoneCall *call); bool_t linphone_call_state_is_early(LinphoneCallState state);