summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pjnath/build/pjnath.dsp4
-rw-r--r--pjnath/include/pjnath.h2
-rw-r--r--pjnath/include/pjnath/errno.h11
-rw-r--r--pjnath/include/pjnath/ice.h24
-rw-r--r--pjnath/include/pjnath/ice_mt.h114
-rw-r--r--pjnath/include/pjnath/ice_stream_transport.h185
-rw-r--r--pjnath/src/pjnath-test/ice_test.c200
-rw-r--r--pjnath/src/pjnath/ice.c243
-rw-r--r--pjnath/src/pjnath/ice_mt.c281
-rw-r--r--pjnath/src/pjnath/ice_stream_transport.c688
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);
+ }
+}
+
+