blob: 5d4ee05c2258cbd24d054b4f62ca5f03d7a87ef2 [file] [log] [blame]
/**
* @file cand.c ICE Candidates
*
* Copyright (C) 2010 Creytiv.com
*/
#include <string.h>
#include <re_types.h>
#include <re_fmt.h>
#include <re_mem.h>
#include <re_mbuf.h>
#include <re_list.h>
#include <re_tmr.h>
#include <re_sa.h>
#include <re_sys.h>
#include <re_stun.h>
#include <re_turn.h>
#include <re_ice.h>
#include "ice.h"
#define DEBUG_MODULE "icecand"
#define DEBUG_LEVEL 5
#include <re_dbg.h>
static void cand_destructor(void *arg)
{
struct ice_cand *cand = arg;
list_unlink(&cand->le);
mem_deref(cand->foundation);
mem_deref(cand->ifname);
if (cand != cand->base)
mem_deref(cand->base);
}
/** Foundation is a hash of IP address and candidate type */
static int compute_foundation(struct ice_cand *cand)
{
uint32_t v;
v = sa_hash(&cand->addr, SA_ADDR);
v ^= cand->type;
return re_sdprintf(&cand->foundation, "%08x", v);
}
static int cand_alloc(struct ice_cand **candp, struct icem *icem,
enum ice_cand_type type, unsigned compid,
uint32_t prio, const char *ifname,
enum ice_transp transp, const struct sa *addr)
{
struct ice_cand *cand;
int err;
if (!icem)
return EINVAL;
cand = mem_zalloc(sizeof(*cand), cand_destructor);
if (!cand)
return ENOMEM;
list_append(&icem->lcandl, &cand->le, cand);
cand->type = type;
cand->compid = compid;
cand->prio = prio;
cand->transp = transp;
sa_cpy(&cand->addr, addr);
err = compute_foundation(cand);
if (ifname)
err |= str_dup(&cand->ifname, ifname);
if (err)
mem_deref(cand);
else if (candp)
*candp = cand;
return err;
}
int icem_lcand_add_base(struct icem *icem, unsigned compid, uint16_t lprio,
const char *ifname, enum ice_transp transp,
const struct sa *addr)
{
struct icem_comp *comp;
struct ice_cand *cand;
int err;
comp = icem_comp_find(icem, compid);
if (!comp)
return ENOENT;
err = cand_alloc(&cand, icem, ICE_CAND_TYPE_HOST, compid,
ice_cand_calc_prio(ICE_CAND_TYPE_HOST, lprio, compid),
ifname, transp, addr);
if (err)
return err;
/* the base is itself */
cand->base = cand;
sa_set_port(&cand->addr, comp->lport);
return 0;
}
int icem_lcand_add(struct icem *icem, struct ice_cand *base,
enum ice_cand_type type,
const struct sa *addr)
{
struct ice_cand *cand;
int err;
if (!base)
return EINVAL;
err = cand_alloc(&cand, icem, type, base->compid,
ice_cand_calc_prio(type, 0, base->compid),
base->ifname, base->transp, addr);
if (err)
return err;
cand->base = mem_ref(base);
sa_cpy(&cand->rel, &base->addr);
return 0;
}
int icem_rcand_add(struct icem *icem, enum ice_cand_type type, unsigned compid,
uint32_t prio, const struct sa *addr,
const struct sa *rel_addr, const struct pl *foundation)
{
struct ice_cand *rcand;
int err;
if (!icem || !foundation)
return EINVAL;
rcand = mem_zalloc(sizeof(*rcand), cand_destructor);
if (!rcand)
return ENOMEM;
list_append(&icem->rcandl, &rcand->le, rcand);
rcand->type = type;
rcand->compid = compid;
rcand->prio = prio;
sa_cpy(&rcand->addr, addr);
sa_cpy(&rcand->rel, rel_addr);
err = pl_strdup(&rcand->foundation, foundation);
if (err)
mem_deref(rcand);
return err;
}
int icem_rcand_add_prflx(struct ice_cand **rcp, struct icem *icem,
unsigned compid, uint32_t prio,
const struct sa *addr)
{
struct ice_cand *rcand;
int err;
if (!icem || !addr)
return EINVAL;
rcand = mem_zalloc(sizeof(*rcand), cand_destructor);
if (!rcand)
return ENOMEM;
list_append(&icem->rcandl, &rcand->le, rcand);
rcand->type = ICE_CAND_TYPE_PRFLX;
rcand->compid = compid;
rcand->prio = prio;
rcand->addr = *addr;
err = re_sdprintf(&rcand->foundation, "%08x", rand_u32());
if (err)
goto out;
icecomp_printf(icem_comp_find(icem, compid),
"added PeerReflexive remote candidate"
" with priority %u (%J)\n", prio, addr);
out:
if (err)
mem_deref(rcand);
else if (rcp)
*rcp = rcand;
return err;
}
struct ice_cand *icem_cand_find(const struct list *lst, unsigned compid,
const struct sa *addr)
{
struct le *le;
for (le = list_head(lst); le; le = le->next) {
struct ice_cand *cand = le->data;
if (compid && cand->compid != compid)
continue;
if (addr && !sa_cmp(&cand->addr, addr, SA_ALL))
continue;
return cand;
}
return NULL;
}
/**
* Find the highest priority LCAND on the check-list of type HOST/RELAY
*
* @param icem ICE Media object
* @param compid Component ID
*
* @return Local candidate if found, otherwise NULL
*/
struct ice_cand *icem_lcand_find_checklist(const struct icem *icem,
unsigned compid)
{
struct le *le;
for (le = icem->checkl.head; le; le = le->next) {
struct ice_candpair *cp = le->data;
if (cp->lcand->compid != compid)
continue;
switch (cp->lcand->type) {
case ICE_CAND_TYPE_HOST:
case ICE_CAND_TYPE_RELAY:
return cp->lcand;
default:
break;
}
}
return NULL;
}
struct ice_cand *icem_lcand_base(struct ice_cand *lcand)
{
return lcand ? lcand->base : NULL;
}
const struct sa *icem_lcand_addr(const struct ice_cand *cand)
{
return cand ? &cand->addr : NULL;
}
int icem_cands_debug(struct re_printf *pf, const struct list *lst)
{
struct le *le;
int err;
err = re_hprintf(pf, " (%u)\n", list_count(lst));
for (le = list_head(lst); le && !err; le = le->next) {
const struct ice_cand *cand = le->data;
err |= re_hprintf(pf, " {%u} fnd=%-2s prio=%08x %24H",
cand->compid, cand->foundation, cand->prio,
icem_cand_print, cand);
if (sa_isset(&cand->rel, SA_ADDR))
err |= re_hprintf(pf, " (rel-addr=%J)", &cand->rel);
err |= re_hprintf(pf, "\n");
}
return err;
}
int icem_cand_print(struct re_printf *pf, const struct ice_cand *cand)
{
int err = 0;
if (!cand)
return 0;
if (cand->ifname)
err |= re_hprintf(pf, "%s:", cand->ifname);
err |= re_hprintf(pf, "%s:%J",
ice_cand_type2name(cand->type), &cand->addr);
return err;
}
enum ice_cand_type icem_cand_type(const struct ice_cand *cand)
{
return cand ? cand->type : (enum ice_cand_type)-1;
}