📄 ice_session.c
字号:
pj_str_t *data)
{
pj_stun_session *sess = (pj_stun_session *)user_data;
stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess);
pj_ice_sess *ice = sd->ice;
PJ_UNUSED_ARG(pool);
realm->slen = nonce->slen = 0;
if (PJ_STUN_IS_RESPONSE(msg->hdr.type)) {
/* Outgoing responses need to have the same credential as
* incoming requests.
*/
*username = ice->rx_uname;
*data_type = 0;
*data = ice->rx_pass;
}
else {
*username = ice->tx_uname;
*data_type = 0;
*data = ice->tx_pass;
}
return PJ_SUCCESS;
}
/* Get password to be used to authenticate incoming message */
static pj_status_t stun_auth_get_password(const pj_stun_msg *msg,
void *user_data,
const pj_str_t *realm,
const pj_str_t *username,
pj_pool_t *pool,
int *data_type,
pj_str_t *data)
{
pj_stun_session *sess = (pj_stun_session *)user_data;
stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess);
pj_ice_sess *ice = sd->ice;
PJ_UNUSED_ARG(realm);
PJ_UNUSED_ARG(pool);
if (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) ||
PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
{
/* Incoming response is authenticated with TX credential */
/* Verify username */
if (pj_strcmp(username, &ice->tx_uname) != 0)
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNKNOWN_USERNAME);
*data_type = 0;
*data = ice->tx_pass;
} else {
/* Incoming request is authenticated with RX credential */
/* The agent MUST accept a credential if the username consists
* of two values separated by a colon, where the first value is
* equal to the username fragment generated by the agent in an offer
* or answer for a session in-progress, and the MESSAGE-INTEGRITY
* is the output of a hash of the password and the STUN packet's
* contents.
*/
const char *pos;
pj_str_t ufrag;
pos = (const char*)pj_memchr(username->ptr, ':', username->slen);
if (pos == NULL)
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNKNOWN_USERNAME);
ufrag.ptr = (char*)username->ptr;
ufrag.slen = (pos - username->ptr);
if (pj_strcmp(&ufrag, &ice->rx_ufrag) != 0)
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNKNOWN_USERNAME);
*data_type = 0;
*data = ice->rx_pass;
}
return PJ_SUCCESS;
}
static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice,
pj_ice_cand_type type,
pj_uint32_t local_pref,
pj_uint32_t comp_id)
{
return ((ice->prefs[type] & 0xFF) << 24) +
((local_pref & 0xFFFF) << 8) +
(((256 - comp_id) & 0xFF) << 0);
}
/*
* Add ICE candidate
*/
PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice,
unsigned comp_id,
pj_ice_cand_type type,
pj_uint16_t local_pref,
const pj_str_t *foundation,
const pj_sockaddr_t *addr,
const pj_sockaddr_t *base_addr,
const pj_sockaddr_t *rel_addr,
int addr_len,
unsigned *p_cand_id)
{
pj_ice_sess_cand *lcand;
pj_status_t status = PJ_SUCCESS;
char tmp[128];
PJ_ASSERT_RETURN(ice && comp_id &&
foundation && addr && base_addr && addr_len,
PJ_EINVAL);
PJ_ASSERT_RETURN(comp_id <= ice->comp_cnt, PJ_EINVAL);
pj_mutex_lock(ice->mutex);
if (ice->lcand_cnt >= PJ_ARRAY_SIZE(ice->lcand)) {
status = PJ_ETOOMANY;
goto on_error;
}
lcand = &ice->lcand[ice->lcand_cnt];
lcand->comp_id = comp_id;
lcand->type = type;
pj_strdup(ice->pool, &lcand->foundation, foundation);
lcand->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id);
pj_memcpy(&lcand->addr, addr, addr_len);
pj_memcpy(&lcand->base_addr, base_addr, addr_len);
if (rel_addr)
pj_memcpy(&lcand->rel_addr, rel_addr, addr_len);
else
pj_bzero(&lcand->rel_addr, sizeof(lcand->rel_addr));
pj_ansi_strcpy(tmp, pj_inet_ntoa(lcand->addr.ipv4.sin_addr));
LOG4((ice->obj_name,
"Candidate %d added: comp_id=%d, type=%s, foundation=%.*s, "
"addr=%s:%d, base=%s:%d, prio=0x%x (%u)",
ice->lcand_cnt,
lcand->comp_id,
cand_type_names[lcand->type],
(int)lcand->foundation.slen,
lcand->foundation.ptr,
tmp,
(int)pj_ntohs(lcand->addr.ipv4.sin_port),
pj_inet_ntoa(lcand->base_addr.ipv4.sin_addr),
(int)pj_htons(lcand->base_addr.ipv4.sin_port),
lcand->prio, lcand->prio));
if (p_cand_id)
*p_cand_id = ice->lcand_cnt;
++ice->lcand_cnt;
on_error:
pj_mutex_unlock(ice->mutex);
return status;
}
/* Find default candidate ID for the component */
PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice,
unsigned comp_id,
int *cand_id)
{
unsigned i;
PJ_ASSERT_RETURN(ice && comp_id && cand_id, PJ_EINVAL);
PJ_ASSERT_RETURN(comp_id <= ice->comp_cnt, PJ_EINVAL);
*cand_id = -1;
pj_mutex_lock(ice->mutex);
/* First find in valid list if we have nominated pair */
for (i=0; i<ice->valid_list.count; ++i) {
pj_ice_sess_check *check = &ice->valid_list.checks[i];
if (check->lcand->comp_id == comp_id) {
*cand_id = GET_LCAND_ID(check->lcand);
pj_mutex_unlock(ice->mutex);
return PJ_SUCCESS;
}
}
/* If there's no nominated pair, find relayed candidate */
for (i=0; i<ice->lcand_cnt; ++i) {
pj_ice_sess_cand *lcand = &ice->lcand[i];
if (lcand->comp_id==comp_id &&
lcand->type == PJ_ICE_CAND_TYPE_RELAYED)
{
*cand_id = GET_LCAND_ID(lcand);
pj_mutex_unlock(ice->mutex);
return PJ_SUCCESS;
}
}
/* If there's no relayed candidate, find reflexive candidate */
for (i=0; i<ice->lcand_cnt; ++i) {
pj_ice_sess_cand *lcand = &ice->lcand[i];
if (lcand->comp_id==comp_id &&
(lcand->type == PJ_ICE_CAND_TYPE_SRFLX ||
lcand->type == PJ_ICE_CAND_TYPE_PRFLX))
{
*cand_id = GET_LCAND_ID(lcand);
pj_mutex_unlock(ice->mutex);
return PJ_SUCCESS;
}
}
/* Otherwise return host candidate */
for (i=0; i<ice->lcand_cnt; ++i) {
pj_ice_sess_cand *lcand = &ice->lcand[i];
if (lcand->comp_id==comp_id &&
lcand->type == PJ_ICE_CAND_TYPE_HOST)
{
*cand_id = GET_LCAND_ID(lcand);
pj_mutex_unlock(ice->mutex);
return PJ_SUCCESS;
}
}
/* Still no candidate is found! :( */
pj_mutex_unlock(ice->mutex);
pj_assert(!"Should have a candidate by now");
return PJ_EBUG;
}
#ifndef MIN
# define MIN(a,b) (a < b ? a : b)
#endif
#ifndef MAX
# define MAX(a,b) (a > b ? a : b)
#endif
static pj_timestamp CALC_CHECK_PRIO(const pj_ice_sess *ice,
const pj_ice_sess_cand *lcand,
const pj_ice_sess_cand *rcand)
{
pj_uint32_t O, A;
pj_timestamp prio;
/* Original formula:
* pair priority = 2^32*MIN(O,A) + 2*MAX(O,A) + (O>A?1:0)
*/
if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) {
O = lcand->prio;
A = rcand->prio;
} else {
O = rcand->prio;
A = lcand->prio;
}
/*
return ((pj_uint64_t)1 << 32) * MIN(O, A) +
(pj_uint64_t)2 * MAX(O, A) + (O>A ? 1 : 0);
*/
prio.u32.hi = MIN(O,A);
prio.u32.lo = (MAX(O, A) << 1) + (O>A ? 1 : 0);
return prio;
}
PJ_INLINE(int) CMP_CHECK_PRIO(const pj_ice_sess_check *c1,
const pj_ice_sess_check *c2)
{
return pj_cmp_timestamp(&c1->prio, &c2->prio);
}
#if PJ_LOG_MAX_LEVEL >= 4
static const char *dump_check(char *buffer, unsigned bufsize,
const pj_ice_sess_checklist *clist,
const pj_ice_sess_check *check)
{
const pj_ice_sess_cand *lcand = check->lcand;
const pj_ice_sess_cand *rcand = check->rcand;
char laddr[CHECK_NAME_LEN];
int len;
pj_ansi_strcpy(laddr, pj_inet_ntoa(lcand->addr.ipv4.sin_addr));
if (lcand->addr.addr.sa_family == PJ_AF_INET) {
len = pj_ansi_snprintf(buffer, bufsize,
"%d: [%d] %s:%d-->%s:%d",
GET_CHECK_ID(clist, check),
check->lcand->comp_id,
laddr, (int)pj_ntohs(lcand->addr.ipv4.sin_port),
pj_inet_ntoa(rcand->addr.ipv4.sin_addr),
(int)pj_ntohs(rcand->addr.ipv4.sin_port));
} else {
len = pj_ansi_snprintf(buffer, bufsize, "IPv6->IPv6");
}
if (len < 0)
len = 0;
else if (len >= (int)bufsize)
len = bufsize - 1;
buffer[len] = '\0';
return buffer;
}
static void dump_checklist(const char *title, const pj_ice_sess *ice,
const pj_ice_sess_checklist *clist)
{
unsigned i;
char buffer[CHECK_NAME_LEN];
LOG4((ice->obj_name, "%s", title));
for (i=0; i<clist->count; ++i) {
const pj_ice_sess_check *c = &clist->checks[i];
LOG4((ice->obj_name, " %s (%s, state=%s)",
dump_check(buffer, sizeof(buffer), clist, c),
(c->nominated ? "nominated" : "not nominated"),
check_state_name[c->state]));
}
}
#else
#define dump_checklist(title, ice, clist)
#endif
static void check_set_state(pj_ice_sess *ice, pj_ice_sess_check *check,
pj_ice_sess_check_state st,
pj_status_t err_code)
{
char buf[CHECK_NAME_LEN];
pj_assert(check->state < PJ_ICE_SESS_CHECK_STATE_SUCCEEDED);
LOG5((ice->obj_name, "Check %s: state changed from %s to %s",
dump_check(buf, sizeof(buf), &ice->clist, check),
check_state_name[check->state],
check_state_name[st]));
check->state = st;
check->err_code = err_code;
}
static void clist_set_state(pj_ice_sess *ice, pj_ice_sess_checklist *clist,
pj_ice_sess_checklist_state st)
{
if (clist->state != st) {
LOG5((ice->obj_name, "Checklist: state changed from %s to %s",
clist_state_name[clist->state],
clist_state_name[st]));
clist->state = st;
}
}
/* Sort checklist based on priority */
static void sort_checklist(pj_ice_sess_checklist *clist)
{
unsigned i;
for (i=0; i<clist->count-1; ++i) {
unsigned j, highest = i;
for (j=i+1; j<clist->count; ++j) {
if (CMP_CHECK_PRIO(&clist->checks[j], &clist->checks[highest]) > 0) {
highest = j;
}
}
if (highest != i) {
pj_ice_sess_check tmp;
pj_memcpy(&tmp, &clist->checks[i], sizeof(pj_ice_sess_check));
pj_memcpy(&clist->checks[i], &clist->checks[highest],
sizeof(pj_ice_sess_check));
pj_memcpy(&clist->checks[highest], &tmp,
sizeof(pj_ice_sess_check));
}
}
}
enum
{
SOCKADDR_EQUAL = 0,
SOCKADDR_NOT_EQUAL = 1
};
/* Utility: compare sockaddr.
* Returns 0 if equal.
*/
static int sockaddr_cmp(const pj_sockaddr *a1, const pj_sockaddr *a2)
{
if (a1->addr.sa_family != a2->addr.sa_family)
return SOCKADDR_NOT_EQUAL;
if (a1->addr.sa_family == PJ_AF_INET) {
return !(a1->ipv4.sin_addr.s_addr == a2->ipv4.sin_addr.s_addr &&
a1->ipv4.sin_port == a2->ipv4.sin_port);
} else if (a1->addr.sa_family == PJ_AF_INET6) {
return pj_memcmp(&a1->ipv6, &a2->ipv6, sizeof(a1->ipv6));
} else {
pj_assert(!"Invalid address family!");
return SOCKADDR_NOT_EQUAL;
}
}
/* Prune checklist, this must have been done after the checklist
* is sorted.
*/
static pj_status_t prune_checklist(pj_ice_sess *ice,
pj_ice_sess_checklist *clist)
{
unsigned i;
/* Since an agent cannot send requests directly from a reflexive
* candidate, but only from its base, the agent next goes through the
* sorted list of candidate pairs. For each pair where the local
* candidate is server reflexive, the server reflexive candidate MUST be
* replaced by its base. Once this has been done, the agent MUST prune
* the list. This is done by removing 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. The result is a sequence of ordered
* candidate pairs, called the check list for that media stream.
*/
/* First replace SRFLX candidates with their base */
for (i=0; i<clist->count; ++i) {
pj_ice_sess_cand *srflx = clist->checks[i].lcand;
if (clist->checks[i].lcand->type == PJ_ICE_CAND_TYPE_SRFLX) {
/* Find the base for this candidate */
unsigned j;
for (j=0; j<ice->lcand_cnt; ++j) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -