⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ice_session.c

📁 一个开源的sip源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
		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 + -