diff options
author | Benny Prijono <bennylp@teluu.com> | 2007-03-26 13:25:07 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2007-03-26 13:25:07 +0000 |
commit | 63bbc72f8536347656ac59dea7fb9576c82ac55d (patch) | |
tree | 4a8b7fbb7b357b82cdeb49c44672439843155785 /pjnath | |
parent | 84b0defcf6903a8b014ab1ba38d8923282f230ed (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')
-rw-r--r-- | pjnath/build/pjnath_test.dsp | 4 | ||||
-rw-r--r-- | pjnath/include/pjnath/ice.h | 12 | ||||
-rw-r--r-- | pjnath/include/pjnath/ice_stream_transport.h | 16 | ||||
-rw-r--r-- | pjnath/include/pjnath/stun_session.h | 20 | ||||
-rw-r--r-- | pjnath/src/pjnath-test/ice_test.c | 268 | ||||
-rw-r--r-- | pjnath/src/pjnath-test/test.c | 2 | ||||
-rw-r--r-- | pjnath/src/pjnath/ice.c | 139 | ||||
-rw-r--r-- | pjnath/src/pjnath/ice_stream_transport.c | 154 | ||||
-rw-r--r-- | pjnath/src/pjnath/stun_session.c | 25 |
9 files changed, 458 insertions, 182 deletions
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, |