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

📄 ice_session.c

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