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

📄 ice_session.c

📁 一个开源的sip源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
    pj_ice_sess_checklist   *clist;
    unsigned		     ckid;
};


/* Perform check on the specified candidate pair */
static pj_status_t perform_check(pj_ice_sess *ice, 
				 pj_ice_sess_checklist *clist,
				 unsigned check_id)
{
    pj_ice_sess_comp *comp;
    struct req_data *rd;
    pj_ice_sess_check *check;
    const pj_ice_sess_cand *lcand;
    const pj_ice_sess_cand *rcand;
    pj_uint32_t prio;
    char buffer[128];
    pj_status_t status;

    check = &clist->checks[check_id];
    lcand = check->lcand;
    rcand = check->rcand;
    comp = find_comp(ice, lcand->comp_id);

    LOG5((ice->obj_name, 
	 "Sending connectivity check for check %s", 
	 dump_check(buffer, sizeof(buffer), clist, check)));

    /* Create request */
    status = pj_stun_session_create_req(comp->stun_sess, 
					PJ_STUN_BINDING_REQUEST, 
					NULL, &check->tdata);
    if (status != PJ_SUCCESS) {
	pjnath_perror(ice->obj_name, "Error creating STUN request", status);
	return status;
    }

    /* Attach data to be retrieved later when STUN request transaction
     * completes and on_stun_request_complete() callback is called.
     */
    rd = PJ_POOL_ZALLOC_T(check->tdata->pool, struct req_data);
    rd->ice = ice;
    rd->clist = clist;
    rd->ckid = check_id;
    check->tdata->user_data = (void*) rd;

    /* Add PRIORITY */
    prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535, 
			  lcand->comp_id);
    pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg, 
			      PJ_STUN_ATTR_PRIORITY, prio);

    /* Add USE-CANDIDATE and set this check to nominated.
     * Also add ICE-CONTROLLING or ICE-CONTROLLED
     */
    if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) {
	pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg, 
				   PJ_STUN_ATTR_USE_CANDIDATE);
	check->nominated = PJ_TRUE;

	pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, 
				    PJ_STUN_ATTR_ICE_CONTROLLING,
				    &ice->tie_breaker);

    } else {
	pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, 
				    PJ_STUN_ATTR_ICE_CONTROLLED,
				    &ice->tie_breaker);
    }


    /* Note that USERNAME and MESSAGE-INTEGRITY will be added by the 
     * STUN session.
     */

    /* Initiate STUN transaction to send the request */
    status = pj_stun_session_send_msg(comp->stun_sess, PJ_FALSE, 
				      &rcand->addr, 
				      sizeof(pj_sockaddr_in), check->tdata);
    if (status != PJ_SUCCESS) {
	check->tdata = NULL;
	pjnath_perror(ice->obj_name, "Error sending STUN request", status);
	return status;
    }

    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, 
	            PJ_SUCCESS);
    return PJ_SUCCESS;
}


/* Start periodic check for the specified checklist.
 * This callback is called by timer on every Ta (20msec by default)
 */
static pj_status_t start_periodic_check(pj_timer_heap_t *th, 
					pj_timer_entry *te)
{
    timer_data *td;
    pj_ice_sess *ice;
    pj_ice_sess_checklist *clist;
    unsigned i, start_count=0;
    pj_status_t status;

    td = (struct timer_data*) te->user_data;
    ice = td->ice;
    clist = td->clist;

    pj_mutex_lock(ice->mutex);

    /* Set timer ID to FALSE first */
    te->id = PJ_FALSE;

    /* Set checklist state to Running */
    clist_set_state(ice, clist, PJ_ICE_SESS_CHECKLIST_ST_RUNNING);

    LOG5((ice->obj_name, "Starting checklist periodic check"));

    /* Send STUN Binding request for check with highest priority on
     * Waiting state.
     */
    for (i=0; i<clist->count; ++i) {
	pj_ice_sess_check *check = &clist->checks[i];

	if (check->state == PJ_ICE_SESS_CHECK_STATE_WAITING) {
	    status = perform_check(ice, clist, i);
	    if (status != PJ_SUCCESS) {
		pj_mutex_unlock(ice->mutex);
		return status;
	    }

	    ++start_count;
	    break;
	}
    }

    /* If we don't have anything in Waiting state, perform check to
     * highest priority pair that is in Frozen state.
     */
    if (start_count==0) {
	for (i=0; i<clist->count; ++i) {
	    pj_ice_sess_check *check = &clist->checks[i];

	    if (check->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) {
		status = perform_check(ice, clist, i);
		if (status != PJ_SUCCESS) {
		    pj_mutex_unlock(ice->mutex);
		    return status;
		}

		++start_count;
		break;
	    }
	}
    }

    /* Cannot start check because there's no suitable candidate pair.
     */
    if (start_count!=0) {
	/* Schedule for next timer */
	pj_time_val timeout = {0, PJ_ICE_TA_VAL};

	te->id = PJ_TRUE;
	pj_time_val_normalize(&timeout);
	pj_timer_heap_schedule(th, te, &timeout);
    }

    pj_mutex_unlock(ice->mutex);
    return PJ_SUCCESS;
}


/* Timer callback to perform periodic check */
static void periodic_timer(pj_timer_heap_t *th, 
			   pj_timer_entry *te)
{
    start_periodic_check(th, te);
}


/* Utility: find string in string array */
const pj_str_t *find_str(const pj_str_t *strlist[], unsigned count,
			 const pj_str_t *str)
{
    unsigned i;
    for (i=0; i<count; ++i) {
	if (pj_strcmp(strlist[i], str)==0)
	    return strlist[i];
    }
    return NULL;
}


/*
 * Start ICE periodic check. This function will return immediately, and
 * application will be notified about the connectivity check status in
 * #pj_ice_sess_cb callback.
 */
PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice)
{
    pj_ice_sess_checklist *clist;
    const pj_ice_sess_cand *cand0;
    const pj_str_t *flist[PJ_ICE_MAX_CAND];
    pj_ice_rx_check *rcheck;
    unsigned i, flist_cnt = 0;

    PJ_ASSERT_RETURN(ice, PJ_EINVAL);

    /* Checklist must have been created */
    PJ_ASSERT_RETURN(ice->clist.count > 0, PJ_EINVALIDOP);

    LOG4((ice->obj_name, "Starting ICE check.."));

    /* The agent examines the check list for the first media stream (a
     * media stream is the first media stream when it is described by
     * the first m-line in the SDP offer and answer).  For that media
     * stream, it:
     * 
     * -  Groups together all of the pairs with the same foundation,
     * 
     * -  For each group, sets the state of the pair with the lowest
     *    component ID to Waiting.  If there is more than one such pair,
     *    the one with the highest priority is used.
     */

    clist = &ice->clist;

    /* Pickup the first pair for component 1. */
    for (i=0; i<clist->count; ++i) {
	if (clist->checks[i].lcand->comp_id == 1)
	    break;
    }
    if (i == clist->count) {
	pj_assert(!"Unable to find checklist for component 1");
	return PJNATH_EICEINCOMPID;
    }

    /* Set this check to WAITING */
    check_set_state(ice, &clist->checks[i], 
		    PJ_ICE_SESS_CHECK_STATE_WAITING, PJ_SUCCESS);
    cand0 = clist->checks[i].lcand;
    flist[flist_cnt++] = &clist->checks[i].lcand->foundation;

    /* Find all of the other pairs in that check list with the same
     * component ID, but different foundations, and sets all of their
     * states to Waiting as well.
     */
    for (++i; i<clist->count; ++i) {
	const pj_ice_sess_cand *cand1;

	cand1 = clist->checks[i].lcand;

	if (cand1->comp_id==cand0->comp_id &&
	    find_str(flist, flist_cnt, &cand1->foundation)==NULL)
	{
	    check_set_state(ice, &clist->checks[i], 
			    PJ_ICE_SESS_CHECK_STATE_WAITING, PJ_SUCCESS);
	    flist[flist_cnt++] = &cand1->foundation;
	}
    }

    /* First, perform all pending triggered checks, simultaneously. */
    rcheck = ice->early_check.next;
    while (rcheck != &ice->early_check) {
	LOG4((ice->obj_name, 
	      "Performing delayed triggerred check for component %d",
	      rcheck->comp_id));
	handle_incoming_check(ice, rcheck);
	rcheck = rcheck->next;
    }
    pj_list_init(&ice->early_check);

    /* Start periodic check */
    return start_periodic_check(ice->stun_cfg.timer_heap, &clist->timer);
}


//////////////////////////////////////////////////////////////////////////////

/* Callback called by STUN session to send the STUN message.
 * STUN session also doesn't have a transport, remember?!
 */
static pj_status_t on_stun_send_msg(pj_stun_session *sess,
				    const void *pkt,
				    pj_size_t pkt_size,
				    const pj_sockaddr_t *dst_addr,
				    unsigned addr_len)
{
    stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess);
    pj_ice_sess *ice = sd->ice;
    return (*ice->cb.on_tx_pkt)(ice, sd->comp_id, 
				pkt, pkt_size, 
				dst_addr, addr_len);
}


