diff options
author | Benny Prijono <bennylp@teluu.com> | 2007-03-22 21:00:53 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2007-03-22 21:00:53 +0000 |
commit | f8d17a0d196e42dd886bcdd908085c810b3bba8d (patch) | |
tree | bc17175bf3659f55fe7c8835edfb4cff629268b6 /pjnath | |
parent | d83a429d3e7d308b725924078d893c77224d336e (diff) |
Final ICE stream transport
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1096 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjnath')
-rw-r--r-- | pjnath/build/pjnath.dsp | 4 | ||||
-rw-r--r-- | pjnath/include/pjnath.h | 2 | ||||
-rw-r--r-- | pjnath/include/pjnath/errno.h | 11 | ||||
-rw-r--r-- | pjnath/include/pjnath/ice.h | 24 | ||||
-rw-r--r-- | pjnath/include/pjnath/ice_mt.h | 114 | ||||
-rw-r--r-- | pjnath/include/pjnath/ice_stream_transport.h | 185 | ||||
-rw-r--r-- | pjnath/src/pjnath-test/ice_test.c | 200 | ||||
-rw-r--r-- | pjnath/src/pjnath/ice.c | 243 | ||||
-rw-r--r-- | pjnath/src/pjnath/ice_mt.c | 281 | ||||
-rw-r--r-- | pjnath/src/pjnath/ice_stream_transport.c | 688 |
10 files changed, 1097 insertions, 655 deletions
diff --git a/pjnath/build/pjnath.dsp b/pjnath/build/pjnath.dsp index 081f89ec..83bc6a23 100644 --- a/pjnath/build/pjnath.dsp +++ b/pjnath/build/pjnath.dsp @@ -95,7 +95,7 @@ SOURCE=..\src\pjnath\ice.c # End Source File
# Begin Source File
-SOURCE=..\src\pjnath\ice_mt.c
+SOURCE=..\src\pjnath\ice_stream_transport.c
# End Source File
# Begin Source File
@@ -135,7 +135,7 @@ SOURCE=..\include\pjnath\ice.h # End Source File
# Begin Source File
-SOURCE=..\include\pjnath\ice_mt.h
+SOURCE=..\include\pjnath\ice_stream_transport.h
# End Source File
# Begin Source File
diff --git a/pjnath/include/pjnath.h b/pjnath/include/pjnath.h index c3f85fc3..8574631d 100644 --- a/pjnath/include/pjnath.h +++ b/pjnath/include/pjnath.h @@ -20,7 +20,7 @@ #include <pjnath/config.h> #include <pjnath/errno.h> #include <pjnath/ice.h> -#include <pjnath/ice_mt.h> +#include <pjnath/ice_stream_transport.h> #include <pjnath/stun_auth.h> #include <pjnath/stun_config.h> #include <pjnath/stun_msg.h> diff --git a/pjnath/include/pjnath/errno.h b/pjnath/include/pjnath/errno.h index 0a4d5888..3a7c591f 100644 --- a/pjnath/include/pjnath/errno.h +++ b/pjnath/include/pjnath/errno.h @@ -155,6 +155,17 @@ #define PJ_EICEINCOMPID -1 /** * @hideinitializer + * Invalid ICE candidate ID + */ +#define PJ_EICEINCANDID -1 +/** + * @hideinitializer + * ICE session not available + */ +#define PJ_ENOICE -1 + + /** + * @hideinitializer * ICE check is in progress */ #define PJ_EICEINPROGRESS -1 diff --git a/pjnath/include/pjnath/ice.h b/pjnath/include/pjnath/ice.h index 94cd7a22..8aa77cbc 100644 --- a/pjnath/include/pjnath/ice.h +++ b/pjnath/include/pjnath/ice.h @@ -81,8 +81,6 @@ typedef struct pj_ice pj_ice; typedef struct pj_ice_comp { unsigned comp_id; - pj_stun_session *stun_sess; - pj_sockaddr local_addr; int nominated_check_id; } pj_ice_comp; @@ -99,6 +97,7 @@ typedef struct pj_ice_cand pj_sockaddr addr; pj_sockaddr base_addr; pj_sockaddr srv_addr; + pj_stun_session *stun_sess; } pj_ice_cand; typedef enum pj_ice_check_state @@ -145,11 +144,13 @@ typedef struct pj_ice_checklist typedef struct pj_ice_cb { void (*on_ice_complete)(pj_ice *ice, pj_status_t status); - pj_status_t (*on_tx_pkt)(pj_ice *ice, unsigned comp_id, + pj_status_t (*on_tx_pkt)(pj_ice *ice, unsigned comp_id, + unsigned cand_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len); - pj_status_t (*on_rx_data)(pj_ice *ice, unsigned comp_id, + void (*on_rx_data)(pj_ice *ice, unsigned comp_id, + unsigned cand_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len); @@ -212,17 +213,11 @@ PJ_DECL(pj_status_t) pj_ice_create(pj_stun_config *stun_cfg, const char *name, pj_ice_role role, const pj_ice_cb *cb, + const pj_str_t *local_ufrag, + const pj_str_t *local_passwd, pj_ice **p_ice); PJ_DECL(pj_status_t) pj_ice_destroy(pj_ice *ice); -PJ_DECL(pj_status_t) pj_ice_add_comp(pj_ice *ice, - unsigned comp_id, - const pj_sockaddr_t *local_addr, - unsigned addr_len); -PJ_DECL(pj_status_t) pj_ice_set_credentials(pj_ice *ice, - const pj_str_t *local_ufrag, - const pj_str_t *local_pass, - const pj_str_t *remote_ufrag, - const pj_str_t *remote_pass); +PJ_DECL(pj_status_t) pj_ice_add_comp(pj_ice *ice, unsigned comp_id); PJ_DECL(pj_status_t) pj_ice_add_cand(pj_ice *ice, unsigned comp_id, pj_ice_cand_type type, @@ -246,6 +241,8 @@ PJ_DECL(pj_status_t) pj_ice_get_cand(pj_ice *ice, pj_ice_cand **p_cand); PJ_DECL(pj_status_t) pj_ice_create_check_list(pj_ice *ice, + const pj_str_t *rem_ufrag, + const pj_str_t *rem_passwd, unsigned rem_cand_cnt, const pj_ice_cand rem_cand[]); PJ_DECL(pj_status_t) pj_ice_start_check(pj_ice *ice); @@ -256,6 +253,7 @@ PJ_DECL(pj_status_t) pj_ice_send_data(pj_ice *ice, pj_size_t data_len); PJ_DECL(pj_status_t) pj_ice_on_rx_pkt(pj_ice *ice, unsigned comp_id, + unsigned cand_id, void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *src_addr, diff --git a/pjnath/include/pjnath/ice_mt.h b/pjnath/include/pjnath/ice_mt.h deleted file mode 100644 index d96f39b6..00000000 --- a/pjnath/include/pjnath/ice_mt.h +++ /dev/null @@ -1,114 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef __PJNATH_ICE_MT_H__ -#define __PJNATH_ICE_MT_H__ - - -/** - * @file ice_mt.h - * @brief ICE Media Transport. - */ -#include <pjnath/ice.h> -#include <pj/ioqueue.h> - - -PJ_BEGIN_DECL - - -/** - * @defgroup PJNATH_ICE_MEDIA_TRANSPORT ICE Media Transport - * @brief ICE Media Transport - * @ingroup PJNATH_ICE - * @{ - */ - -typedef struct pj_icemt pj_icemt; - -typedef struct pj_icemt_cb -{ - void (*on_ice_complete)(pj_icemt *icemt, - pj_status_t status); - void (*on_rx_rtp)(pj_icemt *icemt, - void *pkt, pj_size_t size, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len); - void (*on_rx_rtcp)(pj_icemt *icemt, - void *pkt, pj_size_t size, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len); - -} pj_icemt_cb; - - -typedef struct pj_icemt_sock -{ - pj_icemt *icemt; - unsigned comp_id; - pj_sock_t sock; - pj_sockaddr addr; - pj_sockaddr base_addr; - pj_ioqueue_key_t *key; - pj_uint8_t pkt[1500]; - pj_ioqueue_op_key_t read_op; - pj_ioqueue_op_key_t write_op; - pj_sockaddr src_addr; - int src_addr_len; -} pj_icemt_sock; - - -struct pj_icemt -{ - pj_pool_t *pool; - pj_ice *ice; - void *user_data; - - pj_icemt_cb cb; - - pj_icemt_sock rtp; - pj_icemt_sock rtcp; - - pj_bool_t has_turn; - pj_sockaddr stun_srv; -}; - - -PJ_DECL(pj_status_t) pj_icemt_create(pj_stun_config *stun_cfg, - const char *name, - pj_ice_role role, - const pj_icemt_cb *cb, - unsigned rtp_port, - pj_bool_t has_rtcp, - pj_bool_t has_turn, - const pj_sockaddr *srv, - pj_icemt **p_icemt); -PJ_DECL(pj_status_t) pj_icemt_destroy(pj_icemt *icemt); - - - -/** - * @} - */ - - -PJ_END_DECL - - - -#endif /* __PJNATH_ICE_MT_H__ */ - diff --git a/pjnath/include/pjnath/ice_stream_transport.h b/pjnath/include/pjnath/ice_stream_transport.h new file mode 100644 index 00000000..c85aff98 --- /dev/null +++ b/pjnath/include/pjnath/ice_stream_transport.h @@ -0,0 +1,185 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJNATH_ICE_STREAM_TRANSPORT_H__ +#define __PJNATH_ICE_STREAM_TRANSPORT_H__ + + +/** + * @file ice_mt.h + * @brief ICE Media Transport. + */ +#include <pjnath/ice.h> +#include <pjlib-util/resolver.h> +#include <pj/ioqueue.h> + + +PJ_BEGIN_DECL + + +/** + * @defgroup PJNATH_ICE_STREAM_TRANSPORT ICE Stream Transport + * @brief Transport for media stream using ICE + * @ingroup PJNATH_ICE + * @{ + */ + +typedef struct pj_ice_st pj_ice_st; + +typedef struct pj_ice_st_cb +{ + void (*on_rx_data)(pj_ice_st *ice_st, + unsigned comp_id, unsigned cand_id, + void *pkt, pj_size_t size, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); + + void (*on_stun_srv_resolved)(pj_ice_st *ice_st, + pj_status_t status); + void (*on_interface_status)(pj_ice_st *ice_st, + void *notify_data, + pj_status_t status, + int itf_id); + void (*on_ice_complete)(pj_ice_st *ice_st, + pj_status_t status); + +} pj_ice_st_cb; + + +typedef struct pj_ice_st_comp +{ + unsigned comp_id; +} pj_ice_st_comp; + + +typedef struct pj_ice_st_interface +{ + pj_ice_st *ice_st; + pj_ice_cand_type type; + unsigned comp_id; + int cand_id; + pj_str_t foundation; + pj_uint16_t local_pref; + pj_sock_t sock; + pj_sockaddr addr; + pj_sockaddr base_addr; + pj_ioqueue_key_t *key; + pj_uint8_t pkt[1500]; + pj_ioqueue_op_key_t read_op; + pj_ioqueue_op_key_t write_op; + pj_sockaddr src_addr; + int src_addr_len; +} pj_ice_st_interface; + + +struct pj_ice_st +{ + char obj_name[PJ_MAX_OBJ_NAME]; + pj_pool_t *pool; + void *user_data; + pj_stun_config stun_cfg; + pj_ice_st_cb cb; + + pj_ice *ice; + + unsigned comp_cnt; + unsigned comps[PJ_ICE_MAX_COMP]; + + unsigned itf_cnt; + pj_ice_st_interface *itfs[PJ_ICE_MAX_CAND]; + + pj_dns_resolver *resolver; + pj_bool_t relay_enabled; + pj_str_t stun_domain; + pj_sockaddr_in stun_srv; +}; + + +PJ_DECL(pj_status_t) pj_ice_st_create(pj_stun_config *stun_cfg, + const char *name, + void *user_data, + const pj_ice_st_cb *cb, + pj_ice_st **p_ice_st); +PJ_DECL(pj_status_t) pj_ice_st_destroy(pj_ice_st *ice_st); + +PJ_DECL(pj_status_t) pj_ice_st_set_stun(pj_ice_st *ice_st, + pj_dns_resolver *resolver, + pj_bool_t enable_relay, + const pj_str_t *domain); +PJ_DECL(pj_status_t) pj_ice_st_set_stun_addr(pj_ice_st *ice_st, + pj_bool_t enable_relay, + const pj_sockaddr_in *srv_addr); + +PJ_DECL(pj_status_t) pj_ice_st_add_comp(pj_ice_st *ice_st, + unsigned comp_id); + +PJ_DECL(pj_status_t) pj_ice_st_add_host_interface(pj_ice_st *ice_st, + unsigned comp_id, + pj_uint16_t local_pref, + const pj_sockaddr_in *addr, + unsigned *p_itf_id, + pj_bool_t notify, + void *notify_data); +PJ_DECL(pj_status_t) pj_ice_st_add_all_host_interfaces(pj_ice_st *ice_st, + unsigned comp_id, + unsigned port, + pj_bool_t notify, + void *notify_data); +PJ_DECL(pj_status_t) pj_ice_st_add_stun_interface(pj_ice_st *ice_st, + unsigned comp_id, + unsigned local_port, + pj_bool_t notify, + void *notify_data); +PJ_DECL(pj_status_t) pj_ice_st_add_relay_interface(pj_ice_st *ice_st, + unsigned comp_id, + unsigned local_port, + pj_bool_t notify, + void *notify_data); + + +PJ_DECL(pj_status_t) pj_ice_st_init_ice(pj_ice_st *ice_st, + pj_ice_role role, + const pj_str_t *local_ufrag, + const pj_str_t *local_passwd); +PJ_DECL(pj_status_t) pj_ice_st_enum_cands(pj_ice_st *ice_st, + unsigned *count, + pj_ice_cand cand[]); +PJ_DECL(pj_status_t) pj_ice_st_start_ice(pj_ice_st *ice_st, + const pj_str_t *rem_ufrag, + const pj_str_t *rem_passwd, + unsigned rem_cand_cnt, + const pj_ice_cand rem_cand[]); +PJ_DECL(pj_status_t) pj_ice_st_stop_ice(pj_ice_st *ice_st); + +PJ_DECL(pj_status_t) pj_ice_st_send_data(pj_ice_st *ice_st, + unsigned comp_id, + const void *data, + pj_size_t data_len); + + +/** + * @} + */ + + +PJ_END_DECL + + + +#endif /* __PJNATH_ICE_STREAM_TRANSPORT_H__ */ + diff --git a/pjnath/src/pjnath-test/ice_test.c b/pjnath/src/pjnath-test/ice_test.c index efa2311a..d097a19a 100644 --- a/pjnath/src/pjnath-test/ice_test.c +++ b/pjnath/src/pjnath-test/ice_test.c @@ -37,10 +37,10 @@ struct ice_data static pj_stun_config stun_cfg; -static void on_ice_complete(pj_icemt *icemt, +static void on_ice_complete(pj_ice_st *icest, pj_status_t status) { - struct ice_data *id = (struct ice_data*) icemt->user_data; + 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, @@ -48,33 +48,27 @@ static void on_ice_complete(pj_icemt *icemt, } -static void on_rx_rtp(pj_icemt *icemt, - void *pkt, pj_size_t size, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len) -{ - struct ice_data *id = (struct ice_data*) icemt->user_data; - - id->rx_rtp_cnt++; - pj_memcpy(id->last_rx_rtp_data, pkt, size); - id->last_rx_rtp_data[size] = '\0'; - - PJ_UNUSED_ARG(src_addr); - PJ_UNUSED_ARG(src_addr_len); -} - - -static void on_rx_rtcp(pj_icemt *icemt, +static void on_rx_data(pj_ice_st *icest, + unsigned comp_id, unsigned cand_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { - struct ice_data *id = (struct ice_data*) icemt->user_data; - - id->rx_rtcp_cnt++; - pj_memcpy(id->last_rx_rtcp_data, pkt, size); - id->last_rx_rtcp_data[size] = '\0'; + struct ice_data *id = (struct ice_data*) icest->user_data; + + if (comp_id == 1) { + id->rx_rtp_cnt++; + pj_memcpy(id->last_rx_rtp_data, pkt, size); + id->last_rx_rtp_data[size] = '\0'; + } else if (comp_id == 2) { + id->rx_rtcp_cnt++; + pj_memcpy(id->last_rx_rtcp_data, pkt, size); + id->last_rx_rtcp_data[size] = '\0'; + } else { + pj_assert(!"Invalid component ID"); + } + PJ_UNUSED_ARG(cand_id); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); } @@ -97,57 +91,43 @@ static void handle_events(unsigned msec_timeout) /* Basic create and destroy test */ static int ice_basic_create_destroy_test() { - pj_icemt *im; - pj_ice *ice; - pj_icemt_cb icemt_cb; + pj_ice_st *im; + pj_ice_st_cb icest_cb; pj_status_t status; PJ_LOG(3,(THIS_FILE, "...basic create/destroy")); - pj_bzero(&icemt_cb, sizeof(icemt_cb)); - icemt_cb.on_ice_complete = &on_ice_complete; - icemt_cb.on_rx_rtp = &on_rx_rtp; - icemt_cb.on_rx_rtcp = &on_rx_rtcp; + pj_bzero(&icest_cb, sizeof(icest_cb)); + icest_cb.on_ice_complete = &on_ice_complete; + icest_cb.on_rx_data = &on_rx_data; - status = pj_icemt_create(&stun_cfg, NULL, PJ_ICE_ROLE_CONTROLLING, - &icemt_cb, 0, PJ_FALSE, PJ_FALSE, NULL, &im); + status = pj_ice_st_create(&stun_cfg, NULL, NULL, &icest_cb, &im); if (status != PJ_SUCCESS) return -10; - ice = im->ice; - - pj_icemt_destroy(im); + pj_ice_st_destroy(im); return 0; } -static pj_status_t set_remote_list(pj_icemt *src, pj_icemt *dst) +static pj_status_t start_ice(pj_ice_st *ist, pj_ice_st *remote) { - unsigned i, count; - unsigned cand_id[PJ_ICE_MAX_CAND]; + unsigned count; pj_ice_cand cand[PJ_ICE_MAX_CAND]; pj_status_t status; - count = PJ_ARRAY_SIZE(cand_id); - status = pj_ice_enum_cands(src->ice, &count, cand_id); + count = PJ_ARRAY_SIZE(cand); + status = pj_ice_st_enum_cands(remote, &count, cand); if (status != PJ_SUCCESS) return status; - for (i=0; i<count; ++i) { - pj_ice_cand *p_cand; - status = pj_ice_get_cand(src->ice, cand_id[i], &p_cand); - if (status != PJ_SUCCESS) - return status; - - pj_memcpy(&cand[i], p_cand, sizeof(pj_ice_cand)); - } - - status = pj_ice_create_check_list(dst->ice, count, cand); - return status; + return pj_ice_st_start_ice(ist, &remote->ice->rx_ufrag, &remote->ice->rx_pass, + count, cand); } + /* Perform ICE test with the following parameters: * * - title: The title of the test @@ -162,31 +142,24 @@ static pj_status_t set_remote_list(pj_icemt *src, pj_icemt *dst) */ static int perform_ice_test(const char *title, unsigned wait_before_send, - unsigned max_total_time, - unsigned ocand_cnt, - const pj_ice_cand ocand[], - unsigned acand_cnt, - const pj_ice_cand acand[]) + unsigned max_total_time) { - pj_icemt *im1, *im2; - pj_icemt_cb icemt_cb; + 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; pj_str_t data_from_offerer, data_from_answerer; - unsigned i; pj_status_t status; PJ_LOG(3,(THIS_FILE, "...%s", title)); - pj_bzero(&icemt_cb, sizeof(icemt_cb)); - icemt_cb.on_ice_complete = &on_ice_complete; - icemt_cb.on_rx_rtp = &on_rx_rtp; - icemt_cb.on_rx_rtcp = &on_rx_rtcp; + 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_icemt_create(&stun_cfg, "offerer", PJ_ICE_ROLE_CONTROLLING, - &icemt_cb, 0, PJ_FALSE, PJ_FALSE, NULL, &im1); + status = pj_ice_st_create(&stun_cfg, "offerer", NULL, &icest_cb, &im1); if (status != PJ_SUCCESS) return -20; @@ -194,19 +167,18 @@ static int perform_ice_test(const char *title, id1->obj_name = "offerer"; im1->user_data = id1; - /* Add additional candidates */ - for (i=0; i<ocand_cnt; ++i) { - status = pj_ice_add_cand(im1->ice, 1, ocand[i].type, 65535, - &ocand[i].foundation, &ocand[i].addr, - &ocand[i].base_addr, &ocand[i].srv_addr, - sizeof(pj_sockaddr_in), NULL); - if (status != PJ_SUCCESS) - return -22; - } + /* 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, PJ_FALSE, NULL); + if (status != PJ_SUCCESS) + return -21; /* Create second ICE */ - status = pj_icemt_create(&stun_cfg, "answerer", PJ_ICE_ROLE_CONTROLLED, - &icemt_cb, 0, PJ_FALSE, PJ_FALSE, NULL, &im2); + status = pj_ice_st_create(&stun_cfg, "answerer", NULL, &icest_cb, &im2); if (status != PJ_SUCCESS) return -25; @@ -214,51 +186,39 @@ static int perform_ice_test(const char *title, id2->obj_name = "answerer"; im2->user_data = id2; - /* Add additional candidates */ - for (i=0; i<acand_cnt; ++i) { - status = pj_ice_add_cand(im1->ice, 1, acand[i].type, 65535, - &acand[i].foundation, &acand[i].addr, - &acand[i].base_addr, &acand[i].srv_addr, - sizeof(pj_sockaddr_in), NULL); - if (status != PJ_SUCCESS) - return -22; - } + /* Add first component */ + status = pj_ice_st_add_comp(im2, 1); + if (status != PJ_SUCCESS) + return -26; - /* Set credentials */ - { - pj_str_t u1 = pj_str("offerer"); - pj_str_t p1 = pj_str("pass1"); - pj_str_t u2 = pj_str("answerer"); - pj_str_t p2 = pj_str("pass2"); + /* Add host candidate */ + status = pj_ice_st_add_host_interface(im2, 1, 65535, NULL, NULL, PJ_FALSE, NULL); + if (status != PJ_SUCCESS) + return -27; - pj_ice_set_credentials(im1->ice, &u1, &p1, &u2, &p2); - pj_ice_set_credentials(im2->ice, &u2, &p2, &u1, &p1); - } + /* Init ICE on im1 */ + status = pj_ice_st_init_ice(im1, PJ_ICE_ROLE_CONTROLLING, NULL, NULL); + if (status != PJ_SUCCESS) + return -29; + + /* Init ICE on im2 */ + status = pj_ice_st_init_ice(im2, PJ_ICE_ROLE_CONTROLLED, NULL, NULL); + if (status != PJ_SUCCESS) + return -29; - /* Send offer to im2 */ - status = set_remote_list(im1, im2); + /* Start ICE on im2 */ + status = start_ice(im2, im1); if (status != PJ_SUCCESS) return -30; - /* Send answer to im1 */ - status = set_remote_list(im2, im1); + /* Start ICE on im1 */ + status = start_ice(im1, im2); if (status != PJ_SUCCESS) return -35; /* Mark start time */ pj_get_timestamp(&t_start); - /* Both can start now */ - status = pj_ice_start_check(im1->ice); - if (status != PJ_SUCCESS) - return -40; - -#if 1 - status = pj_ice_start_check(im2->ice); - if (status != PJ_SUCCESS) - return -45; -#endif - /* Poll for wait_before_send msecs before we send the first data */ for (;;) { pj_timestamp t_now; @@ -365,8 +325,8 @@ static int perform_ice_test(const char *title, } - pj_icemt_destroy(im1); - pj_icemt_destroy(im2); + pj_ice_st_destroy(im1); + pj_ice_st_destroy(im2); return 0; } @@ -377,9 +337,6 @@ int ice_test(void) pj_pool_t *pool; pj_ioqueue_t *ioqueue; pj_timer_heap_t *timer_heap; - pj_ice_cand ocand[PJ_ICE_MAX_CAND]; - pj_ice_cand acand[PJ_ICE_MAX_CAND]; - pj_str_t s; pool = pj_pool_create(mem, NULL, 4000, 4000, NULL); pj_ioqueue_create(pool, 12, &ioqueue); @@ -395,24 +352,17 @@ int ice_test(void) goto on_return; /* Direct communication */ - rc = perform_ice_test("Direct connection", 500, 1000, 0, NULL, 0, NULL); + rc = perform_ice_test("Direct connection", 500, 1000); if (rc != 0) goto on_return; /* Direct communication with invalid address */ - pj_bzero(ocand, sizeof(ocand)); - pj_sockaddr_in_init(&ocand[0].addr.ipv4, pj_cstr(&s, "127.0.0.127"), 1234); - pj_sockaddr_in_init(&ocand[0].base_addr.ipv4, pj_cstr(&s, "127.0.0.128"), 1234); - ocand[0].comp_id = 1; - ocand[0].foundation = pj_str("H2"); - ocand[0].type = PJ_ICE_CAND_TYPE_HOST; - - rc = perform_ice_test("Direct connection with 1 invalid address", 500, 1000, 1, ocand, 0, NULL); + rc = perform_ice_test("Direct connection with 1 invalid address", 500, 1000); if (rc != 0) goto on_return; /* Direct communication with two components */ - rc = perform_ice_test("Direct connection with two components", 500, 1000, 0, NULL, 0, NULL); + rc = perform_ice_test("Direct connection with two components", 500, 1000); if (rc != 0) goto on_return; diff --git a/pjnath/src/pjnath/ice.c b/pjnath/src/pjnath/ice.c index de781706..9fb59238 100644 --- a/pjnath/src/pjnath/ice.c +++ b/pjnath/src/pjnath/ice.c @@ -21,6 +21,7 @@ #include <pj/addr_resolv.h> #include <pj/array.h> #include <pj/assert.h> +#include <pj/guid.h> #include <pj/log.h> #include <pj/os.h> #include <pj/pool.h> @@ -68,8 +69,8 @@ const pj_str_t peer_mapped_foundation = {"peer", 4}; typedef struct stun_data { pj_ice *ice; - unsigned comp_id; - pj_ice_comp *comp; + unsigned lcand_id; + pj_ice_cand *lcand; } stun_data; typedef struct timer_data @@ -132,20 +133,27 @@ static pj_bool_t stun_auth_verify_nonce(const pj_stun_msg *msg, const pj_str_t *nonce); +/* + * Create ICE stream session. + */ PJ_DEF(pj_status_t) pj_ice_create(pj_stun_config *stun_cfg, const char *name, pj_ice_role role, const pj_ice_cb *cb, + const pj_str_t *local_ufrag, + const pj_str_t *local_passwd, pj_ice **p_ice) { pj_pool_t *pool; pj_ice *ice; + char tmp[32]; + pj_str_t s; unsigned i; pj_status_t status; PJ_ASSERT_RETURN(stun_cfg && cb && p_ice, PJ_EINVAL); - if (!name) + if (name == NULL) name = "ice%p"; pool = pj_pool_create(stun_cfg->pf, name, 4000, 4000, NULL); @@ -170,15 +178,34 @@ PJ_DEF(pj_status_t) pj_ice_create(pj_stun_config *stun_cfg, ice->comp[i].nominated_check_id = -1; } + if (local_ufrag == NULL) { + pj_ansi_snprintf(tmp, sizeof(tmp), "%x", pj_rand()); + s = pj_str(tmp); + local_ufrag = &s; + } + pj_strdup(ice->pool, &ice->rx_ufrag, local_ufrag); + + if (local_passwd == NULL) { + pj_ansi_snprintf(tmp, sizeof(tmp), "%x", pj_rand()); + s = pj_str(tmp); + local_passwd = &s; + } + pj_strdup(ice->pool, &ice->rx_pass, local_passwd); + + /* Done */ *p_ice = ice; - LOG((ice->obj_name, "ICE media stream created")); + LOG((ice->obj_name, "ICE stream session created, role is %s agent", + (ice->role==PJ_ICE_ROLE_CONTROLLING ? "controlling" : "controlled"))); return PJ_SUCCESS; } +/* + * Destroy + */ static void destroy_ice(pj_ice *ice, pj_status_t reason) { @@ -189,11 +216,13 @@ static void destroy_ice(pj_ice *ice, } for (i=0; i<ice->comp_cnt; ++i) { - pj_ice_comp *comp = &ice->comp[i]; + /* Nothing to do */ + } - if (comp->stun_sess) { - pj_stun_session_destroy(comp->stun_sess); - comp->stun_sess = NULL; + for (i=0; i<ice->lcand_cnt; ++i) { + if (ice->lcand[i].stun_sess) { + pj_stun_session_destroy(ice->lcand[i].stun_sess); + ice->lcand[i].stun_sess = NULL; } } @@ -222,31 +251,27 @@ PJ_DEF(pj_status_t) pj_ice_destroy(pj_ice *ice) } +/* Find component by ID */ static pj_ice_comp *find_comp(const pj_ice *ice, unsigned comp_id) { unsigned i; for (i=0; i<ice->comp_cnt; ++i) { if (ice->comp[i].comp_id == comp_id) - return (pj_ice_comp*) &ice->comp[i]; + return (pj_ice_comp *) &ice->comp[i]; } return NULL; } -PJ_DEF(pj_status_t) pj_ice_add_comp(pj_ice *ice, - unsigned comp_id, - const pj_sockaddr_t *local_addr, - unsigned addr_len) +/* Add a new component */ +PJ_DEF(pj_status_t) pj_ice_add_comp(pj_ice *ice, unsigned comp_id) { - pj_stun_session_cb sess_cb; pj_ice_comp *comp; - pj_stun_auth_cred auth_cred; - stun_data *sd; - pj_status_t status; - PJ_ASSERT_RETURN(ice && local_addr && addr_len, PJ_EINVAL); + PJ_ASSERT_RETURN(ice && comp_id, PJ_EINVAL); PJ_ASSERT_RETURN(ice->comp_cnt < PJ_ARRAY_SIZE(ice->comp), PJ_ETOOMANY); + PJ_ASSERT_RETURN(comp_id==ice->comp_cnt+1, PJ_EICEINCOMPID); PJ_ASSERT_RETURN(find_comp(ice, comp_id) == NULL, PJ_EEXISTS); pj_mutex_lock(ice->mutex); @@ -254,40 +279,6 @@ PJ_DEF(pj_status_t) pj_ice_add_comp(pj_ice *ice, comp = &ice->comp[ice->comp_cnt]; comp->comp_id = comp_id; comp->nominated_check_id = -1; - pj_memcpy(&comp->local_addr, local_addr, addr_len); - - /* Init STUN callbacks */ - pj_bzero(&sess_cb, sizeof(sess_cb)); - sess_cb.on_request_complete = &on_stun_request_complete; - sess_cb.on_rx_indication = &on_stun_rx_indication; - sess_cb.on_rx_request = &on_stun_rx_request; - sess_cb.on_send_msg = &on_stun_send_msg; - - /* Create STUN session for this component */ - status = pj_stun_session_create(&ice->stun_cfg, ice->obj_name, - &sess_cb, PJ_FALSE, - &comp->stun_sess); - if (status != PJ_SUCCESS) { - pj_mutex_unlock(ice->mutex); - return status; - } - - /* Associate data with this STUN session */ - sd = PJ_POOL_ZALLOC_T(ice->pool, struct stun_data); - sd->ice = ice; - sd->comp_id = comp_id; - sd->comp = comp; - pj_stun_session_set_user_data(comp->stun_sess, sd); - - /* Init STUN authentication credential */ - pj_bzero(&auth_cred, sizeof(auth_cred)); - auth_cred.type = PJ_STUN_AUTH_CRED_DYNAMIC; - auth_cred.data.dyn_cred.get_auth = &stun_auth_get_auth; - auth_cred.data.dyn_cred.get_cred = &stun_auth_get_cred; - auth_cred.data.dyn_cred.get_password = &stun_auth_get_password; - auth_cred.data.dyn_cred.verify_nonce = &stun_auth_verify_nonce; - auth_cred.data.dyn_cred.user_data = comp->stun_sess; - pj_stun_session_set_credential(comp->stun_sess, &auth_cred); /* Done */ ice->comp_cnt++; @@ -408,42 +399,6 @@ static pj_bool_t stun_auth_verify_nonce(const pj_stun_msg *msg, } -PJ_DEF(pj_status_t) pj_ice_set_credentials(pj_ice *ice, - const pj_str_t *local_ufrag, - const pj_str_t *local_pass, - const pj_str_t *remote_ufrag, - const pj_str_t *remote_pass) -{ - char buf[128]; - pj_str_t username; - - username.ptr = buf; - - PJ_ASSERT_RETURN(ice && local_ufrag && local_pass && - remote_ufrag && remote_pass, PJ_EINVAL); - PJ_ASSERT_RETURN(local_ufrag->slen + remote_ufrag->slen < - sizeof(buf), PJ_ENAMETOOLONG); - - pj_strcpy(&username, remote_ufrag); - pj_strcat2(&username, ":"); - pj_strcat(&username, local_ufrag); - - pj_strdup(ice->pool, &ice->tx_uname, &username); - pj_strdup(ice->pool, &ice->tx_ufrag, remote_ufrag); - pj_strdup(ice->pool, &ice->tx_pass, remote_pass); - - pj_strcpy(&username, local_ufrag); - pj_strcat2(&username, ":"); - pj_strcat(&username, remote_ufrag); - - pj_strdup(ice->pool, &ice->rx_uname, &username); - pj_strdup(ice->pool, &ice->rx_ufrag, local_ufrag); - pj_strdup(ice->pool, &ice->rx_pass, local_pass); - - return PJ_SUCCESS; -} - - static pj_uint32_t CALC_CAND_PRIO(pj_ice_cand_type type, pj_uint32_t local_pref, pj_uint32_t comp_id) @@ -462,6 +417,9 @@ static pj_uint32_t CALC_CAND_PRIO(pj_ice_cand_type type, } +/* + * Add ICE candidate + */ PJ_DEF(pj_status_t) pj_ice_add_cand(pj_ice *ice, unsigned comp_id, pj_ice_cand_type type, @@ -474,6 +432,9 @@ PJ_DEF(pj_status_t) pj_ice_add_cand(pj_ice *ice, unsigned *p_cand_id) { pj_ice_cand *lcand; + pj_stun_session_cb sess_cb; + pj_stun_auth_cred auth_cred; + stun_data *sd; pj_status_t status = PJ_SUCCESS; char tmp[128]; @@ -500,8 +461,39 @@ PJ_DEF(pj_status_t) pj_ice_add_cand(pj_ice *ice, else pj_bzero(&lcand->srv_addr, sizeof(lcand->srv_addr)); - if (p_cand_id) - *p_cand_id = ice->lcand_cnt; + /* Init STUN callbacks */ + pj_bzero(&sess_cb, sizeof(sess_cb)); + sess_cb.on_request_complete = &on_stun_request_complete; + sess_cb.on_rx_indication = &on_stun_rx_indication; + sess_cb.on_rx_request = &on_stun_rx_request; + sess_cb.on_send_msg = &on_stun_send_msg; + + /* Create STUN session for this candidate */ + status = pj_stun_session_create(&ice->stun_cfg, ice->obj_name, + &sess_cb, PJ_FALSE, + &lcand->stun_sess); + if (status != PJ_SUCCESS) { + pj_mutex_unlock(ice->mutex); + return status; + } + + /* Associate data with this STUN session */ + sd = PJ_POOL_ZALLOC_T(ice->pool, struct stun_data); + sd->ice = ice; + sd->lcand_id = GET_LCAND_ID(lcand); + sd->lcand = lcand; + pj_stun_session_set_user_data(lcand->stun_sess, sd); + + /* Init STUN authentication credential */ + pj_bzero(&auth_cred, sizeof(auth_cred)); + auth_cred.type = PJ_STUN_AUTH_CRED_DYNAMIC; + auth_cred.data.dyn_cred.get_auth = &stun_auth_get_auth; + auth_cred.data.dyn_cred.get_cred = &stun_auth_get_cred; + auth_cred.data.dyn_cred.get_password = &stun_auth_get_password; + auth_cred.data.dyn_cred.verify_nonce = &stun_auth_verify_nonce; + auth_cred.data.dyn_cred.user_data = lcand->stun_sess; + pj_stun_session_set_credential(lcand->stun_sess, &auth_cred); + pj_ansi_strcpy(tmp, pj_inet_ntoa(lcand->addr.ipv4.sin_addr)); LOG((ice->obj_name, @@ -518,6 +510,9 @@ PJ_DEF(pj_status_t) pj_ice_add_cand(pj_ice *ice, (int)pj_htons(lcand->base_addr.ipv4.sin_port), lcand->prio, lcand->prio)); + if (p_cand_id) + *p_cand_id = ice->lcand_cnt; + ++ice->lcand_cnt; on_error: @@ -1041,19 +1036,42 @@ static pj_bool_t on_check_complete(pj_ice *ice, PJ_DEF(pj_status_t) pj_ice_create_check_list(pj_ice *ice, + const pj_str_t *rem_ufrag, + const pj_str_t *rem_passwd, unsigned rcand_cnt, const pj_ice_cand rcand[]) { pj_ice_checklist *clist; + char buf[128]; + pj_str_t username; timer_data *td; unsigned i, j; - PJ_ASSERT_RETURN(ice && rcand_cnt && rcand, PJ_EINVAL); + PJ_ASSERT_RETURN(ice && rem_ufrag && rem_passwd && rcand_cnt && rcand, + PJ_EINVAL); PJ_ASSERT_RETURN(rcand_cnt + ice->rcand_cnt <= PJ_ICE_MAX_CAND, PJ_ETOOMANY); pj_mutex_lock(ice->mutex); + /* Save credentials */ + username.ptr = buf; + + pj_strcpy(&username, rem_ufrag); + pj_strcat2(&username, ":"); + pj_strcat(&username, &ice->rx_ufrag); + + pj_strdup(ice->pool, &ice->tx_uname, &username); + pj_strdup(ice->pool, &ice->tx_ufrag, rem_ufrag); + pj_strdup(ice->pool, &ice->tx_pass, rem_passwd); + + pj_strcpy(&username, &ice->rx_ufrag); + pj_strcat2(&username, ":"); + pj_strcat(&username, rem_ufrag); + + pj_strdup(ice->pool, &ice->rx_uname, &username); + + /* Save remote candidates */ ice->rcand_cnt = 0; for (i=0; i<rcand_cnt; ++i) { @@ -1154,7 +1172,7 @@ static pj_status_t perform_check(pj_ice *ice, pj_ice_checklist *clist, dump_check(buffer, sizeof(buffer), ice, check))); /* Create request */ - status = pj_stun_session_create_req(comp->stun_sess, + status = pj_stun_session_create_req(lcand->stun_sess, PJ_STUN_BINDING_REQUEST, &tdata); if (status != PJ_SUCCESS) return status; @@ -1186,7 +1204,7 @@ 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(comp->stun_sess, PJ_FALSE, + status = pj_stun_session_send_msg(lcand->stun_sess, PJ_FALSE, &rcand->addr, sizeof(pj_sockaddr_in), tdata); if (status != PJ_SUCCESS) @@ -1331,7 +1349,7 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess, unsigned addr_len) { stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess); - return (*sd->ice->cb.on_tx_pkt)(sd->ice, sd->comp_id, + return (*sd->ice->cb.on_tx_pkt)(sd->ice, sd->lcand->comp_id, sd->lcand_id, pkt, pkt_size, dst_addr, addr_len); } @@ -1534,7 +1552,8 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, sd = (stun_data*) pj_stun_session_get_user_data(sess); ice = sd->ice; - comp = sd->comp; + lcand = sd->lcand; + comp = find_comp(ice, lcand->comp_id); pj_mutex_lock(ice->mutex); @@ -1621,26 +1640,6 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, PJ_TODO(DETERMINE_IF_REQUEST_COMES_FROM_RELAYED_CANDIDATE); is_relayed = PJ_FALSE; - /* Next find local candidate, by first finding a check in the checklist - * which base address is equal to the local address. - */ - for (i=0; i<ice->clist.count; ++i) { - pj_ice_check *c = &ice->clist.checks[i]; - if (sockaddr_cmp(&c->lcand->base_addr, &comp->local_addr)==0) - break; - } - - /* MUST find a local candidate! */ - pj_assert(i != ice->clist.count); - if (i == ice->clist.count) { - pj_mutex_unlock(ice->mutex); - LOG((ice->obj_name, "Error: unable to find local candidate for " - "incoming request")); - return PJ_SUCCESS; - } - - lcand = ice->clist.checks[i].lcand; - /* Now that we have local and remote candidate, check if we already * have this pair in our checklist. */ @@ -1758,6 +1757,7 @@ 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); @@ -1776,8 +1776,9 @@ PJ_DEF(pj_status_t) pj_ice_send_data( pj_ice *ice, } check = &ice->clist.checks[comp->nominated_check_id]; + cand_id = GET_LCAND_ID(check->lcand); - status = (*ice->cb.on_tx_pkt)(ice, comp_id, data, data_len, + status = (*ice->cb.on_tx_pkt)(ice, comp_id, cand_id, data, data_len, &check->rcand->addr, sizeof(pj_sockaddr_in)); @@ -1789,6 +1790,7 @@ on_return: PJ_DEF(pj_status_t) pj_ice_on_rx_pkt( pj_ice *ice, unsigned comp_id, + unsigned cand_id, void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *src_addr, @@ -1796,6 +1798,7 @@ PJ_DEF(pj_status_t) pj_ice_on_rx_pkt( pj_ice *ice, { pj_status_t status = PJ_SUCCESS; pj_ice_comp *comp; + pj_ice_cand *lcand; pj_status_t stun_status; PJ_ASSERT_RETURN(ice, PJ_EINVAL); @@ -1808,14 +1811,16 @@ PJ_DEF(pj_status_t) pj_ice_on_rx_pkt( pj_ice *ice, goto on_return; } + lcand = &ice->lcand[cand_id]; + stun_status = pj_stun_msg_check(pkt, pkt_size, PJ_STUN_IS_DATAGRAM); if (stun_status == PJ_SUCCESS) { - status = pj_stun_session_on_rx_pkt(comp->stun_sess, pkt, pkt_size, + status = pj_stun_session_on_rx_pkt(lcand->stun_sess, pkt, pkt_size, PJ_STUN_IS_DATAGRAM, NULL, src_addr, src_addr_len); } else { - status = (*ice->cb.on_rx_data)(ice, comp_id, pkt, pkt_size, - src_addr, src_addr_len); + (*ice->cb.on_rx_data)(ice, comp_id, cand_id, pkt, pkt_size, + src_addr, src_addr_len); } diff --git a/pjnath/src/pjnath/ice_mt.c b/pjnath/src/pjnath/ice_mt.c deleted file mode 100644 index 187a1baa..00000000 --- a/pjnath/src/pjnath/ice_mt.c +++ /dev/null @@ -1,281 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include <pjnath/ice_mt.h> -#include <pjnath/errno.h> -#include <pj/addr_resolv.h> -#include <pj/assert.h> -#include <pj/pool.h> -#include <pj/string.h> - - -#define RTP_COMP_ID 1 -#define RTCP_COMP_ID 2 - - - -/* ICE callbacks */ -static void on_ice_complete(pj_ice *ice, pj_status_t status); -static pj_status_t on_tx_pkt(pj_ice *ice, unsigned comp_id, - const void *pkt, pj_size_t size, - const pj_sockaddr_t *dst_addr, - unsigned dst_addr_len); -static pj_status_t on_rx_data(pj_ice *ice, unsigned comp_id, - void *pkt, pj_size_t size, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len); - -/* Ioqueue callback */ -static void on_read_complete(pj_ioqueue_key_t *key, - pj_ioqueue_op_key_t *op_key, - pj_ssize_t bytes_read); - -static void destroy_ice_sock(pj_icemt_sock *is); - -static pj_status_t create_ice_sock(pj_icemt *icemt, - pj_ioqueue_t *ioqueue, - unsigned comp_id, - unsigned port, - pj_icemt_sock *is) -{ - pj_ioqueue_callback ioqueue_cb; - const pj_str_t H1 = { "H1", 2 }; - int addr_len; - pj_status_t status; - - pj_bzero(is, sizeof(*is)); - is->sock = PJ_INVALID_SOCKET; - is->comp_id = comp_id; - is->icemt = icemt; - - status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &is->sock); - if (status != PJ_SUCCESS) - return status; - - /* Bind and get the local IP address */ - pj_sockaddr_in_init(&is->base_addr.ipv4, NULL, (pj_uint16_t)port); - status = pj_sock_bind(is->sock, &is->base_addr, sizeof(pj_sockaddr_in)); - if (status != PJ_SUCCESS) - goto on_error; - - addr_len = sizeof(is->base_addr); - status = pj_sock_getsockname(is->sock, &is->base_addr, &addr_len); - if (status != PJ_SUCCESS) - goto on_error; - - if (is->base_addr.ipv4.sin_addr.s_addr == 0) { - status = pj_gethostip(&is->base_addr.ipv4.sin_addr); - if (status != PJ_SUCCESS) - goto on_error; - } - - /* Register to ioqueue */ - pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); - ioqueue_cb.on_read_complete = &on_read_complete; - status = pj_ioqueue_register_sock(icemt->pool, ioqueue, is->sock, is, - &ioqueue_cb, &is->key); - if (status != PJ_SUCCESS) - goto on_error; - - pj_ioqueue_op_key_init(&is->read_op, sizeof(is->read_op)); - pj_ioqueue_op_key_init(&is->write_op, sizeof(is->write_op)); - - on_read_complete(is->key, &is->read_op, 0); - - /* Add new ICE component */ - status = pj_ice_add_comp(icemt->ice, comp_id, &is->base_addr, - sizeof(pj_sockaddr_in)); - if (status != PJ_SUCCESS) - goto on_error; - - /* Add host candidate */ - status = pj_ice_add_cand(icemt->ice, comp_id, PJ_ICE_CAND_TYPE_HOST, - 65535, &H1, &is->base_addr, &is->base_addr, - NULL, sizeof(pj_sockaddr_in), NULL); - if (status != PJ_SUCCESS) - goto on_error; - - return PJ_SUCCESS; - -on_error: - destroy_ice_sock(is); - return status; -} - - -static void on_read_complete(pj_ioqueue_key_t *key, - pj_ioqueue_op_key_t *op_key, - pj_ssize_t bytes_read) -{ - pj_icemt_sock *is = (pj_icemt_sock*) pj_ioqueue_get_user_data(key); - pj_ssize_t pkt_size; - pj_status_t status; - - if (bytes_read > 0) { - status = pj_ice_on_rx_pkt(is->icemt->ice, is->comp_id, - is->pkt, bytes_read, - &is->src_addr, is->src_addr_len); - } - - pkt_size = sizeof(is->pkt); - is->src_addr_len = sizeof(is->src_addr); - status = pj_ioqueue_recvfrom(key, op_key, is->pkt, &pkt_size, - PJ_IOQUEUE_ALWAYS_ASYNC, - &is->src_addr, &is->src_addr_len); - pj_assert(status == PJ_SUCCESS || status == PJ_EPENDING); -} - - -static void destroy_ice_sock(pj_icemt_sock *is) -{ - if (is->key) { - pj_ioqueue_unregister(is->key); - is->key = NULL; - is->sock = PJ_INVALID_SOCKET; - } else if (is->sock != PJ_INVALID_SOCKET && is->sock != 0) { - pj_sock_close(is->sock); - is->sock = PJ_INVALID_SOCKET; - } -} - - -PJ_DEF(pj_status_t) pj_icemt_create( pj_stun_config *stun_cfg, - const char *name, - pj_ice_role role, - const pj_icemt_cb *cb, - unsigned rtp_port, - pj_bool_t has_rtcp, - pj_bool_t has_turn, - const pj_sockaddr *srv, - pj_icemt **p_icemt) -{ - pj_pool_t *pool; - pj_icemt *icemt; - pj_ice_cb ice_cb; - pj_status_t status; - - pool = pj_pool_create(stun_cfg->pf, name, 512, 512, NULL); - icemt = PJ_POOL_ZALLOC_T(pool, struct pj_icemt); - icemt->pool = pool; - - - pj_bzero(&ice_cb, sizeof(ice_cb)); - ice_cb.on_ice_complete = &on_ice_complete; - ice_cb.on_tx_pkt = &on_tx_pkt; - ice_cb.on_rx_data = &on_rx_data; - - pj_memcpy(&icemt->cb, cb, sizeof(*cb)); - - status = pj_ice_create(stun_cfg, name, role, &ice_cb, &icemt->ice); - if (status != PJ_SUCCESS) - goto on_error; - - icemt->ice->user_data = (void*)icemt; - - icemt->has_turn = has_turn; - if (srv) - pj_memcpy(&icemt->stun_srv, srv, sizeof(pj_sockaddr)); - - status = create_ice_sock(icemt, stun_cfg->ioqueue, RTP_COMP_ID, - rtp_port, &icemt->rtp); - if (status != PJ_SUCCESS) - goto on_error; - - if (has_rtcp) { - if (rtp_port) ++rtp_port; - - status = create_ice_sock(icemt, stun_cfg->ioqueue, RTCP_COMP_ID, - rtp_port, &icemt->rtcp); - if (status != PJ_SUCCESS) - goto on_error; - } - - *p_icemt = icemt; - return PJ_SUCCESS; - -on_error: - if (icemt->ice) - pj_ice_destroy(icemt->ice); - pj_pool_release(pool); - return status; -} - - -PJ_DEF(pj_status_t) pj_icemt_destroy(pj_icemt *icemt) -{ - destroy_ice_sock(&icemt->rtp); - destroy_ice_sock(&icemt->rtcp); - - pj_ice_destroy(icemt->ice); - pj_pool_release(icemt->pool); - - return PJ_SUCCESS; -} - - -static void on_ice_complete(pj_ice *ice, pj_status_t status) -{ - pj_icemt *icemt = (pj_icemt*)ice->user_data; - (*icemt->cb.on_ice_complete)(icemt, status); -} - - -static pj_status_t on_tx_pkt(pj_ice *ice, unsigned comp_id, - const void *pkt, pj_size_t size, - const pj_sockaddr_t *dst_addr, - unsigned dst_addr_len) -{ - pj_icemt *icemt = (pj_icemt*)ice->user_data; - pj_icemt_sock *is; - pj_ssize_t pkt_size; - pj_status_t status; - - if (comp_id == RTP_COMP_ID) - is = &icemt->rtp; - else if (comp_id == RTCP_COMP_ID) - is = &icemt->rtcp; - else { - pj_assert(!"Invalid comp_id"); - return -1; - } - - pkt_size = size; - status = pj_ioqueue_sendto(is->key, &is->write_op, - pkt, &pkt_size, 0, - dst_addr, dst_addr_len); - - return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status; -} - - -static pj_status_t on_rx_data(pj_ice *ice, unsigned comp_id, - void *pkt, pj_size_t size, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len) -{ - pj_icemt *icemt = (pj_icemt*)ice->user_data; - - if (comp_id == RTP_COMP_ID && icemt->cb.on_rx_rtp) { - (*icemt->cb.on_rx_rtp)(icemt, pkt, size, src_addr, src_addr_len); - } else if (comp_id == RTCP_COMP_ID && icemt->cb.on_rx_rtcp) { - (*icemt->cb.on_rx_rtcp)(icemt, pkt, size, src_addr, src_addr_len); - } - return PJ_SUCCESS; -} - - diff --git a/pjnath/src/pjnath/ice_stream_transport.c b/pjnath/src/pjnath/ice_stream_transport.c new file mode 100644 index 00000000..475aa35d --- /dev/null +++ b/pjnath/src/pjnath/ice_stream_transport.c @@ -0,0 +1,688 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjnath/ice_stream_transport.h> +#include <pjnath/errno.h> +#include <pj/addr_resolv.h> +#include <pj/assert.h> +#include <pj/log.h> +#include <pj/pool.h> +#include <pj/string.h> + + + +/* ICE callbacks */ +static void on_ice_complete(pj_ice *ice, pj_status_t status); +static pj_status_t on_tx_pkt(pj_ice *ice, + unsigned comp_id, unsigned cand_id, + const void *pkt, pj_size_t size, + const pj_sockaddr_t *dst_addr, + unsigned dst_addr_len); +static void on_rx_data(pj_ice *ice, + unsigned comp_id, unsigned cand_id, + void *pkt, pj_size_t size, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); + +/* Ioqueue callback */ +static void on_read_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_read); + +static void destroy_ice_interface(pj_ice_st_interface *is); +static void destroy_ice_st(pj_ice_st *ice_st, pj_status_t reason); + +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)); +} + + +/* Get the prefix for the foundation */ +static int get_type_prefix(pj_ice_cand_type type) +{ + switch (type) { + case PJ_ICE_CAND_TYPE_HOST: return 'H'; + case PJ_ICE_CAND_TYPE_SRFLX: return 'S'; + case PJ_ICE_CAND_TYPE_PRFLX: return 'P'; + case PJ_ICE_CAND_TYPE_RELAYED: return 'R'; + default: + pj_assert(!"Invalid type"); + return 'U'; + } +} + + +/* + * Create new interface (i.e. socket) + */ +static pj_status_t create_ice_interface(pj_ice_st *ice_st, + pj_ice_cand_type type, + unsigned comp_id, + pj_uint16_t local_pref, + const pj_sockaddr_in *addr, + pj_ice_st_interface **p_is) +{ + pj_ioqueue_callback ioqueue_cb; + pj_ice_st_interface *is; + char foundation[32]; + int addr_len; + pj_status_t status; + + is = PJ_POOL_ZALLOC_T(ice_st->pool, pj_ice_st_interface); + is->type = type; + is->comp_id = comp_id; + is->cand_id = -1; + is->sock = PJ_INVALID_SOCKET; + is->ice_st = ice_st; + is->local_pref = local_pref; + + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &is->sock); + if (status != PJ_SUCCESS) + return status; + + /* Bind and get the local IP address */ + if (addr) + pj_memcpy(&is->base_addr, addr, sizeof(pj_sockaddr_in)); + else + pj_sockaddr_in_init(&is->base_addr.ipv4, NULL, 0); + + status = pj_sock_bind(is->sock, &is->base_addr, sizeof(pj_sockaddr_in)); + if (status != PJ_SUCCESS) + goto on_error; + + addr_len = sizeof(is->base_addr); + status = pj_sock_getsockname(is->sock, &is->base_addr, &addr_len); + if (status != PJ_SUCCESS) + goto on_error; + + if (is->base_addr.ipv4.sin_addr.s_addr == 0) { + status = pj_gethostip(&is->base_addr.ipv4.sin_addr); + if (status != PJ_SUCCESS) + goto on_error; + } + + /* Assign foundation */ + pj_ansi_snprintf(foundation, sizeof(foundation), "%c%x", + get_type_prefix(type), + (int)pj_ntohl(is->base_addr.ipv4.sin_addr.s_addr)); + pj_strdup2(ice_st->pool, &is->foundation, foundation); + + + /* Register to ioqueue */ + pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); + ioqueue_cb.on_read_complete = &on_read_complete; + status = pj_ioqueue_register_sock(ice_st->pool, ice_st->stun_cfg.ioqueue, + is->sock, is, &ioqueue_cb, &is->key); + if (status != PJ_SUCCESS) + goto on_error; + + pj_ioqueue_op_key_init(&is->read_op, sizeof(is->read_op)); + pj_ioqueue_op_key_init(&is->write_op, sizeof(is->write_op)); + + /* Kick start reading the socket */ + on_read_complete(is->key, &is->read_op, 0); + + /* Done */ + *p_is = is; + return PJ_SUCCESS; + +on_error: + destroy_ice_interface(is); + return status; +} + + +/* + * This is callback called by ioqueue on incoming packet + */ +static void on_read_complete(pj_ioqueue_key_t *key, + pj_ioqueue_op_key_t *op_key, + pj_ssize_t bytes_read) +{ + pj_ice_st_interface *is = (pj_ice_st_interface*) + pj_ioqueue_get_user_data(key); + pj_ssize_t pkt_size; + pj_status_t status; + + if (bytes_read > 0) { + + /* If we have an active ICE session, hand over all incoming + * packets to the ICE session. Otherwise just drop the packet. + */ + if (is->ice_st->ice) { + status = pj_ice_on_rx_pkt(is->ice_st->ice, + is->comp_id, is->cand_id, + is->pkt, bytes_read, + &is->src_addr, is->src_addr_len); + } + + } else if (bytes_read < 0) { + ice_st_perror(is->ice_st, "ioqueue read callback error", -bytes_read); + } + + /* Read next packet */ + pkt_size = sizeof(is->pkt); + is->src_addr_len = sizeof(is->src_addr); + status = pj_ioqueue_recvfrom(key, op_key, is->pkt, &pkt_size, + PJ_IOQUEUE_ALWAYS_ASYNC, + &is->src_addr, &is->src_addr_len); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + ice_st_perror(is->ice_st, "ioqueue recvfrom() error", status); + } +} + + +/* + * Destroy an interface + */ +static void destroy_ice_interface(pj_ice_st_interface *is) +{ + if (is->key) { + pj_ioqueue_unregister(is->key); + is->key = NULL; + is->sock = PJ_INVALID_SOCKET; + } else if (is->sock != PJ_INVALID_SOCKET && is->sock != 0) { + pj_sock_close(is->sock); + is->sock = PJ_INVALID_SOCKET; + } +} + + +/* + * Create ICE stream transport + */ +PJ_DECL(pj_status_t) pj_ice_st_create(pj_stun_config *stun_cfg, + const char *name, + void *user_data, + const pj_ice_st_cb *cb, + pj_ice_st **p_ice_st) +{ + pj_pool_t *pool; + pj_ice_st *ice_st; + + PJ_ASSERT_RETURN(stun_cfg && cb && p_ice_st, PJ_EINVAL); + PJ_ASSERT_RETURN(stun_cfg->ioqueue && stun_cfg->timer_heap, PJ_EINVAL); + + if (name == NULL) + name = "icest%p"; + + pool = pj_pool_create(stun_cfg->pf, name, 4000, 4000, NULL); + ice_st = PJ_POOL_ZALLOC_T(pool, pj_ice_st); + ice_st->pool = pool; + pj_memcpy(ice_st->obj_name, pool->obj_name, PJ_MAX_OBJ_NAME); + ice_st->user_data = user_data; + + pj_memcpy(&ice_st->cb, cb, sizeof(*cb)); + pj_memcpy(&ice_st->stun_cfg, stun_cfg, sizeof(*stun_cfg)); + + PJ_LOG(4,(ice_st->obj_name, "ICE stream transport created")); + + *p_ice_st = ice_st; + return PJ_SUCCESS; +} + + +static void destroy_ice_st(pj_ice_st *ice_st, pj_status_t reason) +{ + unsigned i; + char obj_name[PJ_MAX_OBJ_NAME]; + + if (reason == PJ_SUCCESS) { + pj_memcpy(obj_name, ice_st->obj_name, PJ_MAX_OBJ_NAME); + PJ_LOG(4,(obj_name, "ICE stream transport shutting down")); + } + + /* Destroy ICE if we have ICE */ + if (ice_st->ice) { + pj_ice_destroy(ice_st->ice); + ice_st->ice = NULL; + } + + /* Destroy all interfaces */ + for (i=0; i<ice_st->itf_cnt; ++i) { + destroy_ice_interface(ice_st->itfs[i]); + ice_st->itfs[i] = NULL; + } + ice_st->itf_cnt = 0; + + /* Done */ + pj_pool_release(ice_st->pool); + + if (reason == PJ_SUCCESS) { + PJ_LOG(4,(obj_name, "ICE stream transport destroyed")); + } +} + + +/* + * Destroy ICE stream transport. + */ +PJ_DEF(pj_status_t) pj_ice_st_destroy(pj_ice_st *ice_st) +{ + destroy_ice_st(ice_st, PJ_SUCCESS); + return PJ_SUCCESS; +} + + +/* + * Resolve STUN server + */ +PJ_DEF(pj_status_t) pj_ice_st_set_stun( pj_ice_st *ice_st, + pj_dns_resolver *resolver, + pj_bool_t enable_relay, + const pj_str_t *domain) +{ + /* Yeah, TODO */ + PJ_UNUSED_ARG(ice_st); + PJ_UNUSED_ARG(resolver); + PJ_UNUSED_ARG(enable_relay); + PJ_UNUSED_ARG(domain); + return -1; +} + + +/* + * Set STUN server address. + */ +PJ_DEF(pj_status_t) pj_ice_st_set_stun_addr( pj_ice_st *ice_st, + pj_bool_t enable_relay, + const pj_sockaddr_in *srv_addr) +{ + + PJ_ASSERT_RETURN(ice_st && srv_addr, PJ_EINVAL); + + ice_st->relay_enabled = enable_relay; + pj_strdup2(ice_st->pool, &ice_st->stun_domain, + pj_inet_ntoa(srv_addr->sin_addr)); + pj_memcpy(&ice_st->stun_srv, srv_addr, sizeof(pj_sockaddr_in)); + + return PJ_SUCCESS; +} + + +/* + * Add new component. + */ +PJ_DEF(pj_status_t) pj_ice_st_add_comp(pj_ice_st *ice_st, + unsigned comp_id) +{ + /* Verify arguments */ + PJ_ASSERT_RETURN(ice_st && comp_id, PJ_EINVAL); + + /* Can only add component when we don't have active ICE session */ + PJ_ASSERT_RETURN(ice_st->ice == NULL, PJ_EBUSY); + + /* Check that we don't have too many components */ + PJ_ASSERT_RETURN(ice_st->comp_cnt < PJ_ICE_MAX_COMP, PJ_ETOOMANY); + + /* Component ID must be valid */ + PJ_ASSERT_RETURN(comp_id <= PJ_ICE_MAX_COMP, PJ_EICEINCOMPID); + + /* First component ID must be 1, second must be 2, etc., and + * they must be registered in order. + */ + PJ_ASSERT_RETURN(ice_st->comps[comp_id-1] == ice_st->comp_cnt, + PJ_EICEINCOMPID); + + /* All in order, add the component. */ + ice_st->comps[ice_st->comp_cnt++] = comp_id; + + return PJ_SUCCESS; +} + + +/* Add interface */ +static void add_interface(pj_ice_st *ice_st, pj_ice_st_interface *is, + unsigned *p_itf_id, pj_bool_t notify, + void *notify_data) +{ + unsigned itf_id; + + itf_id = ice_st->itf_cnt++; + ice_st->itfs[itf_id] = is; + + if (p_itf_id) + *p_itf_id = itf_id; + + if (notify && ice_st->cb.on_interface_status) { + (*ice_st->cb.on_interface_status)(ice_st, notify_data, + PJ_SUCCESS, itf_id); + } +} + +/* + * Add new host interface. + */ +PJ_DEF(pj_status_t) pj_ice_st_add_host_interface(pj_ice_st *ice_st, + unsigned comp_id, + pj_uint16_t local_pref, + const pj_sockaddr_in *addr, + unsigned *p_itf_id, + pj_bool_t notify, + void *notify_data) +{ + pj_ice_st_interface *is; + pj_status_t status; + + /* Verify arguments */ + PJ_ASSERT_RETURN(ice_st && comp_id, PJ_EINVAL); + + /* Check that component ID present */ + PJ_ASSERT_RETURN(comp_id <= ice_st->comp_cnt, PJ_EICEINCOMPID); + + /* Can't add new interface while ICE is running */ + PJ_ASSERT_RETURN(ice_st->ice == NULL, PJ_EBUSY); + + /* Create interface */ + status = create_ice_interface(ice_st, PJ_ICE_CAND_TYPE_HOST, comp_id, + local_pref, addr, &is); + if (status != PJ_SUCCESS) + return status; + + /* For host interface, the address is the base address */ + pj_memcpy(&is->addr, &is->base_addr, sizeof(is->addr)); + + /* Store this interface */ + add_interface(ice_st, is, p_itf_id, notify, notify_data); + + return PJ_SUCCESS; +} + + +/* + * Enumerate and add all host interfaces. + */ +PJ_DEF(pj_status_t) pj_ice_st_add_all_host_interfaces(pj_ice_st *ice_st, + unsigned comp_id, + unsigned port, + pj_bool_t notify, + void *notify_data) +{ + pj_sockaddr_in addr; + pj_status_t status; + + /* Yeah, TODO. + * For now just add the default interface. + */ + pj_sockaddr_in_init(&addr, NULL, (pj_uint16_t)port); + + status = pj_gethostip(&addr.sin_addr); + if (status != PJ_SUCCESS) + return status; + + return pj_ice_st_add_host_interface(ice_st, comp_id, 65535, &addr, + NULL, notify, notify_data); +} + + +/* + * Add STUN mapping interface. + */ +PJ_DEF(pj_status_t) pj_ice_st_add_stun_interface(pj_ice_st *ice_st, + unsigned comp_id, + unsigned local_port, + pj_bool_t notify, + void *notify_data) +{ + /* Yeah, TODO */ + PJ_UNUSED_ARG(ice_st); + PJ_UNUSED_ARG(comp_id); + PJ_UNUSED_ARG(local_port); + PJ_UNUSED_ARG(notify); + PJ_UNUSED_ARG(notify_data); + return -1; +} + + +/* + * Add TURN mapping interface. + */ +PJ_DEF(pj_status_t) pj_ice_st_add_relay_interface(pj_ice_st *ice_st, + unsigned comp_id, + unsigned local_port, + pj_bool_t notify, + void *notify_data) +{ + /* Yeah, TODO */ + PJ_UNUSED_ARG(ice_st); + PJ_UNUSED_ARG(comp_id); + PJ_UNUSED_ARG(local_port); + PJ_UNUSED_ARG(notify); + PJ_UNUSED_ARG(notify_data); + return -1; +} + + +/* + * Create ICE! + */ +PJ_DEF(pj_status_t) pj_ice_st_init_ice(pj_ice_st *ice_st, + pj_ice_role role, + const pj_str_t *local_ufrag, + const pj_str_t *local_passwd) +{ + pj_status_t status; + unsigned i; + pj_ice_cb ice_cb; + + /* Check arguments */ + PJ_ASSERT_RETURN(ice_st, PJ_EINVAL); + /* Must not have ICE */ + PJ_ASSERT_RETURN(ice_st->ice == NULL, PJ_EINVALIDOP); + + /* Init callback */ + pj_bzero(&ice_cb, sizeof(ice_cb)); + ice_cb.on_ice_complete = &on_ice_complete; + ice_cb.on_rx_data = &on_rx_data; + ice_cb.on_tx_pkt = &on_tx_pkt; + + /* Create! */ + status = pj_ice_create(&ice_st->stun_cfg, ice_st->obj_name, role, + &ice_cb, local_ufrag, local_passwd, &ice_st->ice); + if (status != PJ_SUCCESS) + return status; + + /* Associate user data */ + ice_st->ice->user_data = (void*)ice_st; + + /* Add components */ + for (i=0; i<ice_st->comp_cnt; ++i) { + status = pj_ice_add_comp(ice_st->ice, ice_st->comps[i]); + if (status != PJ_SUCCESS) + goto on_error; + } + + /* Add candidates */ + for (i=0; i<ice_st->itf_cnt; ++i) { + pj_ice_st_interface *is= ice_st->itfs[i]; + status = pj_ice_add_cand(ice_st->ice, is->comp_id, is->type, + is->local_pref, &is->foundation, + &is->addr, &is->base_addr, NULL, + sizeof(pj_sockaddr_in), + (unsigned*)&is->cand_id); + if (status != PJ_SUCCESS) + goto on_error; + } + + return PJ_SUCCESS; + +on_error: + for (i=0; i<ice_st->itf_cnt; ++i) { + ice_st->itfs[i]->cand_id = -1; + } + if (ice_st->ice) { + pj_ice_destroy(ice_st->ice); + ice_st->ice = NULL; + } + return status; +} + + +/* + * Enum candidates + */ +PJ_DEF(pj_status_t) pj_ice_st_enum_cands(pj_ice_st *ice_st, + unsigned *count, + pj_ice_cand cand[]) +{ + unsigned i, cnt; + pj_ice_cand *pcand; + pj_status_t status; + + PJ_ASSERT_RETURN(ice_st && count && cand, PJ_EINVAL); + PJ_ASSERT_RETURN(ice_st->ice, PJ_EINVALIDOP); + + cnt = pj_ice_get_cand_cnt(ice_st->ice); + cnt = (cnt > *count) ? *count : cnt; + *count = 0; + + for (i=0; i<cnt; ++i) { + status = pj_ice_get_cand(ice_st->ice, i, &pcand); + if (status != PJ_SUCCESS) + return status; + + pj_memcpy(&cand[i], pcand, sizeof(pj_ice_cand)); + } + + *count = cnt; + return PJ_SUCCESS; +} + + +/* + * Start ICE processing ! + */ +PJ_DEF(pj_status_t) pj_ice_st_start_ice( pj_ice_st *ice_st, + const pj_str_t *rem_ufrag, + const pj_str_t *rem_passwd, + unsigned rem_cand_cnt, + const pj_ice_cand rem_cand[]) +{ + pj_status_t status; + + status = pj_ice_create_check_list(ice_st->ice, rem_ufrag, rem_passwd, + rem_cand_cnt, rem_cand); + if (status != PJ_SUCCESS) + return status; + + return pj_ice_start_check(ice_st->ice); +} + + +/* + * Stop ICE! + */ +PJ_DECL(pj_status_t) pj_ice_st_stop_ice(pj_ice_st *ice_st) +{ + if (ice_st->ice) { + pj_ice_destroy(ice_st->ice); + ice_st->ice = NULL; + } + + return PJ_SUCCESS; +} + + +/* + * Send data to peer agent. + */ +PJ_DEF(pj_status_t) pj_ice_st_send_data( pj_ice_st *ice_st, + unsigned comp_id, + const void *data, + pj_size_t data_len) +{ + if (!ice_st->ice) + return PJ_ENOICE; + + return pj_ice_send_data(ice_st->ice, comp_id, data, data_len); +} + + + +/* + * Callback called by ICE session when ICE processing is complete, either + * successfully or with failure. + */ +static void on_ice_complete(pj_ice *ice, pj_status_t status) +{ + pj_ice_st *ice_st = (pj_ice_st*)ice->user_data; + if (ice_st->cb.on_ice_complete) { + (*ice_st->cb.on_ice_complete)(ice_st, status); + } +} + + +/* + * Callback called by ICE session when it wants to send outgoing packet. + */ +static pj_status_t on_tx_pkt(pj_ice *ice, + unsigned comp_id, unsigned cand_id, + const void *pkt, pj_size_t size, + const pj_sockaddr_t *dst_addr, + unsigned dst_addr_len) +{ + pj_ice_st *ice_st = (pj_ice_st*)ice->user_data; + pj_ice_st_interface *is = NULL; + unsigned i; + pj_ssize_t pkt_size; + pj_status_t status; + + PJ_UNUSED_ARG(comp_id); + + for (i=0; i<ice_st->itf_cnt; ++i) { + if (ice_st->itfs[i]->cand_id == (int)cand_id) { + is = ice_st->itfs[i]; + break; + } + } + if (is == NULL) { + return PJ_EICEINCANDID; + } + + pkt_size = size; + status = pj_ioqueue_sendto(is->key, &is->write_op, + pkt, &pkt_size, 0, + dst_addr, dst_addr_len); + + return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status; +} + + +/* + * Callback called by ICE session when it receives application data. + */ +static void on_rx_data(pj_ice *ice, + unsigned comp_id, unsigned cand_id, + void *pkt, pj_size_t size, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ + pj_ice_st *ice_st = (pj_ice_st*)ice->user_data; + + if (ice_st->cb.on_rx_data) { + (*ice_st->cb.on_rx_data)(ice_st, comp_id, cand_id, + pkt, size, src_addr, src_addr_len); + } +} + + |