📄 ice_session.c
字号:
pj_ice_sess_cand *host = &ice->lcand[j];
if (host->type != PJ_ICE_CAND_TYPE_HOST)
continue;
if (sockaddr_cmp(&srflx->base_addr, &host->addr) == 0) {
/* Replace this SRFLX with its BASE */
clist->checks[i].lcand = host;
break;
}
}
if (j==ice->lcand_cnt) {
/* Host candidate not found this this srflx! */
LOG4((ice->obj_name,
"Base candidate %s:%d not found for srflx candidate %d",
pj_inet_ntoa(srflx->base_addr.ipv4.sin_addr),
pj_ntohs(srflx->base_addr.ipv4.sin_port),
GET_LCAND_ID(clist->checks[i].lcand)));
return PJNATH_EICENOHOSTCAND;
}
}
}
/* Next remove a pair if its local and remote candidates are identical
* to the local and remote candidates of a pair higher up on the priority
* list
*/
/*
* Not in ICE!
* Remove host candidates if their base are the the same!
*/
for (i=0; i<clist->count; ++i) {
pj_ice_sess_cand *licand = clist->checks[i].lcand;
pj_ice_sess_cand *ricand = clist->checks[i].rcand;
unsigned j;
for (j=i+1; j<clist->count;) {
pj_ice_sess_cand *ljcand = clist->checks[j].lcand;
pj_ice_sess_cand *rjcand = clist->checks[j].rcand;
const char *reason = NULL;
if ((licand == ljcand) && (ricand == rjcand)) {
reason = "duplicate found";
} else if ((rjcand == ricand) &&
(sockaddr_cmp(&ljcand->base_addr,
&licand->base_addr)==0))
{
reason = "equal base";
}
if (reason != NULL) {
/* Found duplicate, remove it */
char buf[CHECK_NAME_LEN];
LOG5((ice->obj_name, "Check %s pruned (%s)",
dump_check(buf, sizeof(buf), &ice->clist,
&clist->checks[j]),
reason));
pj_array_erase(clist->checks, sizeof(clist->checks[0]),
clist->count, j);
--clist->count;
} else {
++j;
}
}
}
return PJ_SUCCESS;
}
/* This function is called when ICE processing completes */
static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
{
if (!ice->is_complete) {
char errmsg[PJ_ERR_MSG_SIZE];
ice->is_complete = PJ_TRUE;
ice->ice_status = status;
/* Log message */
LOG4((ice->obj_name, "ICE process complete, status=%s",
pj_strerror(status, errmsg, sizeof(errmsg)).ptr));
dump_checklist("Valid list", ice, &ice->valid_list);
/* Call callback */
if (ice->cb.on_ice_complete) {
(*ice->cb.on_ice_complete)(ice, status);
}
}
}
/* This function is called when one check completes */
static pj_bool_t on_check_complete(pj_ice_sess *ice,
pj_ice_sess_check *check)
{
unsigned i;
pj_assert(check->state >= PJ_ICE_SESS_CHECK_STATE_SUCCEEDED);
/* 7.1.2.2.2. Updating Pair States
*
* The agent sets the state of the pair that generated the check to
* Succeeded. The success of this check might also cause the state of
* other checks to change as well. The agent MUST perform the following
* two steps:
*
* 1. The agent changes the states for all other Frozen pairs for the
* same media stream and same foundation to Waiting. Typically
* these other pairs will have different component IDs but not
* always.
*/
if (check->err_code==PJ_SUCCESS) {
for (i=0; i<ice->clist.count; ++i) {
pj_ice_sess_check *c = &ice->clist.checks[i];
if (pj_strcmp(&c->lcand->foundation, &check->lcand->foundation)==0
&& c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN)
{
check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, 0);
}
}
}
/* 8.2. Updating States
*
* For both controlling and controlled agents, the state of ICE
* processing depends on the presence of nominated candidate pairs in
* the valid list and on the state of the check list:
*
* o If there are no nominated pairs in the valid list for a media
* stream and the state of the check list is Running, ICE processing
* continues.
*
* o If there is at least one nominated pair in the valid list:
*
* - The agent MUST remove all Waiting and Frozen pairs in the check
* list for the same component as the nominated pairs for that
* media stream
*
* - If an In-Progress pair in the check list is for the same
* component as a nominated pair, the agent SHOULD cease
* retransmissions for its check if its pair priority is lower
* than the lowest priority nominated pair for that component
*/
if (check->err_code==PJ_SUCCESS && check->nominated) {
pj_ice_sess_comp *comp;
char buf[CHECK_NAME_LEN];
LOG5((ice->obj_name, "Check %d is successful and nominated",
GET_CHECK_ID(&ice->clist, check)));
comp = find_comp(ice, check->lcand->comp_id);
for (i=0; i<ice->clist.count; ++i) {
pj_ice_sess_check *c = &ice->clist.checks[i];
if (c->lcand->comp_id == check->lcand->comp_id) {
if (c->state < PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) {
/* Just fail Frozen/Waiting check */
LOG5((ice->obj_name,
"Check %s to be failed because state is %s",
dump_check(buf, sizeof(buf), &ice->clist, c),
check_state_name[c->state]));
check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_FAILED,
PJ_ECANCELLED);
} else if (c->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS
&& (PJ_ICE_CANCEL_ALL ||
CMP_CHECK_PRIO(c, check) < 0)) {
/* State is IN_PROGRESS, cancel transaction */
if (c->tdata) {
LOG5((ice->obj_name,
"Cancelling check %s (In Progress)",
dump_check(buf, sizeof(buf), &ice->clist, c)));
pj_stun_session_cancel_req(comp->stun_sess,
c->tdata, PJ_FALSE, 0);
c->tdata = NULL;
check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_FAILED,
PJ_ECANCELLED);
}
}
}
}
/* Update the nominated check for the component */
if (comp->valid_check == NULL) {
comp->valid_check = check;
} else {
if (CMP_CHECK_PRIO(comp->valid_check, check) < 0)
comp->valid_check = check;
}
}
/* Still in 8.2. Updating States
*
* o Once there is at least one nominated pair in the valid list for
* every component of at least one media stream and the state of the
* check list is Running:
*
* * The agent MUST change the state of processing for its check
* list for that media stream to Completed.
*
* * The agent MUST continue to respond to any checks it may still
* receive for that media stream, and MUST perform triggered
* checks if required by the processing of Section 7.2.
*
* * The agent MAY begin transmitting media for this media stream as
* described in Section 11.1
*/
/* See if all components have nominated pair. If they do, then mark
* ICE processing as success, otherwise wait.
*/
for (i=0; i<ice->comp_cnt; ++i) {
if (ice->comp[i].valid_check == NULL)
break;
}
if (i == ice->comp_cnt) {
/* All components have nominated pair */
on_ice_complete(ice, PJ_SUCCESS);
return PJ_TRUE;
}
/* Note: this is the stuffs that we don't do in 7.1.2.2.2, since our
* ICE session only supports one media stream for now:
*
* 7.1.2.2.2. Updating Pair States
*
* 2. If there is a pair in the valid list for every component of this
* media stream (where this is the actual number of components being
* used, in cases where the number of components signaled in the SDP
* differs from offerer to answerer), the success of this check may
* unfreeze checks for other media streams.
*/
/* 7.1.2.3. Check List and Timer State Updates
* Regardless of whether the check was successful or failed, the
* completion of the transaction may require updating of check list and
* timer states.
*
* If all of the pairs in the check list are now either in the Failed or
* Succeeded state, and there is not a pair in the valid list for each
* component of the media stream, the state of the check list is set to
* Failed.
*/
/*
* See if all checks in the checklist have completed. If we do,
* then mark ICE processing as failed.
*/
for (i=0; i<ice->clist.count; ++i) {
pj_ice_sess_check *c = &ice->clist.checks[i];
if (c->state < PJ_ICE_SESS_CHECK_STATE_SUCCEEDED) {
break;
}
}
if (i == ice->clist.count) {
/* All checks have completed, but we don't have nominated pair.
* If agent's role is controlled, check if all components have
* valid pair. If it does, this means the controlled agent has
* finished the check list early and it's waiting for controlling
* agent to send a check with USE-CANDIDATE flag set.
*/
if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED) {
unsigned comp_id;
for (comp_id=1; comp_id <= ice->comp_cnt; ++comp_id) {
unsigned j;
for (j=0; j<ice->valid_list.count; ++j) {
pj_ice_sess_check *vc = &ice->valid_list.checks[j];
if (vc->lcand->comp_id == comp_id)
break;
}
if (j == ice->valid_list.count)
break;
}
if (comp_id <= ice->comp_cnt) {
/* This component ID doesn't have valid pair.
* Mark ICE as failed.
*/
on_ice_complete(ice, PJNATH_EICEFAILED);
return PJ_TRUE;
} else {
/* All components have a valid pair.
* We should wait until we receive nominated checks.
*/
return PJ_FALSE;
}
}
on_ice_complete(ice, PJNATH_EICEFAILED);
return PJ_TRUE;
}
/* We still have checks to perform */
return PJ_FALSE;
}
/* Create checklist by pairing local candidates with remote candidates */
PJ_DEF(pj_status_t)
pj_ice_sess_create_check_list(pj_ice_sess *ice,
const pj_str_t *rem_ufrag,
const pj_str_t *rem_passwd,
unsigned rcand_cnt,
const pj_ice_sess_cand rcand[])
{
pj_ice_sess_checklist *clist;
char buf[128];
pj_str_t username;
timer_data *td;
unsigned i, j;
pj_status_t status;
PJ_ASSERT_RETURN(ice && rem_ufrag && rem_passwd && rcand_cnt && rcand,
PJ_EINVAL);
PJ_ASSERT_RETURN(rcand_cnt + ice->rcand_cnt <= PJ_ICE_MAX_CAND,
PJ_ETOOMANY);
pj_mutex_lock(ice->mutex);
/* Save credentials */
username.ptr = buf;
pj_strcpy(&username, rem_ufrag);
pj_strcat2(&username, ":");
pj_strcat(&username, &ice->rx_ufrag);
pj_strdup(ice->pool, &ice->tx_uname, &username);
pj_strdup(ice->pool, &ice->tx_ufrag, rem_ufrag);
pj_strdup(ice->pool, &ice->tx_pass, rem_passwd);
pj_strcpy(&username, &ice->rx_ufrag);
pj_strcat2(&username, ":");
pj_strcat(&username, rem_ufrag);
pj_strdup(ice->pool, &ice->rx_uname, &username);
/* Save remote candidates */
ice->rcand_cnt = 0;
for (i=0; i<rcand_cnt; ++i) {
pj_ice_sess_cand *cn = &ice->rcand[ice->rcand_cnt];
/* Ignore candidate which has no matching component ID */
if (rcand[i].comp_id==0 || rcand[i].comp_id > ice->comp_cnt) {
continue;
}
pj_memcpy(cn, &rcand[i], sizeof(pj_ice_sess_cand));
pj_strdup(ice->pool, &cn->foundation, &rcand[i].foundation);
ice->rcand_cnt++;
}
/* Generate checklist */
clist = &ice->clist;
for (i=0; i<ice->lcand_cnt; ++i) {
for (j=0; j<ice->rcand_cnt; ++j) {
pj_ice_sess_cand *lcand = &ice->lcand[i];
pj_ice_sess_cand *rcand = &ice->rcand[j];
pj_ice_sess_check *chk = &clist->checks[clist->count];
if (clist->count >= PJ_ICE_MAX_CHECKS) {
pj_mutex_unlock(ice->mutex);
return PJ_ETOOMANY;
}
/* A local candidate is paired with a remote candidate if
* and only if the two candidates have the same component ID
* and have the same IP address version.
*/
if ((lcand->comp_id != rcand->comp_id) ||
(lcand->addr.addr.sa_family != rcand->addr.addr.sa_family))
{
continue;
}
chk->lcand = lcand;
chk->rcand = rcand;
chk->state = PJ_ICE_SESS_CHECK_STATE_FROZEN;
chk->prio = CALC_CHECK_PRIO(ice, lcand, rcand);
clist->count++;
}
}
/* Sort checklist based on priority */
sort_checklist(clist);
/* Prune the checklist */
status = prune_checklist(ice, clist);
if (status != PJ_SUCCESS) {
pj_mutex_unlock(ice->mutex);
return status;
}
/* Init timer entry in the checklist. Initially the timer ID is FALSE
* because timer is not running.
*/
clist->timer.id = PJ_FALSE;
td = PJ_POOL_ZALLOC_T(ice->pool, timer_data);
td->ice = ice;
td->clist = clist;
clist->timer.user_data = (void*)td;
clist->timer.cb = &periodic_timer;
/* Log checklist */
dump_checklist("Checklist created:", ice, clist);
pj_mutex_unlock(ice->mutex);
return PJ_SUCCESS;
}
/* This is the data that will be attached as user data to outgoing
* STUN requests, and it will be given back when we receive completion
* status of the request.
*/
struct req_data
{
pj_ice_sess *ice;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -