summaryrefslogtreecommitdiff
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
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
-rw-r--r--pjmedia/src/pjmedia/transport_ice.c14
-rw-r--r--pjnath/build/pjnath_test.dsp4
-rw-r--r--pjnath/include/pjnath/ice.h12
-rw-r--r--pjnath/include/pjnath/ice_stream_transport.h16
-rw-r--r--pjnath/include/pjnath/stun_session.h20
-rw-r--r--pjnath/src/pjnath-test/ice_test.c268
-rw-r--r--pjnath/src/pjnath-test/test.c2
-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
-rw-r--r--pjsip/src/pjsua-lib/pjsua_media.c2
11 files changed, 466 insertions, 190 deletions
diff --git a/pjmedia/src/pjmedia/transport_ice.c b/pjmedia/src/pjmedia/transport_ice.c
index 445f3732..3397e3e1 100644
--- a/pjmedia/src/pjmedia/transport_ice.c
+++ b/pjmedia/src/pjmedia/transport_ice.c
@@ -153,20 +153,19 @@ PJ_DEF(pj_status_t) pjmedia_ice_start_init( pjmedia_transport *tp,
const pj_sockaddr_in *turn_srv)
{
struct transport_ice *tp_ice = (struct transport_ice*)tp;
- unsigned comp_id;
pj_status_t status;
status = pj_ice_st_set_stun_srv(tp_ice->ice_st, stun_srv, turn_srv);
if (status != PJ_SUCCESS)
return status;
- status = pj_ice_st_create_comp(tp_ice->ice_st, 1, options, start_addr,
- &comp_id);
+ status = pj_ice_st_create_comp(tp_ice->ice_st, 1, options, start_addr);
if (status != PJ_SUCCESS)
return status;
if (tp_ice->ice_st->comp_cnt > 1) {
pj_sockaddr_in addr;
+ pj_uint16_t port;
pj_memcpy(&addr, &tp_ice->ice_st->comp[0]->local_addr.ipv4,
sizeof(pj_sockaddr_in));
@@ -175,9 +174,10 @@ PJ_DEF(pj_status_t) pjmedia_ice_start_init( pjmedia_transport *tp,
else
addr.sin_addr.s_addr = 0;
- addr.sin_port = (pj_uint16_t)(pj_ntohs(addr.sin_port)+1);
- status = pj_ice_st_create_comp(tp_ice->ice_st, 2, options,
- &addr, &comp_id);
+ port = pj_ntohs(addr.sin_port);
+ ++port;
+ addr.sin_port = pj_htons(port);
+ status = pj_ice_st_create_comp(tp_ice->ice_st, 2, options, &addr);
if (status != PJ_SUCCESS)
return status;
}
@@ -481,7 +481,7 @@ static pj_status_t tp_get_info(pjmedia_transport *tp,
comp = ice_st->comp[1];
pj_assert(comp->default_cand >= 0);
info->rtp_sock = comp->sock;
- pj_memcpy(&info->rtp_addr_name,
+ pj_memcpy(&info->rtcp_addr_name,
&comp->cand_list[comp->default_cand].addr,
sizeof(pj_sockaddr_in));
}
diff --git a/pjnath/build/pjnath_test.dsp b/pjnath/build/pjnath_test.dsp
index c46ded6f..cabea6a7 100644
--- a/pjnath/build/pjnath_test.dsp
+++ b/pjnath/build/pjnath_test.dsp
@@ -50,7 +50,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
-# ADD LINK32 netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../bin/pjnath-test-i386-win32-vc6-release.exe"
+# ADD LINK32 mswsock.lib iphlpapi.lib netapi32.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../bin/pjnath-test-i386-win32-vc6-release.exe"
!ELSEIF "$(CFG)" == "pjnath_test - Win32 Debug"
@@ -74,7 +74,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
-# ADD LINK32 netapi32.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjnath-test-i386-win32-vc6-debug.exe" /pdbtype:sept
+# ADD LINK32 iphlpapi.lib netapi32.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjnath-test-i386-win32-vc6-debug.exe" /pdbtype:sept
!ENDIF
diff --git a/pjnath/include/pjnath/ice.h b/pjnath/include/pjnath/ice.h
index cd3a4e7f..cc8b7918 100644
--- a/pjnath/include/pjnath/ice.h
+++ b/pjnath/include/pjnath/ice.h
@@ -68,7 +68,7 @@ enum pj_ice_type_pref
};
typedef struct pj_ice pj_ice;
-
+typedef struct pj_ice_check pj_ice_check;
#define PJ_ICE_MAX_CAND 16
#define PJ_ICE_MAX_COMP 8
@@ -80,8 +80,7 @@ typedef struct pj_ice pj_ice;
*/
typedef struct pj_ice_comp
{
- unsigned comp_id;
- int nominated_check_id;
+ pj_ice_check *valid_check;
} pj_ice_comp;
@@ -110,16 +109,17 @@ typedef enum pj_ice_check_state
} pj_ice_check_state;
-typedef struct pj_ice_check
+struct pj_ice_check
{
pj_ice_cand *lcand;
pj_ice_cand *rcand;
pj_uint64_t prio;
pj_ice_check_state state;
+ pj_stun_tx_data *tdata;
pj_bool_t nominated;
pj_status_t err_code;
-} pj_ice_check;
+};
typedef enum pj_ice_checklist_state
@@ -206,6 +206,8 @@ struct pj_ice
pj_ice_checklist valid_list;
};
+PJ_DECL(const char*) pj_ice_get_cand_type_name(pj_ice_cand_type type);
+
PJ_DECL(pj_status_t) pj_ice_create(pj_stun_config *stun_cfg,
const char *name,
diff --git a/pjnath/include/pjnath/ice_stream_transport.h b/pjnath/include/pjnath/ice_stream_transport.h
index fbdd9b43..c42476b4 100644
--- a/pjnath/include/pjnath/ice_stream_transport.h
+++ b/pjnath/include/pjnath/ice_stream_transport.h
@@ -60,9 +60,10 @@ typedef struct pj_ice_st_cb
enum pj_ice_st_option
{
- PJ_ICE_ST_OPT_DISABLE_STUN = 1,
- PJ_ICE_ST_OPT_DISABLE_RELAY = 2,
- PJ_ICE_ST_OPT_NO_PORT_RETRY = 4,
+ PJ_ICE_ST_OPT_DONT_ADD_CAND = 1,
+ PJ_ICE_ST_OPT_DISABLE_STUN = 2,
+ PJ_ICE_ST_OPT_DISABLE_RELAY = 4,
+ PJ_ICE_ST_OPT_NO_PORT_RETRY = 8,
};
@@ -143,8 +144,13 @@ PJ_DECL(pj_status_t) pj_ice_st_set_stun_srv(pj_ice_st *ice_st,
PJ_DECL(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_DECL(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_DECL(pj_status_t) pj_ice_st_get_comps_status(pj_ice_st *ice_st);
diff --git a/pjnath/include/pjnath/stun_session.h b/pjnath/include/pjnath/stun_session.h
index 3c5daee7..18d07c0f 100644
--- a/pjnath/include/pjnath/stun_session.h
+++ b/pjnath/include/pjnath/stun_session.h
@@ -318,6 +318,26 @@ PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess,
pj_stun_tx_data *tdata);
/**
+ * Cancel outgoing STUN transaction. This operation is only valid for outgoing
+ * STUN request, to cease retransmission of the request and destroy the
+ * STUN client transaction that is used to send the request.
+ *
+ * @param sess The STUN session instance.
+ * @param tdata The request message previously sent.
+ * @param notify Specify whether \a on_request_complete() callback should
+ * be called.
+ * @param status If \a on_request_complete() callback is to be called,
+ * specify the error status to be given when calling the
+ * callback. This error status MUST NOT be PJ_SUCCESS.
+ *
+ * @return PJ_SUCCESS if transaction is successfully cancelled.
+ */
+PJ_DECL(pj_status_t) pj_stun_session_cancel_req(pj_stun_session *sess,
+ pj_stun_tx_data *tdata,
+ pj_bool_t notify,
+ pj_status_t status);
+
+/**
* Application must call this function to notify the STUN session about
* the arrival of STUN packet. The STUN packet MUST have been checked
* first with #pj_stun_msg_check() to verify that this is indeed a valid
diff --git a/pjnath/src/pjnath-test/ice_test.c b/pjnath/src/pjnath-test/ice_test.c
index 4a956958..f8673356 100644
--- a/pjnath/src/pjnath-test/ice_test.c
+++ b/pjnath/src/pjnath-test/ice_test.c
@@ -18,7 +18,7 @@
*/
#include "test.h"
-#define THIS_FILE "ice.c"
+#define THIS_FILE "ice_test.c"
struct ice_data
@@ -43,13 +43,12 @@ static void on_ice_complete(pj_ice_st *icest,
struct ice_data *id = (struct ice_data*) icest->user_data;
id->complete = PJ_TRUE;
id->err_code = status;
- PJ_LOG(3,(THIS_FILE, " ICE %s complete %s", id->obj_name,
+ PJ_LOG(3,(THIS_FILE, " ICE %s complete %s", id->obj_name,
(status==PJ_SUCCESS ? "successfully" : "with failure")));
}
-static void on_rx_data(pj_ice_st *icest,
- unsigned comp_id, unsigned cand_id,
+static void on_rx_data(pj_ice_st *icest, unsigned comp_id,
void *pkt, pj_size_t size,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
@@ -68,7 +67,6 @@ static void on_rx_data(pj_ice_st *icest,
pj_assert(!"Invalid component ID");
}
- PJ_UNUSED_ARG(cand_id);
PJ_UNUSED_ARG(src_addr);
PJ_UNUSED_ARG(src_addr_len);
}
@@ -101,7 +99,7 @@ static int ice_basic_create_destroy_test()
icest_cb.on_ice_complete = &on_ice_complete;
icest_cb.on_rx_data = &on_rx_data;
- status = pj_ice_st_create(&stun_cfg, NULL, NULL, &icest_cb, &im);
+ status = pj_ice_st_create(&stun_cfg, "icetest", 2, NULL, &icest_cb, &im);
if (status != PJ_SUCCESS)
return -10;
@@ -127,6 +125,88 @@ static pj_status_t start_ice(pj_ice_st *ist, pj_ice_st *remote)
}
+struct dummy_cand
+{
+ unsigned comp_id;
+ pj_ice_cand_type type;
+ const char *addr;
+ unsigned port;
+};
+
+static int init_ice_st(pj_ice_st *ice_st,
+ pj_bool_t add_valid_comp,
+ unsigned dummy_cnt,
+ struct dummy_cand cand[])
+{
+ pj_str_t a;
+ pj_status_t status;
+ unsigned i;
+
+ /* Create components */
+ for (i=0; i<ice_st->comp_cnt; ++i) {
+ status = pj_ice_st_create_comp(ice_st, i+1, PJ_ICE_ST_OPT_DONT_ADD_CAND, NULL);
+ if (status != PJ_SUCCESS)
+ return -21;
+ }
+
+ /* Add dummy candidates */
+ for (i=0; i<dummy_cnt; ++i) {
+ pj_sockaddr_in addr;
+
+ pj_sockaddr_in_init(&addr, pj_cstr(&a, cand[i].addr), (pj_uint16_t)cand[i].port);
+ status = pj_ice_st_add_cand(ice_st, cand[i].comp_id, cand[i].type,
+ 65535, &addr, PJ_FALSE);
+ if (status != PJ_SUCCESS)
+ return -22;
+ }
+
+ /* Add the real candidate */
+ if (add_valid_comp) {
+ for (i=0; i<ice_st->comp_cnt; ++i) {
+ status = pj_ice_st_add_cand(ice_st, i+1, PJ_ICE_CAND_TYPE_HOST, 65535,
+ &ice_st->comp[i]->local_addr.ipv4, PJ_TRUE);
+ if (status != PJ_SUCCESS)
+ return -23;
+ }
+ }
+
+ return 0;
+}
+
+
+/* When ICE completes, both agents should agree on the same candidate pair.
+ * Check that the remote address selected by agent1 is equal to the
+ * local address of selected by agent 2.
+ */
+static int verify_address(pj_ice_st *agent1, pj_ice_st *agent2,
+ unsigned comp_id)
+{
+ pj_ice_cand *rcand, *lcand;
+ int lcand_id;
+
+ if (agent1->ice->comp[comp_id-1].valid_check == NULL) {
+ PJ_LOG(3,(THIS_FILE, "....error: valid_check not set for comp_id %d", comp_id));
+ return -60;
+ }
+
+ /* Get default remote candidate of agent 1 */
+ rcand = agent1->ice->comp[comp_id-1].valid_check->rcand;
+
+ /* Get default local candidate of agent 2 */
+ pj_ice_find_default_cand(agent2->ice, comp_id, &lcand_id);
+ if (lcand_id < 0)
+ return -62;
+
+ lcand = &agent2->ice->lcand[lcand_id];
+
+ if (pj_memcmp(&rcand->addr, &lcand->addr, sizeof(pj_sockaddr_in))!=0) {
+ PJ_LOG(3,(THIS_FILE, "....error: the selected addresses are incorrect for comp_id %d", comp_id));
+ return -64;
+ }
+
+ return 0;
+}
+
/* Perform ICE test with the following parameters:
*
@@ -141,25 +221,38 @@ static pj_status_t start_ice(pj_ice_st *ist, pj_ice_st *remote)
* ICE processing.
*/
static int perform_ice_test(const char *title,
+ pj_bool_t expected_success,
+ unsigned comp_cnt,
+ pj_bool_t add_valid_comp,
unsigned wait_before_send,
- unsigned max_total_time)
+ unsigned max_total_time,
+ unsigned ocand_cnt,
+ struct dummy_cand ocand[],
+ unsigned acand_cnt,
+ struct dummy_cand acand[])
{
pj_ice_st *im1, *im2;
pj_ice_st_cb icest_cb;
struct ice_data *id1, *id2;
pj_timestamp t_start, t_end;
- pj_ice_cand *rcand;
+ unsigned i;
pj_str_t data_from_offerer, data_from_answerer;
pj_status_t status;
+#define CHECK_COMPLETE() if (id1->complete && id2->complete) { \
+ if (t_end.u32.lo==0) pj_get_timestamp(&t_end); \
+ } else {}
+
PJ_LOG(3,(THIS_FILE, "...%s", title));
+ pj_bzero(&t_end, sizeof(t_end));
+
pj_bzero(&icest_cb, sizeof(icest_cb));
icest_cb.on_ice_complete = &on_ice_complete;
icest_cb.on_rx_data = &on_rx_data;
/* Create first ICE */
- status = pj_ice_st_create(&stun_cfg, "offerer", NULL, &icest_cb, &im1);
+ status = pj_ice_st_create(&stun_cfg, "offerer", comp_cnt, NULL, &icest_cb, &im1);
if (status != PJ_SUCCESS)
return -20;
@@ -167,18 +260,13 @@ static int perform_ice_test(const char *title,
id1->obj_name = "offerer";
im1->user_data = id1;
- /* Add first component */
- status = pj_ice_st_add_comp(im1, 1);
- if (status != PJ_SUCCESS)
- return -21;
-
- /* Add host candidate */
- status = pj_ice_st_add_host_interface(im1, 1, 65535, NULL, NULL);
- if (status != PJ_SUCCESS)
- return -21;
+ /* Init components */
+ status = init_ice_st(im1, add_valid_comp, ocand_cnt, ocand);
+ if (status != 0)
+ return status;
/* Create second ICE */
- status = pj_ice_st_create(&stun_cfg, "answerer", NULL, &icest_cb, &im2);
+ status = pj_ice_st_create(&stun_cfg, "answerer", comp_cnt, NULL, &icest_cb, &im2);
if (status != PJ_SUCCESS)
return -25;
@@ -186,15 +274,11 @@ static int perform_ice_test(const char *title,
id2->obj_name = "answerer";
im2->user_data = id2;
- /* Add first component */
- status = pj_ice_st_add_comp(im2, 1);
- if (status != PJ_SUCCESS)
- return -26;
+ /* Init components */
+ status = init_ice_st(im2, add_valid_comp, acand_cnt, acand);
+ if (status != 0)
+ return status;
- /* Add host candidate */
- status = pj_ice_st_add_host_interface(im2, 1, 65535, NULL, NULL);
- if (status != PJ_SUCCESS)
- return -27;
/* Init ICE on im1 */
status = pj_ice_st_init_ice(im1, PJ_ICE_ROLE_CONTROLLING, NULL, NULL);
@@ -216,47 +300,55 @@ static int perform_ice_test(const char *title,
if (status != PJ_SUCCESS)
return -35;
+ /* Apply delay to let other checks commence */
+ pj_thread_sleep(40);
+
/* Mark start time */
pj_get_timestamp(&t_start);
/* Poll for wait_before_send msecs before we send the first data */
- for (;;) {
- pj_timestamp t_now;
+ if (expected_success) {
+ for (;;) {
+ pj_timestamp t_now;
- handle_events(1);
+ handle_events(1);
- pj_get_timestamp(&t_now);
- if (pj_elapsed_msec(&t_start, &t_now) >= wait_before_send)
- break;
- }
+ CHECK_COMPLETE();
- /* Send data. It must be successful! */
- data_from_offerer = pj_str("from offerer");
- status = pj_ice_send_data(im1->ice, 1, data_from_offerer.ptr, data_from_offerer.slen);
- if (status != PJ_SUCCESS)
- return -47;
-
- data_from_answerer = pj_str("from answerer");
- status = pj_ice_send_data(im2->ice, 1, data_from_answerer.ptr, data_from_answerer.slen);
- if (status != PJ_SUCCESS)
- return -48;
+ pj_get_timestamp(&t_now);
+ if (pj_elapsed_msec(&t_start, &t_now) >= wait_before_send)
+ break;
+ }
- /* Poll to allow data to be received */
- for (;;) {
- pj_timestamp t_now;
- handle_events(1);
- pj_get_timestamp(&t_now);
- if (pj_elapsed_msec(&t_start, &t_now) >= (wait_before_send + 200))
- break;
+ /* Send data. It must be successful! */
+ data_from_offerer = pj_str("from offerer");
+ status = pj_ice_send_data(im1->ice, 1, data_from_offerer.ptr, data_from_offerer.slen);
+ if (status != PJ_SUCCESS)
+ return -47;
+
+ data_from_answerer = pj_str("from answerer");
+ status = pj_ice_send_data(im2->ice, 1, data_from_answerer.ptr, data_from_answerer.slen);
+ if (status != PJ_SUCCESS)
+ return -48;
+
+ /* Poll to allow data to be received */
+ for (;;) {
+ pj_timestamp t_now;
+ handle_events(1);
+ CHECK_COMPLETE();
+ pj_get_timestamp(&t_now);
+ if (pj_elapsed_msec(&t_start, &t_now) >= (wait_before_send + 200))
+ break;
+ }
}
-
/* Just wait until both completes, or timed out */
while (!id1->complete || !id2->complete) {
pj_timestamp t_now;
handle_events(1);
+ CHECK_COMPLETE();
pj_get_timestamp(&t_now);
if (pj_elapsed_msec(&t_start, &t_now) >= max_total_time) {
PJ_LOG(3,(THIS_FILE, "....error: timed-out"));
@@ -265,7 +357,17 @@ static int perform_ice_test(const char *title,
}
/* Mark end-time */
- pj_get_timestamp(&t_end);
+ CHECK_COMPLETE();
+
+ /* If expected to fail, then just check that both fail */
+ if (!expected_success) {
+ /* Check status */
+ if (id1->err_code == PJ_SUCCESS)
+ return -51;
+ if (id2->err_code == PJ_SUCCESS)
+ return -52;
+ goto on_return;
+ }
/* Check status */
if (id1->err_code != PJ_SUCCESS)
@@ -274,17 +376,17 @@ static int perform_ice_test(const char *title,
return -56;
/* Verify that offerer gets answerer's transport address */
- rcand = im1->ice->clist.checks[im1->ice->comp[0].nominated_check_id].rcand;
- if (pj_memcmp(&rcand->addr, &im2->ice->lcand[0].addr, sizeof(pj_sockaddr_in))!=0) {
- PJ_LOG(3,(THIS_FILE, "....error: address mismatch"));
- return -60;
+ for (i=0; i<comp_cnt; ++i) {
+ status = verify_address(im1, im2, i+1);
+ if (status != 0)
+ return status;
}
/* And the other way around */
- rcand = im2->ice->clist.checks[im2->ice->comp[0].nominated_check_id].rcand;
- if (pj_memcmp(&rcand->addr, &im1->ice->lcand[0].addr, sizeof(pj_sockaddr_in))!=0) {
- PJ_LOG(3,(THIS_FILE, "....error: address mismatch"));
- return -70;
+ for (i=0; i<comp_cnt; ++i) {
+ status = verify_address(im2, im1, i+1);
+ if (status != 0)
+ return status;
}
/* Check that data is received in offerer */
@@ -308,12 +410,13 @@ static int perform_ice_test(const char *title,
}
+on_return:
+
/* Done */
- PJ_LOG(3,(THIS_FILE, "....success: ICE completed in %d msec",
+ PJ_LOG(3,(THIS_FILE, "....success: ICE completed in %d msec, waiting..",
pj_elapsed_msec(&t_start, &t_end)));
/* Wait for some more time */
- PJ_LOG(3,(THIS_FILE, ".....waiting.."));
for (;;) {
pj_timestamp t_now;
@@ -327,6 +430,7 @@ static int perform_ice_test(const char *title,
pj_ice_st_destroy(im1);
pj_ice_st_destroy(im2);
+ handle_events(100);
return 0;
}
@@ -337,6 +441,17 @@ int ice_test(void)
pj_pool_t *pool;
pj_ioqueue_t *ioqueue;
pj_timer_heap_t *timer_heap;
+ enum { D1=500, D2=5000, D3=15000 };
+ struct dummy_cand ocand[] =
+ {
+ {1, PJ_ICE_CAND_TYPE_SRFLX, "127.1.1.1", 65534 },
+ {2, PJ_ICE_CAND_TYPE_SRFLX, "127.1.1.1", 65535 },
+ };
+ struct dummy_cand acand[] =
+ {
+ {1, PJ_ICE_CAND_TYPE_SRFLX, "127.2.2.2", 65534 },
+ {2, PJ_ICE_CAND_TYPE_SRFLX, "127.2.2.2", 65535 },
+ };
pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
pj_ioqueue_create(pool, 12, &ioqueue);
@@ -344,7 +459,7 @@ int ice_test(void)
pj_stun_config_init(&stun_cfg, mem, 0, ioqueue, timer_heap);
- pj_log_set_level(5);
+ //pj_log_set_level(4);
/* Basic create/destroy */
rc = ice_basic_create_destroy_test();
@@ -352,24 +467,41 @@ int ice_test(void)
goto on_return;
/* Direct communication */
- rc = perform_ice_test("Direct connection", 500, 1000);
+ rc = perform_ice_test("Simple test (1 component)", PJ_TRUE, 1, PJ_TRUE, D1, D2, 0, NULL, 0, NULL);
+ if (rc != 0)
+ goto on_return;
+
+ /* Failure case (all checks fail) */
+ rc = perform_ice_test("Failure case (all checks fail)", PJ_FALSE, 1, PJ_FALSE, D3, D3, 1, ocand, 1, acand);
+ if (rc != 0)
+ goto on_return;
+
+ /* Direct communication with invalid address */
+ rc = perform_ice_test("With 1 unreachable address", PJ_TRUE, 1, PJ_TRUE, D1, D2, 1, ocand, 0, NULL);
if (rc != 0)
goto on_return;
/* Direct communication with invalid address */
- rc = perform_ice_test("Direct connection with 1 invalid address", 500, 1000);
+ rc = perform_ice_test("With 2 unreachable addresses (one each)", PJ_TRUE, 1, PJ_TRUE, D1, D2, 1, ocand, 1, acand);
if (rc != 0)
goto on_return;
/* Direct communication with two components */
- rc = perform_ice_test("Direct connection with two components", 500, 1000);
+ rc = perform_ice_test("With two components (RTP and RTCP)", PJ_TRUE, 2, PJ_TRUE, D1, D2, 0, NULL, 0, NULL);
+ if (rc != 0)
+ goto on_return;
+
+ /* Direct communication with mismatch number of components */
+
+ /* Direct communication with 2 components and 2 invalid address */
+ rc = perform_ice_test("With 2 two components and 2 unreachable address", PJ_TRUE, 2, PJ_TRUE, D1, D2, 1, ocand, 1, acand);
if (rc != 0)
goto on_return;
on_return:
- pj_log_set_level(3);
+ //pj_log_set_level(3);
pj_ioqueue_destroy(stun_cfg.ioqueue);
pj_pool_release(pool);
return rc;
diff --git a/pjnath/src/pjnath-test/test.c b/pjnath/src/pjnath-test/test.c
index 3dc2085d..6cc829d5 100644
--- a/pjnath/src/pjnath-test/test.c
+++ b/pjnath/src/pjnath-test/test.c
@@ -49,11 +49,9 @@ static int test_inner(void)
mem = &caching_pool.factory;
-#if 0
pj_log_set_level(3);
pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME |
PJ_LOG_HAS_MICRO_SEC);
-#endif
rc = pj_init();
if (rc != 0) {
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,
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index 2e3b88a4..c91a1ead 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -563,7 +563,7 @@ static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
pj_ice_st_comp comp;
int next_port;
- status = pjmedia_ice_create(pjsua_var.med_endpt, NULL, 1,
+ status = pjmedia_ice_create(pjsua_var.med_endpt, NULL, 2,
&pjsua_var.stun_cfg,
&pjsua_var.calls[i].med_tp);
if (status != PJ_SUCCESS) {