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: https://svn.pjsip.org/repos/pjproject/trunk@2724 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjlib-util/src/pjlib-util/resolver.c b/pjlib-util/src/pjlib-util/resolver.c
index ebe4b3f..ee0b9e7 100644
--- a/pjlib-util/src/pjlib-util/resolver.c
+++ b/pjlib-util/src/pjlib-util/resolver.c
@@ -28,6 +28,7 @@
 #include <pj/os.h>
 #include <pj/pool.h>
 #include <pj/pool_buf.h>
+#include <pj/rand.h>
 #include <pj/string.h>
 #include <pj/sock.h>
 #include <pj/timer.h>
@@ -783,6 +784,7 @@
     q = alloc_qnode(resolver, options, user_data, cb);
 
     /* Save the ID and key */
+    /* TODO: dnsext-forgery-resilient: randomize id for security */
     q->id = resolver->last_id++;
     if (resolver->last_id == 0)
 	resolver->last_id = 1;
diff --git a/pjmedia/src/pjmedia-codec/ipp_codecs.c b/pjmedia/src/pjmedia-codec/ipp_codecs.c
index b22b4f1..4777b0b 100644
--- a/pjmedia/src/pjmedia-codec/ipp_codecs.c
+++ b/pjmedia/src/pjmedia-codec/ipp_codecs.c
@@ -233,17 +233,19 @@
 ipp_codec[] = 
 {
 #   if PJMEDIA_HAS_INTEL_IPP_CODEC_AMR
+    /* AMR-NB SID seems to produce noise, so let's just disable its VAD. */
     {1, "AMR",	    PJMEDIA_RTP_PT_AMR,       &USC_GSMAMR_Fxns,  8000, 1, 160, 
-		    5900, 12200, 4, 1, 1, 
-		    &predecode_amr, &parse_amr, &pack_amr
-		    /*, {1, {{{"octet-align", 11}, {"1", 1}}} } */
+		    7400, 12200, 2, 0, 1, 
+		    &predecode_amr, &parse_amr, &pack_amr,
+		    {1, {{{"octet-align", 11}, {"1", 1}}} }
     },
 #   endif
 
 #   if PJMEDIA_HAS_INTEL_IPP_CODEC_AMRWB
     {1, "AMR-WB",   PJMEDIA_RTP_PT_AMRWB,     &USC_AMRWB_Fxns,  16000, 1, 320,
 		    15850, 23850, 1, 1, 1, 
-		    &predecode_amr, &parse_amr, &pack_amr
+		    &predecode_amr, &parse_amr, &pack_amr,
+		    {1, {{{"octet-align", 11}, {"1", 1}}} }
     },
 #   endif
 
@@ -560,7 +562,14 @@
 
     /* Check Change Mode Request. */
     if ((setting->amr_nb && cmr <= 7) || (!setting->amr_nb && cmr <= 8)) {
+	struct ipp_codec *ippc = &ipp_codec[codec_data->codec_idx];
+
 	s->enc_mode = cmr;
+	codec_data->info->params.modes.bitrate = s->enc_setting.amr_nb?
+				pjmedia_codec_amrnb_bitrates[s->enc_mode] :
+				pjmedia_codec_amrwb_bitrates[s->enc_mode];
+	ippc->fxns->std.Control(&codec_data->info->params.modes, 
+				codec_data->enc);
     }
 
     return PJ_SUCCESS;
@@ -1033,7 +1042,7 @@
     codec_data->info->params.direction = USC_DECODE;
 
     /* Not sure if VAD affects decoder, just try to be safe */
-    codec_data->info->params.modes.vad = ippc->has_native_vad;
+    //codec_data->info->params.modes.vad = ippc->has_native_vad;
 
     /* Get number of memory blocks needed by the decoder */
     if (USC_NoError != ippc->fxns->std.NumAlloc(&codec_data->info->params, 
@@ -1084,34 +1093,79 @@
     if (ippc->pt == PJMEDIA_RTP_PT_AMR || ippc->pt == PJMEDIA_RTP_PT_AMRWB) {
 	amr_settings_t *s;
 	pj_uint8_t octet_align = 0;
-	const pj_str_t STR_FMTP_OCTET_ALIGN = {"octet-align", 11};
+	pj_int8_t enc_mode = -1;
 
-	/* Check octet-align */
+	/* Check AMR specific attributes */
+
 	for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) {
+	    /* octet-align, one of the parameters that must have same value 
+	     * in offer & answer (RFC 4867 Section 8.3.1). Just check fmtp
+	     * in the decoder side, since it's value is guaranteed to fulfil 
+	     * above requirement (by SDP negotiator).
+	     */
+	    const pj_str_t STR_FMTP_OCTET_ALIGN = {"octet-align", 11};
+	    
 	    if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name, 
 			   &STR_FMTP_OCTET_ALIGN) == 0)
 	    {
 		octet_align=(pj_uint8_t)
-			    (pj_strtoul(&attr->setting.dec_fmtp.param[i].val));
+			    pj_strtoul(&attr->setting.dec_fmtp.param[i].val);
+		break;
+	    }
+	}
+	for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
+	    /* mode-set */
+	    const pj_str_t STR_FMTP_MODE_SET = {"mode-set", 8};
+	    
+	    if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, 
+			   &STR_FMTP_MODE_SET) == 0)
+	    {
+		pj_int8_t tmp;
+
+		/* Just get the first value. */
+		tmp = (pj_int8_t)
+		      pj_strtoul(&attr->setting.enc_fmtp.param[i].val);
+
+		if ((ippc->pt == PJMEDIA_RTP_PT_AMR && tmp > 0 && tmp < 8) ||
+		    (ippc->pt == PJMEDIA_RTP_PT_AMRWB && tmp > 0 && tmp < 9))
+		{
+		    enc_mode = tmp;
+		    PJ_LOG(4,(THIS_FILE, "Remote specifies AMR mode-set attr, "
+			      "selected: %d", enc_mode));
+		}
 		break;
 	    }
 	}
 
