summaryrefslogtreecommitdiff
path: root/pjnath/src/pjnath
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2007-03-26 13:25:07 +0000
committerBenny Prijono <bennylp@teluu.com>2007-03-26 13:25:07 +0000
commit63bbc72f8536347656ac59dea7fb9576c82ac55d (patch)
tree4a8b7fbb7b357b82cdeb49c44672439843155785 /pjnath/src/pjnath
parent84b0defcf6903a8b014ab1ba38d8923282f230ed (diff)
ICE: implement RTCP component and cancelling check in progress
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1106 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjnath/src/pjnath')
-rw-r--r--pjnath/src/pjnath/ice.c139
-rw-r--r--pjnath/src/pjnath/ice_stream_transport.c154
-rw-r--r--pjnath/src/pjnath/stun_session.c25
3 files changed, 218 insertions, 100 deletions
diff --git a/pjnath/src/pjnath/ice.c b/pjnath/src/pjnath/ice.c
index bfae8dae..3dc92646 100644
--- a/pjnath/src/pjnath/ice.c
+++ b/pjnath/src/pjnath/ice.c
@@ -56,11 +56,11 @@ static const char *clist_state_name[] =
"Completed"
};
-#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(chk) (chk - ice->clist.checks)
+#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)
typedef struct stun_data
@@ -130,6 +130,12 @@ static pj_bool_t stun_auth_verify_nonce(const pj_stun_msg *msg,
const pj_str_t *nonce);
+PJ_DEF(const char*) pj_ice_get_cand_type_name(pj_ice_cand_type type)
+{
+ return cand_type_names[type];
+}
+
+
/*
* Create ICE stream session.
*/
@@ -176,8 +182,7 @@ PJ_DEF(pj_status_t) pj_ice_create(pj_stun_config *stun_cfg,
for (i=0; i<comp_cnt; ++i) {
pj_ice_comp *comp;
comp = &ice->comp[i];
- comp->comp_id = i+1;
- comp->nominated_check_id = -1;
+ comp->valid_check = NULL;
}
if (local_ufrag == NULL) {
@@ -411,6 +416,7 @@ PJ_DEF(pj_status_t) pj_ice_add_cand(pj_ice *ice,
PJ_ASSERT_RETURN(ice && comp_id && local_pref &&
foundation && addr && base_addr && addr_len,
PJ_EINVAL);
+ PJ_ASSERT_RETURN(comp_id <= ice->comp_cnt, PJ_EINVAL);
pj_mutex_lock(ice->mutex);
@@ -498,6 +504,7 @@ PJ_DEF(pj_status_t) pj_ice_find_default_cand(pj_ice *ice,
unsigned i;
PJ_ASSERT_RETURN(ice && comp_id && cand_id, PJ_EINVAL);
+ PJ_ASSERT_RETURN(comp_id <= ice->comp_cnt, PJ_EINVAL);
*cand_id = -1;
@@ -585,7 +592,7 @@ static pj_uint64_t CALC_CHECK_PRIO(const pj_ice *ice,
}
static const char *dump_check(char *buffer, unsigned bufsize,
- const pj_ice *ice,
+ const pj_ice_checklist *clist,
const pj_ice_check *check)
{
const pj_ice_cand *lcand = check->lcand;
@@ -597,8 +604,9 @@ static const char *dump_check(char *buffer, unsigned bufsize,
if (lcand->addr.addr.sa_family == PJ_AF_INET) {
len = pj_ansi_snprintf(buffer, bufsize,
- "%d: %s:%d-->%s:%d",
- GET_CHECK_ID(check),
+ "%d: [%d] %s:%d-->%s:%d",
+ GET_CHECK_ID(clist, check),
+ check->lcand->comp_id,
laddr, (int)pj_ntohs(lcand->addr.ipv4.sin_port),
pj_inet_ntoa(rcand->addr.ipv4.sin_addr),
(int)pj_ntohs(rcand->addr.ipv4.sin_port));
@@ -627,7 +635,7 @@ static void dump_checklist(const char *title, const pj_ice *ice,
for (i=0; i<clist->count; ++i) {
const pj_ice_check *c = &clist->checks[i];
LOG4((ice->obj_name, " %s (%s, state=%s)",
- dump_check(buffer, sizeof(buffer), ice, c),
+ dump_check(buffer, sizeof(buffer), clist, c),
(c->nominated ? "nominated" : "not nominated"),
check_state_name[c->state]));
}
@@ -646,7 +654,7 @@ static void check_set_state(pj_ice *ice, pj_ice_check *check,
pj_assert(check->state < PJ_ICE_CHECK_STATE_SUCCEEDED);
LOG5((ice->obj_name, "Check %s: state changed from %s to %s",
- dump_check(buf, sizeof(buf), ice, check),
+ dump_check(buf, sizeof(buf), &ice->clist, check),
check_state_name[check->state],
check_state_name[st]));
check->state = st;
@@ -759,7 +767,8 @@ static void prune_checklist(pj_ice *ice, pj_ice_checklist *clist)
char buf[CHECK_NAME_LEN];
LOG5((ice->obj_name, "Check %s pruned",
- dump_check(buf, sizeof(buf), ice, &clist->checks[j])));
+ dump_check(buf, sizeof(buf), &ice->clist,
+ &clist->checks[j])));
pj_array_erase(clist->checks, sizeof(clist->checks[0]),
clist->count, j);
@@ -812,33 +821,46 @@ static pj_bool_t on_check_complete(pj_ice *ice,
*/
if (check->err_code==PJ_SUCCESS && check->nominated) {
pj_ice_comp *comp;
+ char buf[CHECK_NAME_LEN];
LOG5((ice->obj_name, "Check %d is successful and nominated",
- GET_CHECK_ID(check)));
+ GET_CHECK_ID(&ice->clist, check)));
for (i=0; i<ice->clist.count; ++i) {
pj_ice_check *c = &ice->clist.checks[i];
- if (c->lcand->comp_id == check->lcand->comp_id &&
- (c->state==PJ_ICE_CHECK_STATE_FROZEN ||
- c->state==PJ_ICE_CHECK_STATE_WAITING))
- {
- LOG5((ice->obj_name,
- "Check %d to be failed because state is %s",
- i, check_state_name[c->state]));
- check_set_state(ice, c, PJ_ICE_CHECK_STATE_FAILED,
- PJ_ECANCELLED);
+ if (c->lcand->comp_id == check->lcand->comp_id) {
+ if (c->state < PJ_ICE_CHECK_STATE_IN_PROGRESS) {
+ /* Just fail Frozen/Waiting check */
+ LOG5((ice->obj_name,
+ "Check %s to be failed because state is %s",
+ dump_check(buf, sizeof(buf), &ice->clist, c),
+ check_state_name[c->state]));
+ check_set_state(ice, c, PJ_ICE_CHECK_STATE_FAILED,
+ PJ_ECANCELLED);
+
+ } else if (c->state == PJ_ICE_CHECK_STATE_IN_PROGRESS) {
+ /* State is IN_PROGRESS, cancel transaction */
+ if (c->tdata) {
+ LOG5((ice->obj_name,
+ "Cancelling check %s (In Progress)",
+ dump_check(buf, sizeof(buf), &ice->clist, c)));
+ pj_stun_session_cancel_req(c->lcand->stun_sess,
+ c->tdata, PJ_FALSE, 0);
+ c->tdata = NULL;
+ check_set_state(ice, c, PJ_ICE_CHECK_STATE_FAILED,
+ PJ_ECANCELLED);
+ }
+ }
}
}
/* Update the nominated check for the component */
comp = find_comp(ice, check->lcand->comp_id);
- if (comp->nominated_check_id < 0) {
- comp->nominated_check_id = GET_CHECK_ID(check);
+ if (comp->valid_check == NULL) {
+ comp->valid_check = check;
} else {
- pj_ice_check *nom_check;
- nom_check = &ice->clist.checks[comp->nominated_check_id];
- if (nom_check->prio < check->prio)
- comp->nominated_check_id = GET_CHECK_ID(check);
+ if (comp->valid_check->prio < check->prio)
+ comp->valid_check = check;
}
}
@@ -877,7 +899,7 @@ static pj_bool_t on_check_complete(pj_ice *ice,
* ICE processing as success, otherwise wait.
*/
for (i=0; i<ice->comp_cnt; ++i) {
- if (ice->comp[i].nominated_check_id == -1)
+ if (ice->comp[i].valid_check == NULL)
break;
}
if (i == ice->comp_cnt) {
@@ -949,9 +971,17 @@ PJ_DEF(pj_status_t) pj_ice_create_check_list(pj_ice *ice,
/* Save remote candidates */
ice->rcand_cnt = 0;
for (i=0; i<rcand_cnt; ++i) {
- pj_ice_cand *cn = &ice->rcand[ice->rcand_cnt++];
+ pj_ice_cand *cn = &ice->rcand[ice->rcand_cnt];
+
+ /* Ignore candidate which has no matching component ID */
+ pj_assert(rcand[i].comp_id > 0);
+ if (rcand[i].comp_id==0 || rcand[i].comp_id > ice->comp_cnt) {
+ continue;
+ }
+
pj_memcpy(cn, &rcand[i], sizeof(pj_ice_cand));
pj_strdup(ice->pool, &cn->foundation, &rcand[i].foundation);
+ ice->rcand_cnt++;
}
/* Generate checklist */
@@ -966,9 +996,7 @@ PJ_DEF(pj_status_t) pj_ice_create_check_list(pj_ice *ice,
if (clist->count > PJ_ICE_MAX_CHECKS) {
pj_mutex_unlock(ice->mutex);
return PJ_ETOOMANY;
- } else {
- clist->count++;
- }
+ }
/* A local candidate is paired with a remote candidate if
* and only if the two candidates have the same component ID
@@ -986,6 +1014,8 @@ PJ_DEF(pj_status_t) pj_ice_create_check_list(pj_ice *ice,
chk->state = PJ_ICE_CHECK_STATE_FROZEN;
chk->prio = CALC_CHECK_PRIO(ice, lcand, rcand);
+
+ clist->count++;
}
}
@@ -1026,7 +1056,6 @@ struct req_data
static pj_status_t perform_check(pj_ice *ice, pj_ice_checklist *clist,
unsigned check_id)
{
- pj_stun_tx_data *tdata;
pj_ice_comp *comp;
struct req_data *rd;
pj_ice_check *check;
@@ -1043,32 +1072,33 @@ static pj_status_t perform_check(pj_ice *ice, pj_ice_checklist *clist,
LOG5((ice->obj_name,
"Sending connectivity check for check %s",
- dump_check(buffer, sizeof(buffer), ice, check)));
+ dump_check(buffer, sizeof(buffer), clist, check)));
/* Create request */
status = pj_stun_session_create_req(lcand->stun_sess,
- PJ_STUN_BINDING_REQUEST, &tdata);
+ PJ_STUN_BINDING_REQUEST,
+ &check->tdata);
if (status != PJ_SUCCESS)
return status;
/* Attach data to be retrieved later when STUN request transaction
* completes and on_stun_request_complete() callback is called.
*/
- rd = PJ_POOL_ZALLOC_T(tdata->pool, struct req_data);
+ rd = PJ_POOL_ZALLOC_T(check->tdata->pool, struct req_data);
rd->ice = ice;
rd->clist = clist;
rd->ckid = check_id;
- tdata->user_data = (void*) rd;
+ check->tdata->user_data = (void*) rd;
/* Add PRIORITY */
prio = CALC_CAND_PRIO(PJ_ICE_CAND_TYPE_PRFLX, 65535,
lcand->comp_id);
- pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_PRIORITY,
- prio);
+ pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg,
+ PJ_STUN_ATTR_PRIORITY, prio);
/* Add USE-CANDIDATE and set this check to nominated */
if (ice->role == PJ_ICE_ROLE_CONTROLLING) {
- pj_stun_msg_add_empty_attr(tdata->pool, tdata->msg,
+ pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg,
PJ_STUN_ATTR_USE_CANDIDATE);
check->nominated = PJ_TRUE;
}
@@ -1080,9 +1110,11 @@ static pj_status_t perform_check(pj_ice *ice, pj_ice_checklist *clist,
/* Initiate STUN transaction to send the request */
status = pj_stun_session_send_msg(lcand->stun_sess, PJ_FALSE,
&rcand->addr,
- sizeof(pj_sockaddr_in), tdata);
- if (status != PJ_SUCCESS)
+ sizeof(pj_sockaddr_in), check->tdata);
+ if (status != PJ_SUCCESS) {
+ check->tdata = NULL;
return status;
+ }
check_set_state(ice, check, PJ_ICE_CHECK_STATE_IN_PROGRESS, PJ_SUCCESS);
return PJ_SUCCESS;
@@ -1249,6 +1281,10 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
check = &rd->clist->checks[rd->ckid];
clist = rd->clist;
+ /* Mark STUN transaction as complete */
+ pj_assert(tdata == check->tdata);
+ check->tdata = NULL;
+
pj_mutex_lock(ice->mutex);
/* Init lcand to NULL. lcand will be found from the mapped address
@@ -1258,7 +1294,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
LOG4((ice->obj_name,
"Check %s%s: connectivity check %s",
- dump_check(buffer, sizeof(buffer), ice, check),
+ dump_check(buffer, sizeof(buffer), &ice->clist, check),
(check->nominated ? " (nominated)" : " (not nominated)"),
(status==PJ_SUCCESS ? "SUCCESS" : "FAILED")));
@@ -1500,7 +1536,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
*/
if (i == ice->rcand_cnt) {
rcand = &ice->rcand[ice->rcand_cnt++];
- rcand->comp_id = comp->comp_id;
+ rcand->comp_id = lcand->comp_id;
rcand->type = PJ_ICE_CAND_TYPE_PRFLX;
rcand->prio = ap->value;
pj_memcpy(&rcand->addr, src_addr, src_addr_len);
@@ -1648,9 +1684,8 @@ PJ_DEF(pj_status_t) pj_ice_send_data( pj_ice *ice,
pj_status_t status = PJ_SUCCESS;
pj_ice_comp *comp;
unsigned cand_id;
- pj_ice_check *check;
- PJ_ASSERT_RETURN(ice, PJ_EINVAL);
+ PJ_ASSERT_RETURN(ice && comp_id && comp_id <= ice->comp_cnt, PJ_EINVAL);
pj_mutex_lock(ice->mutex);
@@ -1660,16 +1695,14 @@ PJ_DEF(pj_status_t) pj_ice_send_data( pj_ice *ice,
goto on_return;
}
- if (comp->nominated_check_id == -1) {
+ if (comp->valid_check == NULL) {
status = PJNATH_EICEINPROGRESS;
goto on_return;
}
- check = &ice->clist.checks[comp->nominated_check_id];
- cand_id = GET_LCAND_ID(check->lcand);
-
+ cand_id = GET_LCAND_ID(comp->valid_check->lcand);
status = (*ice->cb.on_tx_pkt)(ice, comp_id, cand_id, data, data_len,
- &check->rcand->addr,
+ &comp->valid_check->rcand->addr,
sizeof(pj_sockaddr_in));
on_return:
diff --git a/pjnath/src/pjnath/ice_stream_transport.c b/pjnath/src/pjnath/ice_stream_transport.c
index 86dcab37..f9748d25 100644
--- a/pjnath/src/pjnath/ice_stream_transport.c
+++ b/pjnath/src/pjnath/ice_stream_transport.c
@@ -60,14 +60,18 @@ static void stun_on_request_complete(pj_stun_session *sess,
const pj_stun_msg *response);
/* Utility: print error */
+#if PJ_LOG_MAX_LEVEL >= 3
static void ice_st_perror(pj_ice_st *ice_st, const char *title,
pj_status_t status)
{
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
- PJ_LOG(1,(ice_st->obj_name, "%s: %s", title, errmsg));
+ PJ_LOG(3,(ice_st->obj_name, "%s: %s", title, errmsg));
}
+#else
+# define ice_st_perror(ice_st, title, status)
+#endif
/* Get the prefix for the foundation */
@@ -224,6 +228,44 @@ static pj_str_t calc_foundation(pj_pool_t *pool,
return result;
}
+/* Add new candidate */
+static pj_status_t add_cand( pj_ice_st *ice_st,
+ pj_ice_st_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_st_cand *cand;
+
+ PJ_ASSERT_RETURN(ice_st && comp && addr, PJ_EINVAL);
+ PJ_ASSERT_RETURN(comp->cand_cnt < PJ_ICE_ST_MAX_ALIASES, PJ_ETOOMANY);
+
+ 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->cand_id = -1;
+ cand->local_pref = local_pref;
+ cand->foundation = calc_foundation(ice_st->pool, type, &addr->sin_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_st *ice_st,
unsigned comp_id,
@@ -300,7 +342,9 @@ static pj_status_t create_component(pj_ice_st *ice_st,
* to a specific interface, then only add that specific interface to
* cand_list.
*/
- if (comp->local_addr.ipv4.sin_addr.s_addr == 0) {
+ 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_ALIASES-2];
@@ -317,65 +361,63 @@ static pj_status_t create_component(pj_ice_st *ice_st,
/* Set default IP interface as the base address */
status = pj_gethostip(&comp->local_addr.ipv4.sin_addr);
if (status != PJ_SUCCESS)
- return status;
+ goto on_error;
/* Add candidate entry for each interface */
for (i=0; i<ifs_cnt; ++i) {
- pj_ice_st_cand *cand = &comp->cand_list[i];
-
- cand->type = PJ_ICE_CAND_TYPE_HOST;
- cand->status = PJ_SUCCESS;
- pj_memcpy(&cand->addr, &comp->local_addr, sizeof(pj_sockaddr_in));
- cand->addr.ipv4.sin_addr.s_addr = ifs[i].s_addr;
- cand->cand_id = -1;
- cand->local_pref = 65535;
- cand->foundation = calc_foundation(ice_st->pool,
- PJ_ICE_CAND_TYPE_HOST,
- &cand->addr.ipv4.sin_addr);
+ pj_sockaddr_in cand_addr;
+ pj_bool_t set_default;
+
+ /* 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.
*/
- if (cand->addr.ipv4.sin_addr.s_addr ==
- comp->local_addr.ipv4.sin_addr.s_addr)
- {
- comp->default_cand = i;
+ if (ifs[i].s_addr == comp->local_addr.ipv4.sin_addr.s_addr) {
+ set_default = PJ_TRUE;
+ } else {
+ set_default = PJ_FALSE;
}
- PJ_LOG(5,(ice_st->obj_name,
- "Interface %s:%d added to component %d",
- pj_inet_ntoa(cand->addr.ipv4.sin_addr),
- (int)pj_ntohs(cand->addr.ipv4.sin_port), comp_id));
+ status = add_cand(ice_st, comp, comp_id,
+ PJ_ICE_CAND_TYPE_HOST,
+ (pj_uint16_t)(65535-i), &cand_addr,
+ set_default);
+ if (status != PJ_SUCCESS)
+ goto on_error;
}
- comp->cand_cnt = ifs_cnt;
- } else {
+ } else if ((options & PJ_ICE_ST_OPT_DONT_ADD_CAND)==0) {
/* Socket is bound to specific address.
* In this case only add that address as a single entry in the
* cand_list table.
*/
- pj_ice_st_cand *cand = &comp->cand_list[0];
-
- cand->type = PJ_ICE_CAND_TYPE_HOST;
- cand->status = PJ_SUCCESS;
- pj_memcpy(&cand->addr, &comp->local_addr, sizeof(pj_sockaddr_in));
- cand->cand_id = -1;
- cand->local_pref = 65535;
- cand->foundation = calc_foundation(ice_st->pool,
- PJ_ICE_CAND_TYPE_HOST,
- &cand->addr.ipv4.sin_addr);
-
- comp->cand_cnt = 1;
- comp->default_cand = 0;
-
- PJ_LOG(5,(ice_st->obj_name,
- "Interface %s:%d added to component %d",
- pj_inet_ntoa(cand->addr.ipv4.sin_addr),
- (int)pj_ntohs(cand->addr.ipv4.sin_port), comp_id));
+ status = add_cand(ice_st, comp, comp_id,
+ PJ_ICE_CAND_TYPE_HOST,
+ 65535, &comp->local_addr.ipv4,
+ PJ_TRUE);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ } else if (options & PJ_ICE_ST_OPT_DONT_ADD_CAND) {
+ /* If application doesn't want to add candidate, just fix local_addr
+ * in case its value is zero.
+ */
+ if (comp->local_addr.ipv4.sin_addr.s_addr == 0) {
+ status = pj_gethostip(&comp->local_addr.ipv4.sin_addr);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
}
+
/* Done */
if (p_comp)
*p_comp = comp;
@@ -568,8 +610,7 @@ static pj_status_t get_stun_mapped_addr(pj_ice_st *ice_st,
PJ_DEF(pj_status_t) pj_ice_st_create_comp(pj_ice_st *ice_st,
unsigned comp_id,
pj_uint32_t options,
- const pj_sockaddr_in *addr,
- unsigned *p_itf_id)
+ const pj_sockaddr_in *addr)
{
pj_ice_st_comp *comp;
pj_status_t status;
@@ -601,15 +642,32 @@ PJ_DEF(pj_status_t) pj_ice_st_create_comp(pj_ice_st *ice_st,
}
/* Store this component */
- if (p_itf_id)
- *p_itf_id = ice_st->comp_cnt;
-
ice_st->comp[comp_id-1] = comp;
return PJ_SUCCESS;
}
+PJ_DEF(pj_status_t) pj_ice_st_add_cand( pj_ice_st *ice_st,
+ 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_st_comp *comp;
+
+
+ PJ_ASSERT_RETURN(ice_st && comp_id && addr, PJ_EINVAL);
+ PJ_ASSERT_RETURN(comp_id <= ice_st->comp_cnt, PJ_EINVAL);
+ PJ_ASSERT_RETURN(ice_st->comp[comp_id-1] != NULL, PJ_EINVALIDOP);
+
+ comp = ice_st->comp[comp_id-1];
+ return add_cand(ice_st, comp, comp_id, type, local_pref, addr,
+ set_default);
+}
+
+
PJ_DEF(pj_status_t) pj_ice_st_get_comps_status(pj_ice_st *ice_st)
{
unsigned i;
@@ -651,6 +709,8 @@ PJ_DEF(pj_status_t) pj_ice_st_init_ice(pj_ice_st *ice_st,
PJ_ASSERT_RETURN(ice_st, PJ_EINVAL);
/* Must not have ICE */
PJ_ASSERT_RETURN(ice_st->ice == NULL, PJ_EINVALIDOP);
+ /* Components must have been created */
+ PJ_ASSERT_RETURN(ice_st->comp[0] != NULL, PJ_EINVALIDOP);
/* Init callback */
pj_bzero(&ice_cb, sizeof(ice_cb));
diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c
index 1a048f2c..9c275a11 100644
--- a/pjnath/src/pjnath/stun_session.c
+++ b/pjnath/src/pjnath/stun_session.c
@@ -677,6 +677,31 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
return status;
}
+/*
+ * Cancel outgoing STUN transaction.
+ */
+PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess,
+ pj_stun_tx_data *tdata,
+ pj_bool_t notify,
+ pj_status_t notify_status)
+{
+ PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL);
+ PJ_ASSERT_RETURN(!notify || notify_status!=PJ_SUCCESS, PJ_EINVAL);
+ PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL);
+
+ pj_mutex_lock(sess->mutex);
+
+ if (notify) {
+ (sess->cb.on_request_complete)(sess, notify_status, tdata, NULL);
+ }
+
+ /* Just destroy tdata. This will destroy the transaction as well */
+ pj_stun_msg_destroy_tdata(sess, tdata);
+
+ pj_mutex_unlock(sess->mutex);
+ return PJ_SUCCESS;
+
+}
/* Send response */
static pj_status_t send_response(pj_stun_session *sess,