📄 ice_session.c
字号:
/* $Id: ice_session.c 1291 2007-05-23 07:12:23Z bennylp $ */
/*
* Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjnath/ice_session.h>
#include <pjnath/errno.h>
#include <pj/addr_resolv.h>
#include <pj/array.h>
#include <pj/assert.h>
#include <pj/guid.h>
#include <pj/log.h>
#include <pj/os.h>
#include <pj/pool.h>
#include <pj/rand.h>
#include <pj/string.h>
/* String names for candidate types */
static const char *cand_type_names[] =
{
"Host",
"Server Reflexive",
"Peer Reflexive",
"Relayed"
};
/* String names for pj_ice_sess_check_state */
#if PJ_LOG_MAX_LEVEL >= 4
static const char *check_state_name[] =
{
"Frozen",
"Waiting",
"In Progress",
"Succeeded",
"Failed"
};
static const char *clist_state_name[] =
{
"Idle",
"Running",
"Completed"
};
#endif /* PJ_LOG_MAX_LEVEL >= 4 */
static const char *role_names[] =
{
"Unknown",
"Controlled",
"Controlling"
};
/* Default ICE session preferences, according to draft-ice */
static pj_uint8_t cand_type_prefs[4] =
{
126, /**< PJ_ICE_HOST_PREF */
100, /**< PJ_ICE_SRFLX_PREF. */
110, /**< PJ_ICE_PRFLX_PREF */
0 /**< PJ_ICE_RELAYED_PREF */
};
#define CHECK_NAME_LEN 128
#define LOG4(expr) PJ_LOG(4,expr)
#define LOG5(expr) PJ_LOG(4,expr)
#define GET_LCAND_ID(cand) (cand - ice->lcand)
#define GET_CHECK_ID(cl, chk) (chk - (cl)->checks)
/* The data that will be attached to the STUN session on each
* component.
*/
typedef struct stun_data
{
pj_ice_sess *ice;
unsigned comp_id;
pj_ice_sess_comp *comp;
} stun_data;
/* The data that will be attached to the timer to perform
* periodic check.
*/
typedef struct timer_data
{
pj_ice_sess *ice;
pj_ice_sess_checklist *clist;
} timer_data;
/* Forward declarations */
static void destroy_ice(pj_ice_sess *ice,
pj_status_t reason);
static pj_status_t start_periodic_check(pj_timer_heap_t *th,
pj_timer_entry *te);
static void periodic_timer(pj_timer_heap_t *th,
pj_timer_entry *te);
static void handle_incoming_check(pj_ice_sess *ice,
const pj_ice_rx_check *rcheck);
/* These are the callbacks registered to the STUN sessions */
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);
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);
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);
static pj_status_t on_stun_rx_indication(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);
/* These are the callbacks for performing STUN authentication */
static pj_status_t stun_auth_get_auth(void *user_data,
pj_pool_t *pool,
pj_str_t *realm,
pj_str_t *nonce);
static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg,
void *user_data,
pj_pool_t *pool,
pj_str_t *realm,
pj_str_t *username,
pj_str_t *nonce,
int *data_type,
pj_str_t *data);
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_DEF(const char*) pj_ice_get_cand_type_name(pj_ice_cand_type type)
{
PJ_ASSERT_RETURN(type <= PJ_ICE_CAND_TYPE_RELAYED, "???");
return cand_type_names[type];
}
/* Get the prefix for the foundation */
static int get_type_prefix(pj_ice_cand_type type)
{
switch (type) {
case PJ_ICE_CAND_TYPE_HOST: return 'H';
case PJ_ICE_CAND_TYPE_SRFLX: return 'S';
case PJ_ICE_CAND_TYPE_PRFLX: return 'P';
case PJ_ICE_CAND_TYPE_RELAYED: return 'R';
default:
pj_assert(!"Invalid type");
return 'U';
}
}
/* Calculate foundation */
PJ_DEF(void) pj_ice_calc_foundation(pj_pool_t *pool,
pj_str_t *foundation,
pj_ice_cand_type type,
const pj_sockaddr *base_addr)
{
char buf[64];
pj_ansi_snprintf(buf, sizeof(buf), "%c%x",
get_type_prefix(type),
(int)pj_ntohl(base_addr->ipv4.sin_addr.s_addr));
pj_strdup2(pool, foundation, buf);
}
/* Init component */
static pj_status_t init_comp(pj_ice_sess *ice,
unsigned comp_id,
pj_ice_sess_comp *comp)
{
pj_stun_session_cb sess_cb;
pj_stun_auth_cred auth_cred;
stun_data *sd;
pj_status_t status;
/* Init STUN callbacks */
pj_bzero(&sess_cb, sizeof(sess_cb));
sess_cb.on_request_complete = &on_stun_request_complete;
sess_cb.on_rx_indication = &on_stun_rx_indication;
sess_cb.on_rx_request = &on_stun_rx_request;
sess_cb.on_send_msg = &on_stun_send_msg;
/* Create STUN session for this candidate */
status = pj_stun_session_create(&ice->stun_cfg, NULL,
&sess_cb, PJ_FALSE,
&comp->stun_sess);
if (status != PJ_SUCCESS)
return status;
/* Associate data with this STUN session */
sd = PJ_POOL_ZALLOC_T(ice->pool, struct stun_data);
sd->ice = ice;
sd->comp_id = comp_id;
sd->comp = comp;
pj_stun_session_set_user_data(comp->stun_sess, sd);
/* Init STUN authentication credential */
pj_bzero(&auth_cred, sizeof(auth_cred));
auth_cred.type = PJ_STUN_AUTH_CRED_DYNAMIC;
auth_cred.data.dyn_cred.get_auth = &stun_auth_get_auth;
auth_cred.data.dyn_cred.get_cred = &stun_auth_get_cred;
auth_cred.data.dyn_cred.get_password = &stun_auth_get_password;
auth_cred.data.dyn_cred.user_data = comp->stun_sess;
pj_stun_session_set_credential(comp->stun_sess, &auth_cred);
return PJ_SUCCESS;
}
/*
* Create ICE session.
*/
PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
const char *name,
pj_ice_sess_role role,
unsigned comp_cnt,
const pj_ice_sess_cb *cb,
const pj_str_t *local_ufrag,
const pj_str_t *local_passwd,
pj_ice_sess **p_ice)
{
pj_pool_t *pool;
pj_ice_sess *ice;
unsigned i;
pj_status_t status;
PJ_ASSERT_RETURN(stun_cfg && cb && p_ice, PJ_EINVAL);
if (name == NULL)
name = "ice%p";
pool = pj_pool_create(stun_cfg->pf, name, 4000, 4000, NULL);
ice = PJ_POOL_ZALLOC_T(pool, pj_ice_sess);
ice->pool = pool;
ice->role = role;
ice->tie_breaker.u32.hi = pj_rand();
ice->tie_breaker.u32.lo = pj_rand();
ice->prefs = cand_type_prefs;
pj_ansi_snprintf(ice->obj_name, sizeof(ice->obj_name),
name, ice);
status = pj_mutex_create_recursive(pool, ice->obj_name,
&ice->mutex);
if (status != PJ_SUCCESS) {
destroy_ice(ice, status);
return status;
}
pj_memcpy(&ice->cb, cb, sizeof(*cb));
pj_memcpy(&ice->stun_cfg, stun_cfg, sizeof(*stun_cfg));
ice->comp_cnt = comp_cnt;
for (i=0; i<comp_cnt; ++i) {
pj_ice_sess_comp *comp;
comp = &ice->comp[i];
comp->valid_check = NULL;
status = init_comp(ice, i+1, comp);
if (status != PJ_SUCCESS) {
destroy_ice(ice, status);
return status;
}
}
if (local_ufrag == NULL) {
ice->rx_ufrag.ptr = (char*) pj_pool_alloc(ice->pool, 16);
pj_create_random_string(ice->rx_ufrag.ptr, 16);
ice->rx_ufrag.slen = 16;
} else {
pj_strdup(ice->pool, &ice->rx_ufrag, local_ufrag);
}
if (local_passwd == NULL) {
ice->rx_pass.ptr = (char*) pj_pool_alloc(ice->pool, 16);
pj_create_random_string(ice->rx_pass.ptr, 16);
ice->rx_pass.slen = 16;
} else {
pj_strdup(ice->pool, &ice->rx_pass, local_passwd);
}
pj_list_init(&ice->early_check);
/* Done */
*p_ice = ice;
LOG4((ice->obj_name,
"ICE session created, comp_cnt=%d, role is %s agent",
comp_cnt, role_names[ice->role]));
return PJ_SUCCESS;
}
/*
* Destroy
*/
static void destroy_ice(pj_ice_sess *ice,
pj_status_t reason)
{
unsigned i;
if (reason == PJ_SUCCESS) {
LOG4((ice->obj_name, "Destroying ICE session"));
}
for (i=0; i<ice->comp_cnt; ++i) {
if (ice->comp[i].stun_sess) {
pj_stun_session_destroy(ice->comp[i].stun_sess);
ice->comp[i].stun_sess = NULL;
}
}
if (ice->clist.timer.id) {
pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->clist.timer);
ice->clist.timer.id = PJ_FALSE;
}
if (ice->mutex) {
pj_mutex_destroy(ice->mutex);
ice->mutex = NULL;
}
if (ice->pool) {
pj_pool_t *pool = ice->pool;
ice->pool = NULL;
pj_pool_release(pool);
}
}
/*
* Destroy
*/
PJ_DEF(pj_status_t) pj_ice_sess_destroy(pj_ice_sess *ice)
{
PJ_ASSERT_RETURN(ice, PJ_EINVAL);
destroy_ice(ice, PJ_SUCCESS);
return PJ_SUCCESS;
}
/*
* Change session role.
*/
PJ_DEF(pj_status_t) pj_ice_sess_change_role(pj_ice_sess *ice,
pj_ice_sess_role new_role)
{
PJ_ASSERT_RETURN(ice, PJ_EINVAL);
if (new_role != ice->role) {
ice->role = new_role;
LOG4((ice->obj_name, "Role changed to %s", role_names[new_role]));
}
return PJ_SUCCESS;
}
/*
* Change type preference
*/
PJ_DEF(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice,
const pj_uint8_t prefs[4])
{
PJ_ASSERT_RETURN(ice && prefs, PJ_EINVAL);
ice->prefs = (pj_uint8_t*) pj_pool_calloc(ice->pool, PJ_ARRAY_SIZE(prefs),
sizeof(pj_uint8_t));
pj_memcpy(ice->prefs, prefs, sizeof(prefs));
return PJ_SUCCESS;
}
/* Find component by ID */
static pj_ice_sess_comp *find_comp(const pj_ice_sess *ice, unsigned comp_id)
{
pj_assert(comp_id > 0 && comp_id <= ice->comp_cnt);
return (pj_ice_sess_comp*) &ice->comp[comp_id-1];
}
/* Callback by STUN authentication when it needs to send 401 */
static pj_status_t stun_auth_get_auth(void *user_data,
pj_pool_t *pool,
pj_str_t *realm,
pj_str_t *nonce)
{
PJ_UNUSED_ARG(user_data);
PJ_UNUSED_ARG(pool);
realm->slen = 0;
nonce->slen = 0;
return PJ_SUCCESS;
}
/* Get credential to be sent with outgoing message */
static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg,
void *user_data,
pj_pool_t *pool,
pj_str_t *realm,
pj_str_t *username,
pj_str_t *nonce,
int *data_type,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -