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

📄 ice_session.c

📁 一个开源的sip源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	pj_mutex_unlock(ice->mutex);
	return;
    }

    /* Find local candidate that matches the XOR-MAPPED-ADDRESS */
    pj_assert(lcand == NULL);
    for (i=0; i<ice->lcand_cnt; ++i) {
	if (sockaddr_cmp(&xaddr->sockaddr, &ice->lcand[i].addr) == 0) {
	    /* Match */
	    lcand = &ice->lcand[i];
	    break;
	}
    }

    /* 7.1.2.2.1.  Discovering Peer Reflexive Candidates
     * If the transport address returned in XOR-MAPPED-ADDRESS does not match
     * any of the local candidates that the agent knows about, the mapped 
     * address represents a new candidate - a peer reflexive candidate.
     */
    if (lcand == NULL) {
	unsigned cand_id;
	pj_str_t foundation;

	pj_ice_calc_foundation(ice->pool, &foundation, PJ_ICE_CAND_TYPE_PRFLX,
			       &check->lcand->base_addr);

	/* Still in 7.1.2.2.1.  Discovering Peer Reflexive Candidates
	 * Its priority is set equal to the value of the PRIORITY attribute
         * in the Binding Request.
	 *
	 * I think the priority calculated by add_cand() should be the same
	 * as the one calculated in perform_check(), so there's no need to
	 * get the priority from the PRIORITY attribute.
	 */

	/* Add new peer reflexive candidate */
	status = pj_ice_sess_add_cand(ice, lcand->comp_id, 
				      PJ_ICE_CAND_TYPE_PRFLX,
				      65535, &foundation,
				      &xaddr->sockaddr, 
				      &check->lcand->base_addr, NULL,
				      sizeof(pj_sockaddr_in), &cand_id);
	if (status != PJ_SUCCESS) {
	    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, 
			    status);
	    on_check_complete(ice, check);
	    pj_mutex_unlock(ice->mutex);
	    return;
	}

	/* Update local candidate */
	lcand = &ice->lcand[cand_id];

    }

    /* 7.1.2.2.3.  Constructing a Valid Pair
     * Next, the agent constructs a candidate pair whose local candidate
     * equals the mapped address of the response, and whose remote candidate
     * equals the destination address to which the request was sent.    
     */

    /* Add pair to valid list */
    pj_assert(ice->valid_list.count < PJ_ICE_MAX_CHECKS);
    new_check = &ice->valid_list.checks[ice->valid_list.count++];
    new_check->lcand = lcand;
    new_check->rcand = check->rcand;
    new_check->prio = CALC_CHECK_PRIO(ice, lcand, check->rcand);
    new_check->state = PJ_ICE_SESS_CHECK_STATE_SUCCEEDED;
    new_check->nominated = check->nominated;
    new_check->err_code = PJ_SUCCESS;

    /* Sort valid_list */
    sort_checklist(&ice->valid_list);


    /* 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.
     */
    check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_SUCCEEDED, 
		    PJ_SUCCESS);

    /* Perform 7.1.2.2.2.  Updating Pair States.
     * This may terminate ICE processing.
     */
    if (on_check_complete(ice, check)) {
	/* ICE complete! */
	pj_mutex_unlock(ice->mutex);
	return;
    }

    pj_mutex_unlock(ice->mutex);
}


/* This callback is called by the STUN session associated with a candidate
 * when it receives incoming request.
 */
static pj_status_t on_stun_rx_request(pj_stun_session *sess,
				      const pj_uint8_t *pkt,
				      unsigned pkt_len,
				      const pj_stun_msg *msg,
				      const pj_sockaddr_t *src_addr,
				      unsigned src_addr_len)
{
    stun_data *sd;
    pj_ice_sess *ice;
    pj_stun_priority_attr *prio_attr;
    pj_stun_use_candidate_attr *uc_attr;
    pj_stun_uint64_attr *role_attr;
    pj_stun_tx_data *tdata;
    pj_ice_rx_check *rcheck, tmp_rcheck;
    pj_status_t status;

    PJ_UNUSED_ARG(pkt);
    PJ_UNUSED_ARG(pkt_len);

    /* Reject any requests except Binding request */
    if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) {
	status = pj_stun_session_create_res(sess, msg, 
					    PJ_STUN_SC_BAD_REQUEST,
					    NULL, &tdata);
	if (status != PJ_SUCCESS)
	    return status;

	return pj_stun_session_send_msg(sess, PJ_TRUE, 
					src_addr, src_addr_len, tdata);
    }


    sd = (stun_data*) pj_stun_session_get_user_data(sess);
    ice = sd->ice;

    pj_mutex_lock(ice->mutex);

    /*
     * Note:
     *  Be aware that when STUN request is received, we might not get
     *  SDP answer yet, so we might not have remote candidates and
     *  checklist yet. This case will be handled after we send
     *  a response.
     */

    /* Get PRIORITY attribute */
    prio_attr = (pj_stun_priority_attr*)
	        pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PRIORITY, 0);
    if (prio_attr == NULL) {
	LOG5((ice->obj_name, "Received Binding request with no PRIORITY"));
	pj_mutex_unlock(ice->mutex);
	return PJ_SUCCESS;
    }

    /* Get USE-CANDIDATE attribute */
    uc_attr = (pj_stun_use_candidate_attr*)
	      pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USE_CANDIDATE, 0);


    /* Get ICE-CONTROLLING or ICE-CONTROLLED */
    role_attr = (pj_stun_uint64_attr*)
	        pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ICE_CONTROLLING, 0);
    if (role_attr == NULL) {
	role_attr = (pj_stun_uint64_attr*)
	            pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ICE_CONTROLLED, 0);
    }

    /* Handle the case when request comes before answer is received.
     * We need to put credential in the response, and since we haven't
     * got the response, copy the username from the request.
     */
    if (ice->rcand_cnt == 0) {
	pj_stun_string_attr *uname_attr;

	uname_attr = (pj_stun_string_attr*)
		     pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0);
	pj_assert(uname_attr != NULL);
	pj_strdup(ice->pool, &ice->rx_uname, &uname_attr->value);
    }

    /* 7.2.1.1.  Detecting and Repairing Role Conflicts
     */
    if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING &&
	role_attr && role_attr->hdr.type == PJ_STUN_ATTR_ICE_CONTROLLING)
    {
	if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) {
	    /* Switch role to controlled */
	    LOG4((ice->obj_name, 
		  "Changing role because of ICE-CONTROLLING attribute"));
	    pj_ice_sess_change_role(ice, PJ_ICE_SESS_ROLE_CONTROLLED);
	} else {
	    /* Generate 487 response */
	    status = pj_stun_session_create_res(sess, msg, 
					        PJ_STUN_SC_ROLE_CONFLICT,
						NULL, &tdata);
	    if (status == PJ_SUCCESS) {
		pj_stun_session_send_msg(sess, PJ_TRUE, 
				         src_addr, src_addr_len, tdata);
	    }
	    pj_mutex_unlock(ice->mutex);
	    return PJ_SUCCESS;
	}

    } else if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED &&
	       role_attr && role_attr->hdr.type == PJ_STUN_ATTR_ICE_CONTROLLED)
    {
	if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) {
	    /* Generate 487 response */
	    status = pj_stun_session_create_res(sess, msg, 
					        PJ_STUN_SC_ROLE_CONFLICT,
						NULL, &tdata);
	    if (status == PJ_SUCCESS) {
		pj_stun_session_send_msg(sess, PJ_TRUE, 
				         src_addr, src_addr_len, tdata);
	    }
	    pj_mutex_unlock(ice->mutex);
	    return PJ_SUCCESS;
	} else {
	    /* Switch role to controlled */
	    LOG4((ice->obj_name, 
		  "Changing role because of ICE-CONTROLLED attribute"));
	    pj_ice_sess_change_role(ice, PJ_ICE_SESS_ROLE_CONTROLLING);
	}
    }

    /* 
     * First send response to this request 
     */
    status = pj_stun_session_create_res(sess, msg, 0, NULL, &tdata);
    if (status != PJ_SUCCESS) {
	pj_mutex_unlock(ice->mutex);
	return status;
    }

    status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, 
					   PJ_STUN_ATTR_XOR_MAPPED_ADDR,
					   PJ_TRUE, src_addr, src_addr_len);

    status = pj_stun_session_send_msg(sess, PJ_TRUE, 
				      src_addr, src_addr_len, tdata);


    /* 
     * Handling early check.
     *
     * It's possible that we receive this request before we receive SDP
     * answer. In this case, we can't perform trigger check since we
     * don't have checklist yet, so just save this check in a pending
     * triggered check array to be acted upon later.
     */
    if (ice->rcand_cnt == 0) {
	rcheck = PJ_POOL_ZALLOC_T(ice->pool, pj_ice_rx_check);
    } else {
	rcheck = &tmp_rcheck;
    }

    /* Init rcheck */
    rcheck->comp_id = sd->comp_id;
    rcheck->src_addr_len = src_addr_len;
    pj_memcpy(&rcheck->src_addr, src_addr, src_addr_len);
    rcheck->use_candidate = (uc_attr != NULL);
    rcheck->priority = prio_attr->value;
    rcheck->role_attr = role_attr;

    if (ice->rcand_cnt == 0) {
	/* We don't have answer yet, so keep this request for later */
	LOG4((ice->obj_name, "Received an early check for comp %d",
	      rcheck->comp_id));
	pj_list_push_back(&ice->early_check, rcheck);
    } else {
	/* Handle this check */
	handle_incoming_check(ice, rcheck);
    }

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


/* Handle incoming Binding request and perform triggered check.
 * This function may be called by on_stun_rx_request(), or when
 * SDP answer is received and we have received early checks.
 */
static void handle_incoming_check(pj_ice_sess *ice,
				  const pj_ice_rx_check *rcheck)
{
    pj_ice_sess_comp *comp;
    pj_ice_sess_cand *lcand = NULL;
    pj_ice_sess_cand *rcand;
    unsigned i;
    pj_bool_t is_relayed;

    comp = find_comp(ice, rcheck->comp_id);

    /* Find remote candidate based on the source transport address of 
     * the request.
     */
    for (i=0; i<ice->rcand_cnt; ++i) {
	if (sockaddr_cmp(&rcheck->src_addr, &ice->rcand[i].addr)==0)
	    break;
    }

    /* 7.2.1.3.  Learning Peer Reflexive Candidates
     * If the source transport address of the request does not match any
     * existing remote candidates, it represents a new peer reflexive remote
     * candidate.
     */
    if (i == ice->rcand_cnt) {
	rcand = &ice->rcand[ice->rcand_cnt++];
	rcand->comp_id = rcheck->comp_id;
	rcand->type = PJ_ICE_CAND_TYPE_PRFLX;
	rcand->prio = rcheck->priority;
	pj_memcpy(&rcand->addr, &rcheck->src_addr, rcheck->src_addr_len);

	/* Foundation is random, unique from other foundation */
	rcand->foundation.ptr = (char*) pj_pool_alloc(ice->pool, 36);
	rcand->foundation.slen = pj_ansi_snprintf(rcand->foundation.ptr, 36,
						  "f%p", 
						  rcand->foundation.ptr);

	LOG4((ice->obj_name, 
	     "Added new remote candidate from the request: %s:%d",
	     pj_inet_ntoa(rcand->addr.ipv4.sin_addr),
	     (int)pj_ntohs(rcand->addr.ipv4.sin_port)));

    } else {
	/* Remote candidate found */
	rcand = &ice->rcand[i];
    }

#if 0
    /* Find again the local candidate by matching the base address
     * with the local candidates in the checklist. Checks may have
     * been pruned before, so it's possible that if we use the lcand
     * as it is, we wouldn't be able to find the check in the checklist
     * and we will end up creating a new check unnecessarily.
     */
    for (i=0; i<ice->clist.count; ++i) {
	pj_ice_sess_check *c = &ice->clist.checks[i];
	if (/*c->lcand == lcand ||*/
	    sockaddr_cmp(&c->lcand->base_addr, &lcand->base_addr)==0)
	{
	    lcand = c->lcand;
	    break;
	}
    }
#else
    /* Just get candidate with the highest priority for the specified 
     * component ID in the checklist.
     */
    for (i=0; i<ice->clist.count; ++i) {
	pj_ice_sess_check *c = &ice->clist.checks[i];
	if (c->lcand->comp_id == rcheck->comp_id) {
	    lcand = c->lcand;
	    break;
	}
    }
    if (lcand == NULL) {
	/* Should not happen, but just in case remote is sending a
	 * Binding request for a component which it doesn't have.
	 */
	LOG4((ice->obj_name, 
	     "Received Binding request but no local candidate is found!"));
	return;
    }
#endif

    /* 
     * Create candidate pair for this request. 
     */
    /* First check if the source address is the source address of the 
     * STUN relay, to determine if local candidate is relayed candidate.
     */
    PJ_TODO(DETERMINE_IF_REQUEST_COMES_FROM_RELAYED_CANDIDATE);
    is_relayed = PJ_FALSE;

    /* 
     * 7.2.1.4.  Triggered Checks
     *
     * Now that we have local and remote candidate, check if we already
     * have this pair in our checklist.
     */
    for (i=0; i<ice->clist.count; ++i) {
	pj_ice_sess_check *c = &ice->clist.checks[i];
	if (c->lcand == lcand && c->rcand == rcand)
	    break;
    }

    /* If the pair is already on the check list:
     * - If the state of that pair is Waiting or Frozen, its state is
     *   changed to In-Progress and a check for that pair is performed
     *   immediately.  This is called a triggered check.
     *
     * - If the state of that pair is In-Progress, the agent SHOULD
     *   generate an immediate retransmit of the Binding Request for the
     *   check in progress.  This is to facilitate rapid completion of
     *   ICE when both agents are behind NAT.
     * 
     * - If the state of that pair is Failed or Succeeded, no triggered
     *   check is sent.
     */
    if (i != ice->clist.count) {
	pj_ice_sess_check *c = &ice->clist.checks[i];

	/* If USE-CANDIDATE is present, set nominated flag 
	 * Note: DO NOT overwrite nominated flag if one is already set.
	 */
	c->nominated = ((rcheck->use_candidate) || c->nominated);

	if (c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN ||
	    c->state == PJ_ICE_SESS_CHECK_STATE_WAITING)
	{
	    LOG5((ice->obj_name, "Performing triggered check for check %d",i));
	    perform_check(ice, &ice->clist, i);

	} else if (c->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) {
	    /* Should retransmit immediately
	     */
	    LOG5((ice->obj_name, "Triggered check for check %d not performed "
		  "because it's in progress. Retransmitting", i));
	    pj_stun_session_retransmit_req(comp->stun_sess, c->tdata);

	} else if (c->state == PJ_ICE_SESS_CHECK_STATE_SUCCEEDED) {
	    /* Check complete for this component.
	     * Note this may end ICE process.
	     */
	    pj_bool_t complete;
	    unsigned j;

	    /* If this check is nominated, scan the valid_list for the
	     * same check and update the nominated flag. A controlled 
	     * agent might have finished the check earlier.
	     */
	    if (rcheck->use_candidate) {

⌨️ 快捷键说明

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