summaryrefslogtreecommitdiff
path: root/pjnath
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2009-05-29 13:04:03 +0000
committerBenny Prijono <bennylp@teluu.com>2009-05-29 13:04:03 +0000
commit08640cc9411ca092e6456304bcce41f81b3bd3ce (patch)
tree6d1ad4e6304b1fb8d95b00858648cfbcd829a2ea /pjnath
parentad8907b8ea9f5715c05b19b41ea7b85509591153 (diff)
Integration of Sipit24 branch, many tickets involved:
- #793: AMR encoder should regard 'mode-set' param specified by remote decoder. - #831: Automatically switch to TCP transport when sending large request - #832: Support for outbound proxy setting without using Route header - #849: Modify conference audio switch behavior in connecting ports. - #850: Remove 'Require=replaces' param in 'Refer-To' header (in call transfer with replaces). - #851: Support for regular nomination in ICE - #852: --ip-addr support for IPv6 for media transport in pjsua - #854: Adding SOFTWARE attribute in all outgoing requests may cause compatibility problem with older STUN server (thanks Alexei Kuznetsov for the report) - #855: Bug in digit map frequencies for DTMF digits (thanks FCCH for the report) - #856: Put back the ICE candidate priority values according to the default values in the draft-mmusic-ice - #857: Support for ICE keep-alive with Binding indication - #858: Do not authenticate STUN 438 response - #859: AMR-WB format param in the SDP is not negotiated correctly. - #867: Return error instead of asserting when PJSUA-LIB fails to open log file git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2724 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjnath')
-rw-r--r--pjnath/include/pjnath/config.h75
-rw-r--r--pjnath/include/pjnath/errno.h7
-rw-r--r--pjnath/include/pjnath/ice_session.h90
-rw-r--r--pjnath/include/pjnath/ice_strans.h41
-rw-r--r--pjnath/include/pjnath/stun_msg.h4
-rw-r--r--pjnath/include/pjnath/stun_session.h12
-rw-r--r--pjnath/src/pjnath-test/ice_test.c6
-rw-r--r--pjnath/src/pjnath/errno.c1
-rw-r--r--pjnath/src/pjnath/ice_session.c512
-rw-r--r--pjnath/src/pjnath/ice_strans.c65
-rw-r--r--pjnath/src/pjnath/stun_auth.c1
-rw-r--r--pjnath/src/pjnath/stun_msg_dump.c10
-rw-r--r--pjnath/src/pjnath/stun_session.c23
13 files changed, 742 insertions, 105 deletions
diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h
index f9552721..f265e2c7 100644
--- a/pjnath/include/pjnath/config.h
+++ b/pjnath/include/pjnath/config.h
@@ -259,12 +259,23 @@
*/
#define PJ_ICE_MAX_COMP (2<<PJ_ICE_COMP_BITS)
+/**
+ * Use the priority value according to the ice-draft.
+ */
+#ifndef PJNATH_ICE_PRIO_STD
+# define PJNATH_ICE_PRIO_STD 1
+#endif
+
/**
* The number of bits to represent candidate type preference.
*/
#ifndef PJ_ICE_CAND_TYPE_PREF_BITS
-# define PJ_ICE_CAND_TYPE_PREF_BITS 2
+# if PJNATH_ICE_PRIO_STD
+# define PJ_ICE_CAND_TYPE_PREF_BITS 8
+# else
+# define PJ_ICE_CAND_TYPE_PREF_BITS 2
+# endif
#endif
@@ -324,32 +335,74 @@
/**
+ * For a controlled agent, specify how long it wants to wait (in milliseconds)
+ * for the controlling agent to complete sending connectivity check with
+ * nominated flag set to true for all components after the controlled agent
+ * has found that all connectivity checks in its checklist have been completed
+ * and there is at least one successful (but not nominated) check for every
+ * component.
+ *
+ * When selecting the value, bear in mind that the connectivity check from
+ * controlling agent may be delayed because of delay in receiving SDP answer
+ * from the controlled agent.
+ *
+ * Application may set this value to -1 to disable this timer.
+ *
+ * Default: 10000 (milliseconds)
+ */
+#ifndef ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT
+# define ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT 10000
+#endif
+
+
+/**
+ * For controlling agent if it uses regular nomination, specify the delay to
+ * perform nominated check (connectivity check with USE-CANDIDATE attribute)
+ * after all components have a valid pair.
+ *
+ * Default: 4*PJ_STUN_RTO_VALUE (milliseconds)
+ */
+#ifndef PJ_ICE_NOMINATED_CHECK_DELAY
+# define PJ_ICE_NOMINATED_CHECK_DELAY (4*PJ_STUN_RTO_VALUE)
+#endif
+
+
+/**
* Minimum interval value to be used for sending STUN keep-alive on the ICE
- * stream transport, in seconds. This minimum interval, plus a random value
- * which maximum is PJ_ICE_ST_KEEP_ALIVE_MAX_RAND, specify the actual interval
+ * session, in seconds. This minimum interval, plus a random value
+ * which maximum is PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND, specify the actual interval
* of the STUN keep-alive.
*
- * Default: 20 seconds
+ * Default: 15 seconds
*
- * @see PJ_ICE_ST_KEEP_ALIVE_MAX_RAND
+ * @see PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND
*/
-#ifndef PJ_ICE_ST_KEEP_ALIVE_MIN
-# define PJ_ICE_ST_KEEP_ALIVE_MIN 20
+#ifndef PJ_ICE_SESS_KEEP_ALIVE_MIN
+# define PJ_ICE_SESS_KEEP_ALIVE_MIN 20
#endif
+/* Warn about deprecated macro */
+#ifdef PJ_ICE_ST_KEEP_ALIVE_MIN
+# error PJ_ICE_ST_KEEP_ALIVE_MIN is deprecated
+#endif
/**
* To prevent STUN keep-alives to be sent simultaneously, application should
- * add random interval to minimum interval (PJ_ICE_ST_KEEP_ALIVE_MIN). This
+ * add random interval to minimum interval (PJ_ICE_SESS_KEEP_ALIVE_MIN). This
* setting specifies the maximum random value to be added to the minimum
* interval, in seconds.
*
* Default: 5 seconds
*
- * @see PJ_ICE_ST_KEEP_ALIVE_MIN
+ * @see PJ_ICE_SESS_KEEP_ALIVE_MIN
*/
-#ifndef PJ_ICE_ST_KEEP_ALIVE_MAX_RAND
-# define PJ_ICE_ST_KEEP_ALIVE_MAX_RAND 5
+#ifndef PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND
+# define PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND 5
+#endif
+
+/* Warn about deprecated macro */
+#ifdef PJ_ICE_ST_KEEP_ALIVE_MAX_RAND
+# error PJ_ICE_ST_KEEP_ALIVE_MAX_RAND is deprecated
#endif
diff --git a/pjnath/include/pjnath/errno.h b/pjnath/include/pjnath/errno.h
index e17f9460..2bc47dc1 100644
--- a/pjnath/include/pjnath/errno.h
+++ b/pjnath/include/pjnath/errno.h
@@ -196,7 +196,12 @@
* host candidate.
*/
#define PJNATH_EICENOHOSTCAND (PJNATH_ERRNO_START+92) /* 370092 */
-
+/**
+ * @hideinitializer
+ * Controlled agent timed-out in waiting for the controlling agent to
+ * send nominated check after all connectivity checks have completed.
+ */
+#define PJNATH_EICENOMTIMEOUT (PJNATH_ERRNO_START+93) /* 370093 */
/************************************************************
* TURN ERROR CODES
diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h
index 18f7ffd6..e606f075 100644
--- a/pjnath/include/pjnath/ice_session.h
+++ b/pjnath/include/pjnath/ice_session.h
@@ -176,13 +176,20 @@ typedef struct pj_ice_sess_check pj_ice_sess_check;
typedef struct pj_ice_sess_comp
{
/**
- * The pointer to ICE check which was nominated for this component.
- * The value will be NULL if a nominated check has not been found
- * for this component.
+ * Pointer to ICE check with highest priority which connectivity check
+ * has been successful. The value will be NULL if a no successful check
+ * has not been found for this component.
*/
pj_ice_sess_check *valid_check;
/**
+ * Pointer to ICE check with highest priority which connectivity check
+ * has been successful and it has been nominated. The value may be NULL
+ * if there is no such check yet.
+ */
+ pj_ice_sess_check *nominated_check;
+
+ /**
* The STUN session to be used to send and receive STUN messages for this
* component.
*/
@@ -553,6 +560,44 @@ typedef struct pj_ice_rx_check
/**
+ * This structure describes various ICE session options. Application
+ * configure the ICE session with these options by calling
+ * #pj_ice_sess_set_options().
+ */
+typedef struct pj_ice_sess_options
+{
+ /**
+ * Specify whether to use aggressive nomination.
+ */
+ pj_bool_t aggressive;
+
+ /**
+ * For controlling agent if it uses regular nomination, specify the delay
+ * to perform nominated check (connectivity check with USE-CANDIDATE
+ * attribute) after all components have a valid pair.
+ *
+ * Default value is PJ_ICE_NOMINATED_CHECK_DELAY.
+ */
+ unsigned nominated_check_delay;
+
+ /**
+ * For a controlled agent, specify how long it wants to wait (in
+ * milliseconds) for the controlling agent to complete sending
+ * connectivity check with nominated flag set to true for all components
+ * after the controlled agent has found that all connectivity checks in
+ * its checklist have been completed and there is at least one successful
+ * (but not nominated) check for every component.
+ *
+ * Default value for this option is
+ * ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT. Specify -1 to disable
+ * this timer.
+ */
+ int controlled_agent_want_nom_timeout;
+
+} pj_ice_sess_options;
+
+
+/**
* This structure describes the ICE session. For this version of PJNATH,
* an ICE session corresponds to a single media stream (unlike the ICE
* session described in the ICE standard where an ICE session covers the
@@ -569,11 +614,13 @@ struct pj_ice_sess
void *user_data; /**< App. data. */
pj_mutex_t *mutex; /**< Mutex. */
pj_ice_sess_role role; /**< ICE role. */
+ pj_ice_sess_options opt; /**< Options */
pj_timestamp tie_breaker; /**< Tie breaker value */
pj_uint8_t *prefs; /**< Type preference. */
+ pj_bool_t is_nominating; /**< Nominating stage */
pj_bool_t is_complete; /**< Complete? */
pj_status_t ice_status; /**< Error status. */
- pj_timer_entry completion_timer; /**< To call callback. */
+ pj_timer_entry timer; /**< ICE timer. */
pj_ice_sess_cb cb; /**< Callback. */
pj_stun_config stun_cfg; /**< STUN settings. */
@@ -589,6 +636,7 @@ struct pj_ice_sess
/* Components */
unsigned comp_cnt; /**< # of components. */
pj_ice_sess_comp comp[PJ_ICE_MAX_COMP]; /**< Component array */
+ unsigned comp_ka; /**< Next comp for KA */
/* Local candidates */
unsigned lcand_cnt; /**< # of local cand. */
@@ -654,6 +702,12 @@ PJ_DECL(void) pj_ice_calc_foundation(pj_pool_t *pool,
pj_ice_cand_type type,
const pj_sockaddr *base_addr);
+/**
+ * Initialize ICE session options with library default values.
+ *
+ * @param opt ICE session options.
+ */
+PJ_DECL(void) pj_ice_sess_options_default(pj_ice_sess_options *opt);
/**
* Create ICE session with the specified role and number of components.
@@ -689,6 +743,34 @@ PJ_DECL(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
pj_ice_sess **p_ice);
/**
+ * Get the value of various options of the ICE session.
+ *
+ * @param ice The ICE session.
+ * @param opt The options to be initialized with the values
+ * from the ICE session.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error.
+ */
+PJ_DECL(pj_status_t) pj_ice_sess_get_options(pj_ice_sess *ice,
+ pj_ice_sess_options *opt);
+
+/**
+ * Specify various options for this ICE session. Application MUST only
+ * call this function after the ICE session has been created but before
+ * any connectivity check is started.
+ *
+ * Application should call #pj_ice_sess_get_options() to initialize the
+ * options with their default values.
+ *
+ * @param ice The ICE session.
+ * @param opt Options to be applied to the ICE session.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error.
+ */
+PJ_DECL(pj_status_t) pj_ice_sess_set_options(pj_ice_sess *ice,
+ const pj_ice_sess_options *opt);
+
+/**
* Destroy ICE session. This will cancel any connectivity checks currently
* running, if any, and any other events scheduled by this session, as well
* as all memory resources.
diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h
index 02397073..28487870 100644
--- a/pjnath/include/pjnath/ice_strans.h
+++ b/pjnath/include/pjnath/ice_strans.h
@@ -197,6 +197,13 @@ typedef struct pj_ice_strans_cfg
pj_dns_resolver *resolver;
/**
+ * This contains various STUN session options. Once the ICE stream
+ * transport is created, application may also change the options
+ * with #pj_ice_strans_set_options().
+ */
+ pj_ice_sess_options opt;
+
+ /**
* STUN and local transport settings. This specifies the
* settings for local UDP socket, which will be resolved
* to get the STUN mapped address.
@@ -209,12 +216,12 @@ typedef struct pj_ice_strans_cfg
pj_stun_sock_cfg cfg;
/**
- * Disable host candidates. When this option is set, no
- * host candidates will be added.
+ * Maximum number of host candidates to be added. If the
+ * value is zero, no host candidates will be added.
*
- * Default: PJ_FALSE
+ * Default: 64
*/
- pj_bool_t no_host_cands;
+ unsigned max_host_cands;
/**
* Include loopback addresses in the host candidates.
@@ -386,6 +393,32 @@ PJ_DECL(void*) pj_ice_strans_get_user_data(pj_ice_strans *ice_st);
/**
+ * Get the value of various options of the ICE stream transport.
+ *
+ * @param ice_st The ICE stream transport.
+ * @param opt The options to be initialized with the values
+ * from the ICE stream transport.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error.
+ */
+PJ_DECL(pj_status_t) pj_ice_strans_get_options(pj_ice_strans *ice_st,
+ pj_ice_sess_options *opt);
+
+/**
+ * Specify various options for this ICE stream transport. Application
+ * should call #pj_ice_strans_get_options() to initialize the options
+ * with their default values.
+ *
+ * @param ice_st The ICE stream transport.
+ * @param opt Options to be applied to this ICE stream transport.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error.
+ */
+PJ_DECL(pj_status_t) pj_ice_strans_set_options(pj_ice_strans *ice_st,
+ const pj_ice_sess_options *opt);
+
+
+/**
* Initialize the ICE session in the ICE stream transport.
* When application is about to send an offer containing ICE capability,
* or when it receives an offer containing ICE capability, it must
diff --git a/pjnath/include/pjnath/stun_msg.h b/pjnath/include/pjnath/stun_msg.h
index 57fc38c4..5339f1b2 100644
--- a/pjnath/include/pjnath/stun_msg.h
+++ b/pjnath/include/pjnath/stun_msg.h
@@ -197,6 +197,10 @@ typedef enum pj_stun_msg_type
*/
PJ_STUN_BINDING_ERROR_RESPONSE = 0x0111,
+ /**
+ * Binding Indication (ICE)
+ */
+ PJ_STUN_BINDING_INDICATION = 0x0011,
/**
* STUN SHARED-SECRET reqeust.
diff --git a/pjnath/include/pjnath/stun_session.h b/pjnath/include/pjnath/stun_session.h
index 2a6fd022..686a09a1 100644
--- a/pjnath/include/pjnath/stun_session.h
+++ b/pjnath/include/pjnath/stun_session.h
@@ -485,6 +485,18 @@ PJ_DECL(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess,
*/
PJ_DECL(void) pj_stun_session_set_log(pj_stun_session *sess,
unsigned flags);
+/**
+ * Configure whether the STUN session should utilize FINGERPRINT in
+ * outgoing messages.
+ *
+ * @param sess The STUN session instance.
+ * @param use Boolean for the setting.
+ *
+ * @return The previous configured value of FINGERPRINT
+ * utilization of the sessoin.
+ */
+PJ_DECL(pj_bool_t) pj_stun_session_use_fingerprint(pj_stun_session *sess,
+ pj_bool_t use);
/**
* Create a STUN request message. After the message has been successfully
diff --git a/pjnath/src/pjnath-test/ice_test.c b/pjnath/src/pjnath-test/ice_test.c
index ffa1077d..7603b402 100644
--- a/pjnath/src/pjnath-test/ice_test.c
+++ b/pjnath/src/pjnath-test/ice_test.c
@@ -64,6 +64,8 @@ struct test_cfg
unsigned destroy_delay; /* Delay before destroy() */
struct test_result expected;/* Expected result */
+
+ pj_bool_t nom_regular; /* Use regular nomination? */
};
/* ICE endpoint state */
@@ -141,9 +143,9 @@ static int create_ice_strans(struct test_sess *test_sess,
}
if (ept->cfg.enable_host == 0) {
- ice_cfg.stun.no_host_cands = PJ_TRUE;
+ ice_cfg.stun.max_host_cands = 0;
} else {
- ice_cfg.stun.no_host_cands = PJ_FALSE;
+ //ice_cfg.stun.no_host_cands = PJ_FALSE;
ice_cfg.stun.loop_addr = PJ_TRUE;
}
diff --git a/pjnath/src/pjnath/errno.c b/pjnath/src/pjnath/errno.c
index d102a09d..1656ce33 100644
--- a/pjnath/src/pjnath/errno.c
+++ b/pjnath/src/pjnath/errno.c
@@ -67,6 +67,7 @@ static const struct
PJ_BUILD_ERR( PJNATH_EICEMISSINGSDP, "Missing ICE SDP attribute"),
PJ_BUILD_ERR( PJNATH_EICEINCANDSDP, "Invalid SDP \"candidate\" attribute"),
PJ_BUILD_ERR( PJNATH_EICENOHOSTCAND, "No host candidate associated with srflx"),
+ PJ_BUILD_ERR( PJNATH_EICENOMTIMEOUT, "Controlled agent timed out waiting for nomination"),
/* TURN related errors */
PJ_BUILD_ERR( PJNATH_ETURNINTP, "Invalid/unsupported transport"),
diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
index f5ba8e3c..ad08639c 100644
--- a/pjnath/src/pjnath/ice_session.c
+++ b/pjnath/src/pjnath/ice_session.c
@@ -29,7 +29,6 @@
#include <pj/rand.h>
#include <pj/string.h>
-
/* String names for candidate types */
static const char *cand_type_names[] =
{
@@ -66,6 +65,20 @@ static const char *role_names[] =
"Controlling"
};
+enum timer_type
+{
+ TIMER_NONE, /**< Timer not active */
+ TIMER_COMPLETION_CALLBACK, /**< Call on_ice_complete() callback */
+ TIMER_CONTROLLED_WAIT_NOM, /**< Controlled agent is waiting for
+ controlling agent to send connectivity
+ check with nominated flag after it has
+ valid check for every components. */
+ TIMER_START_NOMINATED_CHECK,/**< Controlling agent start connectivity
+ checks with USE-CANDIDATE flag. */
+ TIMER_KEEP_ALIVE /**< ICE keep-alive timer. */
+
+};
+
/* Candidate type preference */
static pj_uint8_t cand_type_prefs[4] =
{
@@ -118,10 +131,14 @@ typedef struct timer_data
/* Forward declarations */
+static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te);
+static void on_ice_complete(pj_ice_sess *ice, pj_status_t status);
+static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now);
static void destroy_ice(pj_ice_sess *ice,
pj_status_t reason);
static pj_status_t start_periodic_check(pj_timer_heap_t *th,
pj_timer_entry *te);
+static void start_nominated_check(pj_ice_sess *ice);
static void periodic_timer(pj_timer_heap_t *th,
pj_timer_entry *te);
static void handle_incoming_check(pj_ice_sess *ice,
@@ -225,7 +242,7 @@ PJ_DEF(void) pj_ice_calc_foundation(pj_pool_t *pool,
pj_ice_cand_type type,
const pj_sockaddr *base_addr)
{
-#if 0
+#if PJNATH_ICE_PRIO_STD
char buf[64];
pj_uint32_t val;
@@ -296,6 +313,15 @@ static pj_status_t init_comp(pj_ice_sess *ice,
}
+/* Init options with default values */
+PJ_DEF(void) pj_ice_sess_options_default(pj_ice_sess_options *opt)
+{
+ opt->aggressive = PJ_TRUE;
+ opt->nominated_check_delay = PJ_ICE_NOMINATED_CHECK_DELAY;
+ opt->controlled_agent_want_nom_timeout =
+ ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT;
+}
+
/*
* Create ICE session.
*/
@@ -326,6 +352,9 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
ice->tie_breaker.u32.hi = pj_rand();
ice->tie_breaker.u32.lo = pj_rand();
ice->prefs = cand_type_prefs;
+ pj_ice_sess_options_default(&ice->opt);
+
+ pj_timer_entry_init(&ice->timer, TIMER_NONE, (void*)ice, &on_timer);
pj_ansi_snprintf(ice->obj_name, sizeof(ice->obj_name),
name, ice);
@@ -345,6 +374,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
pj_ice_sess_comp *comp;
comp = &ice->comp[i];
comp->valid_check = NULL;
+ comp->nominated_check = NULL;
status = init_comp(ice, i+1, comp);
if (status != PJ_SUCCESS) {
@@ -389,6 +419,31 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
/*
+ * Get the value of various options of the ICE session.
+ */
+PJ_DEF(pj_status_t) pj_ice_sess_get_options(pj_ice_sess *ice,
+ pj_ice_sess_options *opt)
+{
+ PJ_ASSERT_RETURN(ice, PJ_EINVAL);
+ pj_memcpy(opt, &ice->opt, sizeof(*opt));
+ return PJ_SUCCESS;
+}
+
+/*
+ * Specify various options for this ICE session.
+ */
+PJ_DEF(pj_status_t) pj_ice_sess_set_options(pj_ice_sess *ice,
+ const pj_ice_sess_options *opt)
+{
+ PJ_ASSERT_RETURN(ice && opt, PJ_EINVAL);
+ pj_memcpy(&ice->opt, opt, sizeof(*opt));
+ LOG5((ice->obj_name, "ICE nomination type set to %s",
+ (ice->opt.aggressive ? "aggressive" : "regular")));
+ return PJ_SUCCESS;
+}
+
+
+/*
* Destroy
*/
static void destroy_ice(pj_ice_sess *ice,
@@ -406,10 +461,10 @@ static void destroy_ice(pj_ice_sess *ice,
pj_mutex_unlock(ice->mutex);
}
- if (ice->completion_timer.id) {
+ if (ice->timer.id) {
pj_timer_heap_cancel(ice->stun_cfg.timer_heap,
- &ice->completion_timer);
- ice->completion_timer.id = PJ_FALSE;
+ &ice->timer);
+ ice->timer.id = PJ_FALSE;
}
for (i=0; i<ice->comp_cnt; ++i) {
@@ -603,7 +658,7 @@ static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice,
pj_uint32_t local_pref,
pj_uint32_t comp_id)
{
-#if 0
+#if PJNATH_ICE_PRIO_STD
return ((ice->prefs[type] & 0xFF) << 24) +
((local_pref & 0xFFFF) << 8) +
(((256 - comp_id) & 0xFF) << 0);
@@ -1039,18 +1094,112 @@ static pj_status_t prune_checklist(pj_ice_sess *ice,
return PJ_SUCCESS;
}
-/* Timer callback to call on_ice_complete() callback */
-static void on_completion_timer(pj_timer_heap_t *th,
- pj_timer_entry *te)
+/* Timer callback */
+static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te)
{
pj_ice_sess *ice = (pj_ice_sess*) te->user_data;
+ enum timer_type type = (enum timer_type)te->id;
PJ_UNUSED_ARG(th);
- te->id = PJ_FALSE;
+ pj_mutex_lock(ice->mutex);
+
+ te->id = TIMER_NONE;
+
+ switch (type) {
+ case TIMER_CONTROLLED_WAIT_NOM:
+ LOG4((ice->obj_name,
+ "Controlled agent timed-out in waiting for the controlling "
+ "agent to send nominated check. Setting state to fail now.."));
+ on_ice_complete(ice, PJNATH_EICENOMTIMEOUT);
+ break;
+ case TIMER_COMPLETION_CALLBACK:
+ /* Start keep-alive timer but don't send any packets yet.
+ * Need to do it here just in case app destroy the session
+ * in the callback.
+ */
+ if (ice->ice_status == PJ_SUCCESS)
+ ice_keep_alive(ice, PJ_FALSE);
+
+ /* Notify app about ICE completion*/
+ if (ice->cb.on_ice_complete)
+ (*ice->cb.on_ice_complete)(ice, ice->ice_status);
+ break;
+ case TIMER_START_NOMINATED_CHECK:
+ start_nominated_check(ice);
+ break;
+ case TIMER_KEEP_ALIVE:
+ ice_keep_alive(ice, PJ_TRUE);
+ break;
+ case TIMER_NONE:
+ /* Nothing to do, just to get rid of gcc warning */
+ break;
+ }
- if (ice->cb.on_ice_complete)
- (*ice->cb.on_ice_complete)(ice, ice->ice_status);
+ pj_mutex_unlock(ice->mutex);
+}
+
+/* Send keep-alive */
+static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now)
+{
+ if (send_now) {
+ /* Send Binding Indication for the component */
+ pj_ice_sess_comp *comp = &ice->comp[ice->comp_ka];
+ pj_stun_tx_data *tdata;
+ pj_ice_sess_check *the_check;
+ pj_ice_msg_data *msg_data;
+ int addr_len;
+ pj_bool_t saved;
+ pj_status_t status;
+
+ /* Must have nominated check by now */
+ pj_assert(comp->nominated_check != NULL);
+ the_check = comp->nominated_check;
+
+ /* Create the Binding Indication */
+ status = pj_stun_session_create_ind(comp->stun_sess,
+ PJ_STUN_BINDING_INDICATION,
+ &tdata);
+ if (status != PJ_SUCCESS)
+ goto done;
+
+ /* Need the transport_id */
+ msg_data = PJ_POOL_ZALLOC_T(tdata->pool, pj_ice_msg_data);
+ msg_data->transport_id = the_check->lcand->transport_id;
+
+ /* Temporarily disable FINGERPRINT. The Binding Indication
+ * SHOULD NOT contain any attributes.
+ */
+ saved = pj_stun_session_use_fingerprint(comp->stun_sess, PJ_FALSE);
+
+ /* Send to session */
+ addr_len = pj_sockaddr_get_len(&the_check->rcand->addr);
+ status = pj_stun_session_send_msg(comp->stun_sess, msg_data,
+ PJ_FALSE, PJ_FALSE,
+ &the_check->rcand->addr,
+ addr_len, tdata);
+
+ /* Restore FINGERPRINT usage */
+ pj_stun_session_use_fingerprint(comp->stun_sess, saved);
+
+done:
+ ice->comp_ka = (ice->comp_ka + 1) % ice->comp_cnt;
+ }
+
+ if (ice->timer.id == TIMER_NONE) {
+ pj_time_val delay = { 0, 0 };
+
+ delay.msec = (PJ_ICE_SESS_KEEP_ALIVE_MIN +
+ (pj_rand() % PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND)) * 1000 /
+ ice->comp_cnt;
+ pj_time_val_normalize(&delay);
+
+ ice->timer.id = TIMER_KEEP_ALIVE;
+ pj_timer_heap_schedule(ice->stun_cfg.timer_heap, &ice->timer, &delay);
+
+ } else {
+ pj_assert(!"Not expected any timer active");
+ }
}
/* This function is called when ICE processing completes */
@@ -1060,6 +1209,11 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
ice->is_complete = PJ_TRUE;
ice->ice_status = status;
+ if (ice->timer.id != TIMER_NONE) {
+ pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer);
+ ice->timer.id = TIMER_NONE;
+ }
+
/* Log message */
LOG4((ice->obj_name, "ICE process complete, status=%s",
pj_strerror(status, ice->tmp.errmsg,
@@ -1071,26 +1225,49 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
if (ice->cb.on_ice_complete) {
pj_time_val delay = {0, 0};
- ice->completion_timer.cb = &on_completion_timer;
- ice->completion_timer.user_data = (void*) ice;
- ice->completion_timer.id = PJ_TRUE;
-
+ ice->timer.id = TIMER_COMPLETION_CALLBACK;
pj_timer_heap_schedule(ice->stun_cfg.timer_heap,
- &ice->completion_timer,
- &delay);
+ &ice->timer, &delay);
}
}
}
+/* Update valid check and nominated check for the candidate */
+static void update_comp_check(pj_ice_sess *ice, unsigned comp_id,
+ pj_ice_sess_check *check)
+{
+ pj_ice_sess_comp *comp;
+
+ comp = find_comp(ice, comp_id);
+ if (comp->valid_check == NULL) {
+ comp->valid_check = check;
+ } else {
+ if (CMP_CHECK_PRIO(comp->valid_check, check) < 0)
+ comp->valid_check = check;
+ }
+
+ if (check->nominated) {
+ /* Update the nominated check for the component */
+ if (comp->nominated_check == NULL) {
+ comp->nominated_check = check;
+ } else {
+ if (CMP_CHECK_PRIO(comp->nominated_check, check) < 0)
+ comp->nominated_check = check;
+ }
+ }
+}
/* This function is called when one check completes */
static pj_bool_t on_check_complete(pj_ice_sess *ice,
pj_ice_sess_check *check)
{
+ pj_ice_sess_comp *comp;
unsigned i;
pj_assert(check->state >= PJ_ICE_SESS_CHECK_STATE_SUCCEEDED);
+ comp = find_comp(ice, check->lcand->comp_id);
+
/* 7.1.2.2.2. Updating Pair States
*
* The agent sets the state of the pair that generated the check to
@@ -1104,6 +1281,7 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
* always.
*/
if (check->err_code==PJ_SUCCESS) {
+
for (i=0; i<ice->clist.count; ++i) {
pj_ice_sess_check *c = &ice->clist.checks[i];
if (pj_strcmp(&c->lcand->foundation, &check->lcand->foundation)==0
@@ -1112,6 +1290,11 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, 0);
}
}
+
+ LOG5((ice->obj_name, "Check %d is successful%s",
+ GET_CHECK_ID(&ice->clist, check),
+ (check->nominated ? " and nominated" : "")));
+
}
/* 8.2. Updating States
@@ -1136,12 +1319,6 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
* than the lowest priority nominated pair for that component
*/
if (check->err_code==PJ_SUCCESS && check->nominated) {
- pj_ice_sess_comp *comp;
-
- LOG5((ice->obj_name, "Check %d is successful and nominated",
- GET_CHECK_ID(&ice->clist, check)));
-
- comp = find_comp(ice, check->lcand->comp_id);
for (i=0; i<ice->clist.count; ++i) {
@@ -1179,14 +1356,6 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
}
}
}
-
- /* Update the nominated check for the component */
- if (comp->valid_check == NULL) {
- comp->valid_check = check;
- } else {
- if (CMP_CHECK_PRIO(comp->valid_check, check) < 0)
- comp->valid_check = check;
- }
}
@@ -1211,7 +1380,7 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
* ICE processing as success, otherwise wait.
*/
for (i=0; i<ice->comp_cnt; ++i) {
- if (ice->comp[i].valid_check == NULL)
+ if (ice->comp[i].nominated_check == NULL)
break;
}
if (i == ice->comp_cnt) {
@@ -1258,23 +1427,16 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
/* All checks have completed, but we don't have nominated pair.
* If agent's role is controlled, check if all components have
* valid pair. If it does, this means the controlled agent has
- * finished the check list early and it's waiting for controlling
- * agent to send a check with USE-CANDIDATE flag set.
+ * finished the check list and it's waiting for controlling
+ * agent to send checks with USE-CANDIDATE flag set.
*/
if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED) {
- unsigned comp_id;
- for (comp_id=1; comp_id <= ice->comp_cnt; ++comp_id) {
- unsigned j;
- for (j=0; j<ice->valid_list.count; ++j) {
- pj_ice_sess_check *vc = &ice->valid_list.checks[j];
- if (vc->lcand->comp_id == comp_id)
- break;
- }
- if (j == ice->valid_list.count)
+ for (i=0; i < ice->comp_cnt; ++i) {
+ if (ice->comp[i].valid_check == NULL)
break;
}
- if (comp_id <= ice->comp_cnt) {
+ if (i < ice->comp_cnt) {
/* This component ID doesn't have valid pair.
* Mark ICE as failed.
*/
@@ -1284,12 +1446,109 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
/* All components have a valid pair.
* We should wait until we receive nominated checks.
*/
+ if (ice->timer.id == TIMER_NONE &&
+ ice->opt.controlled_agent_want_nom_timeout >= 0)
+ {
+ pj_time_val delay;
+
+ delay.sec = 0;
+ delay.msec = ice->opt.controlled_agent_want_nom_timeout;
+ pj_time_val_normalize(&delay);
+
+ ice->timer.id = TIMER_CONTROLLED_WAIT_NOM;
+ pj_timer_heap_schedule(ice->stun_cfg.timer_heap,
+ &ice->timer,
+ &delay);
+
+ LOG5((ice->obj_name,
+ "All checks have completed. Controlled agent now "
+ "waits for nomination from controlling agent "
+ "(timeout=%d msec)",
+ ice->opt.controlled_agent_want_nom_timeout));
+ }
return PJ_FALSE;
}
+
+ /* Unreached */
+
+ } else if (ice->is_nominating) {
+ /* We are controlling agent and all checks have completed but
+ * there's at least one component without nominated pair (or
+ * more likely we don't have any nominated pairs at all).
+ */
+ on_ice_complete(ice, PJNATH_EICEFAILED);
+ return PJ_TRUE;
+
+ } else {
+ /* We are controlling agent and all checks have completed. If
+ * we have valid list for every component, then move on to
+ * sending nominated check, otherwise we have failed.
+ */
+ for (i=0; i<ice->comp_cnt; ++i) {
+ if (ice->comp[i].valid_check == NULL)
+ break;
+ }
+
+ if (i < ice->comp_cnt) {
+ /* At least one component doesn't have a valid check. Mark
+ * ICE as failed.
+ */
+ on_ice_complete(ice, PJNATH_EICEFAILED);
+ return PJ_TRUE;
+ }
+
+ /* Now it's time to send connectivity check with nomination
+ * flag set.
+ */
+ LOG4((ice->obj_name,
+ "All checks have completed, starting nominated checks now"));
+ start_nominated_check(ice);
+ return PJ_FALSE;
}
+ }
- on_ice_complete(ice, PJNATH_EICEFAILED);
- return PJ_TRUE;
+ /* If this connectivity check has been successful, scan all components
+ * and see if they have a valid pair, if we are controlling and we haven't
+ * started our nominated check yet.
+ */
+ if (check->err_code == PJ_SUCCESS &&
+ ice->role==PJ_ICE_SESS_ROLE_CONTROLLING &&
+ !ice->is_nominating &&
+ ice->timer.id == TIMER_NONE)
+ {
+ pj_time_val delay;
+
+ for (i=0; i<ice->comp_cnt; ++i) {
+ if (ice->comp[i].valid_check == NULL)
+ break;
+ }
+
+ if (i < ice->comp_cnt) {
+ /* Some components still don't have valid pair, continue
+ * processing.
+ */
+ return PJ_FALSE;
+ }
+
+ LOG4((ice->obj_name,
+ "Scheduling nominated check in %d ms",
+ ice->opt.nominated_check_delay));
+
+ if (ice->timer.id != TIMER_NONE) {
+ pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer);
+ ice->timer.id = TIMER_NONE;
+ }
+
+ /* All components have valid pair. Let connectivity checks run for
+ * a little bit more time, then start our nominated check.
+ */
+ delay.sec = 0;
+ delay.msec = ice->opt.nominated_check_delay;
+ pj_time_val_normalize(&delay);
+
+ ice->timer.id = TIMER_START_NOMINATED_CHECK;
+ pj_timer_heap_schedule(ice->stun_cfg.timer_heap, &ice->timer, &delay);
+ return PJ_FALSE;
}
/* We still have checks to perform */
@@ -1297,7 +1556,6 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
}
-
/* Create checklist by pairing local candidates with remote candidates */
PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
pj_ice_sess *ice,
@@ -1430,10 +1688,11 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
return PJ_SUCCESS;
}
-/* Perform check on the specified candidate pair */
+/* Perform check on the specified candidate pair. */
static pj_status_t perform_check(pj_ice_sess *ice,
pj_ice_sess_checklist *clist,
- unsigned check_id)
+ unsigned check_id,
+ pj_bool_t nominate)
{
pj_ice_sess_comp *comp;
pj_ice_msg_data *msg_data;
@@ -1472,8 +1731,13 @@ static pj_status_t perform_check(pj_ice_sess *ice,
msg_data->data.req.ckid = check_id;
/* Add PRIORITY */
+#if PJNATH_ICE_PRIO_STD
+ prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535,
+ lcand->comp_id);
+#else
prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 0,
lcand->comp_id);
+#endif
pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg,
PJ_STUN_ATTR_PRIORITY, prio);
@@ -1481,9 +1745,11 @@ static pj_status_t perform_check(pj_ice_sess *ice,
* Also add ICE-CONTROLLING or ICE-CONTROLLED
*/
if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) {
- pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg,
- PJ_STUN_ATTR_USE_CANDIDATE);
- check->nominated = PJ_TRUE;
+ if (nominate) {
+ pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg,
+ PJ_STUN_ATTR_USE_CANDIDATE);
+ check->nominated = PJ_TRUE;
+ }
pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg,
PJ_STUN_ATTR_ICE_CONTROLLING,
@@ -1549,7 +1815,7 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
pj_ice_sess_check *check = &clist->checks[i];
if (check->state == PJ_ICE_SESS_CHECK_STATE_WAITING) {
- status = perform_check(ice, clist, i);
+ status = perform_check(ice, clist, i, ice->is_nominating);
if (status != PJ_SUCCESS) {
pj_mutex_unlock(ice->mutex);
return status;
@@ -1568,7 +1834,7 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
pj_ice_sess_check *check = &clist->checks[i];
if (check->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) {
- status = perform_check(ice, clist, i);
+ status = perform_check(ice, clist, i, ice->is_nominating);
if (status != PJ_SUCCESS) {
pj_mutex_unlock(ice->mutex);
return status;
@@ -1596,6 +1862,66 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
}
+/* Start sending connectivity check with USE-CANDIDATE */
+static void start_nominated_check(pj_ice_sess *ice)
+{
+ pj_time_val delay;
+ unsigned i;
+ pj_status_t status;
+
+ LOG4((ice->obj_name, "Starting nominated check.."));
+
+ pj_assert(ice->is_nominating == PJ_FALSE);
+
+ /* Stop our timer if it's active */
+ if (ice->timer.id == TIMER_START_NOMINATED_CHECK) {
+ pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer);
+ ice->timer.id = TIMER_NONE;
+ }
+
+ /* For each component, set the check state of valid check with
+ * highest priority to Waiting (it should have Success state now).
+ */
+ for (i=0; i<ice->comp_cnt; ++i) {
+ unsigned j;
+ const pj_ice_sess_check *vc = ice->comp[i].valid_check;
+
+ pj_assert(ice->comp[i].nominated_check == NULL);
+ pj_assert(vc->err_code == PJ_SUCCESS);
+
+ for (j=0; j<ice->clist.count; ++j) {
+ pj_ice_sess_check *c = &ice->clist.checks[j];
+ if (c->lcand->transport_id == vc->lcand->transport_id &&
+ c->rcand == vc->rcand)
+ {
+ pj_assert(c->err_code == PJ_SUCCESS);
+ c->state = PJ_ICE_SESS_CHECK_STATE_FROZEN;
+ check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING,
+ PJ_SUCCESS);
+ break;
+ }
+ }
+ }
+
+ /* And (re)start the periodic check */
+ if (!ice->clist.timer.id) {
+ pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->clist.timer);
+ ice->clist.timer.id = PJ_FALSE;
+ }
+
+ ice->clist.timer.id = PJ_TRUE;
+ delay.sec = delay.msec = 0;
+ status = pj_timer_heap_schedule(ice->stun_cfg.timer_heap,
+ &ice->clist.timer, &delay);
+ if (status != PJ_SUCCESS) {
+ ice->clist.timer.id = PJ_FALSE;
+ } else {
+ LOG5((ice->obj_name, "Periodic timer rescheduled.."));
+ }
+
+ ice->is_nominating = PJ_TRUE;
+}
+
/* Timer callback to perform periodic check */
static void periodic_timer(pj_timer_heap_t *th,
pj_timer_entry *te)
@@ -1642,6 +1968,10 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice)
LOG4((ice->obj_name, "Starting ICE check.."));
+ /* If we are using aggressive nomination, set the is_nominating state */
+ if (ice->opt.aggressive)
+ ice->is_nominating = PJ_TRUE;
+
/* The agent examines the check list for the first media stream (a
* media stream is the first media stream when it is described by
* the first m-line in the SDP offer and answer). For that media
@@ -1826,7 +2156,8 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
/* Resend request */
LOG4((ice->obj_name, "Resending check because of role conflict"));
check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_WAITING, 0);
- perform_check(ice, clist, msg_data->data.req.ckid);
+ perform_check(ice, clist, msg_data->data.req.ckid,
+ check->nominated || ice->is_nominating);
pj_mutex_unlock(ice->mutex);
return;
}
@@ -1959,19 +2290,34 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
* equals the destination address to which the request was sent.
*/
- /* Add pair to valid list */
- pj_assert(ice->valid_list.count < PJ_ICE_MAX_CHECKS);
- new_check = &ice->valid_list.checks[ice->valid_list.count++];
- new_check->lcand = lcand;
- new_check->rcand = check->rcand;
- new_check->prio = CALC_CHECK_PRIO(ice, lcand, check->rcand);
- new_check->state = PJ_ICE_SESS_CHECK_STATE_SUCCEEDED;
- new_check->nominated = check->nominated;
- new_check->err_code = PJ_SUCCESS;
+ /* Add pair to valid list, if it's not there, otherwise just update
+ * nominated flag
+ */
+ for (i=0; i<ice->valid_list.count; ++i) {
+ if (ice->valid_list.checks[i].lcand == lcand &&
+ ice->valid_list.checks[i].rcand == check->rcand)
+ break;
+ }
+
+ if (i==ice->valid_list.count) {
+ pj_assert(ice->valid_list.count < PJ_ICE_MAX_CHECKS);
+ new_check = &ice->valid_list.checks[ice->valid_list.count++];
+ new_check->lcand = lcand;
+ new_check->rcand = check->rcand;
+ new_check->prio = CALC_CHECK_PRIO(ice, lcand, check->rcand);
+ new_check->state = PJ_ICE_SESS_CHECK_STATE_SUCCEEDED;
+ new_check->nominated = check->nominated;
+ new_check->err_code = PJ_SUCCESS;
+ } else {
+ new_check = &ice->valid_list.checks[i];
+ ice->valid_list.checks[i].nominated = check->nominated;
+ }
/* Sort valid_list */
sort_checklist(&ice->valid_list);
+ /* Update valid check and nominated check for the component */
+ update_comp_check(ice, new_check->lcand->comp_id, new_check);
/* 7.1.2.2.2. Updating Pair States
*
@@ -2312,8 +2658,11 @@ static void handle_incoming_check(pj_ice_sess *ice,
if (c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN ||
c->state == PJ_ICE_SESS_CHECK_STATE_WAITING)
{
+ /* See if we shall nominate this check */
+ pj_bool_t nominate = (c->nominated || ice->is_nominating);
+
LOG5((ice->obj_name, "Performing triggered check for check %d",i));
- perform_check(ice, &ice->clist, i);
+ perform_check(ice, &ice->clist, i, nominate);
} else if (c->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) {
/* Should retransmit immediately
@@ -2336,8 +2685,17 @@ static void handle_incoming_check(pj_ice_sess *ice,
if (rcheck->use_candidate) {
for (j=0; j<ice->valid_list.count; ++j) {
pj_ice_sess_check *vc = &ice->valid_list.checks[j];
- if (vc->lcand == c->lcand && vc->rcand == c->rcand) {
+ if (vc->lcand->transport_id == c->lcand->transport_id &&
+ vc->rcand == c->rcand)
+ {
+ /* Set nominated flag */
vc->nominated = PJ_TRUE;
+
+ /* Update valid check and nominated check for the component */
+ update_comp_check(ice, vc->lcand->comp_id, vc);
+
+ dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->valid_list, vc);
+ LOG5((ice->obj_name, "Valid check %s is nominated", ice->tmp.txt));
}
}
}
@@ -2361,6 +2719,7 @@ static void handle_incoming_check(pj_ice_sess *ice,
else if (ice->clist.count < PJ_ICE_MAX_CHECKS) {
pj_ice_sess_check *c = &ice->clist.checks[ice->clist.count];
+ pj_bool_t nominate;
c->lcand = lcand;
c->rcand = rcand;
@@ -2369,9 +2728,11 @@ static void handle_incoming_check(pj_ice_sess *ice,
c->nominated = rcheck->use_candidate;
c->err_code = PJ_SUCCESS;
+ nominate = (c->nominated || ice->is_nominating);
+
LOG4((ice->obj_name, "New triggered check added: %d",
ice->clist.count));
- perform_check(ice, &ice->clist, ice->clist.count++);
+ perform_check(ice, &ice->clist, ice->clist.count++, nominate);
} else {
LOG4((ice->obj_name, "Error: unable to perform triggered check: "
@@ -2388,6 +2749,8 @@ static pj_status_t on_stun_rx_indication(pj_stun_session *sess,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
+ struct stun_data *sd;
+
PJ_UNUSED_ARG(sess);
PJ_UNUSED_ARG(pkt);
PJ_UNUSED_ARG(pkt_len);
@@ -2396,9 +2759,18 @@ static pj_status_t on_stun_rx_indication(pj_stun_session *sess,
PJ_UNUSED_ARG(src_addr);
PJ_UNUSED_ARG(src_addr_len);
- PJ_TODO(SUPPORT_RX_BIND_REQUEST_AS_INDICATION);
+ sd = (struct stun_data*) pj_stun_session_get_user_data(sess);
- return PJ_ENOTSUP;
+ if (msg->hdr.type == PJ_STUN_BINDING_INDICATION) {
+ LOG5((sd->ice->obj_name, "Received Binding Indication keep-alive "
+ "for component %d", sd->comp_id));
+ } else {
+ LOG4((sd->ice->obj_name, "Received unexpected %s indication "
+ "for component %d", pj_stun_get_method_name(msg->hdr.type),
+ sd->comp_id));
+ }
+
+ return PJ_SUCCESS;
}
diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c
index b244f0fc..92f550d0 100644
--- a/pjnath/src/pjnath/ice_strans.c
+++ b/pjnath/src/pjnath/ice_strans.c
@@ -50,20 +50,34 @@ enum tp_type
/* Candidate's local preference values. This is mostly used to
* specify preference among candidates with the same type. Since
* we don't have the facility to specify that, we'll just set it
- * all to zero.
+ * all to the same value.
*/
-#define SRFLX_PREF 0
-#define HOST_PREF 0
-#define RELAY_PREF 0
+#if PJNATH_ICE_PRIO_STD
+# define SRFLX_PREF 65535
+# define HOST_PREF 65535
+# define RELAY_PREF 65535
+#else
+# define SRFLX_PREF 0
+# define HOST_PREF 0
+# define RELAY_PREF 0
+#endif
+
/* The candidate type preference when STUN candidate is used */
static pj_uint8_t srflx_pref_table[4] =
{
+#if PJNATH_ICE_PRIO_STD
+ 100, /**< PJ_ICE_HOST_PREF */
+ 126, /**< PJ_ICE_SRFLX_PREF */
+ 110, /**< PJ_ICE_PRFLX_PREF */
+ 0 /**< PJ_ICE_RELAYED_PREF */
+#else
/* Keep it to 2 bits */
1, /**< PJ_ICE_HOST_PREF */
2, /**< PJ_ICE_SRFLX_PREF */
3, /**< PJ_ICE_PRFLX_PREF */
0 /**< PJ_ICE_RELAYED_PREF */
+#endif
};
@@ -197,9 +211,13 @@ PJ_DEF(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg)
pj_stun_sock_cfg_default(&cfg->stun.cfg);
pj_turn_alloc_param_default(&cfg->turn.alloc_param);
+ pj_ice_sess_options_default(&cfg->opt);
+
cfg->af = pj_AF_INET();
cfg->stun.port = PJ_STUN_PORT;
cfg->turn.conn_type = PJ_TURN_TP_UDP;
+
+ cfg->stun.max_host_cands = 64;
}
@@ -245,7 +263,7 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id)
comp->default_cand = 0;
/* Create STUN transport if configured */
- if (ice_st->cfg.stun.server.slen || !ice_st->cfg.stun.no_host_cands) {
+ if (ice_st->cfg.stun.server.slen || ice_st->cfg.stun.max_host_cands) {
pj_stun_sock_cb stun_sock_cb;
pj_ice_sess_cand *cand;
@@ -309,10 +327,10 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id)
}
- /* Add local addresses to host candidates, unless no_host_cands
- * flag is set.
+ /* Add local addresses to host candidates, unless max_host_cands
+ * is set to zero.
*/
- if (ice_st->cfg.stun.no_host_cands == PJ_FALSE) {
+ if (ice_st->cfg.stun.max_host_cands) {
pj_stun_sock_info stun_sock_info;
unsigned i;
@@ -321,7 +339,9 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id)
if (status != PJ_SUCCESS)
return status;
- for (i=0; i<stun_sock_info.alias_cnt; ++i) {
+ for (i=0; i<stun_sock_info.alias_cnt &&
+ i<ice_st->cfg.stun.max_host_cands; ++i)
+ {
char addrinfo[PJ_INET6_ADDRSTRLEN+10];
const pj_sockaddr *addr = &stun_sock_info.aliases[i];
@@ -647,6 +667,30 @@ PJ_DEF(void*) pj_ice_strans_get_user_data(pj_ice_strans *ice_st)
/*
+ * Get the value of various options of the ICE stream transport.
+ */
+PJ_DEF(pj_status_t) pj_ice_strans_get_options( pj_ice_strans *ice_st,
+ pj_ice_sess_options *opt)
+{
+ PJ_ASSERT_RETURN(ice_st && opt, PJ_EINVAL);
+ pj_memcpy(opt, &ice_st->cfg.opt, sizeof(*opt));
+ return PJ_SUCCESS;
+}
+
+/*
+ * Specify various options for this ICE stream transport.
+ */
+PJ_DEF(pj_status_t) pj_ice_strans_set_options(pj_ice_strans *ice_st,
+ const pj_ice_sess_options *opt)
+{
+ PJ_ASSERT_RETURN(ice_st && opt, PJ_EINVAL);
+ pj_memcpy(&ice_st->cfg.opt, opt, sizeof(*opt));
+ if (ice_st->ice)
+ pj_ice_sess_set_options(ice_st->ice, &ice_st->cfg.opt);
+ return PJ_SUCCESS;
+}
+
+/*
* Create ICE!
*/
PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
@@ -682,6 +726,9 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
/* Associate user data */
ice_st->ice->user_data = (void*)ice_st;
+ /* Set options */
+ pj_ice_sess_set_options(ice_st->ice, &ice_st->cfg.opt);
+
/* If default candidate for components are SRFLX one, upload a custom
* type priority to ICE session so that SRFLX candidates will get
* checked first.
diff --git a/pjnath/src/pjnath/stun_auth.c b/pjnath/src/pjnath/stun_auth.c
index cfca3382..7cb0ab12 100644
--- a/pjnath/src/pjnath/stun_auth.c
+++ b/pjnath/src/pjnath/stun_auth.c
@@ -528,6 +528,7 @@ PJ_DEF(pj_bool_t) pj_stun_auth_valid_for_msg(const pj_stun_msg *msg)
switch (err_attr->err_code) {
case PJ_STUN_SC_BAD_REQUEST: /* 400 (Bad Request) */
case PJ_STUN_SC_UNAUTHORIZED: /* 401 (Unauthorized) */
+ case PJ_STUN_SC_STALE_NONCE: /* 438 (Stale Nonce) */
/* Due to the way this response is generated here, we can't really
* authenticate 420 (Unknown Attribute) response */
diff --git a/pjnath/src/pjnath/stun_msg_dump.c b/pjnath/src/pjnath/stun_msg_dump.c
index 2ff4a74b..afe9530c 100644
--- a/pjnath/src/pjnath/stun_msg_dump.c
+++ b/pjnath/src/pjnath/stun_msg_dump.c
@@ -57,11 +57,19 @@ static int print_attr(char *buffer, unsigned length,
const pj_stun_attr_hdr *ahdr)
{
char *p = buffer, *end = buffer + length;
+ const char *attr_name = pj_stun_get_attr_name(ahdr->type);
+ char attr_buf[32];
int len;
+ if (*attr_name == '?') {
+ pj_ansi_snprintf(attr_buf, sizeof(attr_buf), "Attr 0x%x",
+ ahdr->type);
+ attr_name = attr_buf;
+ }
+
len = pj_ansi_snprintf(p, end-p,
" %s: length=%d",
- pj_stun_get_attr_name(ahdr->type),
+ attr_name,
(int)ahdr->length);
APPLY();
diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c
index fd71beb8..fb5c88c6 100644
--- a/pjnath/src/pjnath/stun_session.c
+++ b/pjnath/src/pjnath/stun_session.c
@@ -219,10 +219,15 @@ static pj_status_t apply_msg_options(pj_stun_session *sess,
/* If the agent is sending a request, it SHOULD add a SOFTWARE attribute
* to the request. The server SHOULD include a SOFTWARE attribute in all
- * responses
+ * responses.
+ *
+ * If magic value is not PJ_STUN_MAGIC, only apply the attribute for
+ * responses.
*/
- if (sess->srv_name.slen && !PJ_STUN_IS_INDICATION(msg->hdr.type) &&
- pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_SOFTWARE, 0)==NULL)
+ if (sess->srv_name.slen &&
+ pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_SOFTWARE, 0)==NULL &&
+ (PJ_STUN_IS_RESPONSE(msg->hdr.type) ||
+ PJ_STUN_IS_REQUEST(msg->hdr.type) && msg->hdr.magic==PJ_STUN_MAGIC))
{
pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE,
&sess->srv_name);
@@ -630,6 +635,18 @@ PJ_DEF(void) pj_stun_session_set_log( pj_stun_session *sess,
sess->log_flag = flags;
}
+PJ_DEF(pj_bool_t) pj_stun_session_use_fingerprint(pj_stun_session *sess,
+ pj_bool_t use)
+{
+ pj_bool_t old_use;
+
+ PJ_ASSERT_RETURN(sess, PJ_FALSE);
+
+ old_use = sess->use_fingerprint;
+ sess->use_fingerprint = use;
+ return old_use;
+}
+
static pj_status_t get_auth(pj_stun_session *sess,
pj_stun_tx_data *tdata)
{