Index: /pjproject/trunk/pjlib-util/include/pjlib-util/stun_simple.h
===================================================================
--- /pjproject/trunk/pjlib-util/include/pjlib-util/stun_simple.h	(revision 5635)
+++ /pjproject/trunk/pjlib-util/include/pjlib-util/stun_simple.h	(revision 5636)
@@ -211,4 +211,9 @@
      */
     pj_bool_t	use_stun2;
+    
+    /**
+     * Address family of the STUN servers.
+     */
+    int af;
 
     /**
Index: /pjproject/trunk/pjlib-util/src/pjlib-util/stun_simple_client.c
===================================================================
--- /pjproject/trunk/pjlib-util/src/pjlib-util/stun_simple_client.c	(revision 5635)
+++ /pjproject/trunk/pjlib-util/src/pjlib-util/stun_simple_client.c	(revision 5636)
@@ -64,5 +64,5 @@
     const pj_str_t *srv1, *srv2;
     int port1, port2;
-    pj_sockaddr_in srv_addr[2];
+    pj_sockaddr srv_addr[2];
     int i, send_cnt = 0, nfds;
     pj_pool_t *pool;
@@ -117,5 +117,5 @@
 
     /* Resolve servers. */
-    status = pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1);
+    status = pj_sockaddr_init(opt->af, &srv_addr[0], srv1, (pj_uint16_t)port1);
     if (status != PJ_SUCCESS)
 	goto on_error;
@@ -124,11 +124,10 @@
 
     if (srv2 && port2) {
-	status = pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2);
+	status = pj_sockaddr_init(opt->af, &srv_addr[1], srv2,
+				  (pj_uint16_t)port2);
 	if (status != PJ_SUCCESS)
 	    goto on_error;
 
-	if (srv_addr[1].sin_addr.s_addr != srv_addr[0].sin_addr.s_addr &&
-	    srv_addr[1].sin_port != srv_addr[0].sin_port)
-	{
+	if (pj_sockaddr_cmp(&srv_addr[1], &srv_addr[0]) != 0) {
 	    srv_cnt++;
 	}
@@ -182,5 +181,5 @@
 		status = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
 					(pj_sockaddr_t*)&srv_addr[j],
-					sizeof(pj_sockaddr_in));
+					pj_sockaddr_get_len(&srv_addr[j]));
 	    }
 	}
@@ -222,5 +221,5 @@
                 pj_ssize_t len;
 		pjstun_msg msg;
-		pj_sockaddr_in addr;
+		pj_sockaddr addr;
 		int addrlen = sizeof(addr);
 		pjstun_mapped_addr_attr *attr;
Index: /pjproject/trunk/pjlib/include/pj/sock.h
===================================================================
--- /pjproject/trunk/pjlib/include/pj/sock.h	(revision 5635)
+++ /pjproject/trunk/pjlib/include/pj/sock.h	(revision 5636)
@@ -934,4 +934,22 @@
 PJ_DECL(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src);
 
+/*
+ * If the source's and desired address family matches, copy the address,
+ * otherwise synthesize a new address with the desired address family,
+ * from the source address. This can be useful to generate an IPv4-mapped
+ * IPv6 address.
+ *
+ * @param dst_af    Desired address family.
+ * @param dst	    Destination socket address, invalid if synthesis is
+ *		    required and failed.
+ * @param src	    Source socket address.
+ *
+ * @return	    PJ_SUCCESS on success, or the error status
+ *		    if synthesis is required and failed.
+ */
+PJ_DECL(pj_status_t) pj_sockaddr_synthesize(int dst_af,
+				            pj_sockaddr_t *dst,
+				            const pj_sockaddr_t *src);
+
 /**
  * Get the IP address of an IPv4 socket address.
Index: /pjproject/trunk/pjlib/src/pj/sock_common.c
===================================================================
--- /pjproject/trunk/pjlib/src/pj/sock_common.c	(revision 5635)
+++ /pjproject/trunk/pjlib/src/pj/sock_common.c	(revision 5636)
@@ -414,4 +414,38 @@
 {
     pj_memcpy(dst, src, pj_sockaddr_get_len(src));
+}
+
+/*
+ * Synthesize address.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_synthesize(int dst_af,
+				           pj_sockaddr_t *dst,
+				           const pj_sockaddr_t *src)
+{
+    char ip_addr_buf[PJ_INET6_ADDRSTRLEN];
+    unsigned int count = 1;
+    pj_addrinfo ai[1];
+    pj_str_t ip_addr;
+    pj_status_t status;
+
+    /* Validate arguments */
+    PJ_ASSERT_RETURN(src && dst, PJ_EINVAL);
+
+    if (dst_af == ((const pj_sockaddr *)src)->addr.sa_family) {
+        pj_sockaddr_cp(dst, src);
+        return PJ_SUCCESS;
+    }
+
+    pj_sockaddr_print(src, ip_addr_buf, sizeof(ip_addr_buf), 0);
+    ip_addr = pj_str(ip_addr_buf);
+    
+    /* Try to synthesize address using pj_getaddrinfo(). */
+    status = pj_getaddrinfo(dst_af, &ip_addr, &count, ai); 
+    if (status == PJ_SUCCESS && count > 0) {
+    	pj_sockaddr_cp(dst, &ai[0].ai_addr);
+    	pj_sockaddr_set_port(dst, pj_sockaddr_get_port(src));
+    }
+    
+    return status;
 }
 
Index: /pjproject/trunk/pjmedia/src/pjmedia/transport_udp.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/transport_udp.c	(revision 5635)
+++ /pjproject/trunk/pjmedia/src/pjmedia/transport_udp.c	(revision 5636)
@@ -653,4 +653,7 @@
     struct transport_udp *udp = (struct transport_udp*) tp;
     const pj_sockaddr *rtcp_addr;
+    pj_sockaddr sock_addr, remote_addr, remote_rtcp;
+    int rem_addr_len;
+    pj_status_t status;
 
     /* Validate arguments */
@@ -668,11 +671,31 @@
     /* "Attach" the application: */
 
+    rem_addr_len = sizeof(pj_sockaddr);
+    pj_sock_getsockname(udp->rtp_sock, &sock_addr, &rem_addr_len);
+
+    /* Synthesize address, if necessary. */
+    status = pj_sockaddr_synthesize(sock_addr.addr.sa_family,
+    				    &remote_addr, rem_addr);
+    if (status != PJ_SUCCESS) {
+    	pj_perror(3, tp->name, status, "Failed to synthesize the correct"
+    				       "IP address for RTP");
+    }
+    rem_addr_len = pj_sockaddr_get_len(&remote_addr);
+
     /* Copy remote RTP address */
