summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pjnath/build/pjnath.dsp4
-rw-r--r--pjnath/include/pjnath/ice.h60
-rw-r--r--pjnath/include/pjnath/stun_msg.h26
-rw-r--r--pjnath/include/pjnath/stun_session.h2
-rw-r--r--pjnath/src/pjnath/ice.c659
-rw-r--r--pjnath/src/pjnath/stun_msg.c17
6 files changed, 693 insertions, 75 deletions
diff --git a/pjnath/build/pjnath.dsp b/pjnath/build/pjnath.dsp
index 74af4bf2..9cef5642 100644
--- a/pjnath/build/pjnath.dsp
+++ b/pjnath/build/pjnath.dsp
@@ -41,7 +41,7 @@ RSC=rc.exe
# PROP Intermediate_Dir "./output/pjnath-i386-win32-vc6-release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
-# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /Ob2 /I "../include" /I "../../pjlib/include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /c
+# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /Ob2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
@@ -65,7 +65,7 @@ LIB32=link.exe -lib
# PROP Intermediate_Dir "./output/pjnath-i386-win32-vc6-debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
diff --git a/pjnath/include/pjnath/ice.h b/pjnath/include/pjnath/ice.h
index 3277dc77..f42a793b 100644
--- a/pjnath/include/pjnath/ice.h
+++ b/pjnath/include/pjnath/ice.h
@@ -24,7 +24,7 @@
* @brief ICE socket.
*/
#include <pjnath/types.h>
-#include <pjnath/stun_endpoint.h>
+#include <pjnath/stun_session.h>
#include <pjlib-util/resolver.h>
#include <pj/sock.h>
@@ -64,9 +64,9 @@ enum pj_ice_type_pref
typedef struct pj_ice pj_ice;
-#define PJ_ICE_MAX_CAND 32
+#define PJ_ICE_MAX_CAND 16
#define PJ_ICE_MAX_COMP 8
-
+#define PJ_ICE_MAX_CHECKS 32
/**
* ICE component
@@ -104,7 +104,10 @@ typedef enum pj_ice_check_state
typedef struct pj_ice_check
{
- unsigned local_cand_id;
+ unsigned cand_id;
+ pj_uint32_t comp_id;
+ pj_str_t foundation;
+
pj_uint64_t check_prio;
pj_ice_check_state check_state;
@@ -127,7 +130,7 @@ typedef struct pj_ice_checklist
{
pj_ice_checklist_state state;
unsigned count;
- pj_ice_check *checks;
+ pj_ice_check checks[PJ_ICE_MAX_CHECKS];
} pj_ice_checklist;
@@ -136,10 +139,10 @@ typedef struct pj_ice_checklist
*/
typedef struct pj_ice_cb
{
- pj_bool_t (*on_found_cand)(pj_ice *sock,
- pj_ice_cand_type type,
- const pj_sockaddr_t *addr,
- int addr_len);
+ pj_status_t (*on_send_pkt)(pj_ice *ice,
+ const void *pkt, pj_size_t size,
+ const pj_sockaddr_t *dst_addr,
+ unsigned addr_len);
} pj_ice_cb;
@@ -153,6 +156,12 @@ typedef enum pj_ice_state
PJ_ICE_STATE_RESV_ERROR
} pj_ice_state;
+typedef enum pj_ice_role
+{
+ PJ_ICE_ROLE_CONTROLLED,
+ PJ_ICE_ROLE_CONTROLLING
+} pj_ice_role;
+
/**
* ICE structure.
*/
@@ -164,30 +173,49 @@ struct pj_ice
pj_mutex_t *mutex;
int af;
int sock_type;
-
+ pj_ice_role role;
pj_ice_state state;
+ pj_ice_cb cb;
+
+ /* STUN credentials */
+ pj_str_t tx_uname;
+ pj_str_t tx_pass;
+ pj_str_t rx_uname;
+ pj_str_t rx_pass;
+
/* Components */
unsigned comp_cnt;
pj_ice_comp comp[PJ_ICE_MAX_COMP];
/* Local candidates */
- unsigned cand_cnt;
- pj_ice_cand cand[PJ_ICE_MAX_CAND];
+ unsigned lcand_cnt;
+ pj_ice_cand lcand[PJ_ICE_MAX_CAND];
+
+ /* Remote candidates */
+ unsigned rcand_cnt;
+ pj_ice_cand rcand[PJ_ICE_MAX_CAND];
- /* Checklist */
+ /* Checklists */
pj_ice_checklist cklist;
+ pj_ice_checklist valid_list;
/* STUN servers */
pj_dns_resolver *resv;
pj_dns_async_query *resv_q;
pj_bool_t relay_enabled;
pj_sockaddr stun_srv;
+
+ /* STUN sessions */
+ pj_stun_session *tx_sess;
+ pj_stun_session *rx_sess;
};
PJ_DECL(pj_status_t) pj_ice_create(pj_stun_config *cfg,
const char *name,
+ pj_ice_role role,
+ const pj_ice_cb *cb,
int af,
int sock_type,
pj_ice **p_ice);
@@ -207,7 +235,11 @@ PJ_DECL(pj_status_t) pj_ice_add_comp(pj_ice *ice,
PJ_DECL(pj_status_t) pj_ice_add_sock_comp(pj_ice *ice,
unsigned comp_id,
pj_sock_t sock);
-
+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_start_gather(pj_ice *ice,
unsigned flags);
diff --git a/pjnath/include/pjnath/stun_msg.h b/pjnath/include/pjnath/stun_msg.h
index ae076295..08e5672d 100644
--- a/pjnath/include/pjnath/stun_msg.h
+++ b/pjnath/include/pjnath/stun_msg.h
@@ -1512,7 +1512,6 @@ PJ_DECL(pj_status_t) pj_stun_binary_attr_create(pj_pool_t *pool,
* if no data to be copied now.
* @param length Length of data, or zero if no data is to be
* copied now.
- * @param p_attr Pointer to receive the attribute.
*
* @return PJ_SUCCESS on success or the appropriate error code.
*/
@@ -1522,6 +1521,31 @@ PJ_DECL(pj_status_t) pj_stun_msg_add_binary_attr(pj_pool_t *pool,
const pj_uint8_t *data,
unsigned length);
+/**
+ * Create STUN empty attribute.
+ *
+ * @param pool The pool to allocate memory from.
+ * @param attr_type The attribute type, from #pj_stun_attr_type.
+ * @param p_attr Pointer to receive the attribute.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_stun_empty_attr_create(pj_pool_t *pool,
+ int attr_type,
+ pj_stun_empty_attr **p_attr);
+
+/**
+ * Create STUN empty attribute and add the attribute to the message.
+ *
+ * @param pool The pool to allocate memory from.
+ * @param msg The STUN message.
+ * @param attr_type The attribute type, from #pj_stun_attr_type.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_stun_msg_add_empty_attr(pj_pool_t *pool,
+ pj_stun_msg *msg,
+ int attr_type);
/**
* @}
diff --git a/pjnath/include/pjnath/stun_session.h b/pjnath/include/pjnath/stun_session.h
index 5f5bda30..bd28ecb6 100644
--- a/pjnath/include/pjnath/stun_session.h
+++ b/pjnath/include/pjnath/stun_session.h
@@ -138,6 +138,8 @@ struct pj_stun_tx_data
pj_stun_session *sess; /**< The STUN session. */
pj_stun_msg *msg; /**< The STUN message. */
+ void *user_data; /**< Arbitrary application data. */
+
pj_stun_client_tsx *client_tsx; /**< Client STUN transaction. */
pj_uint32_t msg_magic; /**< Message magic. */
pj_uint8_t msg_key[12]; /**< Message/transaction key. */
diff --git a/pjnath/src/pjnath/ice.c b/pjnath/src/pjnath/ice.c
index c92ceb5a..eb2ead7f 100644
--- a/pjnath/src/pjnath/ice.c
+++ b/pjnath/src/pjnath/ice.c
@@ -18,29 +18,81 @@
*/
#include <pjnath/ice.h>
#include <pjnath/errno.h>
+#include <pj/addr_resolv.h>
#include <pj/assert.h>
+#include <pj/log.h>
#include <pj/os.h>
#include <pj/pool.h>
#include <pj/string.h>
+static const char *check_state_name[] =
+{
+ "Frozen",
+ "Waiting",
+ "In Progress",
+ "Succeeded",
+ "Failed"
+};
+
static void destroy_ice(pj_ice *ice,
pj_status_t reason);
static void ice_set_state(pj_ice *ice,
pj_ice_state new_state);
+static pj_status_t on_stun_send_msg(pj_stun_session *sess,
+ const void *pkt,
+ pj_size_t pkt_size,
+ const pj_sockaddr_t *dst_addr,
+ unsigned addr_len);
+static pj_status_t on_stun_rx_request(pj_stun_session *sess,
+ const pj_uint8_t *pkt,
+ unsigned pkt_len,
+ const pj_stun_msg *msg,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len);
+static void on_stun_request_complete(pj_stun_session *sess,
+ pj_status_t status,
+ pj_stun_tx_data *tdata,
+ const pj_stun_msg *response);
+static pj_status_t on_stun_rx_indication(pj_stun_session *sess,
+ const pj_uint8_t *pkt,
+ unsigned pkt_len,
+ const pj_stun_msg *msg,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len);
+
+static pj_status_t stun_auth_get_auth(void *user_data,
+ pj_pool_t *pool,
+ pj_str_t *realm,
+ pj_str_t *nonce);
+static pj_status_t stun_auth_get_password(void *user_data,
+ const pj_str_t *realm,
+ const pj_str_t *username,
+ pj_pool_t *pool,
+ int *data_type,
+ pj_str_t *data);
+static pj_bool_t stun_auth_verify_nonce(void *user_data,
+ const pj_str_t *realm,
+ const pj_str_t *username,
+ const pj_str_t *nonce);
+
PJ_DEF(pj_status_t) pj_ice_create(pj_stun_config *cfg,
const char *name,
+ pj_ice_role role,
+ const pj_ice_cb *cb,
int af,
int sock_type,
pj_ice **p_ice)
{
pj_pool_t *pool;
pj_ice *ice;
+ pj_stun_session_cb sess_cb;
+ pj_stun_auth_cred auth_cred;
pj_status_t status;
- PJ_ASSERT_RETURN(cfg && p_ice, PJ_EINVAL);
+ PJ_ASSERT_RETURN(cfg && cb && p_ice, PJ_EINVAL);
PJ_ASSERT_RETURN(sock_type==PJ_SOCK_DGRAM || sock_type==PJ_SOCK_STREAM,
PJ_EINVAL);
@@ -52,6 +104,7 @@ PJ_DEF(pj_status_t) pj_ice_create(pj_stun_config *cfg,
ice->pool = pool;
ice->af = af;
ice->sock_type = sock_type;
+ ice->role = role;
pj_ansi_snprintf(ice->obj_name, sizeof(ice->obj_name),
name, ice);
@@ -63,8 +116,51 @@ PJ_DEF(pj_status_t) pj_ice_create(pj_stun_config *cfg,
return status;
}
+ pj_memcpy(&ice->cb, cb, sizeof(*cb));
+
+ /* 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;
+
+ /* 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_password = &stun_auth_get_password;
+ auth_cred.data.dyn_cred.verify_nonce = &stun_auth_verify_nonce;
+
+ /* Create STUN session for outgoing requests */
+ status = pj_stun_session_create(cfg, ice->obj_name, &sess_cb, PJ_FALSE,
+ &ice->tx_sess);
+ if (status != PJ_SUCCESS) {
+ destroy_ice(ice, status);
+ return status;
+ }
+
+ pj_stun_session_set_user_data(ice->tx_sess, ice);
+ auth_cred.data.dyn_cred.user_data = ice->tx_sess;
+ pj_stun_session_set_credential(ice->tx_sess, &auth_cred);
+
+ /* Create STUN session for incoming requests */
+ status = pj_stun_session_create(cfg, ice->obj_name, &sess_cb, PJ_FALSE,
+ &ice->rx_sess);
+ if (status != PJ_SUCCESS) {
+ destroy_ice(ice, status);
+ return status;
+ }
+
+ pj_stun_session_set_user_data(ice->rx_sess, ice);
+ auth_cred.data.dyn_cred.user_data = ice->rx_sess;
+ pj_stun_session_set_credential(ice->rx_sess, &auth_cred);
+
+ /* Done */
*p_ice = ice;
+ PJ_LOG(4,(ice->obj_name, "ICE session created"));
+
return PJ_SUCCESS;
}
@@ -72,6 +168,10 @@ PJ_DEF(pj_status_t) pj_ice_create(pj_stun_config *cfg,
static void destroy_ice(pj_ice *ice,
pj_status_t reason)
{
+ if (reason == PJ_SUCCESS) {
+ PJ_LOG(4,(ice->obj_name, "Destroying ICE session"));
+ }
+
if (ice->resv_q) {
pj_dns_resolver_cancel_query(ice->resv_q, PJ_FALSE);
ice->resv_q = NULL;
@@ -108,6 +208,9 @@ static void resolver_cb(void *user_data,
pj_dns_parsed_packet *response)
{
pj_assert(!"Not implemented yet!");
+ PJ_UNUSED_ARG(user_data);
+ PJ_UNUSED_ARG(status);
+ PJ_UNUSED_ARG(response);
}
PJ_DEF(pj_status_t) pj_ice_set_srv(pj_ice *ice,
@@ -259,6 +362,105 @@ PJ_DEF(pj_status_t) pj_ice_add_sock_comp( pj_ice *ice,
}
+static pj_status_t stun_auth_get_auth(void *user_data,
+ pj_pool_t *pool,
+ pj_str_t *realm,
+ pj_str_t *nonce)
+{
+ PJ_UNUSED_ARG(user_data);
+ PJ_UNUSED_ARG(pool);
+
+ realm->slen = 0;
+ nonce->slen = 0;
+
+ return PJ_SUCCESS;
+}
+
+
+static pj_status_t stun_auth_get_password(void *user_data,
+ const pj_str_t *realm,
+ const pj_str_t *username,
+ pj_pool_t *pool,
+ int *data_type,
+ pj_str_t *data)
+{
+ pj_stun_session *sess = (pj_stun_session *)user_data;
+ pj_ice *ice = (pj_ice*) pj_stun_session_get_user_data(sess);
+
+ PJ_UNUSED_ARG(realm);
+ PJ_UNUSED_ARG(pool);
+
+ if (sess == ice->tx_sess) {
+ /* Verify username */
+ if (pj_strcmp(username, &ice->tx_uname) != 0)
+ return -1;
+ *data_type = 0;
+ *data = ice->tx_pass;
+ } else {
+ /* The agent MUST accept a credential if the username consists
+ * of two values separated by a colon, where the first value is
+ * equal to the username fragment generated by the agent in an offer
+ * or answer for a session in-progress, and the MESSAGE-INTEGRITY
+ * is the output of a hash of the password and the STUN packet's
+ * contents.
+ */
+ PJ_TODO(CHECK_USERNAME_FOR_INCOMING_STUN_REQUEST);
+ *data_type = 0;
+ *data = ice->rx_pass;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+static pj_bool_t stun_auth_verify_nonce(void *user_data,
+ const pj_str_t *realm,
+ const pj_str_t *username,
+ const pj_str_t *nonce)
+{
+ /* We don't use NONCE */
+ PJ_UNUSED_ARG(user_data);
+ PJ_UNUSED_ARG(realm);
+ PJ_UNUSED_ARG(username);
+ PJ_UNUSED_ARG(nonce);
+ return PJ_TRUE;
+}
+
+
+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_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_pass, local_pass);
+
+ return PJ_SUCCESS;
+}
+
+
static pj_status_t gather_host_cands(pj_ice *ice)
{
unsigned i;
@@ -292,11 +494,13 @@ static pj_status_t gather_host_cands(pj_ice *ice)
static pj_status_t gather_mapped_cands(pj_ice *ice)
{
+ PJ_UNUSED_ARG(ice);
return PJ_ENOTSUP;
}
static pj_status_t gather_relayed_cands(pj_ice *ice)
{
+ PJ_UNUSED_ARG(ice);
return PJ_ENOTSUP;
}
@@ -305,6 +509,8 @@ PJ_DEF(pj_status_t) pj_ice_start_gather(pj_ice *ice,
{
pj_status_t status;
+ PJ_UNUSED_ARG(flags);
+
/* Gather host candidate */
status = gather_host_cands(ice);
if (status != PJ_SUCCESS)
@@ -347,32 +553,32 @@ PJ_DEF(pj_status_t) pj_ice_add_cand(pj_ice *ice,
int addr_len,
unsigned *p_cand_id)
{
- pj_ice_cand *cand;
+ pj_ice_cand *lcand;
pj_status_t status = PJ_SUCCESS;
pj_mutex_lock(ice->mutex);
- if (ice->cand_cnt >= PJ_ARRAY_SIZE(ice->cand)) {
+ if (ice->lcand_cnt >= PJ_ARRAY_SIZE(ice->lcand)) {
status = PJ_ETOOMANY;
goto on_error;
}
- cand = &ice->cand[ice->cand_cnt];
- cand->comp_id = comp_id;
- cand->type = type;
- pj_strdup(ice->pool, &cand->foundation, foundation);
- cand->prio = CALC_CAND_PRIO(type, local_pref, cand->comp_id);
- pj_memcpy(&cand->addr, addr, addr_len);
- pj_memcpy(&cand->base_addr, base_addr, addr_len);
+ lcand = &ice->lcand[ice->lcand_cnt];
+ lcand->comp_id = comp_id;
+ lcand->type = type;
+ pj_strdup(ice->pool, &lcand->foundation, foundation);
+ lcand->prio = CALC_CAND_PRIO(type, local_pref, lcand->comp_id);
+ pj_memcpy(&lcand->addr, addr, addr_len);
+ pj_memcpy(&lcand->base_addr, base_addr, addr_len);
if (srv_addr)
- pj_memcpy(&cand->srv_addr, srv_addr, addr_len);
+ pj_memcpy(&lcand->srv_addr, srv_addr, addr_len);
else
- pj_bzero(&cand->srv_addr, sizeof(cand->srv_addr));
+ pj_bzero(&lcand->srv_addr, sizeof(lcand->srv_addr));
if (p_cand_id)
- *p_cand_id = ice->cand_cnt;
+ *p_cand_id = ice->lcand_cnt;
- ++ice->cand_cnt;
+ ++ice->lcand_cnt;
on_error:
pj_mutex_unlock(ice->mutex);
@@ -382,7 +588,7 @@ on_error:
PJ_DEF(unsigned) pj_ice_get_cand_cnt(pj_ice *ice)
{
- return ice->cand_cnt;
+ return ice->lcand_cnt;
}
@@ -393,9 +599,11 @@ PJ_DEF(pj_status_t) pj_ice_enum_cands(pj_ice *ice,
{
unsigned i, count;
+ PJ_UNUSED_ARG(sort_by);
+
pj_mutex_lock(ice->mutex);
- count = (*p_count < ice->cand_cnt) ? *p_count : ice->cand_cnt;
+ count = (*p_count < ice->lcand_cnt) ? *p_count : ice->lcand_cnt;
for (i=0; i<count; ++i)
cand_ids[i] = i;
@@ -411,9 +619,9 @@ PJ_DEF(pj_status_t) pj_ice_get_cand(pj_ice *ice,
pj_ice_cand **p_cand)
{
PJ_ASSERT_RETURN(ice && p_cand, PJ_EINVAL);
- PJ_ASSERT_RETURN(cand_id <= ice->cand_cnt, PJ_EINVAL);
+ PJ_ASSERT_RETURN(cand_id <= ice->lcand_cnt, PJ_EINVAL);
- *p_cand = &ice->cand[cand_id];
+ *p_cand = &ice->lcand[cand_id];
return PJ_SUCCESS;
}
@@ -432,6 +640,74 @@ static pj_uint64_t CALC_CHECK_PRIO(pj_uint32_t O, pj_uint32_t A)
(pj_uint64_t)2 * MAX(O, A) + (O>A ? 1 : 0);
}
+static const char *dump_check(char *buffer, unsigned bufsize,
+ const pj_ice *ice,
+ const pj_ice_check *check)
+{
+ char local_addr[128];
+ int len;
+
+ pj_ansi_strcpy(local_addr,
+ pj_inet_ntoa(ice->lcand[check->cand_id].addr.ipv4.sin_addr));
+
+ len = pj_ansi_snprintf(buffer, bufsize,
+ "%s:%d-->%s:%d",
+ local_addr,
+ (int)pj_ntohs(ice->lcand[check->cand_id].addr.ipv4.sin_port),
+ pj_inet_ntoa(check->rem_addr.ipv4.sin_addr),
+ (int)pj_ntohs(check->rem_addr.ipv4.sin_port));
+ if (len < 0)
+ len = 0;
+ else if (len >= (int)bufsize)
+ len = bufsize - 1;
+
+ buffer[len] = '\0';
+ return buffer;
+}
+
+#if PJ_LOG_MAX_LEVEL >= 4
+static void dump_checklist(const char *title, const pj_ice *ice,
+ const pj_ice_checklist *clist)
+{
+ unsigned i;
+ char buffer[128];
+
+ PJ_LOG(4,(ice->obj_name, "%s", title));
+ for (i=0; i<clist->count; ++i) {
+ const pj_ice_check *c = &clist->checks[i];
+ PJ_LOG(4,(ice->obj_name, " %d: %s (prio=%u, state=%s)",
+ i, dump_check(buffer, sizeof(buffer), ice, c),
+ c->check_prio, check_state_name[c->check_state]));
+ }
+}
+#else
+#define dump_checklist(ice, clist)
+#endif
+
+static void sort_checklist(pj_ice_checklist *clist)
+{
+ unsigned i;
+
+ for (i=0; i<clist->count-1; ++i) {
+ unsigned j, highest = i;
+ for (j=i+1; j<clist->count; ++j) {
+ if (clist->checks[j].check_prio >
+ clist->checks[highest].check_prio)
+ {
+ highest = j;
+ }
+ }
+
+ if (highest != i) {
+ pj_ice_check tmp;
+
+ pj_memcpy(&tmp, &clist->checks[i], sizeof(pj_ice_check));
+ pj_memcpy(&clist->checks[i], &clist->checks[highest],
+ sizeof(pj_ice_check));
+ pj_memcpy(&clist->checks[highest], &tmp, sizeof(pj_ice_check));
+ }
+ }
+}
PJ_DEF(pj_status_t) pj_ice_create_check_list(pj_ice *ice,
pj_bool_t is_remote_offer,
@@ -439,39 +715,47 @@ PJ_DEF(pj_status_t) pj_ice_create_check_list(pj_ice *ice,
const pj_ice_cand rem_cand[])
{
pj_ice_checklist *clist;
- unsigned i, j, count;
+ unsigned i, j;
PJ_ASSERT_RETURN(ice && rem_cand_cnt && rem_cand, PJ_EINVAL);
+ PJ_ASSERT_RETURN(rem_cand_cnt < PJ_ICE_MAX_CAND, PJ_ETOOMANY);
pj_mutex_lock(ice->mutex);
- /* Create checklist */
+ /* Save remote candidates */
+ ice->rcand_cnt = 0;
+ for (i=0; i<rem_cand_cnt; ++i) {
+ pj_ice_cand *cn = &ice->rcand[ice->rcand_cnt++];
+ pj_memcpy(cn, &rem_cand[i], sizeof(pj_ice_cand));
+ pj_strdup(ice->pool, &cn->foundation, &rem_cand[i].foundation);
+ }
+
+ /* Generate checklist */
clist = &ice->cklist;
- clist->checks = pj_pool_calloc(ice->pool,
- ice->cand_cnt * rem_cand_cnt,
- sizeof(pj_ice_check));
- for (i=0, count=0; i<ice->cand_cnt; ++i) {
+ for (i=0; i<ice->lcand_cnt; ++i) {
for (j=0; j<rem_cand_cnt; ++j) {
- pj_ice_check *c = &clist->checks[count++];
+ pj_ice_check *c = &clist->checks[clist->count++];
/* A local candidate is paired with a remote candidate if
* and only if the two candidates have the same component ID
* and have the same IP address version.
*/
- if (ice->cand[i].comp_id != rem_cand[j].comp_id ||
- pj_strcmp(&ice->cand[i].foundation,&rem_cand[j].foundation)==0)
+ if (ice->lcand[i].comp_id != rem_cand[j].comp_id ||
+ pj_strcmp(&ice->lcand[i].foundation,&rem_cand[j].foundation)==0)
{
continue;
}
- c->local_cand_id = i;
+ c->cand_id = i;
+ c->comp_id = ice->lcand[i].comp_id;
+ c->foundation = ice->lcand[i].foundation;
if (is_remote_offer) {
c->check_prio = CALC_CHECK_PRIO(rem_cand[j].prio,
- ice->cand[i].prio);
+ ice->lcand[i].prio);
} else {
- c->check_prio = CALC_CHECK_PRIO(ice->cand[i].prio,
+ c->check_prio = CALC_CHECK_PRIO(ice->lcand[i].prio,
rem_cand[j].prio);
}
@@ -485,43 +769,139 @@ PJ_DEF(pj_status_t) pj_ice_create_check_list(pj_ice *ice,
}
}
- clist->count = count;
-
/* Sort checklist based on priority */
- for (i=0; i<clist->count-1; ++i) {
- unsigned highest = i;
- for (j=i+1; j<clist->count; ++j) {
- if (clist->checks[j].check_prio >
- clist->checks[highest].check_prio)
- {
- highest = j;
- }
- }
-
- if (highest != i) {
- pj_ice_check tmp;
-
- pj_memcpy(&tmp, &clist->checks[i], sizeof(pj_ice_check));
- pj_memcpy(&clist->checks[i], &clist->checks[highest],
- sizeof(pj_ice_check));
- pj_memcpy(&clist->checks[highest], &tmp, sizeof(pj_ice_check));
- }
- }
+ sort_checklist(clist);
/* Prune the checklist */
for (i=0; i<clist->count; ++i) {
PJ_TODO(PRUNE_CHECKLIST);
}
+ /* Log checklist */
+ dump_checklist("Checklist created:", ice, clist);
+
pj_mutex_lock(ice->mutex);
return PJ_SUCCESS;
}
+struct req_data
+{
+ pj_ice *ice;
+ pj_ice_checklist *clist;
+ unsigned ckid;
+};
+
+/* Perform check on the specified candidate pair */
+static pj_status_t perform_check(pj_ice *ice, pj_ice_checklist *clist,
+ unsigned check_id)
+{
+ pj_stun_tx_data *tdata;
+ struct req_data *rd;
+ pj_ice_check *check;
+ pj_uint32_t prio;
+ char buffer[128];
+ pj_status_t status;
+
+ check = &clist->checks[check_id];
+
+ PJ_LOG(5,(ice->obj_name,
+ "Sending connectivity check for check %d: %s",
+ check_id, dump_check(buffer, sizeof(buffer), ice, check)));
+
+ /* Create request */
+ status = pj_stun_session_create_req(ice->tx_sess,
+ PJ_STUN_BINDING_REQUEST, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Attach data to be retrieved later when STUN request transaction
+ * completes and on_stun_request_complete() callback is called.
+ */
+ rd = PJ_POOL_ZALLOC_T(tdata->pool, struct req_data);
+ rd->ice = ice;
+ rd->clist = clist;
+ rd->ckid = check_id;
+ tdata->user_data = (void*) rd;
+
+ /* Add PRIORITY */
+ prio = CALC_CAND_PRIO(check->rem_type, 65535, check->comp_id);
+ pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_PRIORITY,
+ prio);
+
+ /* Add USE-CANDIDATE */
+ if (ice->role == PJ_ICE_ROLE_CONTROLLING) {
+ pj_stun_msg_add_empty_attr(tdata->pool, tdata->msg,
+ PJ_STUN_ATTR_USE_CANDIDATE);
+ }
+
+ /* Initiate STUN transaction to send the request */
+ status = pj_stun_session_send_msg(ice->tx_sess, PJ_FALSE,
+ &check->rem_addr,
+ sizeof(pj_sockaddr_in), tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ check->check_state = PJ_ICE_CHECK_STATE_IN_PROGRESS;
+ return PJ_SUCCESS;
+}
+
/* Start periodic check for the specified checklist */
static pj_status_t start_periodic_check(pj_ice *ice, pj_ice_checklist *clist)
{
+ unsigned i, start_count=0;
+ pj_status_t status;
+
+ /* Checklist state must be idle or completed */
+ pj_assert(clist->state == PJ_ICE_CHECKLIST_ST_IDLE ||
+ clist->state == PJ_ICE_CHECKLIST_ST_COMPLETED);
+
+ /* Set checklist state to Running */
+ clist->state = PJ_ICE_CHECKLIST_ST_RUNNING;
+
+ PJ_LOG(4,(ice->obj_name, "Starting checklist periodic check"));
+
+ /* Send STUN Binding request for checks in Waiting list */
+ for (i=0; i<clist->count; ++i) {
+ pj_ice_check *check = &clist->checks[i];
+
+ if (check->check_state == PJ_ICE_CHECK_STATE_WAITING) {
+ status = perform_check(ice, clist, i);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ ++start_count;
+ }
+ }
+
+ /* If we don't have anything in Waiting state, perform check to
+ * highest priority pair that is in Frozen state.
+ */
+ if (start_count==0) {
+ for (i=0; i<clist->count; ++i) {
+ pj_ice_check *check = &clist->checks[i];
+
+ if (check->check_state == PJ_ICE_CHECK_STATE_FROZEN) {
+ status = perform_check(ice, clist, i);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ ++start_count;
+ break;
+ }
+ }
+ }
+
+ /* Cannot start check because there's no suitable candidate pair.
+ * Set checklist state to Completed.
+ */
+ if (start_count==0) {
+ clist->state = PJ_ICE_CHECKLIST_ST_COMPLETED;
+ PJ_LOG(4,(ice->obj_name, "Checklist completed"));
+ }
+
+ return PJ_SUCCESS;
}
@@ -529,8 +909,7 @@ static pj_status_t start_periodic_check(pj_ice *ice, pj_ice_checklist *clist)
PJ_DEF(pj_status_t) pj_ice_start_check(pj_ice *ice)
{
pj_ice_checklist *clist;
- unsigned i, comp_id;
- pj_str_t fnd;
+ unsigned i;
PJ_ASSERT_RETURN(ice, PJ_EINVAL);
@@ -546,16 +925,13 @@ PJ_DEF(pj_status_t) pj_ice_start_check(pj_ice *ice)
* component ID, but different foundations, and sets all of their
* states to Waiting as well.
*/
- comp_id = ice->cand[clist->checks[0].local_cand_id].comp_id;
- fnd = ice->cand[clist->checks[0].local_cand_id].foundation;
-
for (i=1; i<clist->count; ++i) {
pj_ice_check *cki = &clist->checks[i];
- if (ice->cand[cki->local_cand_id].comp_id != comp_id)
+ if (cki->comp_id != clist->checks[0].comp_id)
continue;
- if (pj_strcmp(&ice->cand[cki->local_cand_id].foundation, &fnd)==0)
+ if (pj_strcmp(&cki->foundation, &clist->checks[0].foundation)==0)
continue;
clist->checks[i].check_state = PJ_ICE_CHECK_STATE_WAITING;
@@ -564,3 +940,170 @@ PJ_DEF(pj_status_t) pj_ice_start_check(pj_ice *ice)
/* Start periodic check */
return start_periodic_check(ice, clist);
}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+static pj_status_t on_stun_send_msg(pj_stun_session *sess,
+ const void *pkt,
+ pj_size_t pkt_size,
+ const pj_sockaddr_t *dst_addr,
+ unsigned addr_len)
+{
+ pj_ice *ice = (pj_ice*) pj_stun_session_get_user_data(sess);
+ return (*ice->cb.on_send_pkt)(ice, pkt, pkt_size, dst_addr, addr_len);
+}
+
+
+static pj_status_t on_stun_rx_request(pj_stun_session *sess,
+ const pj_uint8_t *pkt,
+ unsigned pkt_len,
+ const pj_stun_msg *msg,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
+{
+ pj_stun_tx_data *tdata;
+ pj_status_t status;
+
+ /* 7.2.1.2. Learning Peer Reflexive Candidates */
+ PJ_TODO(LEARN_PEER_REFLEXIVE_CANDIDATES);
+
+ /* Reject any requests except Binding request */
+ if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) {
+ status = pj_stun_session_create_response(sess, msg,
+ PJ_STUN_SC_BAD_REQUEST,
+ NULL, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = pj_stun_session_send_msg(sess, PJ_TRUE,
+ src_addr, src_addr_len, tdata);
+ return status;
+ }
+
+ status = pj_stun_session_create_response(sess, msg, 0, NULL, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = pj_stun_msg_add_sockaddr_attr(tdata->pool, msg,
+ PJ_STUN_ATTR_XOR_MAPPED_ADDR,
+ PJ_TRUE, src_addr, src_addr_len);
+
+ status = pj_stun_session_send_msg(sess, PJ_TRUE,
+ src_addr, src_addr_len, tdata);
+
+ /* 7.2.1.3. Triggered Checks:
+ * Next, the agent constructs a pair whose local candidate is equal to
+ * the transport address on which the STUN request was received, and a
+ * remote candidate equal to the source transport address where the
+ * request came from (which may be peer-reflexive remote candidate that
+ * was just learned).
+ */
+
+ return status;
+}
+
+/* This callback is called when outgoing STUN request completed */
+static void on_stun_request_complete(pj_stun_session *sess,
+ pj_status_t status,
+ pj_stun_tx_data *tdata,
+ const pj_stun_msg *response)
+{
+ struct req_data *rd = (struct req_data*) tdata->user_data;
+ pj_ice *ice;
+ pj_ice_check *check, *valid_check;
+ pj_ice_checklist *clist;
+ char buffer[128];
+
+ ice = rd->ice;
+ check = &rd->clist->checks[rd->ckid];
+ clist = rd->clist;
+
+ PJ_LOG(5,(ice->obj_name,
+ "Connectivity check %s for check %d: %s",
+ (status==PJ_SUCCESS ? "SUCCESS" : "FAILED"), rd->ckid,
+ dump_check(buffer, sizeof(buffer), ice, check)));
+
+ if (status != PJ_SUCCESS) {
+ check->check_state = PJ_ICE_CHECK_STATE_FAILED;
+ return;
+ }
+
+ /* The agent MUST check that the source IP address and port of the
+ * response equals the destination IP address and port that the Binding
+ * Request was sent to, and that the destination IP address and port of
+ * the response match the source IP address and port that the Binding
+ * Request was sent from.
+ */
+ PJ_TODO(CHECK_ICE_RESPONSE_SOURCE_ADDRESS);
+
+ /* Get the STUN MAPPED-ADDRESS attribute. If the
+ * transport address does not match any of the local candidates that the
+ * agent knows about, the mapped address represents a new candidate - a
+ * peer reflexive candidate
+ */
+ PJ_TODO(CHECK_ICE_RESPONSE_SOURCE_ADDRESS2);
+
+ /* Sets the state of the pair that generated the check to succeeded. */
+ check->check_state = PJ_ICE_CHECK_STATE_SUCCEEDED;
+
+ /* This is a valid pair, so add this to the valid list */
+ valid_check = &ice->valid_list.checks[ice->valid_list.count++];
+ pj_memcpy(valid_check, check, sizeof(*check));
+
+ /* Sort valid_list */
+ sort_checklist(&ice->valid_list);
+
+
+ /* If the pair had a component ID of 1, the agent MUST change the
+ * states for all other Frozen pairs for the same media stream and
+ * same foundation, but different component IDs, to Waiting.
+ */
+ if (check->comp_id == 1) {
+ unsigned i;
+ for (i=0; i<clist->count; ++i) {
+ pj_ice_check *c = &clist->checks[i];
+
+ if (c->check_state == PJ_ICE_CHECK_STATE_FROZEN &&
+ c->comp_id != check->comp_id &&
+ pj_strcmp(&c->foundation, &check->foundation)==0)
+ {
+ /* Unfreeze and start check */
+ PJ_LOG(5,(ice->obj_name, "Unfreezing check %d", i));
+ c->check_state = PJ_ICE_CHECK_STATE_WAITING;
+ perform_check(ice, clist, i);
+ }
+ }
+
+ }
+ /* If the pair had a component ID equal to the number of components
+ * for the media stream (where this is the actual number of
+ * components being used, in cases where the number of components
+ * signaled in the SDP differs from offerer to answerer), the agent
+ * MUST change the state for all other Frozen pairs for the first
+ * component of different media streams (and thus in different check
+ * lists) but the same foundation, to Waiting.
+ */
+ else if (0) {
+ PJ_TODO(UNFREEZE_OTHER_COMPONENT_ID);
+ }
+ /* If the pair has any other component ID, no other pairs can be
+ * unfrozen.
+ */
+ else {
+ PJ_TODO(UNFREEZE_OTHER_COMPONENT_ID1);
+ }
+
+}
+
+static pj_status_t on_stun_rx_indication(pj_stun_session *sess,
+ const pj_uint8_t *pkt,
+ unsigned pkt_len,
+ const pj_stun_msg *msg,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
+{
+ PJ_TODO(SUPPORT_RX_BIND_REQUEST_AS_INDICATION);
+ return PJ_ENOTSUP;
+}
+
diff --git a/pjnath/src/pjnath/stun_msg.c b/pjnath/src/pjnath/stun_msg.c
index b361898f..058ce833 100644
--- a/pjnath/src/pjnath/stun_msg.c
+++ b/pjnath/src/pjnath/stun_msg.c
@@ -873,6 +873,23 @@ pj_stun_empty_attr_create(pj_pool_t *pool,
}
+/*
+ * Create STUN empty attribute and add the attribute to the message.
+ */
+PJ_DEF(pj_status_t) pj_stun_msg_add_empty_attr( pj_pool_t *pool,
+ pj_stun_msg *msg,
+ int attr_type)
+{
+ pj_stun_empty_attr *attr = NULL;
+ pj_status_t status;
+
+ status = pj_stun_empty_attr_create(pool, attr_type, &attr);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ return pj_stun_msg_add_attr(msg, &attr->hdr);
+}
+
static pj_status_t decode_empty_attr(pj_pool_t *pool,
const pj_uint8_t *buf,
void **p_attr)