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

📄 ice_strans.c

📁 一个开源的sip源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* $Id: ice_strans.c 1298 2007-05-23 14:54:48Z bennylp $ */
/* 
 * Copyright (C) 2003-2007 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_strans.h>
#include <pjnath/errno.h>
#include <pj/addr_resolv.h>
#include <pj/assert.h>
#include <pj/ip_helper.h>
#include <pj/log.h>
#include <pj/pool.h>
#include <pj/rand.h>
#include <pj/string.h>


#if 0
#  define TRACE_PKT(expr)	    PJ_LOG(5,expr)
#else
#  define TRACE_PKT(expr)
#endif



/* ICE callbacks */
static void	   on_ice_complete(pj_ice_sess *ice, pj_status_t status);
static pj_status_t ice_tx_pkt(pj_ice_sess *ice, 
			      unsigned comp_id,
			      const void *pkt, pj_size_t size,
			      const pj_sockaddr_t *dst_addr,
			      unsigned dst_addr_len);
static void	   ice_rx_data(pj_ice_sess *ice, 
			      unsigned comp_id, 
			      void *pkt, pj_size_t size,
			      const pj_sockaddr_t *src_addr,
			      unsigned src_addr_len);

/* Ioqueue callback */
static void on_read_complete(pj_ioqueue_key_t *key, 
                             pj_ioqueue_op_key_t *op_key, 
                             pj_ssize_t bytes_read);

static void destroy_component(pj_ice_strans_comp *comp);
static void destroy_ice_st(pj_ice_strans *ice_st, pj_status_t reason);


/* STUN session callback */
static pj_status_t stun_on_send_msg(pj_stun_session *sess,
				    const void *pkt,
				    pj_size_t pkt_size,
				    const pj_sockaddr_t *dst_addr,
				    unsigned addr_len);
static void stun_on_request_complete(pj_stun_session *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);

/* Keep-alive timer */
static void start_ka_timer(pj_ice_strans *ice_st);
static void stop_ka_timer(pj_ice_strans *ice_st);

/* Utility: print error */
#define ice_st_perror(ice_st,msg,rc) pjnath_perror(ice_st->obj_name,msg,rc)

/* 
 * Create ICE stream transport 
 */
PJ_DECL(pj_status_t) pj_ice_strans_create(pj_stun_config *stun_cfg,
					  const char *name,
					  unsigned comp_cnt,
					  void *user_data,
					  const pj_ice_strans_cb *cb,
					  pj_ice_strans **p_ice_st)
{
    pj_pool_t *pool;
    pj_ice_strans *ice_st;

    PJ_ASSERT_RETURN(stun_cfg && comp_cnt && cb && p_ice_st, PJ_EINVAL);
    PJ_ASSERT_RETURN(stun_cfg->ioqueue && stun_cfg->timer_heap, PJ_EINVAL);

    if (name == NULL)
	name = "icstr%p";

    pool = pj_pool_create(stun_cfg->pf, name, 1000, 512, NULL);
    ice_st = PJ_POOL_ZALLOC_T(pool, pj_ice_strans);
    ice_st->pool = pool;
    pj_memcpy(ice_st->obj_name, pool->obj_name, PJ_MAX_OBJ_NAME);
    ice_st->user_data = user_data;
    
    ice_st->comp_cnt = comp_cnt;
    ice_st->comp = (pj_ice_strans_comp**) pj_pool_calloc(pool, comp_cnt, 
						     sizeof(void*));

    pj_memcpy(&ice_st->cb, cb, sizeof(*cb));
    pj_memcpy(&ice_st->stun_cfg, stun_cfg, sizeof(*stun_cfg));


    PJ_LOG(4,(ice_st->obj_name, "ICE stream transport created"));

    *p_ice_st = ice_st;
    return PJ_SUCCESS;
}

/* Destroy ICE */
static void destroy_ice_st(pj_ice_strans *ice_st, pj_status_t reason)
{
    unsigned i;
    char obj_name[PJ_MAX_OBJ_NAME];

    if (reason == PJ_SUCCESS) {
	pj_memcpy(obj_name, ice_st->obj_name, PJ_MAX_OBJ_NAME);
	PJ_LOG(4,(obj_name, "ICE stream transport shutting down"));
    }

    /* Kill keep-alive timer, if any */
    stop_ka_timer(ice_st);

    /* Destroy ICE if we have ICE */
    if (ice_st->ice) {
	pj_ice_sess_destroy(ice_st->ice);
	ice_st->ice = NULL;
    }

    /* Destroy all components */
    for (i=0; i<ice_st->comp_cnt; ++i) {
	if (ice_st->comp[i]) {
	    destroy_component(ice_st->comp[i]);
	    ice_st->comp[i] = NULL;
	}
    }
    ice_st->comp_cnt = 0;

    /* Done */
    pj_pool_release(ice_st->pool);

    if (reason == PJ_SUCCESS) {
	PJ_LOG(4,(obj_name, "ICE stream transport destroyed"));
    }
}

/*
 * Destroy ICE stream transport.
 */
PJ_DEF(pj_status_t) pj_ice_strans_destroy(pj_ice_strans *ice_st)
{
    destroy_ice_st(ice_st, PJ_SUCCESS);
    return PJ_SUCCESS;
}

/*
 * Resolve STUN server
 */
PJ_DEF(pj_status_t) pj_ice_strans_set_stun_domain(pj_ice_strans *ice_st,
						  pj_dns_resolver *resolver,
						  const pj_str_t *domain)
{
    /* Yeah, TODO */
    PJ_UNUSED_ARG(ice_st);
    PJ_UNUSED_ARG(resolver);
    PJ_UNUSED_ARG(domain);
    return -1;
}

/*
 * Set STUN server address.
 */
PJ_DEF(pj_status_t) pj_ice_strans_set_stun_srv( pj_ice_strans *ice_st,
						const pj_sockaddr_in *stun_srv,
						const pj_sockaddr_in *turn_srv)
{
    PJ_ASSERT_RETURN(ice_st, PJ_EINVAL);
    /* Must not have pending resolver job */
    PJ_ASSERT_RETURN(ice_st->has_rjob==PJ_FALSE, PJ_EINVALIDOP);

    if (stun_srv) {
	pj_memcpy(&ice_st->stun_srv, stun_srv, sizeof(pj_sockaddr_in));
    } else {
	pj_bzero(&ice_st->stun_srv, sizeof(pj_sockaddr_in));
    }

    if (turn_srv) {
	pj_memcpy(&ice_st->turn_srv, turn_srv, sizeof(pj_sockaddr_in));
    } else {
	pj_bzero(&ice_st->turn_srv, sizeof(pj_sockaddr_in));
    }

    return PJ_SUCCESS;
}

/* Add new candidate */
static pj_status_t add_cand( pj_ice_strans *ice_st,
			     pj_ice_strans_comp *comp,
			     unsigned comp_id,
			     pj_ice_cand_type type,
			     pj_uint16_t local_pref,
			     const pj_sockaddr_in *addr,
			     pj_bool_t set_default)
{
    pj_ice_strans_cand *cand;
    unsigned i;

    PJ_ASSERT_RETURN(ice_st && comp && addr, PJ_EINVAL);
    PJ_ASSERT_RETURN(comp->cand_cnt < PJ_ICE_ST_MAX_CAND, PJ_ETOOMANY);

    /* Check that we don't have candidate with the same
     * address.
     */
    for (i=0; i<comp->cand_cnt; ++i) {
	if (pj_memcmp(addr, &comp->cand_list[i].addr, 
		      sizeof(pj_sockaddr_in))==0)
	{
	    /* Duplicate */
	    PJ_LOG(5,(ice_st->obj_name, "Duplicate candidate not added"));
	    return PJ_SUCCESS;
	}
    }

    cand = &comp->cand_list[comp->cand_cnt];

    pj_bzero(cand, sizeof(*cand));
    cand->type = type;
    cand->status = PJ_SUCCESS;
    pj_memcpy(&cand->addr, addr, sizeof(pj_sockaddr_in));
    cand->ice_cand_id = -1;
    cand->local_pref = local_pref;
    pj_ice_calc_foundation(ice_st->pool, &cand->foundation, type, 
			   &comp->local_addr);

    if (set_default) 
	comp->default_cand = comp->cand_cnt;

    PJ_LOG(5,(ice_st->obj_name, 
	      "Candidate %s:%d (type=%s) added to component %d",
	      pj_inet_ntoa(addr->sin_addr),
	      (int)pj_ntohs(addr->sin_port), 
	      pj_ice_get_cand_type_name(type),
	      comp_id));
    
    comp->cand_cnt++;
    return PJ_SUCCESS;
}

/*  Create new component (i.e. socket)  */
static pj_status_t create_component(pj_ice_strans *ice_st,
				    unsigned comp_id,
				    pj_uint32_t options,
				    const pj_sockaddr_in *addr,
				    pj_ice_strans_comp **p_comp)
{
    enum { MAX_RETRY=100, PORT_INC=2 };
    pj_ioqueue_callback ioqueue_cb;
    pj_ice_strans_comp *comp;
    int retry, addr_len;
    struct {
	pj_uint32_t a1, a2, a3;
    } tsx_id;
    pj_status_t status;

    comp = PJ_POOL_ZALLOC_T(ice_st->pool, pj_ice_strans_comp);
    comp->ice_st = ice_st;
    comp->comp_id = comp_id;
    comp->options = options;
    comp->sock = PJ_INVALID_SOCKET;
    comp->last_status = PJ_SUCCESS;

    /* Create transaction ID for STUN keep alives */
    tsx_id.a1 = 0;
    tsx_id.a2 = comp_id;
    tsx_id.a3 = (pj_uint32_t) ice_st;
    pj_memcpy(comp->ka_tsx_id, &tsx_id, sizeof(comp->ka_tsx_id));

    /* Create socket */
    status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &comp->sock);
    if (status != PJ_SUCCESS)
	return status;

    /* Init address */
    if (addr) 
	pj_memcpy(&comp->local_addr, addr, sizeof(pj_sockaddr_in));
    else 
	pj_sockaddr_in_init(&comp->local_addr.ipv4, NULL, 0);

    /* Retry binding socket */
    for (retry=0; retry<MAX_RETRY; ++retry) {
	pj_uint16_t port;

	status = pj_sock_bind(comp->sock, &comp->local_addr, 
			      sizeof(pj_sockaddr_in));
	if (status == PJ_SUCCESS)
	    break;

	if (options & PJ_ICE_ST_OPT_NO_PORT_RETRY)
	    goto on_error;

	port = pj_ntohs(comp->local_addr.ipv4.sin_port);
	port += PORT_INC;
	comp->local_addr.ipv4.sin_port = pj_htons(port);
    }

    /* Get the actual port where the socket is bound to.
     * (don't care about the address, it will be retrieved later)
     */
    addr_len = sizeof(comp->local_addr);
    status = pj_sock_getsockname(comp->sock, &comp->local_addr, &addr_len);
    if (status != PJ_SUCCESS)
	goto on_error;

    /* Register to ioqueue */
    pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb));
    ioqueue_cb.on_read_complete = &on_read_complete;
    status = pj_ioqueue_register_sock(ice_st->pool, ice_st->stun_cfg.ioqueue, 
				      comp->sock, comp, &ioqueue_cb, 
				      &comp->key);
    if (status != PJ_SUCCESS)
	goto on_error;

    pj_ioqueue_op_key_init(&comp->read_op, sizeof(comp->read_op));
    pj_ioqueue_op_key_init(&comp->write_op, sizeof(comp->write_op));

    /* Kick start reading the socket */
    on_read_complete(comp->key, &comp->read_op, 0);

    /* If the socket is bound to INADDR_ANY, then lookup all interfaces in
     * the host and add them into cand_list. Otherwise if the socket is bound
     * to a specific interface, then only add that specific interface to
     * cand_list.
     */
    if (((options & PJ_ICE_ST_OPT_DONT_ADD_CAND)==0) &&
	comp->local_addr.ipv4.sin_addr.s_addr == 0) 
    {
	/* Socket is bound to INADDR_ANY */
	unsigned i, ifs_cnt;
	pj_in_addr ifs[PJ_ICE_ST_MAX_CAND-2];

	/* Reset default candidate */
	comp->default_cand = -1;

	/* Enum all IP interfaces in the host */
	ifs_cnt = PJ_ARRAY_SIZE(ifs);
	status = pj_enum_ip_interface(&ifs_cnt, ifs);
	if (status != PJ_SUCCESS)
	    goto on_error;

	/* Set default IP interface as the base address */
	status = pj_gethostip(&comp->local_addr.ipv4.sin_addr);
	if (status != PJ_SUCCESS)
	    goto on_error;

	/* Add candidate entry for each interface */
	for (i=0; i<ifs_cnt; ++i) {
	    pj_sockaddr_in cand_addr;
	    pj_bool_t set_default;
	    pj_uint16_t local_pref;

	    /* Ignore 127.0.0.0/24 address */
	    if ((pj_ntohl(ifs[i].s_addr) >> 24)==127)
		continue;

	    pj_memcpy(&cand_addr, &comp->local_addr, sizeof(pj_sockaddr_in));
	    cand_addr.sin_addr.s_addr = ifs[i].s_addr;


	    /* If the IP address is equal to local address, assign it
	     * as default candidate.
	     */

⌨️ 快捷键说明

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