-    pj_memcpy(&udp->rem_rtp_addr, rem_addr, addr_len);
+    pj_memcpy(&udp->rem_rtp_addr, &remote_addr, rem_addr_len);
 
     /* Copy remote RTP address, if one is specified. */
     rtcp_addr = (const pj_sockaddr*) rem_rtcp;
     if (rtcp_addr && pj_sockaddr_has_addr(rtcp_addr)) {
-	pj_memcpy(&udp->rem_rtcp_addr, rem_rtcp, addr_len);
+    	pj_status_t status;
+
+        status = pj_sockaddr_synthesize(sock_addr.addr.sa_family,
+        		       		&remote_rtcp, rem_rtcp);
+        if (status != PJ_SUCCESS) {
+    	    pj_perror(3, tp->name, status, "Failed to synthesize the correct"
+    				       	   "IP address for RTCP");
+        }
+	pj_memcpy(&udp->rem_rtcp_addr, &remote_rtcp, rem_addr_len);
 
     } else {
@@ -680,5 +703,5 @@
 
 	/* Otherwise guess the RTCP address from the RTP address */
-	pj_memcpy(&udp->rem_rtcp_addr, rem_addr, addr_len);
+	pj_memcpy(&udp->rem_rtcp_addr, &udp->rem_rtp_addr, rem_addr_len);
 	rtcp_port = pj_sockaddr_get_port(&udp->rem_rtp_addr) + 1;
 	pj_sockaddr_set_port(&udp->rem_rtcp_addr, (pj_uint16_t)rtcp_port);
@@ -691,5 +714,5 @@
 
     /* Save address length */
-    udp->addr_len = addr_len;
+    udp->addr_len = rem_addr_len;
 
     /* Last, mark transport as attached */
Index: /pjproject/trunk/pjnath/src/pjnath/ice_session.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 5635)
+++ /pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 5636)
@@ -2186,4 +2186,5 @@
     pj_ice_sess_checklist *clist;
     pj_stun_xor_mapped_addr_attr *xaddr;
+    const pj_sockaddr_t *source_addr = src_addr;
     unsigned i;
 
@@ -2287,6 +2288,24 @@
      * Request was sent from.
      */
-    if (pj_sockaddr_cmp(&check->rcand->addr, (const pj_sockaddr*)src_addr)!=0)
+    if (check->rcand->addr.addr.sa_family == pj_AF_INET() &&
+        ((pj_sockaddr *)src_addr)->addr.sa_family == pj_AF_INET6())
     {
+        /* If the address family is different, we need to check
+         * whether the two addresses are equivalent (i.e. the IPv6
+         * is synthesized from IPv4).
+         */
+        pj_sockaddr synth_addr;
+    	pj_status_t status;
+    	
+    	status = pj_sockaddr_synthesize(pj_AF_INET6(), &synth_addr,
+    					&check->rcand->addr);
+    	if (status == PJ_SUCCESS &&
+    	    pj_sockaddr_cmp(&synth_addr, src_addr) == 0)
+    	{
+    	    source_addr = &check->rcand->addr;
+    	}
+    }
+
+    if (pj_sockaddr_cmp(&check->rcand->addr, source_addr) != 0) {
 	status = PJNATH_EICEINSRCADDR;
 	LOG4((ice->obj_name, 
@@ -2475,4 +2494,6 @@
     pj_stun_tx_data *tdata;
     pj_ice_rx_check *rcheck, tmp_rcheck;
+    const pj_sockaddr_t *source_addr = src_addr;
+    unsigned source_addr_len = src_addr_len;
     pj_status_t status;
 
@@ -2588,8 +2609,52 @@
     }
 
+    if (((pj_sockaddr *)src_addr)->addr.sa_family == pj_AF_INET6()) {
+        unsigned i;
+        unsigned transport_id = ((pj_ice_msg_data*)token)->transport_id;
+        pj_ice_sess_cand *lcand = NULL;
+
+    	for (i = 0; i < ice->clist.count; ++i) {
+	    pj_ice_sess_check *c = &ice->clist.checks[i];
+	    if (c->lcand->comp_id == sd->comp_id &&
+	        c->lcand->transport_id == transport_id) 
+	    {
+	    	lcand = c->lcand;
+	    	break;
+	    }
+    	}
+
+	if (lcand != NULL && lcand->addr.addr.sa_family == pj_AF_INET()) {
+	    /* We are behind NAT64, so src_addr is a synthesized IPv6
+	     * address. Instead of putting this synth IPv6 address as
+             * the XOR-MAPPED-ADDRESS, we need to find its original
+             * IPv4 address.
+             */
+            for (i = 0; i < ice->rcand_cnt; ++i) {
+            	pj_status_t status;
+            	pj_sockaddr synth_addr;
+            
+            	if (ice->rcand[i].addr.addr.sa_family != pj_AF_INET())
+                    continue;
+
+            	status = pj_sockaddr_synthesize(pj_AF_INET6(), &synth_addr,
+            				    	&ice->rcand[i].addr);
+	    	if (status == PJ_SUCCESS &&
+	            pj_sockaddr_cmp(src_addr, &synth_addr) == 0)
+	    	{
+	            /* We find the original IPv4 address. */
+	            source_addr = &ice->rcand[i].addr;
+	            source_addr_len = pj_sockaddr_get_len(source_addr);
+	            break;
+	    	}
+            }
+        }
+    }
+
+
     /* Add XOR-MAPPED-ADDRESS attribute */
     status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, 
 					   PJ_STUN_ATTR_XOR_MAPPED_ADDR,
-					   PJ_TRUE, src_addr, src_addr_len);
+					   PJ_TRUE, source_addr,
+					   source_addr_len);
 
     /* Create a msg_data to be associated with this response */
@@ -2620,6 +2685,6 @@
     rcheck->comp_id = sd->comp_id;
     rcheck->transport_id = ((pj_ice_msg_data*)token)->transport_id;
-    rcheck->src_addr_len = src_addr_len;
-    pj_sockaddr_cp(&rcheck->src_addr, src_addr);
+    rcheck->src_addr_len = source_addr_len;
+    pj_sockaddr_cp(&rcheck->src_addr, source_addr);
     rcheck->use_candidate = (uc_attr != NULL);
     rcheck->priority = prio_attr->value;
Index: /pjproject/trunk/pjnath/src/pjnath/ice_strans.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/ice_strans.c	(revision 5635)
+++ /pjproject/trunk/pjnath/src/pjnath/ice_strans.c	(revision 5636)
@@ -168,4 +168,9 @@
     unsigned		 cand_cnt;	/**< # of candidates/aliaes.	*/
     pj_ice_sess_cand	 cand_list[PJ_ICE_ST_MAX_CAND];	/**< Cand array	*/
+
+    pj_bool_t		 ipv4_mapped;   /**< Is IPv6 addr mapped to IPv4?*/
+    pj_sockaddr		 dst_addr;	/**< Destination address	*/
+    pj_sockaddr		 synth_addr;	/**< Synthesized dest address	*/
+    unsigned 		 synth_addr_len;/**< Synthesized dest addr len  */
 
     unsigned		 default_cand;	/**< Default candidate.		*/
