void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call) { IceCheckList *audio_check_list; IceCheckList *video_check_list; IceSessionState session_state; if (call->ice_session == NULL) return; audio_check_list = ice_session_check_list(call->ice_session, 0); video_check_list = ice_session_check_list(call->ice_session, 1); if (audio_check_list == NULL) return; session_state = ice_session_state(call->ice_session); if ((session_state == IS_Completed) || ((session_state == IS_Failed) && (ice_session_has_completed_check_list(call->ice_session) == TRUE))) { if (ice_check_list_state(audio_check_list) == ICL_Completed) { switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) { case ICT_HostCandidate: call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection; break; case ICT_ServerReflexiveCandidate: case ICT_PeerReflexiveCandidate: call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection; break; case ICT_RelayedCandidate: call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection; break; } } else { call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed; } if (call->params.has_video && (video_check_list != NULL)) { if (ice_check_list_state(video_check_list) == ICL_Completed) { switch (ice_check_list_selected_valid_candidate_type(video_check_list)) { case ICT_HostCandidate: call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateHostConnection; break; case ICT_ServerReflexiveCandidate: case ICT_PeerReflexiveCandidate: call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection; break; case ICT_RelayedCandidate: call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection; break; } } else { call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed; } } } else if (session_state == IS_Running) { call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress; if (call->params.has_video && (video_check_list != NULL)) { call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress; } } else { call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed; if (call->params.has_video && (video_check_list != NULL)) { call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed; } } }
int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call) { char local_addr[64]; sockaddr_x ss; socklen_t ss_len; IceCheckList *audio_check_list; IceCheckList *video_check_list; const char *server = linphone_core_get_stun_server(lc); if ((server == NULL) || (call->ice_session == NULL)) return -1; audio_check_list = ice_session_check_list(call->ice_session, 0); video_check_list = ice_session_check_list(call->ice_session, 1); if (audio_check_list == NULL) return -1; if (lc->sip_conf.ipv6_enabled){ ms_warning("stun support is not implemented for ipv6"); return -1; } if (parse_hostname_to_addr(server, &ss, &ss_len) < 0) { ms_error("Fail to parser stun server address: %s", server); return -1; } if (lc->vtable.display_status != NULL) lc->vtable.display_status(lc, _("ICE local candidates gathering in progress...")); /* Gather local host candidates. */ if (linphone_core_get_local_ip_for(AF_INET, server, local_addr) < 0) { ms_error("Fail to get local ip"); return -1; } if ((ice_check_list_state(audio_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_check_list) == FALSE)) { ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port, 1, NULL); ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port + 1, 2, NULL); call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress; } if (call->params.has_video && (video_check_list != NULL) && (ice_check_list_state(video_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(video_check_list) == FALSE)) { ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port, 1, NULL); ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port + 1, 2, NULL); call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress; } ms_message("ICE: gathering candidate from [%s]",server); /* Gather local srflx candidates. */ /* ice_session_gather_candidates(call->ice_session, ss, ss_len); */ return 0; }
int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call) { char local_addr[64]; const struct addrinfo *ai; IceCheckList *audio_check_list; IceCheckList *video_check_list; const char *server = linphone_core_get_stun_server(lc); if ((server == NULL) || (call->ice_session == NULL)) return -1; audio_check_list = ice_session_check_list(call->ice_session, 0); video_check_list = ice_session_check_list(call->ice_session, 1); if (audio_check_list == NULL) return -1; if (call->af==AF_INET6){ ms_warning("Ice gathering is not implemented for ipv6"); return -1; } ai=linphone_core_get_stun_server_addrinfo(lc); if (ai==NULL){ ms_warning("Fail to resolve STUN server for ICE gathering."); return -1; } if (lc->vtable.display_status != NULL) lc->vtable.display_status(lc, _("ICE local candidates gathering in progress...")); /* Gather local host candidates. */ if (linphone_core_get_local_ip_for(AF_INET, NULL, local_addr) < 0) { ms_error("Fail to get local ip"); return -1; } if ((ice_check_list_state(audio_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_check_list) == FALSE)) { ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port, 1, NULL); ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port + 1, 2, NULL); call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress; } if (call->params.has_video && (video_check_list != NULL) && (ice_check_list_state(video_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(video_check_list) == FALSE)) { ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port, 1, NULL); ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port + 1, 2, NULL); call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress; } ms_message("ICE: gathering candidate from [%s]",server); /* Gather local srflx candidates. */ ice_session_gather_candidates(call->ice_session, ai->ai_addr, ai->ai_addrlen); return 0; }
void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) { bool_t ice_restarted = FALSE; if ((md->ice_pwd[0] != '\0') && (md->ice_ufrag[0] != '\0')) { int i, j; /* Check for ICE restart and set remote credentials. */ if ((strcmp(md->addr, "0.0.0.0") == 0) || (strcmp(md->addr, "::0") == 0)) { ice_session_restart(call->ice_session); ice_restarted = TRUE; } else { for (i = 0; i < md->n_total_streams; i++) { const SalStreamDescription *stream = &md->streams[i]; IceCheckList *cl = ice_session_check_list(call->ice_session, i); if (cl && (strcmp(stream->rtp_addr, "0.0.0.0") == 0)) { ice_session_restart(call->ice_session); ice_restarted = TRUE; break; } } } if ((ice_session_remote_ufrag(call->ice_session) == NULL) && (ice_session_remote_pwd(call->ice_session) == NULL)) { ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd); } else if (ice_session_remote_credentials_changed(call->ice_session, md->ice_ufrag, md->ice_pwd)) { if (ice_restarted == FALSE) { ice_session_restart(call->ice_session); ice_restarted = TRUE; } ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd); } for (i = 0; i < md->n_total_streams; i++) { const SalStreamDescription *stream = &md->streams[i]; IceCheckList *cl = ice_session_check_list(call->ice_session, i); if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) { if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) { if (ice_restarted == FALSE) { ice_session_restart(call->ice_session); ice_restarted = TRUE; } ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd); break; } } } /* Create ICE check lists if needed and parse ICE attributes. */ for (i = 0; i < md->n_total_streams; i++) { const SalStreamDescription *stream = &md->streams[i]; IceCheckList *cl = ice_session_check_list(call->ice_session, i); if ((cl == NULL) && (i < md->n_active_streams)) { cl = ice_check_list_new(); ice_session_add_check_list(call->ice_session, cl); switch (stream->type) { case SalAudio: if (call->audiostream != NULL) call->audiostream->ms.ice_check_list = cl; break; case SalVideo: if (call->videostream != NULL) call->videostream->ms.ice_check_list = cl; break; default: break; } } if (stream->ice_mismatch == TRUE) { ice_check_list_set_state(cl, ICL_Failed); } else if (stream->rtp_port == 0) { ice_session_remove_check_list(call->ice_session, cl); #ifdef VIDEO_ENABLED if (stream->type==SalVideo && call->videostream){ video_stream_stop(call->videostream); call->videostream=NULL; } #endif } else { if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd); for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) { const SalIceCandidate *candidate = &stream->ice_candidates[j]; bool_t default_candidate = FALSE; const char *addr = NULL; int port = 0; if (candidate->addr[0] == '\0') break; if ((candidate->componentID == 0) || (candidate->componentID > 2)) continue; get_default_addr_and_port(candidate->componentID, md, stream, &addr, &port); if (addr && (candidate->port == port) && (strlen(candidate->addr) == strlen(addr)) && (strcmp(candidate->addr, addr) == 0)) default_candidate = TRUE; ice_add_remote_candidate(cl, candidate->type, candidate->addr, candidate->port, candidate->componentID, candidate->priority, candidate->foundation, default_candidate); } if (ice_restarted == FALSE) { bool_t losing_pairs_added = FALSE; for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) { const SalIceRemoteCandidate *candidate = &stream->ice_remote_candidates[j]; const char *addr = NULL; int port = 0; int componentID = j + 1; if (candidate->addr[0] == '\0') break; get_default_addr_and_port(componentID, md, stream, &addr, &port); if (j == 0) { /* If we receive a re-invite and we finished ICE processing on our side, use the candidates given by the remote. */ ice_check_list_unselect_valid_pairs(cl); } ice_add_losing_pair(cl, j + 1, candidate->addr, candidate->port, addr, port); losing_pairs_added = TRUE; } if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl); } } } for (i = ice_session_nb_check_lists(call->ice_session); i > md->n_active_streams; i--) { ice_session_remove_check_list(call->ice_session, ice_session_check_list(call->ice_session, i - 1)); } ice_session_check_mismatch(call->ice_session); } else { /* Response from remote does not contain mandatory ICE attributes, delete the session. */ linphone_call_delete_ice_session(call); return; } if (ice_session_nb_check_lists(call->ice_session) == 0) { linphone_call_delete_ice_session(call); } }
void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session) { const char *rtp_addr, *rtcp_addr; IceSessionState session_state = ice_session_state(session); int nb_candidates; int i, j; bool_t result; if (session_state == IS_Completed) { desc->ice_completed = TRUE; result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, 0), &rtp_addr, NULL, NULL, NULL); if (result == TRUE) { strncpy(desc->addr, rtp_addr, sizeof(desc->addr)); } else { ms_warning("If ICE has completed successfully, rtp_addr should be set!"); } } else { desc->ice_completed = FALSE; } strncpy(desc->ice_pwd, ice_session_local_pwd(session), sizeof(desc->ice_pwd)); strncpy(desc->ice_ufrag, ice_session_local_ufrag(session), sizeof(desc->ice_ufrag)); for (i = 0; i < desc->n_active_streams; i++) { SalStreamDescription *stream = &desc->streams[i]; IceCheckList *cl = ice_session_check_list(session, i); nb_candidates = 0; if (cl == NULL) continue; if (ice_check_list_state(cl) == ICL_Completed) { stream->ice_completed = TRUE; result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port); } else { stream->ice_completed = FALSE; result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port); } if (result == TRUE) { strncpy(stream->rtp_addr, rtp_addr, sizeof(stream->rtp_addr)); strncpy(stream->rtcp_addr, rtcp_addr, sizeof(stream->rtcp_addr)); } else { memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr)); memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr)); } if ((strlen(ice_check_list_local_pwd(cl)) != strlen(desc->ice_pwd)) || (strcmp(ice_check_list_local_pwd(cl), desc->ice_pwd))) strncpy(stream->ice_pwd, ice_check_list_local_pwd(cl), sizeof(stream->ice_pwd)); else memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd)); if ((strlen(ice_check_list_local_ufrag(cl)) != strlen(desc->ice_ufrag)) || (strcmp(ice_check_list_local_ufrag(cl), desc->ice_ufrag))) strncpy(stream->ice_ufrag, ice_check_list_local_ufrag(cl), sizeof(stream->ice_ufrag)); else memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd)); stream->ice_mismatch = ice_check_list_is_mismatch(cl); if ((ice_check_list_state(cl) == ICL_Running) || (ice_check_list_state(cl) == ICL_Completed)) { memset(stream->ice_candidates, 0, sizeof(stream->ice_candidates)); for (j = 0; j < MIN(ms_list_size(cl->local_candidates), SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES); j++) { SalIceCandidate *sal_candidate = &stream->ice_candidates[nb_candidates]; IceCandidate *ice_candidate = ms_list_nth_data(cl->local_candidates, j); const char *default_addr = NULL; int default_port = 0; if (ice_candidate->componentID == 1) { default_addr = stream->rtp_addr; default_port = stream->rtp_port; } else if (ice_candidate->componentID == 2) { default_addr = stream->rtcp_addr; default_port = stream->rtcp_port; } else continue; if (default_addr[0] == '\0') default_addr = desc->addr; /* Only include the candidates matching the default destination for each component of the stream if the state is Completed as specified in RFC5245 section 9.1.2.2. */ if ((ice_check_list_state(cl) == ICL_Completed) && !((ice_candidate->taddr.port == default_port) && (strlen(ice_candidate->taddr.ip) == strlen(default_addr)) && (strcmp(ice_candidate->taddr.ip, default_addr) == 0))) continue; strncpy(sal_candidate->foundation, ice_candidate->foundation, sizeof(sal_candidate->foundation)); sal_candidate->componentID = ice_candidate->componentID; sal_candidate->priority = ice_candidate->priority; strncpy(sal_candidate->type, ice_candidate_type(ice_candidate), sizeof(sal_candidate->type)); strncpy(sal_candidate->addr, ice_candidate->taddr.ip, sizeof(sal_candidate->addr)); sal_candidate->port = ice_candidate->taddr.port; if ((ice_candidate->base != NULL) && (ice_candidate->base != ice_candidate)) { strncpy(sal_candidate->raddr, ice_candidate->base->taddr.ip, sizeof(sal_candidate->raddr)); sal_candidate->rport = ice_candidate->base->taddr.port; } nb_candidates++; } } if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) { int rtp_port, rtcp_port; memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates)); if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port) == TRUE) { strncpy(stream->ice_remote_candidates[0].addr, rtp_addr, sizeof(stream->ice_remote_candidates[0].addr)); stream->ice_remote_candidates[0].port = rtp_port; strncpy(stream->ice_remote_candidates[1].addr, rtcp_addr, sizeof(stream->ice_remote_candidates[1].addr)); stream->ice_remote_candidates[1].port = rtcp_port; } else { ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state"); } } else { for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) { stream->ice_remote_candidates[j].addr[0] = '\0'; stream->ice_remote_candidates[j].port = 0; } } } }
static void ice_turn_call_base(bool_t video_enabled, bool_t forced_relay, bool_t caller_turn_enabled, bool_t callee_turn_enabled, bool_t rtcp_mux_enabled) { LinphoneCoreManager *marie; LinphoneCoreManager *pauline; LinphoneCall *lcall; LinphoneIceState expected_ice_state = LinphoneIceStateHostConnection; LinphoneMediaDirection expected_video_dir = LinphoneMediaDirectionInactive; bctbx_list_t *lcs = NULL; marie = linphone_core_manager_new("marie_rc"); lcs = bctbx_list_append(lcs, marie->lc); pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); lcs = bctbx_list_append(lcs, pauline->lc); configure_nat_policy(marie->lc, caller_turn_enabled); configure_nat_policy(pauline->lc, callee_turn_enabled); if (forced_relay == TRUE) { linphone_core_enable_forced_ice_relay(marie->lc, TRUE); linphone_core_enable_forced_ice_relay(pauline->lc, TRUE); linphone_core_enable_short_turn_refresh(marie->lc, TRUE); linphone_core_enable_short_turn_refresh(pauline->lc, TRUE); expected_ice_state = LinphoneIceStateRelayConnection; } if (rtcp_mux_enabled == TRUE) { lp_config_set_int(linphone_core_get_config(marie->lc), "rtp", "rtcp_mux", 1); lp_config_set_int(linphone_core_get_config(pauline->lc), "rtp", "rtcp_mux", 1); } if (video_enabled) { #ifdef VIDEO_ENABLED video_call_base_2(marie, pauline, FALSE, LinphoneMediaEncryptionNone, TRUE, TRUE); expected_video_dir = LinphoneMediaDirectionSendRecv; #endif } else { BC_ASSERT_TRUE(call(marie, pauline)); } /* Wait for the ICE reINVITE to complete */ BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2)); BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 2)); BC_ASSERT_TRUE(check_ice(pauline, marie, expected_ice_state)); check_nb_media_starts(pauline, marie, 1, 1); check_media_direction(marie, linphone_core_get_current_call(marie->lc), lcs, LinphoneMediaDirectionSendRecv, expected_video_dir); check_media_direction(pauline, linphone_core_get_current_call(pauline->lc), lcs, LinphoneMediaDirectionSendRecv, expected_video_dir); liblinphone_tester_check_rtcp(marie, pauline); lcall = linphone_core_get_current_call(marie->lc); BC_ASSERT_PTR_NOT_NULL(lcall->ice_session); if (lcall->ice_session != NULL) { IceCheckList *cl = ice_session_check_list(lcall->ice_session, 0); BC_ASSERT_PTR_NOT_NULL(cl); if (cl != NULL) { check_turn_context_statistics(cl->rtp_turn_context, forced_relay); if (!rtcp_mux_enabled) check_turn_context_statistics(cl->rtcp_turn_context, forced_relay); } } end_call(marie, pauline); linphone_core_manager_destroy(pauline); linphone_core_manager_destroy(marie); bctbx_list_free(lcs); }