/* This callback is called when outgoing STUN request completed */
static void on_stun_request_complete(pj_stun_session *stun_sess,
				     pj_status_t status,
				     pj_stun_tx_data *tdata,
				     const pj_stun_msg *response,
				     const pj_sockaddr_t *src_addr,
				     unsigned src_addr_len)
{
    struct req_data *rd = (struct req_data*) tdata->user_data;
    pj_ice_sess *ice;
    pj_ice_sess_check *check, *new_check;
    pj_ice_sess_cand *lcand;
    pj_ice_sess_checklist *clist;
    pj_stun_xor_mapped_addr_attr *xaddr;
    char buffer[CHECK_NAME_LEN];
    unsigned i;

    PJ_UNUSED_ARG(stun_sess);
    PJ_UNUSED_ARG(src_addr_len);

    ice = rd->ice;
    check = &rd->clist->checks[rd->ckid];
    clist = rd->clist;

    /* Mark STUN transaction as complete */
    pj_assert(tdata == check->tdata);
    check->tdata = NULL;

    pj_mutex_lock(ice->mutex);

    /* Init lcand to NULL. lcand will be found from the mapped address
     * found in the response.
     */
    lcand = NULL;

    if (status != PJ_SUCCESS) {
	char errmsg[PJ_ERR_MSG_SIZE];

	if (status==PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_ROLE_CONFLICT)) {

	    /* Role conclict response.
	     *
	     * 7.1.2.1.  Failure Cases:
	     *
	     * If the request had contained the ICE-CONTROLLED attribute, 
	     * the agent MUST switch to the controlling role if it has not
	     * already done so.  If the request had contained the 
	     * ICE-CONTROLLING attribute, the agent MUST switch to the 
	     * controlled role if it has not already done so.  Once it has
	     * switched, the agent MUST immediately retry the request with
	     * the ICE-CONTROLLING or ICE-CONTROLLED attribute reflecting 
	     * its new role.
	     */
	    pj_ice_sess_role new_role = PJ_ICE_SESS_ROLE_UNKNOWN;
	    pj_stun_msg *req = tdata->msg;

	    if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_ICE_CONTROLLING, 0)) {
		new_role = PJ_ICE_SESS_ROLE_CONTROLLED;
	    } else if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_ICE_CONTROLLED, 
					     0)) {
		new_role = PJ_ICE_SESS_ROLE_CONTROLLING;
	    } else {
		pj_assert(!"We should have put CONTROLLING/CONTROLLED attr!");
		new_role = PJ_ICE_SESS_ROLE_CONTROLLED;
	    }

	    if (new_role != ice->role) {
		LOG4((ice->obj_name, 
		      "Changing role because of role conflict response"));
		pj_ice_sess_change_role(ice, new_role);
	    }

	    /* Resend request */
	    LOG4((ice->obj_name, "Resending check because of role conflict"));
	    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_WAITING, 0);
	    perform_check(ice, clist, rd->ckid);
	    pj_mutex_unlock(ice->mutex);
	    return;
	}

	pj_strerror(status, errmsg, sizeof(errmsg));
	LOG4((ice->obj_name, 
	     "Check %s%s: connectivity check FAILED: %s",
	     dump_check(buffer, sizeof(buffer), &ice->clist, check),
	     (check->nominated ? " (nominated)" : " (not nominated)"),
	     errmsg));

	check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status);
	on_check_complete(ice, check);
	pj_mutex_unlock(ice->mutex);
	return;
    }


    /* 7.1.2.1.  Failure Cases
     *
     * The agent MUST check that the source IP address and port of the
     * response equals the destination IP address and port that the Binding
     * Request was sent to, and that the destination IP address and port of
     * the response match the source IP address and port that the Binding
     * Request was sent from.
     */
    if (sockaddr_cmp(&check->rcand->addr, (const pj_sockaddr*)src_addr) != 0) {
	status = PJNATH_EICEINSRCADDR;
	LOG4((ice->obj_name, 
	     "Check %s%s: connectivity check FAILED: source address mismatch",
	     dump_check(buffer, sizeof(buffer), &ice->clist, check),
	     (check->nominated ? " (nominated)" : " (not nominated)")));
	check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status);
	on_check_complete(ice, check);
	pj_mutex_unlock(ice->mutex);
	return;
    }

    /* 7.1.2.2.  Success Cases
     * 
     * A check is considered to be a success if all of the following are
     * true:
     * 
     * o  the STUN transaction generated a success response
     * 
     * o  the source IP address and port of the response equals the
     *    destination IP address and port that the Binding Request was sent
     *    to
     * 
     * o  the destination IP address and port of the response match the
     *    source IP address and port that the Binding Request was sent from
     */


    LOG4((ice->obj_name, 
	 "Check %s%s: connectivity check SUCCESS",
	 dump_check(buffer, sizeof(buffer), &ice->clist, check),
	 (check->nominated ? " (nominated)" : " (not nominated)")));

    /* Get the STUN XOR-MAPPED-ADDRESS attribute. */
    xaddr = (pj_stun_xor_mapped_addr_attr*)
	    pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR,0);
    if (!xaddr) {
	check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, 
			PJNATH_ESTUNNOMAPPEDADDR);
	on_check_complete(ice, check);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -