📄 ice_strans.c
字号:
/* $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 + -