@@ -655,5 +660,6 @@
 	     */
 	    if (comp->ice_st->cfg.af != pj_AF_UNSPEC() &&
-	        comp->cand_list[comp->default_cand].addr.addr.sa_family !=
+	        addr->addr.sa_family == comp->ice_st->cfg.af &&
+	        comp->cand_list[comp->default_cand].base_addr.addr.sa_family !=
 	        ice_st->cfg.af)
 	    {
@@ -1134,4 +1140,13 @@
 	    /* Must have address */
 	    pj_assert(pj_sockaddr_has_addr(&cand->addr));
+
+	    /* Skip if we are mapped to IPv4 address and this candidate
+	     * is not IPv4.
+	     */
+	    if (comp->ipv4_mapped &&
+	        cand->addr.addr.sa_family != pj_AF_INET())
+	    {
+	    	continue;
+	    }
 
 	    /* Add the candidate */
@@ -1501,7 +1516,31 @@
 		    PJ_SUCCESS : status;
 	} else {
+    	    const pj_sockaddr_t *dest_addr;
+    	    unsigned dest_addr_len;
+
+    	    if (comp->ipv4_mapped) {
+    	    	if (comp->synth_addr_len == 0 ||
+    	    	    pj_sockaddr_cmp(&comp->dst_addr, dst_addr) != 0)
+    	    	{
+    	    	    status = pj_sockaddr_synthesize(pj_AF_INET6(),
+    	    					    &comp->synth_addr,
+    	    					    dst_addr);
+    	    	    if (status != PJ_SUCCESS)
+    	            	return status;
+
+    	    	    pj_sockaddr_cp(&comp->dst_addr, dst_addr);
+    	    	    comp->synth_addr_len = pj_sockaddr_get_len(
+    	    	    			       &comp->synth_addr);
+    	    	}
+	    	dest_addr = &comp->synth_addr;
+    	    	dest_addr_len = comp->synth_addr_len;
+    	    } else {
+    		dest_addr = dst_addr;
+    		dest_addr_len = dst_addr_len;
+    	    }
+
 	    status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, data,
-					 (unsigned)data_len, 0, dst_addr,
-					 dst_addr_len);
+					 (unsigned)data_len, 0, dest_addr,
+					 dest_addr_len);
 	    return (status==PJ_SUCCESS||status==PJ_EPENDING) ?
 		    PJ_SUCCESS : status;
@@ -1650,7 +1689,29 @@
 	}
     } else if (tp_typ == TP_STUN) {
+    	const pj_sockaddr_t *dest_addr;
+    	unsigned dest_addr_len;
+
+    	if (comp->ipv4_mapped) {
+    	    if (comp->synth_addr_len == 0 ||
+    	    	pj_sockaddr_cmp(&comp->dst_addr, dst_addr) != 0)
+    	    {
+    	    	status = pj_sockaddr_synthesize(pj_AF_INET6(),
+    	    					&comp->synth_addr, dst_addr);
+    	    	if (status != PJ_SUCCESS)
+    	    	    return status;
+    	    
+    	    	pj_sockaddr_cp(&comp->dst_addr, dst_addr);
+    	    	comp->synth_addr_len = pj_sockaddr_get_len(&comp->synth_addr);
+    	    }
+	    dest_addr = &comp->synth_addr;
+    	    dest_addr_len = comp->synth_addr_len;
+    	} else {
+    	    dest_addr = dst_addr;
+    	    dest_addr_len = dst_addr_len;
+    	}
+
 	status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL,
 				     pkt, (unsigned)size, 0,
-				     dst_addr, dst_addr_len);
+				     dest_addr, dest_addr_len);
     } else {
 	pj_assert(!"Invalid transport ID");
@@ -1820,4 +1881,39 @@
 		pj_bool_t dup = PJ_FALSE;
 
+		if (info.mapped_addr.addr.sa_family == pj_AF_INET() &&
+		    cand->base_addr.addr.sa_family == pj_AF_INET6())
+		{
+		    /* We get an IPv4 mapped address for our IPv6
+		     * host address.
+		     */		     
+		    comp->ipv4_mapped = PJ_TRUE;
+
+		    /* Find other host candidates with the same (IPv6)
+		     * address, and replace it with the new (IPv4)
+		     * mapped address.
+		     */
+		    for (i = 0; i < comp->cand_cnt; ++i) {
+		        pj_sockaddr *a1, *a2;
+
+		        if (comp->cand_list[i].type != PJ_ICE_CAND_TYPE_HOST)
+		            continue;
+		        
+		        a1 = &comp->cand_list[i].addr;
+		        a2 = &cand->base_addr;
+		        if (pj_memcmp(pj_sockaddr_get_addr(a1),
+		       		      pj_sockaddr_get_addr(a2),
+		       		      pj_sockaddr_get_addr_len(a1)) == 0)
+		       	{
+		       	    pj_uint16_t port = pj_sockaddr_get_port(a1);
+		       	    pj_sockaddr_cp(a1, &info.mapped_addr);
+		       	    if (port != pj_sockaddr_get_port(a2))
+		       	        pj_sockaddr_set_port(a1, port);
+		       	    pj_sockaddr_cp(&comp->cand_list[i].base_addr, a1);
+		       	}
+		    }
+		    pj_sockaddr_cp(&cand->base_addr, &info.mapped_addr);
+		    pj_sockaddr_cp(&cand->rel_addr, &info.mapped_addr);
+		}
+		
 		/* Eliminate the srflx candidate if the address is
 		 * equal to other (host) candidates.
Index: /pjproject/trunk/pjnath/src/pjnath/nat_detect.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/nat_detect.c	(revision 5635)
+++ /pjproject/trunk/pjnath/src/pjnath/nat_detect.c	(revision 5636)
@@ -89,4 +89,5 @@
     pj_sockaddr	     	     server;
     pj_sockaddr	    	    *cur_server;
+    pj_sockaddr		     cur_addr;
     pj_stun_session	    *stun_sess;
 
@@ -855,9 +856,15 @@
 	goto on_error;
 
-    /* Configure alternate address */
-    if (alt_addr)
-	sess->cur_server = (pj_sockaddr*) alt_addr;
-    else
+    /* Configure alternate address, synthesize it if necessary */
+    if (alt_addr) {
+        status = pj_sockaddr_synthesize(sess->server.addr.sa_family,
+        				&sess->cur_addr, alt_addr);
+        if (status != PJ_SUCCESS)
+            goto on_error;
+
+	sess->cur_server = &sess->cur_addr;
+    } else {
 	sess->cur_server = &sess->server;
+    }
 
     PJ_LOG(5,(sess->pool->obj_name, 
Index: /pjproject/trunk/pjnath/src/pjnath/turn_session.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 5635)
+++ /pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 5636)
@@ -678,5 +678,5 @@
 	    pj_memcpy(addr, &ai[i].ai_addr, sizeof(pj_sockaddr));
 	    addr->addr.sa_family = sess->af;
-	    addr->ipv4.sin_port = pj_htons(sess->default_port);
+	    pj_sockaddr_set_port(addr, sess->default_port);
 	}
 
Index: /pjproject/trunk/pjsip-apps/src/swig/symbols.i
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/symbols.i	(revision 5635)
+++ /pjproject/trunk/pjsip-apps/src/swig/symbols.i	(revision 5636)
@@ -154,4 +154,6 @@
 typedef enum pjsua_ipv6_use {PJSUA_IPV6_DISABLED, PJSUA_IPV6_ENABLED} pjsua_ipv6_use;
 
+typedef enum pjsua_nat64_opt {PJSUA_NAT64_DISABLED, PJSUA_NAT64_ENABLED} pjsua_nat64_opt;
+
 typedef enum pjsua_buddy_status {PJSUA_BUDDY_STATUS_UNKNOWN, PJSUA_BUDDY_STATUS_ONLINE, PJSUA_BUDDY_STATUS_OFFLINE} pjsua_buddy_status;
 
Index: /pjproject/trunk/pjsip-apps/src/swig/symbols.lst
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/symbols.lst	(revision 5635)
+++ /pjproject/trunk/pjsip-apps/src/swig/symbols.lst	(revision 5636)
@@ -34,3 +34,3 @@
 pjsip-ua/sip_inv.h              pjsip_inv_state
 
-pjsua-lib/pjsua.h		pjsua_invalid_id_const_ pjsua_state pjsua_stun_use pjsua_call_hold_type pjsua_acc_id pjsua_destroy_flag pjsua_100rel_use pjsua_sip_timer_use pjsua_ipv6_use pjsua_buddy_status pjsua_call_media_status pjsua_vid_win_id pjsua_call_id pjsua_med_tp_st pjsua_call_vid_strm_op pjsua_vid_req_keyframe_method pjsua_call_flag pjsua_create_media_transport_flag pjsua_snd_dev_mode
+pjsua-lib/pjsua.h		pjsua_invalid_id_const_ pjsua_state pjsua_stun_use pjsua_call_hold_type pjsua_acc_id pjsua_destroy_flag pjsua_100rel_use pjsua_sip_timer_use pjsua_ipv6_use pjsua_nat64_opt pjsua_buddy_status pjsua_call_media_status pjsua_vid_win_id pjsua_call_id pjsua_med_tp_st pjsua_call_vid_strm_op pjsua_vid_req_keyframe_method pjsua_call_flag pjsua_create_media_transport_flag pjsua_snd_dev_mode
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 5635)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 5636)
@@ -1596,4 +1596,13 @@
 
     /**
+     * This specifies if the library should try to do an IPv6 resolution of
+     * the STUN servers if the IPv4 resolution fails. It can be useful
+     * in an IPv6-only environment, including on NAT64.
+     *
+     * Default: PJ_FALSE
+     */
+    pj_bool_t	    stun_try_ipv6;
+
+    /**
      * This specifies if the library should ignore failure with the
      * STUN servers. If this is set to PJ_FALSE, the library will refuse to
@@ -2911,4 +2920,21 @@
 
 /**
+ * Specify NAT64 options to be used in account config.
+ */
+typedef enum pjsua_nat64_opt
+{
+    /**
+     * NAT64 is not used.
+     */
+    PJSUA_NAT64_DISABLED,
+
+    /**
+     * NAT64 is enabled.
+     */
+    PJSUA_NAT64_ENABLED
+    
+} pjsua_nat64_opt;
+
+/**
  * This structure describes account configuration to be specified when
  * adding a new account with #pjsua_acc_add(). Application MUST initialize
@@ -3346,4 +3372,11 @@
      */
     pjsua_transport_config rtp_cfg;
+
+    /**
+     * Specify NAT64 options.
+     *
+     * Default: PJSUA_NAT64_DISABLED
+     */
+    pjsua_nat64_opt 		nat64_opt;
 
     /**
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 5635)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 5636)
@@ -375,4 +375,5 @@
     pj_sockaddr		 addr;	    /**< Result		    */
     pj_stun_sock	*stun_sock; /**< Testing STUN sock  */
+    int			 af;	    /**< Address family	    */
     pj_bool_t 		 async_wait;/**< Async resolution 
     					 of STUN entry      */
Index: /pjproject/trunk/pjsip/include/pjsua2/account.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/account.hpp	(revision 5635)
+++ /pjproject/trunk/pjsip/include/pjsua2/account.hpp	(revision 5636)
@@ -470,4 +470,11 @@
 
     /**
+     * Specify NAT64 options.
+     *
+     * Default: PJSUA_NAT64_DISABLED
+     */
+    pjsua_nat64_opt 	nat64Opt;
+
+    /**
      * Enable ICE for the media transport.
      *
Index: /pjproject/trunk/pjsip/include/pjsua2/endpoint.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/endpoint.hpp	(revision 5635)
+++ /pjproject/trunk/pjsip/include/pjsua2/endpoint.hpp	(revision 5636)
@@ -391,4 +391,13 @@
     StringVector	stunServer;
 
+    /**
+     * This specifies if the library should try to do an IPv6 resolution of
+     * the STUN servers if the IPv4 resolution fails. It can be useful
+     * in an IPv6-only environment, including on NAT64.
+     *
+     * Default: FALSE
+     */
+
+    bool	    	stunTryIpv6;
     /**
      * This specifies if the library startup should ignore failure with the
Index: /pjproject/trunk/pjsip/src/pjsip/sip_transport_udp.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_transport_udp.c	(revision 5635)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_transport_udp.c	(revision 5636)
@@ -701,5 +701,5 @@
     pj_pool_t *pool;
     struct udp_transport *tp;
-    const char *format, *ipv6_quoteb, *ipv6_quotee;
+    const char *format, *ipv6_quoteb = "", *ipv6_quotee = "";
     unsigned i;
     pj_status_t status;
@@ -710,10 +710,16 @@
     /* Object name. */
     if (type & PJSIP_TRANSPORT_IPV6) {
+        pj_in6_addr dummy6;
+
 	format = "udpv6%p";
-	ipv6_quoteb = "[";
-	ipv6_quotee = "]";
+	/* We don't need to add quote if the transport type is IPv6, but
+	 * actually translated to IPv4.
+	 */
+        if (pj_inet_pton(pj_AF_INET6(), &a_name->host, &dummy6)==PJ_SUCCESS) {
+	    ipv6_quoteb = "[";
+	    ipv6_quotee = "]";
+	}
     } else {
 	format = "udp%p";
-	ipv6_quoteb = ipv6_quotee = "";
     }
 
Index: /pjproject/trunk/pjsip/src/pjsip/sip_util.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_util.c	(revision 5635)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_util.c	(revision 5636)
@@ -1220,5 +1220,6 @@
 	 */
         if (tdata->via_addr.host.slen > 0 &&
-            (tdata->via_tp == (void *)stateless_data->cur_transport ||
+            (!tdata->via_tp ||
+             tdata->via_tp == (void *)stateless_data->cur_transport ||
              tdata->msg->line.req.method.id == PJSIP_CANCEL_METHOD))
         {
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_acc.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_acc.c	(revision 5635)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_acc.c	(revision 5636)
@@ -33,4 +33,5 @@
 
 
+static int get_ip_addr_ver(const pj_str_t *host);
 static void schedule_reregistration(pjsua_acc *acc);
 static void keep_alive_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te);
@@ -548,5 +549,5 @@
 
     /* Enclose IPv6 address in square brackets */
-    if (t->type & PJSIP_TRANSPORT_IPV6) {
+    if (get_ip_addr_ver(&t->local_name.host) == 6) {
 	beginquote = "[";
 	endquote = "]";
@@ -1326,4 +1327,5 @@
     }
 
+    acc->cfg.nat64_opt = cfg->nat64_opt;
     acc->cfg.ipv6_media_use = cfg->ipv6_media_use;
 
@@ -1697,7 +1699,10 @@
 	/* Compare the addresses as sockaddr according to the ticket above,
 	 * but only if they have the same family (ipv4 vs ipv4, or
-	 * ipv6 vs ipv6)
+	 * ipv6 vs ipv6).
+	 * Checking for the same address family is currently disabled,
+	 * since it can be useful in cases such as when on NAT64,
+	 * in order to get the IPv4-mapped address from IPv6.
 	 */
-	matched = (contact_addr.addr.sa_family != recv_addr.addr.sa_family) ||
+	matched = //(contact_addr.addr.sa_family != recv_addr.addr.sa_family)||
 	          (uri->port == rport &&
 		   pj_sockaddr_cmp(&contact_addr, &recv_addr)==0);
@@ -3115,4 +3120,5 @@
     pjsip_tpmgr *tpmgr;
     pjsip_tpmgr_fla2_param tfla2_prm;
+    pj_bool_t update_addr = PJ_TRUE;
 
     PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
@@ -3183,4 +3189,23 @@
     addr->host = tfla2_prm.ret_addr;
     addr->port = tfla2_prm.ret_port;
+
+    /* If we are behind NAT64, use the Contact and Via address from
+     * the UDP6 transport, which should be obtained from STUN.
+     */
+    if (acc->cfg.nat64_opt != PJSUA_NAT64_DISABLED) {
+        pjsip_tpmgr_fla2_param tfla2_prm2 = tfla2_prm;
+        
+        tfla2_prm2.tp_type = PJSIP_TRANSPORT_UDP6;
+        tfla2_prm2.tp_sel = NULL;
+        tfla2_prm2.local_if = (!pjsua_sip_acc_is_using_stun(acc_id));
+        status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &tfla2_prm2);
+    	if (status == PJ_SUCCESS) {
+    	    update_addr = PJ_FALSE;
+	    addr->host = tfla2_prm2.ret_addr;
+	    pj_strdup(acc->pool, &acc->via_addr.host, &addr->host);
+	    acc->via_addr.port = addr->port;
+	    acc->via_tp = (pjsip_transport *)tfla2_prm.ret_tp;
+	}
+    }
 
     /* For TCP/TLS, acc may request to specify source port */
@@ -3277,6 +3302,10 @@
 
 	if (status == PJ_SUCCESS) {
-	    /* Got the local transport address */
-	    pj_strdup(pool, &addr->host, &tp->local_name.host);
+	    /* Got the local transport address, don't update if
+	     * we are on NAT64 and already obtained the address
+	     * from STUN above.
+	     */
+	    if (update_addr)
+	        pj_strdup(pool, &addr->host, &tp->local_name.host);
 	    addr->port = tp->local_name.port;
 	}
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 5635)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 5636)
@@ -1298,7 +1298,12 @@
 	stun_resolve_add_ref(sess);
 
-	++sess->idx;
-	if (sess->idx >= sess->count)
-            sess->status = status;
+	if (pjsua_var.ua_cfg.stun_try_ipv6 && sess->af == pj_AF_INET()) {
+	    sess->af = pj_AF_INET6();
+	} else {
+	    ++sess->idx;
+	    sess->af = pj_AF_INET();
+	    if (sess->idx >= sess->count)
+                sess->status = status;
+        }
 
 	resolve_stun_entry(sess);
@@ -1340,5 +1345,8 @@
 
     /* Loop while we have entry to try */
-    for (; sess->idx < sess->count; ++sess->idx) {
+    for (; sess->idx < sess->count;
+    	 (pjsua_var.ua_cfg.stun_try_ipv6 && sess->af == pj_AF_INET())?
+	 sess->af = pj_AF_INET6(): (++sess->idx, sess->af = pj_AF_INET()))
+    {
 	int af;
 	char target[64];
@@ -1359,12 +1367,4 @@
     	    PJ_LOG(2,(THIS_FILE, "Invalid STUN server entry %s", target));
 	    continue;
-	} else if (af != pj_AF_INET()) {
-	    /* Ignore IPv6 STUN server for now */
-	    status = PJ_EAFNOTSUP;
-	    PJ_LOG(3,(THIS_FILE, "Ignored STUN server entry %s, currently "
-				 "only IPv4 STUN server is supported (does "
-				 "IPv6 still need a mapped address?)",
-		      target));
-	    continue;
 	}
 	
@@ -1375,6 +1375,7 @@
 	pj_assert(sess->stun_sock == NULL);
 
-	PJ_LOG(4,(THIS_FILE, "Trying STUN server %s (%d of %d)..",
-		  target, sess->idx+1, sess->count));
+	PJ_LOG(4,(THIS_FILE, "Trying STUN server %s %s (%d of %d)..",
+		  target, (sess->af == pj_AF_INET()? "IPv4": "IPv6"),
+		  sess->idx+1, sess->count));
 
 	/* Use STUN_sock to test this entry */
@@ -1383,5 +1384,5 @@
 	sess->async_wait = PJ_FALSE;
 	status = pj_stun_sock_create(&pjsua_var.stun_cfg, "stunresolve",
-				     pj_AF_INET(), &stun_sock_cb,
+				     sess->af, &stun_sock_cb,
 				     NULL, sess, &sess->stun_sock);
 	if (status != PJ_SUCCESS) {
@@ -1489,4 +1490,5 @@
     sess->waiter = pj_thread_this();
     sess->status = PJ_EPENDING;
+    sess->af = pj_AF_INET();
     sess->srv = (pj_str_t*) pj_pool_calloc(pool, count, sizeof(pj_str_t));
     for (i=0; i<count; ++i) {
@@ -2158,16 +2160,20 @@
 	    pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
 
-    } else if (stun_srv.slen && af == pj_AF_INET()) {
+    } else if (stun_srv.slen &&
+               (af == pj_AF_INET() || pjsua_var.ua_cfg.stun_try_ipv6))
+    {
 	pjstun_setting stun_opt;
 
 	/*
 	 * STUN is specified, resolve the address with STUN.
-	 * Currently, this is available for IPv4 address only.
+	 * Currently, this is only to get IPv4 mapped address
+	 * (does IPv6 still need a mapped address?).
 	 */
 	pj_bzero(&stun_opt, sizeof(stun_opt));
 	stun_opt.use_stun2 = pjsua_var.ua_cfg.stun_map_use_stun2;
+	stun_opt.af = pjsua_var.stun_srv.addr.sa_family;
 	stun_opt.srv1  = stun_opt.srv2  = stun_srv;
 	stun_opt.port1 = stun_opt.port2 = 
-			 pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port);
+			 pj_sockaddr_get_port(&pjsua_var.stun_srv);
 	status = pjstun_get_mapped_addr2(&pjsua_var.cp.factory, &stun_opt,
 					 1, &sock, &p_pub_addr->ipv4);
@@ -2895,12 +2901,12 @@
 
     /* Make sure we have STUN */
-    if (pjsua_var.stun_srv.ipv4.sin_family == 0) {
+    if (pjsua_var.stun_srv.addr.sa_family == 0) {
 	pjsua_var.nat_status = PJNATH_ESTUNINSERVER;
 	return PJNATH_ESTUNINSERVER;
     }
 
-    status = pj_stun_detect_nat_type(&pjsua_var.stun_srv.ipv4, 
-				     &pjsua_var.stun_cfg, 
-				     NULL, &nat_detect_cb);
+    status = pj_stun_detect_nat_type2(&pjsua_var.stun_srv, 
+				      &pjsua_var.stun_cfg, 
+				      NULL, &nat_detect_cb);
 
     if (status != PJ_SUCCESS) {
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 5635)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 5636)
@@ -241,5 +241,5 @@
     };
     int i;
-    pj_bool_t use_ipv6;
+    pj_bool_t use_ipv6, use_nat64;
     int af;
     pj_sockaddr bound_addr;
@@ -251,8 +251,11 @@
 
     use_ipv6 = (acc->cfg.ipv6_media_use != PJSUA_IPV6_DISABLED);
-    af = use_ipv6 ? pj_AF_INET6() : pj_AF_INET();
+    use_nat64 = (acc->cfg.nat64_opt != PJSUA_NAT64_DISABLED);
+    af = (use_ipv6 || use_nat64) ? pj_AF_INET6() : pj_AF_INET();
 
     /* Make sure STUN server resolution has completed */
-    if (!use_ipv6 && pjsua_media_acc_is_using_stun(call_med->call->acc_id)) {
+    if ((!use_ipv6 || use_nat64) &&
+        pjsua_media_acc_is_using_stun(call_med->call->acc_id))
+    {
 	pj_bool_t retry_stun = (acc->cfg.media_stun_use &
 				PJSUA_STUN_RETRY_ON_FAILURE) ==
@@ -354,9 +357,9 @@
 	 * and make sure that the mapped RTCP port is adjacent with the RTP.
 	 */
-	if (!use_ipv6 &&
+	if ((!use_ipv6 || use_nat64) &&
 	    pjsua_media_acc_is_using_stun(call_med->call->acc_id) &&
 	    pjsua_var.stun_srv.addr.sa_family != 0)
 	{
-	    char ip_addr[32];
+	    char ip_addr[PJ_INET6_ADDRSTRLEN];
 	    pj_str_t stun_srv;
 	    pj_sockaddr_in resolved_addr[2];
@@ -368,7 +371,8 @@
 	    pj_bzero(&stun_opt, sizeof(stun_opt));
 	    stun_opt.use_stun2 = pjsua_var.ua_cfg.stun_map_use_stun2;
+	    stun_opt.af = pjsua_var.stun_srv.addr.sa_family;
 	    stun_opt.srv1  = stun_opt.srv2  = stun_srv;
 	    stun_opt.port1 = stun_opt.port2 = 
-			     pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port);
+			     pj_sockaddr_get_port(&pjsua_var.stun_srv);
 	    status=pjstun_get_mapped_addr2(&pjsua_var.cp.factory, &stun_opt,
 					   2, sock, resolved_addr);
@@ -433,7 +437,8 @@
     	            }
     	    
+    	            stun_opt.af = pjsua_var.stun_srv.addr.sa_family;
     	            stun_opt.srv1  = stun_opt.srv2  = stun_srv;
 	            stun_opt.port1 = stun_opt.port2 = 
-			        pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port);
+			        pj_sockaddr_get_port(&pjsua_var.stun_srv);
 	    	    status = pjstun_get_mapped_addr2(&pjsua_var.cp.factory,
 	    				  	     &stun_opt, 2, sock,
@@ -824,8 +829,9 @@
     unsigned comp_cnt;
     pj_status_t status;
-    pj_bool_t use_ipv6;
+    pj_bool_t use_ipv6, use_nat64;
 
     acc_cfg = &pjsua_var.acc[call_med->call->acc_id].cfg;
     use_ipv6 = (acc_cfg->ipv6_media_use != PJSUA_IPV6_DISABLED);
+    use_nat64 = (acc_cfg->nat64_opt != PJSUA_NAT64_DISABLED);
 
     /* Make sure STUN server resolution has completed */
@@ -864,5 +870,5 @@
 	if (pj_stricmp(&c->addr_type, &ID_IP6) == 0)
 	    ice_cfg.af = pj_AF_INET6();
-    } else if (use_ipv6) {
+    } else if (use_ipv6 || use_nat64) {
     	ice_cfg.af = pj_AF_INET6();
     }
@@ -875,5 +881,7 @@
 	ice_cfg.stun_tp_cnt = 1;
 	pj_ice_strans_stun_cfg_default(&ice_cfg.stun_tp[0]);
-	if (use_ipv6 && PJ_ICE_MAX_STUN >= 2) {
+	if (use_nat64) {
+	    ice_cfg.stun_tp[0].af = pj_AF_INET6();
+	} else if (use_ipv6 && PJ_ICE_MAX_STUN >= 2) {
 	    ice_cfg.stun_tp_cnt = 2;
 	    pj_ice_strans_stun_cfg_default(&ice_cfg.stun_tp[1]);
@@ -886,70 +894,67 @@
 	unsigned i;
 
-	/* Configure STUN server (currently only for IPv4) */
-	if (pj_sockaddr_has_addr(&pjsua_var.stun_srv) &&
-	    pjsua_media_acc_is_using_stun(call_med->call->acc_id))
-	{
-	    pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
-	    ice_cfg.stun_tp[0].server = pj_str(stunip);
-	    ice_cfg.stun_tp[0].port = 
-				    pj_sockaddr_get_port(&pjsua_var.stun_srv);
-	}
-
-	/* Configure max host candidates */
-	if (acc_cfg->ice_cfg.ice_max_host_cands >= 0) {
-	    for (i = 0; i < ice_cfg.stun_tp_cnt; ++i)
+	pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
+
+	for (i = 0; i < ice_cfg.stun_tp_cnt; ++i) {
+	    pj_str_t IN6_ADDR_ANY = {"0", 1};
+
+	    /* Configure STUN server */
+	    if (pj_sockaddr_has_addr(&pjsua_var.stun_srv) &&
+	    	pjsua_media_acc_is_using_stun(call_med->call->acc_id))
+	    {
+	    	ice_cfg.stun_tp[i].server = pj_str(stunip);
+	    	ice_cfg.stun_tp[i].port = pj_sockaddr_get_port(
+	    				      &pjsua_var.stun_srv);
+	    }
+
+	    /* Configure max host candidates */
+	    if (acc_cfg->ice_cfg.ice_max_host_cands >= 0) {
 		ice_cfg.stun_tp[i].max_host_cands =
 				acc_cfg->ice_cfg.ice_max_host_cands;
-	}
-
-	/* Configure binding address */
-	pj_sockaddr_init(ice_cfg.stun_tp[0].af,
-			 &ice_cfg.stun_tp[0].cfg.bound_addr,
-			 &cfg->bound_addr, (pj_uint16_t)cfg->port);
-	ice_cfg.stun_tp[0].cfg.port_range = (pj_uint16_t)cfg->port_range;
-	if (cfg->port != 0 && ice_cfg.stun_tp[0].cfg.port_range == 0) {
-	    ice_cfg.stun_tp[0].cfg.port_range = 
+	    }
+
+	    /* Configure binding address */
+	    pj_sockaddr_init(ice_cfg.stun_tp[i].af,
+			     &ice_cfg.stun_tp[i].cfg.bound_addr,
+			     (ice_cfg.stun_tp[i].af == pj_AF_INET()?
+			     &cfg->bound_addr: &IN6_ADDR_ANY),
+			     (pj_uint16_t)cfg->port);
+	    ice_cfg.stun_tp[i].cfg.port_range = (pj_uint16_t)cfg->port_range;
+	    if (cfg->port != 0 && ice_cfg.stun_tp[i].cfg.port_range == 0) {
+	    	ice_cfg.stun_tp[i].cfg.port_range = 
 			    (pj_uint16_t)(pjsua_var.ua_cfg.max_calls * 10);
-	}
-	if (use_ipv6 && ice_cfg.stun_tp_cnt > 1) {
-	    pj_str_t IN6_ADDR_ANY = {"0", 1};
-	    pj_sockaddr_init(pj_AF_INET6(),
-			     &ice_cfg.stun_tp[1].cfg.bound_addr,
-			     &IN6_ADDR_ANY, (pj_uint16_t)cfg->port);
-	    ice_cfg.stun_tp[1].cfg.port_range =
-			    ice_cfg.stun_tp[0].cfg.port_range;
-	}
-
-	/* Configure QoS setting */
-	ice_cfg.stun_tp[0].cfg.qos_type = cfg->qos_type;
-	pj_memcpy(&ice_cfg.stun_tp[0].cfg.qos_params, &cfg->qos_params,
-		  sizeof(cfg->qos_params));
-	if (use_ipv6 && ice_cfg.stun_tp_cnt > 1) {
-	    ice_cfg.stun_tp[1].cfg.qos_type = cfg->qos_type;
-	    pj_memcpy(&ice_cfg.stun_tp[1].cfg.qos_params, &cfg->qos_params,
+	    }
+
+	    /* Configure QoS setting */
+	    ice_cfg.stun_tp[i].cfg.qos_type = cfg->qos_type;
+	    pj_memcpy(&ice_cfg.stun_tp[i].cfg.qos_params, &cfg->qos_params,
 		      sizeof(cfg->qos_params));
-	}
-
-	/* Configure max packet size */
-	ice_cfg.stun_tp[0].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
-	if (use_ipv6 && ice_cfg.stun_tp_cnt > 1)
-	    ice_cfg.stun_tp[1].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
-
+
+	    /* Configure max packet size */
+	    ice_cfg.stun_tp[i].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
+	}
     }
 
     /* Configure TURN settings */
     if (acc_cfg->turn_cfg.enable_turn) {
-	ice_cfg.turn_tp_cnt = 1;
-	pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[0]);
-	if (use_ipv6 && PJ_ICE_MAX_TURN >= 3) {
-	    ice_cfg.turn_tp_cnt = 3;
-
-	    pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[1]);
-	    ice_cfg.turn_tp[1].af = pj_AF_INET6();
+        unsigned i, idx = 0;
+        
+        if (use_ipv6 && !use_nat64 && PJ_ICE_MAX_TURN >= 3) {
+            ice_cfg.turn_tp_cnt = 3;
+            idx = 1;
+        } else {
+	    ice_cfg.turn_tp_cnt = 1;
+	}
+	
+	for (i = 0; i < ice_cfg.turn_tp_cnt; i++)
+	    pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[i]);
+
+	if (use_ipv6 || use_nat64) {
+	    if (!use_nat64)
+	        ice_cfg.turn_tp[idx++].af = pj_AF_INET6();
 
 	    /* Additional candidate: IPv4 relay via IPv6 TURN server */
-	    pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[2]);
-	    ice_cfg.turn_tp[2].af = pj_AF_INET6();
-	    ice_cfg.turn_tp[2].alloc_param.af = pj_AF_INET();
+	    ice_cfg.turn_tp[idx].af = pj_AF_INET6();
+	    ice_cfg.turn_tp[idx].alloc_param.af = pj_AF_INET();
 	}
 
@@ -963,70 +968,36 @@
 	}
 
-	/* Configure TURN connection settings and credential */
 	if (ice_cfg.turn_tp[0].port == 0)
 	    ice_cfg.turn_tp[0].port = 3479;
-	ice_cfg.turn_tp[0].conn_type = acc_cfg->turn_cfg.turn_conn_type;
-	pj_memcpy(&ice_cfg.turn_tp[0].auth_cred, 
-		  &acc_cfg->turn_cfg.turn_auth_cred,
-		  sizeof(ice_cfg.turn_tp[0].auth_cred));
-
-	if (use_ipv6 && ice_cfg.turn_tp_cnt > 1) {
-	    ice_cfg.turn_tp[1].server    = ice_cfg.turn_tp[0].server;
-	    ice_cfg.turn_tp[1].port      = ice_cfg.turn_tp[0].port;
-	    ice_cfg.turn_tp[1].conn_type = ice_cfg.turn_tp[0].conn_type;
-	    pj_memcpy(&ice_cfg.turn_tp[1].auth_cred, 
+
+	for (i = 0; i < ice_cfg.turn_tp_cnt; i++) {
+	    pj_str_t IN6_ADDR_ANY = {"0", 1};
+
+	    /* Configure TURN connection settings and credential */
+	    ice_cfg.turn_tp[i].server    = ice_cfg.turn_tp[0].server;
+	    ice_cfg.turn_tp[i].port      = ice_cfg.turn_tp[0].port;
+	    ice_cfg.turn_tp[i].conn_type = acc_cfg->turn_cfg.turn_conn_type;
+	    pj_memcpy(&ice_cfg.turn_tp[i].auth_cred, 
 		      &acc_cfg->turn_cfg.turn_auth_cred,
-		      sizeof(ice_cfg.turn_tp[1].auth_cred));
-
-	    ice_cfg.turn_tp[2].server    = ice_cfg.turn_tp[0].server;
-	    ice_cfg.turn_tp[2].port      = ice_cfg.turn_tp[0].port;
-	    ice_cfg.turn_tp[2].conn_type = ice_cfg.turn_tp[0].conn_type;
-	    pj_memcpy(&ice_cfg.turn_tp[2].auth_cred, 
-		      &acc_cfg->turn_cfg.turn_auth_cred,
-		      sizeof(ice_cfg.turn_tp[2].auth_cred));
-	}
-
-	/* Configure QoS setting */
-	ice_cfg.turn_tp[0].cfg.qos_type = cfg->qos_type;
-	pj_memcpy(&ice_cfg.turn_tp[0].cfg.qos_params, &cfg->qos_params,
-		  sizeof(cfg->qos_params));
-	if (use_ipv6 && ice_cfg.turn_tp_cnt > 1) {
-	    ice_cfg.turn_tp[1].cfg.qos_type = cfg->qos_type;
-	    pj_memcpy(&ice_cfg.turn_tp[1].cfg.qos_params, &cfg->qos_params,
+		      sizeof(ice_cfg.turn_tp[i].auth_cred));
+
+	    /* Configure QoS setting */
+	    ice_cfg.turn_tp[i].cfg.qos_type = cfg->qos_type;
+	    pj_memcpy(&ice_cfg.turn_tp[i].cfg.qos_params, &cfg->qos_params,
 		      sizeof(cfg->qos_params));
 
-	    ice_cfg.turn_tp[2].cfg.qos_type = cfg->qos_type;
-	    pj_memcpy(&ice_cfg.turn_tp[2].cfg.qos_params, &cfg->qos_params,
-		      sizeof(cfg->qos_params));
-	}
-
-	/* Configure binding address */
-	pj_sockaddr_init(ice_cfg.turn_tp[0].af, &ice_cfg.turn_tp[0].cfg.bound_addr,
-			 &cfg->bound_addr, (pj_uint16_t)cfg->port);
-	ice_cfg.turn_tp[0].cfg.port_range = (pj_uint16_t)cfg->port_range;
-	if (cfg->port != 0 && ice_cfg.turn_tp[0].cfg.port_range == 0)
-	    ice_cfg.turn_tp[0].cfg.port_range = 
+	    /* Configure binding address */
+	    pj_sockaddr_init(ice_cfg.turn_tp[i].af,
+	    		     &ice_cfg.turn_tp[i].cfg.bound_addr,
+			     (ice_cfg.turn_tp[i].af == pj_AF_INET()?
+			     &cfg->bound_addr: &IN6_ADDR_ANY),
+			     (pj_uint16_t)cfg->port);
+	    ice_cfg.turn_tp[i].cfg.port_range = (pj_uint16_t)cfg->port_range;
+	    if (cfg->port != 0 && ice_cfg.turn_tp[i].cfg.port_range == 0)
+	        ice_cfg.turn_tp[i].cfg.port_range = 
 				 (pj_uint16_t)(pjsua_var.ua_cfg.max_calls * 10);
 
-	if (use_ipv6 && ice_cfg.turn_tp_cnt > 1) {
-	    pj_str_t IN6_ADDR_ANY = {"0", 1};
-	    pj_sockaddr_init(pj_AF_INET6(),
-			     &ice_cfg.turn_tp[1].cfg.bound_addr,
-			     &IN6_ADDR_ANY, (pj_uint16_t)cfg->port);
-	    ice_cfg.turn_tp[1].cfg.port_range =
-			    ice_cfg.turn_tp[0].cfg.port_range;
-
-	    pj_sockaddr_init(pj_AF_INET6(),
-			     &ice_cfg.turn_tp[2].cfg.bound_addr,
-			     &IN6_ADDR_ANY, (pj_uint16_t)cfg->port);
-	    ice_cfg.turn_tp[2].cfg.port_range =
-			    ice_cfg.turn_tp[0].cfg.port_range;
-	}
-
-	/* Configure max packet size */
-	ice_cfg.turn_tp[0].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
-	if (use_ipv6 && ice_cfg.turn_tp_cnt > 1) {
-	    ice_cfg.turn_tp[1].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
-	    ice_cfg.turn_tp[2].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
+	    /* Configure max packet size */
+	    ice_cfg.turn_tp[i].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
 	}
     }
@@ -2396,11 +2367,14 @@
 	    if (m->conn == NULL && sdp->conn == NULL) {
 		pj_bool_t use_ipv6;
+		pj_bool_t use_nat64;
 
 		use_ipv6 = (pjsua_var.acc[call->acc_id].cfg.ipv6_media_use !=
 			    PJSUA_IPV6_DISABLED);
+		use_nat64 = (pjsua_var.acc[call->acc_id].cfg.nat64_opt !=
+			     PJSUA_NAT64_DISABLED);
 
 		m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
 		m->conn->net_type = pj_str("IN");
-		if (use_ipv6) {
+		if (use_ipv6 && !use_nat64) {
 		    m->conn->addr_type = pj_str("IP6");
 		    m->conn->addr = pj_str("::1");
Index: /pjproject/trunk/pjsip/src/pjsua2/account.cpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2/account.cpp	(revision 5635)
+++ /pjproject/trunk/pjsip/src/pjsua2/account.cpp	(revision 5636)
@@ -185,4 +185,5 @@
     NODE_READ_NUM_T   ( this_node, pjsua_stun_use, sipStunUse);
     NODE_READ_NUM_T   ( this_node, pjsua_stun_use, mediaStunUse);
+    NODE_READ_NUM_T   ( this_node, pjsua_nat64_opt, nat64Opt);
     NODE_READ_BOOL    ( this_node, iceEnabled);
     NODE_READ_INT     ( this_node, iceMaxHostCands);
@@ -216,4 +217,5 @@
     NODE_WRITE_NUM_T   ( this_node, pjsua_stun_use, sipStunUse);
     NODE_WRITE_NUM_T   ( this_node, pjsua_stun_use, mediaStunUse);
+    NODE_WRITE_NUM_T   ( this_node, pjsua_nat64_opt, nat64Opt);
     NODE_WRITE_BOOL    ( this_node, iceEnabled);
     NODE_WRITE_INT     ( this_node, iceMaxHostCands);
@@ -392,4 +394,5 @@
     ret.sip_stun_use		= natConfig.sipStunUse;
     ret.media_stun_use		= natConfig.mediaStunUse;
+    ret.nat64_opt		= natConfig.nat64Opt;
     ret.ice_cfg_use		= PJSUA_ICE_CONFIG_USE_CUSTOM;
     ret.ice_cfg.enable_ice	= natConfig.iceEnabled;
@@ -535,4 +538,5 @@
     natConfig.sipStunUse	= prm.sip_stun_use;
     natConfig.mediaStunUse	= prm.media_stun_use;
+    natConfig.nat64Opt		= prm.nat64_opt;
     if (prm.ice_cfg_use == PJSUA_ICE_CONFIG_USE_CUSTOM) {
 	natConfig.iceEnabled = PJ2BOOL(prm.ice_cfg.enable_ice);
Index: /pjproject/trunk/pjsip/src/pjsua2/endpoint.cpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2/endpoint.cpp	(revision 5635)
+++ /pjproject/trunk/pjsip/src/pjsua2/endpoint.cpp	(revision 5636)
@@ -149,4 +149,5 @@
     }
 
+    this->stunTryIpv6 = PJ2BOOL(ua_cfg.stun_try_ipv6);
     this->stunIgnoreFailure = PJ2BOOL(ua_cfg.stun_ignore_failure);
     this->natTypeInSdp = ua_cfg.nat_type_in_sdp;
@@ -195,4 +196,5 @@
     NODE_READ_STRING  ( this_node, userAgent);
     NODE_READ_STRINGV ( this_node, stunServer);
+    NODE_READ_BOOL    ( this_node, stunTryIpv6);
     NODE_READ_BOOL    ( this_node, stunIgnoreFailure);
     NODE_READ_INT     ( this_node, natTypeInSdp);
@@ -210,4 +212,5 @@
     NODE_WRITE_STRING  ( this_node, userAgent);
     NODE_WRITE_STRINGV ( this_node, stunServer);
+    NODE_WRITE_BOOL    ( this_node, stunTryIpv6);
     NODE_WRITE_BOOL    ( this_node, stunIgnoreFailure);
     NODE_WRITE_INT     ( this_node, natTypeInSdp);