+	/* Initialize AMR specific settings */
 	s = PJ_POOL_ZALLOC_T(pool, amr_settings_t);
 	codec_data->codec_setting = s;
 
-	s->enc_mode = pjmedia_codec_amr_get_mode(ippc->def_bitrate);
-	if (s->enc_mode < 0)
-	    goto on_error;
-
 	s->enc_setting.amr_nb = (pj_uint8_t)(ippc->pt == PJMEDIA_RTP_PT_AMR);
 	s->enc_setting.octet_aligned = octet_align;
 	s->enc_setting.reorder = PJ_TRUE;
 	s->enc_setting.cmr = 15;
-	
+
 	s->dec_setting.amr_nb = (pj_uint8_t)(ippc->pt == PJMEDIA_RTP_PT_AMR);
 	s->dec_setting.octet_aligned = octet_align;
 	s->dec_setting.reorder = PJ_TRUE;
+
+	s->enc_mode = pjmedia_codec_amr_get_mode(
+				    codec_data->info->params.modes.bitrate);
+	if (s->enc_mode < 0)
+	    goto on_error;
+
+	if (enc_mode != -1) {
+	    s->enc_mode = enc_mode;
+
+	    /* Apply requested encoder bitrate */
+	    codec_data->info->params.modes.bitrate = s->enc_setting.amr_nb?
+				    pjmedia_codec_amrnb_bitrates[s->enc_mode] :
+				    pjmedia_codec_amrwb_bitrates[s->enc_mode];
+	    ippc->fxns->std.Control(&codec_data->info->params.modes, 
+				    codec_data->enc);
+	}  
+
     }
 #endif
 
diff --git a/pjmedia/src/pjmedia/conf_switch.c b/pjmedia/src/pjmedia/conf_switch.c
index da239df..616a85b 100644
--- a/pjmedia/src/pjmedia/conf_switch.c
+++ b/pjmedia/src/pjmedia/conf_switch.c
@@ -553,10 +553,32 @@
 	return PJMEDIA_ENCSAMPLESPFRAME;
     }
     
-    /* Check if sink is listening to other ports */
+    /* If sink is currently listening to other ports, it needs to be released
+     * first before the new connection made.
+     */ 
     if (dst_port->transmitter_cnt > 0) {
-	pj_mutex_unlock(conf->mutex);
-	return PJ_ETOOMANYCONN;
+	unsigned j;
+	pj_bool_t transmitter_found = PJ_FALSE;
+
+	pj_assert(dst_port->transmitter_cnt == 1);
+	for (j=0; j<conf->max_ports && !transmitter_found; ++j) {
+	    if (conf->ports[j]) {
+		unsigned k;
+
+		for (k=0; k < conf->ports[j]->listener_cnt; ++k) {
+		    if (conf->ports[j]->listener_slots[k] == sink_slot) {
+			PJ_LOG(4,(THIS_FILE, "Connection [%d->%d] is "
+				  "disconnected forcedly for the new "
+				  "connection [%d->%d]",
+				  j, sink_slot, src_slot, sink_slot));
+			pjmedia_conf_disconnect_port(conf, j, sink_slot);
+			transmitter_found = PJ_TRUE;
+			break;
+		    }
+		}
+	    }
+	}
+	pj_assert(dst_port->transmitter_cnt == 0);
     }
 
     /* Check if connection has been made */
diff --git a/pjmedia/src/pjmedia/sdp_neg.c b/pjmedia/src/pjmedia/sdp_neg.c
index 60ca36e..9c4e9cf 100644
--- a/pjmedia/src/pjmedia/sdp_neg.c
+++ b/pjmedia/src/pjmedia/sdp_neg.c
@@ -865,12 +865,14 @@
 			     (ar.param.slen==1 && *ar.param.ptr=='1')))
 			{
 			    /* Further check for G7221, negotiate bitrate. */
-			    if (pj_strcmp2(&or_.enc_name, "G7221") == 0) {
+			    if (pj_stricmp2(&or_.enc_name, "G7221") == 0) {
 				if (match_g7221(offer, i, answer, j))
 				    break;
 			    } else
 			    /* Further check for AMR, negotiate fmtp. */
-			    if (pj_strcmp2(&or_.enc_name, "AMR") == 0) {
+			    if (pj_stricmp2(&or_.enc_name, "AMR") == 0 ||
+				pj_stricmp2(&or_.enc_name, "AMR-WB") == 0) 
+			    {
 				if (match_amr(offer, i, answer, j, PJ_FALSE, 
 					      NULL))
 				    break;
@@ -1070,7 +1072,7 @@
 		}
 		pjmedia_sdp_attr_get_rtpmap(a, &or_);
 
-		if (!pj_strcmp2(&or_.enc_name, "telephone-event")) {
+		if (!pj_stricmp2(&or_.enc_name, "telephone-event")) {
 		    master_has_telephone_event = 1;
 		    if (found_matching_telephone_event)
 			continue;
@@ -1097,19 +1099,21 @@
 			 */
 			if (!pj_stricmp(&or_.enc_name, &lr.enc_name) &&
 			    or_.clock_rate == lr.clock_rate &&
-			    (pj_strcmp(&or_.param, &lr.param)==0 ||
+			    (pj_stricmp(&or_.param, &lr.param)==0 ||
 			     (or_.param.slen==1 && *or_.param.ptr=='1'))) 
 			{
 			    /* Match! */
 			    if (is_codec) {
 				/* Further check for G7221, negotiate bitrate */
-				if (pj_strcmp2(&or_.enc_name, "G7221") == 0 &&
+				if (pj_stricmp2(&or_.enc_name, "G7221") == 0 &&
 				    !match_g7221(master, i, slave, j))
 				{
 				    continue;
 				} else 
 				/* Further check for AMR, negotiate fmtp */
-				if (pj_strcmp2(&or_.enc_name, "AMR")==0) {
+				if (pj_stricmp2(&or_.enc_name, "AMR")==0 ||
+				    pj_stricmp2(&or_.enc_name, "AMR-WB")==0) 
+				{
 				    unsigned o_med_idx, a_med_idx;
 
 				    o_med_idx = prefer_remote_codec_order? i:j;
diff --git a/pjmedia/src/pjmedia/tonegen.c b/pjmedia/src/pjmedia/tonegen.c
index f160fb7..2a54505 100644
--- a/pjmedia/src/pjmedia/tonegen.c
+++ b/pjmedia/src/pjmedia/tonegen.c
@@ -384,13 +384,13 @@
 	{ '0', 941,  1336 },
 	{ '1', 697,  1209 },
 	{ '2', 697,  1336 },
-	{ '3', 697,  1447 },
+	{ '3', 697,  1477 },
 	{ '4', 770,  1209 },
 	{ '5', 770,  1336 },
-	{ '6', 770,  1447 },
+	{ '6', 770,  1477 },
 	{ '7', 852,  1209 },
 	{ '8', 852,  1336 },
-	{ '9', 852,  1447 },
+	{ '9', 852,  1477 },
 	{ 'a', 697,  1633 },
 	{ 'b', 770,  1633 },
 	{ 'c', 852,  1633 },
diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h
index f955272..f265e2c 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 @@
 
 
 /**
- * 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
- * of the STUN keep-alive.
+ * 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: 20 seconds
+ * 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.
  *
- * @see PJ_ICE_ST_KEEP_ALIVE_MAX_RAND
+ * Application may set this value to -1 to disable this timer.
+ *
+ * Default: 10000 (milliseconds)
  */
-#ifndef PJ_ICE_ST_KEEP_ALIVE_MIN
-#   define PJ_ICE_ST_KEEP_ALIVE_MIN		    20
+#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
+ * 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: 15 seconds
+ *
+ * @see PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND
+ */
+#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 e17f946..2bc47dc 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 18f7ffd..e606f07 100644
--- a/pjnath/include/pjnath/ice_session.h
+++ b/pjnath/include/pjnath/ice_session.h
@@ -176,13 +176,20 @@
 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 @@
 
 
 /**
+ * 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 @@
     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 @@
     /* 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_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_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 0239707..2848787 100644
--- a/pjnath/include/pjnath/ice_strans.h
+++ b/pjnath/include/pjnath/ice_strans.h
@@ -197,6 +197,13 @@
     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 @@
 	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 @@
 
 
 /**
+ * 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 57fc38c..5339f1b 100644
--- a/pjnath/include/pjnath/stun_msg.h
+++ b/pjnath/include/pjnath/stun_msg.h
@@ -197,6 +197,10 @@
      */
     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 2a6fd02..686a09a 100644
--- a/pjnath/include/pjnath/stun_session.h
+++ b/pjnath/include/pjnath/stun_session.h
@@ -485,6 +485,18 @@
  */
 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 ffa1077..7603b40 100644
--- a/pjnath/src/pjnath-test/ice_test.c
+++ b/pjnath/src/pjnath-test/ice_test.c
@@ -64,6 +64,8 @@
     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 @@
     }
 
     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 d102a09..1656ce3 100644
--- a/pjnath/src/pjnath/errno.c
+++ b/pjnath/src/pjnath/errno.c
@@ -67,6 +67,7 @@
     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 f5ba8e3..ad08639 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 @@
     "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 @@
 
 
 /* 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_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 @@
 }
 
 
+/* 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 @@
     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_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 @@
 
 
 /*
+ * 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 @@
 	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 @@
 				  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 @@
     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);
 
-    if (ice->cb.on_ice_complete)
-	(*ice->cb.on_ice_complete)(ice, ice->ice_status);
+    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;
+    }
+
+    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 @@
 	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 @@
 	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 @@
      *     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 @@
 		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 @@
      *      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 @@
 		}
 	    }
 	}
-
-	/* 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 @@
      * 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 @@
 	/* 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 @@
 		/* 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;
+	}
+    }
+
+    /* 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;
 	}
 
-	on_ice_complete(ice, PJNATH_EICEFAILED);
-	return PJ_TRUE;
+	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 @@
 }
 
 
-
 /* 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 @@
     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 @@
     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 @@
      * 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 @@
 	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 @@
 	    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 @@
 }
 
 
+/* 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 @@
 
     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 @@
 	    /* 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 @@
      * 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 @@
 	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 @@
 	    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 @@
     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 @@
 	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 @@
 					 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 @@
     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 b244f0f..92f550d 100644
--- a/pjnath/src/pjnath/ice_strans.c
+++ b/pjnath/src/pjnath/ice_strans.c
@@ -50,20 +50,34 @@
 /* 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_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 @@
     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 @@
 
 	}
 
-	/* 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 @@
 	    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 @@
 
 
 /*
+ * 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 @@
     /* 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 cfca338..7cb0ab1 100644
--- a/pjnath/src/pjnath/stun_auth.c
+++ b/pjnath/src/pjnath/stun_auth.c
@@ -528,6 +528,7 @@
     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 2ff4a74..afe9530 100644
--- a/pjnath/src/pjnath/stun_msg_dump.c
+++ b/pjnath/src/pjnath/stun_msg_dump.c
@@ -57,11 +57,19 @@
 		      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 fd71beb..fb5c88c 100644
--- a/pjnath/src/pjnath/stun_session.c
+++ b/pjnath/src/pjnath/stun_session.c
@@ -219,10 +219,15 @@
 
     /* 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 @@
     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)
 {
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index dd81f9e..8be930f 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -264,7 +264,8 @@
     puts  ("");
     puts  ("Media Transport Options:");
     puts  ("  --use-ice           Enable ICE (default:no)");
-    puts  ("  --ice-no-host       Disable ICE host candidates (default: no)");
+    puts  ("  --ice-regular       Use ICE regular nomination (default: aggressive)");
+    puts  ("  --ice-max-hosts=N   Set maximum number of ICE host candidates");
     puts  ("  --ice-no-rtcp       Disable RTCP component in ICE (default: no)");
     puts  ("  --rtp-port=N        Base port to try for RTP (default=4000)");
     puts  ("  --rx-drop-pct=PCT   Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
@@ -476,8 +477,8 @@
 	   OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
 	   OPT_AUTO_ANSWER, OPT_AUTO_PLAY, OPT_AUTO_PLAY_HANGUP, OPT_AUTO_LOOP,
 	   OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
-	   OPT_USE_ICE, OPT_USE_SRTP, OPT_SRTP_SECURE,
-	   OPT_USE_TURN, OPT_ICE_NO_HOST, OPT_ICE_NO_RTCP, OPT_TURN_SRV, 
+	   OPT_USE_ICE, OPT_ICE_REGULAR, OPT_USE_SRTP, OPT_SRTP_SECURE,
+	   OPT_USE_TURN, OPT_ICE_MAX_HOSTS, OPT_ICE_NO_RTCP, OPT_TURN_SRV, 
 	   OPT_TURN_TCP, OPT_TURN_USER, OPT_TURN_PASSWD,
 	   OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC, 
 	   OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
@@ -553,8 +554,9 @@
 	{ "rtp-port",	1, 0, OPT_RTP_PORT},
 
 	{ "use-ice",    0, 0, OPT_USE_ICE},
+	{ "ice-regular",0, 0, OPT_ICE_REGULAR},
 	{ "use-turn",	0, 0, OPT_USE_TURN},
-	{ "ice-no-host",0, 0, OPT_ICE_NO_HOST},
+	{ "ice-max-hosts",1, 0, OPT_ICE_MAX_HOSTS},
 	{ "ice-no-rtcp",0, 0, OPT_ICE_NO_RTCP},
 	{ "turn-srv",	1, 0, OPT_TURN_SRV},
 	{ "turn-tcp",	0, 0, OPT_TURN_TCP},
@@ -992,12 +994,16 @@
 	    cfg->media_cfg.enable_ice = PJ_TRUE;
 	    break;
 
+	case OPT_ICE_REGULAR:
+	    cfg->media_cfg.ice_opt.aggressive = PJ_FALSE;
+	    break;
+
 	case OPT_USE_TURN:
 	    cfg->media_cfg.enable_turn = PJ_TRUE;
 	    break;
 
-	case OPT_ICE_NO_HOST:
-	    cfg->media_cfg.ice_no_host_cands = PJ_TRUE;
+	case OPT_ICE_MAX_HOSTS:
+	    cfg->media_cfg.ice_max_host_cands = my_atoi(pj_optarg);
 	    break;
 
 	case OPT_ICE_NO_RTCP:
@@ -1644,11 +1650,17 @@
     if (config->media_cfg.enable_ice)
 	pj_strcat2(&cfg, "--use-ice\n");
 
+    if (config->media_cfg.ice_opt.aggressive == PJ_FALSE)
+	pj_strcat2(&cfg, "--ice-regular\n");
+
     if (config->media_cfg.enable_turn)
 	pj_strcat2(&cfg, "--use-turn\n");
 
-    if (config->media_cfg.ice_no_host_cands)
-	pj_strcat2(&cfg, "--ice-no-host\n");
+    if (config->media_cfg.ice_max_host_cands >= 0) {
+	pj_ansi_sprintf(line, "--ice_max_host_cands %d\n",
+			config->media_cfg.ice_max_host_cands);
+	pj_strcat2(&cfg, line);
+    }
 
     if (config->media_cfg.ice_no_rtcp)
 	pj_strcat2(&cfg, "--ice-no-rtcp\n");
@@ -1885,7 +1897,7 @@
 	pj_strcat2(&cfg, "--use-compact-form\n");
     }
 
-    if (config->cfg.force_lr) {
+    if (!config->cfg.force_lr) {
 	pj_strcat2(&cfg, "--no-force-lr\n");
     }
 
@@ -3687,7 +3699,9 @@
 		    pj_list_push_back(&msg_data.hdr_list, &refer_sub);
 		}
 
-		pjsua_call_xfer_replaces(call, dst_call, 0, &msg_data);
+		pjsua_call_xfer_replaces(call, dst_call, 
+					 PJSUA_XFER_NO_REQUIRE_REPLACES, 
+					 &msg_data);
 	    }
 	    break;
 
@@ -4718,19 +4732,62 @@
 
     for (i=0; i<app_config.cfg.max_calls; ++i) {
 	enum { MAX_RETRY = 10 };
+	pj_sock_t sock[2];
+	pjmedia_sock_info si;
 	unsigned j;
 
 	/* Get rid of uninitialized var compiler warning with MSVC */
 	status = PJ_SUCCESS;
 
 	for (j=0; j<MAX_RETRY; ++j) {
-	    status = pjmedia_transport_udp_create3(pjsua_get_pjmedia_endpt(), 
-						   pj_AF_INET6(),
-						   NULL, 
-						   &app_config.rtp_cfg.bound_addr,
-						   port, 
-						   0, &tp[i].transport);
+	    unsigned k;
 
+	    for (k=0; k<2; ++k) {
+		pj_sockaddr bound_addr;
+
+		status = pj_sock_socket(pj_AF_INET6(), pj_SOCK_DGRAM(), 0, &sock[k]);
+		if (status != PJ_SUCCESS)
+		    break;
+
+		status = pj_sockaddr_init(pj_AF_INET6(), &bound_addr,
+					  &app_config.rtp_cfg.bound_addr, 
+					  (unsigned short)(port+k));
+		if (status != PJ_SUCCESS)
+		    break;
+
+		status = pj_sock_bind(sock[k], &bound_addr, 
+				      pj_sockaddr_get_len(&bound_addr));
+		if (status != PJ_SUCCESS)
+		    break;
+	    }
+	    if (status != PJ_SUCCESS) {
+		if (k==1)
+		    pj_sock_close(sock[0]);
+
+		if (port != 0)
+		    port += 10;
+		else
+		    break;
+
+		continue;
+	    }
+
+	    pj_bzero(&si, sizeof(si));
+	    si.rtp_sock = sock[0];
+	    si.rtcp_sock = sock[1];
+	
+	    pj_sockaddr_init(pj_AF_INET6(), &si.rtp_addr_name, 
+			     &app_config.rtp_cfg.public_addr, 
+			     (unsigned short)(port));
+	    pj_sockaddr_init(pj_AF_INET6(), &si.rtcp_addr_name, 
+			     &app_config.rtp_cfg.public_addr, 
+			     (unsigned short)(port+1));
+
+	    status = pjmedia_transport_udp_attach(pjsua_get_pjmedia_endpt(),
+						  NULL,
+						  &si,
+						  0,
+						  &tp[i].transport);
 	    if (port != 0)
 		port += 10;
 	    else
diff --git a/pjsip-apps/src/samples/debug.c b/pjsip-apps/src/samples/debug.c
index 1a145b9..5417a79 100644
--- a/pjsip-apps/src/samples/debug.c
+++ b/pjsip-apps/src/samples/debug.c
@@ -28,5 +28,5 @@
  * E.g.:
  *  #include "playfile.c"
  */
-#include "auddemo.c"
+#include "icedemo.c"
 
diff --git a/pjsip-apps/src/samples/icedemo.c b/pjsip-apps/src/samples/icedemo.c
index 1c13cf3..f3f7c6c 100644
--- a/pjsip-apps/src/samples/icedemo.c
+++ b/pjsip-apps/src/samples/icedemo.c
@@ -39,7 +39,8 @@
     {
 	unsigned    comp_cnt;
 	pj_str_t    ns;
-	pj_bool_t   no_host;
+	int	    max_host;
+	pj_bool_t   regular;
 	pj_str_t    stun_srv;
 	pj_str_t    turn_srv;
 	pj_bool_t   turn_tcp;
@@ -306,8 +307,15 @@
 
     /* -= Start initializing ICE stream transport config =- */
 
-    /* Disable host candidates? */
-    icedemo.ice_cfg.stun.no_host_cands = icedemo.opt.no_host;
+    /* Maximum number of host candidates */
+    if (icedemo.opt.max_host != -1)
+	icedemo.ice_cfg.stun.max_host_cands = icedemo.opt.max_host;
+
+    /* Nomination strategy */
+    if (icedemo.opt.regular)
+	icedemo.ice_cfg.opt.aggressive = PJ_FALSE;
+    else
+	icedemo.ice_cfg.opt.aggressive = PJ_TRUE;
 
     /* Configure STUN/srflx candidate resolution */
     if (icedemo.opt.stun_srv.slen) {
@@ -960,7 +968,7 @@
     }
     */
 
-    if (comp_id > pj_ice_strans_get_running_comp_cnt(icedemo.icest)) {
+    if (comp_id<1||comp_id>pj_ice_strans_get_running_comp_cnt(icedemo.icest)) {
 	PJ_LOG(1,(THIS_FILE, "Error: invalid component ID"));
 	return;
     }
@@ -1137,7 +1145,8 @@
     puts(" --comp-cnt, -c N          Component count (default=1)");
     puts(" --nameserver, -n IP       Configure nameserver to activate DNS SRV");
     puts("                           resolution");
-    puts(" --no-host, -H             Disable host candidates");
+    puts(" --max-host, -H N          Set max number of host candidates to N");
+    puts(" --regular, -R             Use regular nomination (default aggressive)");
     puts(" --help, -h                Display this screen.");
     puts("");
     puts("STUN related options:");
@@ -1165,21 +1174,23 @@
     struct pj_getopt_option long_options[] = {
 	{ "comp-cnt",           1, 0, 'c'},
 	{ "nameserver",		1, 0, 'n'},
-	{ "no-host",		0, 0, 'H'},
+	{ "max-host",		1, 0, 'H'},
 	{ "help",		0, 0, 'h'},
 	{ "stun-srv",		1, 0, 's'},
 	{ "turn-srv",		1, 0, 't'},
 	{ "turn-tcp",		0, 0, 'T'},
 	{ "turn-username",	1, 0, 'u'},
 	{ "turn-password",	1, 0, 'p'},
-	{ "turn-fingerprint",	0, 0, 'F'}
+	{ "turn-fingerprint",	0, 0, 'F'},
+	{ "regular",		0, 0, 'R'}
     };
     int c, opt_id;
     pj_status_t status;
 
     icedemo.opt.comp_cnt = 1;
+    icedemo.opt.max_host = -1;
 
-    while((c=pj_getopt_long(argc,argv, "n:s:t:u:p:HhTF", long_options, &opt_id))!=-1) {
+    while((c=pj_getopt_long(argc,argv, "c:n:s:t:u:p:H:hTFR", long_options, &opt_id))!=-1) {
 	switch (c) {
 	case 'c':
 	    icedemo.opt.comp_cnt = atoi(pj_optarg);
@@ -1192,7 +1203,7 @@
 	    icedemo.opt.ns = pj_str(pj_optarg);
 	    break;
 	case 'H':
-	    icedemo.opt.no_host = PJ_TRUE;
+	    icedemo.opt.max_host = atoi(pj_optarg);
 	    break;
 	case 'h':
 	    icedemo_usage();
@@ -1215,6 +1226,9 @@
 	case 'F':
 	    icedemo.opt.turn_fingerprint = PJ_TRUE;
 	    break;
+	case 'R':
+	    icedemo.opt.regular = PJ_TRUE;
+	    break;
 	default:
 	    printf("Argument \"%s\" is not valid. Use -h to see help",
 		   argv[pj_optind]);
diff --git a/pjsip-apps/src/samples/siprtp_report.c b/pjsip-apps/src/samples/siprtp_report.c
index e63ef94..84c0072 100644
--- a/pjsip-apps/src/samples/siprtp_report.c
+++ b/pjsip-apps/src/samples/siprtp_report.c
@@ -77,7 +77,7 @@
 
     /* Call identification */
     len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
-    if (len < 1)
+    if (len < 0)
 	pj_ansi_strcpy(userinfo, "<--uri too long-->");
     else
 	userinfo[len] = '\0';
diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
index 1db144c..a171a09 100644
--- a/pjsip/include/pjsip/sip_config.h
+++ b/pjsip/include/pjsip/sip_config.h
@@ -227,6 +227,21 @@
 
 
 /**
+ * RFC 3261 section 18.1.1:
+ * If a request is within 200 bytes of the path MTU, or if it is larger
+ * than 1300 bytes and the path MTU is unknown, the request MUST be sent
+ * using an RFC 2914 [43] congestion controlled transport protocol, such
+ * as TCP.
+ *
+ * This setting controls the threshold of the UDP packet, which if it's
+ * larger than this value the request will be sent with TCP. Default is
+ * 1300 bytes.
+ */
+#ifndef PJSIP_UDP_SIZE_THRESHOLD
+#   define PJSIP_UDP_SIZE_THRESHOLD	1300
+#endif
+
+/**
  * Encode SIP headers in their short forms to reduce size. By default,
  * SIP headers in outgoing messages will be encoded in their full names. 
  * If this option is enabled, then SIP headers for outgoing messages
diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h
index 502a0b0..275196b 100644
--- a/pjsip/include/pjsip/sip_transport.h
+++ b/pjsip/include/pjsip/sip_transport.h
@@ -580,6 +580,17 @@
 PJ_DECL(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata );
 
 /**
+ * Print the SIP message to transmit data buffer's internal buffer. This
+ * may allocate memory for the buffer, if the buffer has not been allocated
+ * yet, and encode the SIP message to that buffer.
+ *
+ * @param tdata	    The transmit buffer.
+ *
+ * @return	    PJ_SUCCESS on success of the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_tx_data_encode(pjsip_tx_data *tdata);
+
+/**
  * Check if transmit data buffer contains a valid message.
  *
  * @param tdata	    The transmit buffer.
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index cb21ea0..95f5bf4 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -4260,9 +4260,16 @@
     pj_bool_t		enable_ice;
 
     /**
-     * Disable ICE host candidates.
+     * Set the maximum number of host candidates.
+     *
+     * Default: -1 (maximum not set)
      */
-    pj_bool_t		ice_no_host_cands;
+    int			ice_max_host_cands;
+
+    /**
+     * ICE session options.
+     */
+    pj_ice_sess_options	ice_opt;
 
     /**
      * Disable RTCP component.
diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c
index 53146b2..7ef5574 100644
--- a/pjsip/src/pjsip/sip_msg.c
+++ b/pjsip/src/pjsip/sip_msg.c
@@ -456,16 +456,18 @@
 
     /* Print each of the headers. */
     for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
-	len = (*hdr->vptr->print_on)(hdr, p, end-p);
-	if (len < 1)
-	    return -1;
-	p += len;
-
-	if (p+3 >= end)
+	len = pjsip_hdr_print_on(hdr, p, end-p);
+	if (len < 0)
 	    return -1;
 
-	*p++ = '\r';
-	*p++ = '\n';
+	if (len > 0) {
+	    p += len;
+	    if (p+3 >= end)
+		return -1;
+
+	    *p++ = '\r';
+	    *p++ = '\n';
+	}
     }
 
     /* Process message body. */
@@ -1601,6 +1603,25 @@
     char *startbuf = buf;
     char *endbuf = buf + size;
     const pjsip_parser_const_t *pc = pjsip_parser_const();
+    pjsip_sip_uri *sip_uri;
+    pjsip_param *p;
+
+    /* Check the proprietary param 'hide', don't print this header 
+     * if it exists in the route URI.
+     */
+    sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->name_addr.uri);
+    p = sip_uri->other_param.next;
+    while (p != &sip_uri->other_param) {
+	const pj_str_t st_hide = {"hide", 4};
+
+	if (pj_stricmp(&p->name, &st_hide) == 0) {
+	    /* Check if param 'hide' is specified without 'lr'. */
+	    pj_assert(sip_uri->lr_param != 0);
+	    return 0;
+	}
+	p = p->next;
+    }
+
     /* Route and Record-Route don't compact forms */
 
     copy_advance(buf, hdr->name);
diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
index 525f9e3..d10906d 100644
--- a/pjsip/src/pjsip/sip_transaction.c
+++ b/pjsip/src/pjsip/sip_transaction.c
@@ -1725,6 +1725,19 @@
 		      "will try next server. Err=%d (%s)",
 		      pjsip_tx_data_get_info(send_state->tdata), -sent,
 		      pj_strerror(-sent, errmsg, sizeof(errmsg)).ptr));
+
+	    /* Reset retransmission count */
+	    tsx->retransmit_count = 0;
+
+	    /* And reset timeout timer */
+	    if (tsx->timeout_timer.id) {
+		pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
+		tsx->timeout_timer.id = TIMER_INACTIVE;
+
+		tsx->timeout_timer.id = TIMER_ACTIVE;
+		pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, 
+					    &timeout_timer_val);
+	    }
 	}
     }
 
diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
index 84b45a6..f34699b 100644
--- a/pjsip/src/pjsip/sip_transport.c
+++ b/pjsip/src/pjsip/sip_transport.c
@@ -434,6 +434,45 @@
     tdata->info = NULL;
 }
 
+/*
+ * Print the SIP message to transmit data buffer's internal buffer.
+ */
+PJ_DEF(pj_status_t) pjsip_tx_data_encode(pjsip_tx_data *tdata)
+{
+    /* Allocate buffer if necessary. */
+    if (tdata->buf.start == NULL) {
+	PJ_USE_EXCEPTION;
+
+	PJ_TRY {
+	    tdata->buf.start = (char*) 
+			       pj_pool_alloc(tdata->pool, PJSIP_MAX_PKT_LEN);
+	}
+	PJ_CATCH_ANY {
+	    return PJ_ENOMEM;
+	}
+	PJ_END
+
+	tdata->buf.cur = tdata->buf.start;
+	tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN;
+    }
+
+    /* Do we need to reprint? */
+    if (!pjsip_tx_data_is_valid(tdata)) {
+	pj_ssize_t size;
+
+	size = pjsip_msg_print( tdata->msg, tdata->buf.start, 
+			        tdata->buf.end - tdata->buf.start);
+	if (size < 0) {
+	    return PJSIP_EMSGTOOLONG;
+	}
+	pj_assert(size != 0);
+	tdata->buf.cur[size] = '\0';
+	tdata->buf.cur += size;
+    }
+
+    return PJ_SUCCESS;
+}
+
 PJ_DEF(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata )
 {
     return tdata->buf.cur != tdata->buf.start;
@@ -567,38 +606,7 @@
  */
 static pj_status_t mod_on_tx_msg(pjsip_tx_data *tdata)
 {
-    /* Allocate buffer if necessary. */
-    if (tdata->buf.start == NULL) {
-	PJ_USE_EXCEPTION;
-
-	PJ_TRY {
-	    tdata->buf.start = (char*) 
-			       pj_pool_alloc(tdata->pool, PJSIP_MAX_PKT_LEN);
-	}
-	PJ_CATCH_ANY {
-	    return PJ_ENOMEM;
-	}
-	PJ_END
-
-	tdata->buf.cur = tdata->buf.start;
-	tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN;
-    }
-
-    /* Do we need to reprint? */
-    if (!pjsip_tx_data_is_valid(tdata)) {
-	pj_ssize_t size;
-
-	size = pjsip_msg_print( tdata->msg, tdata->buf.start, 
-			        tdata->buf.end - tdata->buf.start);
-	if (size < 0) {
-	    return PJSIP_EMSGTOOLONG;
-	}
-	pj_assert(size != 0);
-	tdata->buf.cur[size] = '\0';
-	tdata->buf.cur += size;
-    }
-
-    return PJ_SUCCESS;
+    return pjsip_tx_data_encode(tdata);
 }
 
 /*
diff --git a/pjsip/src/pjsip/sip_ua_layer.c b/pjsip/src/pjsip/sip_ua_layer.c
index 7a7e2d5..5145142 100644
--- a/pjsip/src/pjsip/sip_ua_layer.c
+++ b/pjsip/src/pjsip/sip_ua_layer.c
@@ -907,7 +907,7 @@
     char userinfo[128];
 
     len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
-    if (len < 1)
+    if (len < 0)
 	pj_ansi_strcpy(userinfo, "<--uri too long-->");
     else
 	userinfo[len] = '\0';
diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c
index 381ccad..faf8bdb 100644
--- a/pjsip/src/pjsip/sip_util.c
+++ b/pjsip/src/pjsip/sip_util.c
@@ -1135,6 +1135,8 @@
 	via->sent_by = stateless_data->cur_transport->local_name;
 	via->rport_param = 0;
 
+	pjsip_tx_data_invalidate_msg(tdata);
+
 	/* Send message using this transport. */
 	status = pjsip_transport_send( stateless_data->cur_transport,
 				       tdata,
@@ -1181,6 +1183,51 @@
     /* Copy server addresses */
     pj_memcpy( &stateless_data->addr, addr, sizeof(pjsip_server_addresses));
 
+    /* RFC 3261 section 18.1.1:
+     * If a request is within 200 bytes of the path MTU, or if it is larger
+     * than 1300 bytes and the path MTU is unknown, the request MUST be sent
+     * using an RFC 2914 [43] congestion controlled transport protocol, such
+     * as TCP.
+     */
+    if (stateless_data->tdata->msg->type == PJSIP_REQUEST_MSG &&
+	addr->count > 0 && 
+	addr->entry[0].type == PJSIP_TRANSPORT_UDP)
+    {
+	int len;
+
+	/* Encode the request */
+	status = pjsip_tx_data_encode(stateless_data->tdata);
+	if (status != PJ_SUCCESS) {
+	    if (stateless_data->app_cb) {
+		pj_bool_t cont = PJ_FALSE;
+		(*stateless_data->app_cb)(stateless_data, -status, &cont);
+	    }
+	    pjsip_tx_data_dec_ref(stateless_data->tdata);
+	    return;
+	}
+
+	/* Check if request message is larger than 1300 bytes. */
+	len = stateless_data->tdata->buf.cur - 
+		stateless_data->tdata->buf.start;
+	if (len >= PJSIP_UDP_SIZE_THRESHOLD) {
+	    int i;
+	    int count = stateless_data->addr.count;
+
+	    /* Insert "TCP version" of resolved UDP addresses at the
+	     * beginning.
+	     */
+	    if (count * 2 > PJSIP_MAX_RESOLVED_ADDRESSES)
+		count = PJSIP_MAX_RESOLVED_ADDRESSES / 2;
+	    for (i = 0; i < count; ++i) {
+		pj_memcpy(&stateless_data->addr.entry[i+count],
+			  &stateless_data->addr.entry[i],
+			  sizeof(stateless_data->addr.entry[0]));
+		stateless_data->addr.entry[i].type = PJSIP_TRANSPORT_TCP;
+	    }
+	    stateless_data->addr.count = count * 2;
+	}
+    }
+
     /* Process the addresses. */
     stateless_send_transport_cb( stateless_data, stateless_data->tdata,
 				 -PJ_EPENDING);
diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c
index 5a34d0e..6e9a939 100644
--- a/pjsip/src/pjsua-lib/pjsua_call.c
+++ b/pjsip/src/pjsua-lib/pjsua_call.c
@@ -2694,7 +2694,7 @@
     /* Dump invite sesion info. */
 
     len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
-    if (len < 1)
+    if (len < 0)
 	pj_ansi_strcpy(userinfo, "<--uri too long-->");
     else
 	userinfo[len] = '\0';
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index b57b81e..4cf8cd0 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -182,6 +182,9 @@
     cfg->jb_init = cfg->jb_min_pre = cfg->jb_max_pre = cfg->jb_max = -1;
     cfg->snd_auto_close_time = 1;
 
+    cfg->ice_max_host_cands = -1;
+    pj_ice_sess_options_default(&cfg->ice_opt);
+
     cfg->turn_conn_type = PJ_TURN_TP_UDP;
 }
 
@@ -647,7 +650,8 @@
     /* Initialize logging first so that info/errors can be captured */
     if (log_cfg) {
 	status = pjsua_reconfigure_logging(log_cfg);
-	PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+	if (status != PJ_SUCCESS)
+	    return status;
     }
 
     /* If nameserver is configured, create DNS resolver instance and
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index 8d9643b..b4d7f0d 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -804,13 +804,16 @@
     ice_cfg.af = pj_AF_INET();
     ice_cfg.resolver = pjsua_var.resolver;
     
+    ice_cfg.opt = pjsua_var.media_cfg.ice_opt;
+
     /* Configure STUN settings */
     if (pj_sockaddr_has_addr(&pjsua_var.stun_srv)) {
 	pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
 	ice_cfg.stun.server = pj_str(stunip);
 	ice_cfg.stun.port = pj_sockaddr_get_port(&pjsua_var.stun_srv);
     }
-    ice_cfg.stun.no_host_cands = pjsua_var.media_cfg.ice_no_host_cands;
+    if (pjsua_var.media_cfg.ice_max_host_cands >= 0)
+	ice_cfg.stun.max_host_cands = pjsua_var.media_cfg.ice_max_host_cands;
 
     /* Configure TURN settings */
     if (pjsua_var.media_cfg.enable_turn) {
diff --git a/pjsip/src/test/msg_test.c b/pjsip/src/test/msg_test.c
index d4a7f14..3bb58f6 100644
--- a/pjsip/src/test/msg_test.c
+++ b/pjsip/src/test/msg_test.c
@@ -365,16 +365,16 @@
     hdr2 = ref_msg->hdr.next;
 
     while (hdr1 != &parsed_msg->hdr && hdr2 != &ref_msg->hdr) {
-	len = hdr1->vptr->print_on(hdr1, str1.ptr, BUFLEN);
-	if (len < 1) {
+	len = pjsip_hdr_print_on(hdr1, str1.ptr, BUFLEN);
+	if (len < 0) {
 	    status = -40;
 	    goto on_return;
 	}
 	str1.ptr[len] = '\0';
 	str1.slen = len;
 
-	len = hdr2->vptr->print_on(hdr2, str2.ptr, BUFLEN);
-	if (len < 1) {
+	len = pjsip_hdr_print_on(hdr2, str2.ptr, BUFLEN);
+	if (len < 0) {
 	    status = -50;
 	    goto on_return;
 	}
@@ -1944,7 +1944,7 @@
 	/* Print the parsed header*/
 	output = (char*) pj_pool_alloc(pool, 1024);
 	len = pjsip_hdr_print_on(parsed_hdr1, output, 1024);
-	if (len < 1 || len >= 1024) {
+	if (len < 0 || len >= 1024) {
 	    PJ_LOG(3,(THIS_FILE, "    header too long: %s: %s", test->hname, test->hcontent));
 	    return -530;
 	}