Index: pjproject/trunk/build.symbian/pjnath.mmp
===================================================================
--- pjproject/trunk/build.symbian/pjnath.mmp	(revision 1987)
+++ pjproject/trunk/build.symbian/pjnath.mmp	(revision 1988)
@@ -38,4 +38,5 @@
 SOURCE	stun_msg_dump.c
 SOURCE	stun_session.c
+SOURCE	stun_sock.c
 SOURCE	stun_transaction.c
 SOURCE	turn_session.c
Index: pjproject/trunk/pjnath/build/Makefile
===================================================================
--- pjproject/trunk/pjnath/build/Makefile	(revision 1987)
+++ pjproject/trunk/pjnath/build/Makefile	(revision 1988)
@@ -32,6 +32,6 @@
 export PJNATH_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
 		errno.o ice_session.o ice_strans.o nat_detect.o stun_auth.o \
-		stun_msg.o stun_msg_dump.o stun_session.o stun_transaction.o \
-		turn_session.o turn_sock.o
+		stun_msg.o stun_msg_dump.o stun_session.o stun_sock.o \
+		stun_transaction.o turn_session.o turn_sock.o
 export PJNATH_CFLAGS += $(_CFLAGS)
 
@@ -40,5 +40,6 @@
 #
 export PJNATH_TEST_SRCDIR = ../src/pjnath-test
-export PJNATH_TEST_OBJS += ice_test.o stun.o sess_auth.o test.o
+export PJNATH_TEST_OBJS += ice_test.o stun.o sess_auth.o server.o \
+			    stun_sock_test.o turn_sock_test.o test.o
 export PJNATH_TEST_CFLAGS += $(_CFLAGS)
 export PJNATH_TEST_LDFLAGS += $(_LDFLAGS)
Index: pjproject/trunk/pjnath/build/pjnath.dsp
===================================================================
--- pjproject/trunk/pjnath/build/pjnath.dsp	(revision 1987)
+++ pjproject/trunk/pjnath/build/pjnath.dsp	(revision 1988)
@@ -122,4 +122,8 @@
 # Begin Source File
 
+SOURCE=..\src\pjnath\stun_sock.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\src\pjnath\stun_transaction.c
 # End Source File
@@ -175,4 +179,8 @@
 
 SOURCE=..\include\pjnath\stun_session.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\pjnath\stun_sock.h
 # End Source File
 # Begin Source File
Index: pjproject/trunk/pjnath/build/pjnath.vcproj
===================================================================
--- pjproject/trunk/pjnath/build/pjnath.vcproj	(revision 1987)
+++ pjproject/trunk/pjnath/build/pjnath.vcproj	(revision 1988)
@@ -299,4 +299,8 @@
 			</File>
 			<File
+				RelativePath="..\src\pjnath\stun_sock.c"
+				>
+			</File>
+			<File
 				RelativePath="..\src\pjnath\stun_transaction.c"
 				>
Index: pjproject/trunk/pjnath/build/pjnath_test.dsp
===================================================================
--- pjproject/trunk/pjnath/build/pjnath_test.dsp	(revision 1987)
+++ pjproject/trunk/pjnath/build/pjnath_test.dsp	(revision 1988)
@@ -89,8 +89,19 @@
 
 SOURCE="..\src\pjnath-test\ice_test.c"
+
+!IF  "$(CFG)" == "pjnath_test - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "pjnath_test - Win32 Debug"
+
+!ENDIF 
+
 # End Source File
 # Begin Source File
 
 SOURCE="..\src\pjnath-test\main.c"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pjnath-test\server.c"
 # End Source File
 # Begin Source File
@@ -104,5 +115,13 @@
 # Begin Source File
 
+SOURCE="..\src\pjnath-test\stun_sock_test.c"
+# End Source File
+# Begin Source File
+
 SOURCE="..\src\pjnath-test\test.c"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pjnath-test\turn_sock_test.c"
 # End Source File
 # End Group
@@ -110,4 +129,8 @@
 
 # PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE="..\src\pjnath-test\server.h"
+# End Source File
 # Begin Source File
 
Index: pjproject/trunk/pjnath/build/wince-evc4/pjnath_wince.vcp
===================================================================
--- pjproject/trunk/pjnath/build/wince-evc4/pjnath_wince.vcp	(revision 1987)
+++ pjproject/trunk/pjnath/build/wince-evc4/pjnath_wince.vcp	(revision 1988)
@@ -582,4 +582,5 @@
 
 DEP_CPP_ERRNO=\
+	"..\..\..\pjlib\include\pj\activesock.h"\
 	"..\..\..\pjlib\include\pj\addr_resolv.h"\
 	"..\..\..\pjlib\include\pj\array.h"\
@@ -627,4 +628,5 @@
 	"..\..\..\pjlib\include\pj\lock.h"\
 	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
 	"..\..\..\pjlib\include\pj\os.h"\
 	"..\..\..\pjlib\include\pj\pool.h"\
@@ -1257,4 +1259,5 @@
 
 DEP_CPP_ICE_S=\
+	"..\..\..\pjlib\include\pj\activesock.h"\
 	"..\..\..\pjlib\include\pj\addr_resolv.h"\
 	"..\..\..\pjlib\include\pj\array.h"\
@@ -1302,4 +1305,5 @@
 	"..\..\..\pjlib\include\pj\lock.h"\
 	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
 	"..\..\..\pjlib\include\pj\os.h"\
 	"..\..\..\pjlib\include\pj\pool.h"\
@@ -1986,4 +1990,5 @@
 	"..\..\..\pjlib-util\include\pjlib-util\resolver.h"\
 	"..\..\..\pjlib-util\include\pjlib-util\types.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
 	"..\..\..\pjlib\include\pj\addr_resolv.h"\
 	"..\..\..\pjlib\include\pj\array.h"\
@@ -2032,4 +2037,5 @@
 	"..\..\..\pjlib\include\pj\lock.h"\
 	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
 	"..\..\..\pjlib\include\pj\os.h"\
 	"..\..\..\pjlib\include\pj\pool.h"\
@@ -2055,5 +2061,8 @@
 	"..\..\include\pjnath\stun_msg.h"\
 	"..\..\include\pjnath\stun_session.h"\
+	"..\..\include\pjnath\stun_sock.h"\
 	"..\..\include\pjnath\stun_transaction.h"\
+	"..\..\include\pjnath\turn_session.h"\
+	"..\..\include\pjnath\turn_sock.h"\
 	"..\..\include\pjnath\types.h"\
 	
@@ -2740,4 +2749,5 @@
 
 DEP_CPP_NAT_D=\
+	"..\..\..\pjlib\include\pj\activesock.h"\
 	"..\..\..\pjlib\include\pj\addr_resolv.h"\
 	"..\..\..\pjlib\include\pj\array.h"\
@@ -2786,4 +2796,5 @@
 	"..\..\..\pjlib\include\pj\lock.h"\
 	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
 	"..\..\..\pjlib\include\pj\os.h"\
 	"..\..\..\pjlib\include\pj\pool.h"\
@@ -3470,4 +3481,5 @@
 	"..\..\..\pjlib-util\include\pjlib-util\md5.h"\
 	"..\..\..\pjlib-util\include\pjlib-util\sha1.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
 	"..\..\..\pjlib\include\pj\addr_resolv.h"\
 	"..\..\..\pjlib\include\pj\array.h"\
@@ -3515,4 +3527,5 @@
 	"..\..\..\pjlib\include\pj\lock.h"\
 	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
 	"..\..\..\pjlib\include\pj\os.h"\
 	"..\..\..\pjlib\include\pj\pool.h"\
@@ -4167,4 +4180,5 @@
 	"..\..\..\pjlib-util\include\pjlib-util\sha1.h"\
 	"..\..\..\pjlib-util\include\pjlib-util\types.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
 	"..\..\..\pjlib\include\pj\addr_resolv.h"\
 	"..\..\..\pjlib\include\pj\array.h"\
@@ -4212,4 +4226,5 @@
 	"..\..\..\pjlib\include\pj\lock.h"\
 	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
 	"..\..\..\pjlib\include\pj\os.h"\
 	"..\..\..\pjlib\include\pj\pool.h"\
@@ -4848,4 +4863,5 @@
 
 DEP_CPP_STUN_MS=\
+	"..\..\..\pjlib\include\pj\activesock.h"\
 	"..\..\..\pjlib\include\pj\addr_resolv.h"\
 	"..\..\..\pjlib\include\pj\array.h"\
@@ -4893,4 +4909,5 @@
 	"..\..\..\pjlib\include\pj\lock.h"\
 	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
 	"..\..\..\pjlib\include\pj\os.h"\
 	"..\..\..\pjlib\include\pj\pool.h"\
@@ -5515,4 +5532,5 @@
 
 DEP_CPP_STUN_S=\
+	"..\..\..\pjlib\include\pj\activesock.h"\
 	"..\..\..\pjlib\include\pj\addr_resolv.h"\
 	"..\..\..\pjlib\include\pj\array.h"\
@@ -5560,4 +5578,5 @@
 	"..\..\..\pjlib\include\pj\lock.h"\
 	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
 	"..\..\..\pjlib\include\pj\os.h"\
 	"..\..\..\pjlib\include\pj\pool.h"\
@@ -5926,74 +5945,15 @@
 # Begin Source File
 
-SOURCE=..\..\src\pjnath\stun_transaction.c
+SOURCE=..\..\src\pjnath\stun_sock.c
 
 !IF  "$(CFG)" == "pjnath_wince - Win32 (WCE emulator) Release"
 
-DEP_CPP_STUN_T=\
-	"..\..\..\pjlib\include\pj\addr_resolv.h"\
-	"..\..\..\pjlib\include\pj\array.h"\
-	"..\..\..\pjlib\include\pj\assert.h"\
-	"..\..\..\pjlib\include\pj\compat\assert.h"\
-	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
-	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
-	"..\..\..\pjlib\include\pj\compat\ctype.h"\
-	"..\..\..\pjlib\include\pj\compat\errno.h"\
-	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
-	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
-	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
-	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
-	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
-	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
-	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
-	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
-	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
-	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
-	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
-	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
-	"..\..\..\pjlib\include\pj\compat\size_t.h"\
-	"..\..\..\pjlib\include\pj\compat\string.h"\
-	"..\..\..\pjlib\include\pj\config.h"\
-	"..\..\..\pjlib\include\pj\config_site.h"\
-	"..\..\..\pjlib\include\pj\config_site_sample.h"\
-	"..\..\..\pjlib\include\pj\ctype.h"\
-	"..\..\..\pjlib\include\pj\errno.h"\
-	"..\..\..\pjlib\include\pj\except.h"\
-	"..\..\..\pjlib\include\pj\fifobuf.h"\
-	"..\..\..\pjlib\include\pj\file_access.h"\
-	"..\..\..\pjlib\include\pj\file_io.h"\
-	"..\..\..\pjlib\include\pj\guid.h"\
-	"..\..\..\pjlib\include\pj\hash.h"\
-	"..\..\..\pjlib\include\pj\ioqueue.h"\
-	"..\..\..\pjlib\include\pj\ip_helper.h"\
-	"..\..\..\pjlib\include\pj\list.h"\
-	"..\..\..\pjlib\include\pj\list_i.h"\
-	"..\..\..\pjlib\include\pj\lock.h"\
-	"..\..\..\pjlib\include\pj\log.h"\
-	"..\..\..\pjlib\include\pj\os.h"\
-	"..\..\..\pjlib\include\pj\pool.h"\
-	"..\..\..\pjlib\include\pj\pool_alt.h"\
-	"..\..\..\pjlib\include\pj\pool_buf.h"\
-	"..\..\..\pjlib\include\pj\pool_i.h"\
-	"..\..\..\pjlib\include\pj\rand.h"\
-	"..\..\..\pjlib\include\pj\rbtree.h"\
-	"..\..\..\pjlib\include\pj\sock.h"\
-	"..\..\..\pjlib\include\pj\sock_select.h"\
-	"..\..\..\pjlib\include\pj\string.h"\
-	"..\..\..\pjlib\include\pj\string_i.h"\
-	"..\..\..\pjlib\include\pj\timer.h"\
-	"..\..\..\pjlib\include\pj\types.h"\
-	"..\..\..\pjlib\include\pj\unicode.h"\
-	"..\..\..\pjlib\include\pjlib.h"\
-	"..\..\include\pjnath\config.h"\
-	"..\..\include\pjnath\errno.h"\
-	"..\..\include\pjnath\stun_config.h"\
-	"..\..\include\pjnath\stun_msg.h"\
-	"..\..\include\pjnath\stun_transaction.h"\
-	"..\..\include\pjnath\types.h"\
-	
-
-!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE emulator) Debug"
-
-DEP_CPP_STUN_T=\
+DEP_CPP_STUN_SO=\
+	"..\..\..\pjlib-util\include\pjlib-util\config.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\dns.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\srv_resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\types.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
 	"..\..\..\pjlib\include\pj\addr_resolv.h"\
 	"..\..\..\pjlib\include\pj\array.h"\
@@ -6041,160 +6001,40 @@
 	"..\..\..\pjlib\include\pj\lock.h"\
 	"..\..\..\pjlib\include\pj\log.h"\
-	"..\..\..\pjlib\include\pj\os.h"\
-	"..\..\..\pjlib\include\pj\pool.h"\
-	"..\..\..\pjlib\include\pj\pool_alt.h"\
-	"..\..\..\pjlib\include\pj\pool_buf.h"\
-	"..\..\..\pjlib\include\pj\pool_i.h"\
-	"..\..\..\pjlib\include\pj\rand.h"\
-	"..\..\..\pjlib\include\pj\rbtree.h"\
-	"..\..\..\pjlib\include\pj\sock.h"\
-	"..\..\..\pjlib\include\pj\sock_select.h"\
-	"..\..\..\pjlib\include\pj\string.h"\
-	"..\..\..\pjlib\include\pj\string_i.h"\
-	"..\..\..\pjlib\include\pj\timer.h"\
-	"..\..\..\pjlib\include\pj\types.h"\
-	"..\..\..\pjlib\include\pj\unicode.h"\
-	"..\..\..\pjlib\include\pjlib.h"\
-	"..\..\include\pjnath\config.h"\
-	"..\..\include\pjnath\errno.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
+	"..\..\..\pjlib\include\pj\os.h"\
+	"..\..\..\pjlib\include\pj\pool.h"\
+	"..\..\..\pjlib\include\pj\pool_alt.h"\
+	"..\..\..\pjlib\include\pj\pool_buf.h"\
+	"..\..\..\pjlib\include\pj\pool_i.h"\
+	"..\..\..\pjlib\include\pj\rand.h"\
+	"..\..\..\pjlib\include\pj\rbtree.h"\
+	"..\..\..\pjlib\include\pj\sock.h"\
+	"..\..\..\pjlib\include\pj\sock_select.h"\
+	"..\..\..\pjlib\include\pj\string.h"\
+	"..\..\..\pjlib\include\pj\string_i.h"\
+	"..\..\..\pjlib\include\pj\timer.h"\
+	"..\..\..\pjlib\include\pj\types.h"\
+	"..\..\..\pjlib\include\pj\unicode.h"\
+	"..\..\..\pjlib\include\pjlib.h"\
+	"..\..\include\pjnath\config.h"\
+	"..\..\include\pjnath\errno.h"\
+	"..\..\include\pjnath\stun_auth.h"\
 	"..\..\include\pjnath\stun_config.h"\
 	"..\..\include\pjnath\stun_msg.h"\
+	"..\..\include\pjnath\stun_session.h"\
+	"..\..\include\pjnath\stun_sock.h"\
 	"..\..\include\pjnath\stun_transaction.h"\
 	"..\..\include\pjnath\types.h"\
 	
 
-!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4I) Release"
-
-DEP_CPP_STUN_T=\
-	"..\..\..\pjlib\include\pj\addr_resolv.h"\
-	"..\..\..\pjlib\include\pj\array.h"\
-	"..\..\..\pjlib\include\pj\assert.h"\
-	"..\..\..\pjlib\include\pj\compat\assert.h"\
-	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
-	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
-	"..\..\..\pjlib\include\pj\compat\ctype.h"\
-	"..\..\..\pjlib\include\pj\compat\errno.h"\
-	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
-	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
-	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
-	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
-	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
-	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
-	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
-	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
-	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
-	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
-	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
-	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
-	"..\..\..\pjlib\include\pj\compat\size_t.h"\
-	"..\..\..\pjlib\include\pj\compat\string.h"\
-	"..\..\..\pjlib\include\pj\config.h"\
-	"..\..\..\pjlib\include\pj\config_site.h"\
-	"..\..\..\pjlib\include\pj\config_site_sample.h"\
-	"..\..\..\pjlib\include\pj\ctype.h"\
-	"..\..\..\pjlib\include\pj\errno.h"\
-	"..\..\..\pjlib\include\pj\except.h"\
-	"..\..\..\pjlib\include\pj\fifobuf.h"\
-	"..\..\..\pjlib\include\pj\file_access.h"\
-	"..\..\..\pjlib\include\pj\file_io.h"\
-	"..\..\..\pjlib\include\pj\guid.h"\
-	"..\..\..\pjlib\include\pj\hash.h"\
-	"..\..\..\pjlib\include\pj\ioqueue.h"\
-	"..\..\..\pjlib\include\pj\ip_helper.h"\
-	"..\..\..\pjlib\include\pj\list.h"\
-	"..\..\..\pjlib\include\pj\list_i.h"\
-	"..\..\..\pjlib\include\pj\lock.h"\
-	"..\..\..\pjlib\include\pj\log.h"\
-	"..\..\..\pjlib\include\pj\os.h"\
-	"..\..\..\pjlib\include\pj\pool.h"\
-	"..\..\..\pjlib\include\pj\pool_alt.h"\
-	"..\..\..\pjlib\include\pj\pool_buf.h"\
-	"..\..\..\pjlib\include\pj\pool_i.h"\
-	"..\..\..\pjlib\include\pj\rand.h"\
-	"..\..\..\pjlib\include\pj\rbtree.h"\
-	"..\..\..\pjlib\include\pj\sock.h"\
-	"..\..\..\pjlib\include\pj\sock_select.h"\
-	"..\..\..\pjlib\include\pj\string.h"\
-	"..\..\..\pjlib\include\pj\string_i.h"\
-	"..\..\..\pjlib\include\pj\timer.h"\
-	"..\..\..\pjlib\include\pj\types.h"\
-	"..\..\..\pjlib\include\pj\unicode.h"\
-	"..\..\..\pjlib\include\pjlib.h"\
-	"..\..\include\pjnath\config.h"\
-	"..\..\include\pjnath\errno.h"\
-	"..\..\include\pjnath\stun_config.h"\
-	"..\..\include\pjnath\stun_msg.h"\
-	"..\..\include\pjnath\stun_transaction.h"\
-	"..\..\include\pjnath\types.h"\
-	
-
-!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4I) Debug"
-
-DEP_CPP_STUN_T=\
-	"..\..\..\pjlib\include\pj\addr_resolv.h"\
-	"..\..\..\pjlib\include\pj\array.h"\
-	"..\..\..\pjlib\include\pj\assert.h"\
-	"..\..\..\pjlib\include\pj\compat\assert.h"\
-	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
-	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
-	"..\..\..\pjlib\include\pj\compat\ctype.h"\
-	"..\..\..\pjlib\include\pj\compat\errno.h"\
-	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
-	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
-	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
-	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
-	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
-	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
-	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
-	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
-	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
-	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
-	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
-	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
-	"..\..\..\pjlib\include\pj\compat\size_t.h"\
-	"..\..\..\pjlib\include\pj\compat\string.h"\
-	"..\..\..\pjlib\include\pj\config.h"\
-	"..\..\..\pjlib\include\pj\config_site.h"\
-	"..\..\..\pjlib\include\pj\config_site_sample.h"\
-	"..\..\..\pjlib\include\pj\ctype.h"\
-	"..\..\..\pjlib\include\pj\errno.h"\
-	"..\..\..\pjlib\include\pj\except.h"\
-	"..\..\..\pjlib\include\pj\fifobuf.h"\
-	"..\..\..\pjlib\include\pj\file_access.h"\
-	"..\..\..\pjlib\include\pj\file_io.h"\
-	"..\..\..\pjlib\include\pj\guid.h"\
-	"..\..\..\pjlib\include\pj\hash.h"\
-	"..\..\..\pjlib\include\pj\ioqueue.h"\
-	"..\..\..\pjlib\include\pj\ip_helper.h"\
-	"..\..\..\pjlib\include\pj\list.h"\
-	"..\..\..\pjlib\include\pj\list_i.h"\
-	"..\..\..\pjlib\include\pj\lock.h"\
-	"..\..\..\pjlib\include\pj\log.h"\
-	"..\..\..\pjlib\include\pj\os.h"\
-	"..\..\..\pjlib\include\pj\pool.h"\
-	"..\..\..\pjlib\include\pj\pool_alt.h"\
-	"..\..\..\pjlib\include\pj\pool_buf.h"\
-	"..\..\..\pjlib\include\pj\pool_i.h"\
-	"..\..\..\pjlib\include\pj\rand.h"\
-	"..\..\..\pjlib\include\pj\rbtree.h"\
-	"..\..\..\pjlib\include\pj\sock.h"\
-	"..\..\..\pjlib\include\pj\sock_select.h"\
-	"..\..\..\pjlib\include\pj\string.h"\
-	"..\..\..\pjlib\include\pj\string_i.h"\
-	"..\..\..\pjlib\include\pj\timer.h"\
-	"..\..\..\pjlib\include\pj\types.h"\
-	"..\..\..\pjlib\include\pj\unicode.h"\
-	"..\..\..\pjlib\include\pjlib.h"\
-	"..\..\include\pjnath\config.h"\
-	"..\..\include\pjnath\errno.h"\
-	"..\..\include\pjnath\stun_config.h"\
-	"..\..\include\pjnath\stun_msg.h"\
-	"..\..\include\pjnath\stun_transaction.h"\
-	"..\..\include\pjnath\types.h"\
-	
-
-!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4) Release"
-
-DEP_CPP_STUN_T=\
+!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE emulator) Debug"
+
+DEP_CPP_STUN_SO=\
+	"..\..\..\pjlib-util\include\pjlib-util\config.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\dns.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\srv_resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\types.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
 	"..\..\..\pjlib\include\pj\addr_resolv.h"\
 	"..\..\..\pjlib\include\pj\array.h"\
@@ -6242,4 +6082,1002 @@
 	"..\..\..\pjlib\include\pj\lock.h"\
 	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
+	"..\..\..\pjlib\include\pj\os.h"\
+	"..\..\..\pjlib\include\pj\pool.h"\
+	"..\..\..\pjlib\include\pj\pool_alt.h"\
+	"..\..\..\pjlib\include\pj\pool_buf.h"\
+	"..\..\..\pjlib\include\pj\pool_i.h"\
+	"..\..\..\pjlib\include\pj\rand.h"\
+	"..\..\..\pjlib\include\pj\rbtree.h"\
+	"..\..\..\pjlib\include\pj\sock.h"\
+	"..\..\..\pjlib\include\pj\sock_select.h"\
+	"..\..\..\pjlib\include\pj\string.h"\
+	"..\..\..\pjlib\include\pj\string_i.h"\
+	"..\..\..\pjlib\include\pj\timer.h"\
+	"..\..\..\pjlib\include\pj\types.h"\
+	"..\..\..\pjlib\include\pj\unicode.h"\
+	"..\..\..\pjlib\include\pjlib.h"\
+	"..\..\include\pjnath\config.h"\
+	"..\..\include\pjnath\errno.h"\
+	"..\..\include\pjnath\stun_auth.h"\
+	"..\..\include\pjnath\stun_config.h"\
+	"..\..\include\pjnath\stun_msg.h"\
+	"..\..\include\pjnath\stun_session.h"\
+	"..\..\include\pjnath\stun_sock.h"\
+	"..\..\include\pjnath\stun_transaction.h"\
+	"..\..\include\pjnath\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4I) Release"
+
+DEP_CPP_STUN_SO=\
+	"..\..\..\pjlib-util\include\pjlib-util\config.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\dns.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\srv_resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\types.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
+	"..\..\..\pjlib\include\pj\addr_resolv.h"\
+	"..\..\..\pjlib\include\pj\array.h"\
+	"..\..\..\pjlib\include\pj\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_armcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_codew.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcce.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_mwcc.h"\
+	"..\..\..\pjlib\include\pj\compat\ctype.h"\
+	"..\..\..\pjlib\include\pj\compat\errno.h"\
+	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
+	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
+	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
+	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_symbian.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
+	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
+	"..\..\..\pjlib\include\pj\compat\size_t.h"\
+	"..\..\..\pjlib\include\pj\compat\stdarg.h"\
+	"..\..\..\pjlib\include\pj\compat\string.h"\
+	"..\..\..\pjlib\include\pj\config.h"\
+	"..\..\..\pjlib\include\pj\config_site.h"\
+	"..\..\..\pjlib\include\pj\config_site_sample.h"\
+	"..\..\..\pjlib\include\pj\ctype.h"\
+	"..\..\..\pjlib\include\pj\errno.h"\
+	"..\..\..\pjlib\include\pj\except.h"\
+	"..\..\..\pjlib\include\pj\fifobuf.h"\
+	"..\..\..\pjlib\include\pj\file_access.h"\
+	"..\..\..\pjlib\include\pj\file_io.h"\
+	"..\..\..\pjlib\include\pj\guid.h"\
+	"..\..\..\pjlib\include\pj\hash.h"\
+	"..\..\..\pjlib\include\pj\ioqueue.h"\
+	"..\..\..\pjlib\include\pj\ip_helper.h"\
+	"..\..\..\pjlib\include\pj\list.h"\
+	"..\..\..\pjlib\include\pj\list_i.h"\
+	"..\..\..\pjlib\include\pj\lock.h"\
+	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
+	"..\..\..\pjlib\include\pj\os.h"\
+	"..\..\..\pjlib\include\pj\pool.h"\
+	"..\..\..\pjlib\include\pj\pool_alt.h"\
+	"..\..\..\pjlib\include\pj\pool_buf.h"\
+	"..\..\..\pjlib\include\pj\pool_i.h"\
+	"..\..\..\pjlib\include\pj\rand.h"\
+	"..\..\..\pjlib\include\pj\rbtree.h"\
+	"..\..\..\pjlib\include\pj\sock.h"\
+	"..\..\..\pjlib\include\pj\sock_select.h"\
+	"..\..\..\pjlib\include\pj\string.h"\
+	"..\..\..\pjlib\include\pj\string_i.h"\
+	"..\..\..\pjlib\include\pj\timer.h"\
+	"..\..\..\pjlib\include\pj\types.h"\
+	"..\..\..\pjlib\include\pj\unicode.h"\
+	"..\..\..\pjlib\include\pjlib.h"\
+	"..\..\include\pjnath\config.h"\
+	"..\..\include\pjnath\errno.h"\
+	"..\..\include\pjnath\stun_auth.h"\
+	"..\..\include\pjnath\stun_config.h"\
+	"..\..\include\pjnath\stun_msg.h"\
+	"..\..\include\pjnath\stun_session.h"\
+	"..\..\include\pjnath\stun_sock.h"\
+	"..\..\include\pjnath\stun_transaction.h"\
+	"..\..\include\pjnath\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4I) Debug"
+
+DEP_CPP_STUN_SO=\
+	"..\..\..\pjlib-util\include\pjlib-util\config.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\dns.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\srv_resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\types.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
+	"..\..\..\pjlib\include\pj\addr_resolv.h"\
+	"..\..\..\pjlib\include\pj\array.h"\
+	"..\..\..\pjlib\include\pj\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_armcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_codew.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcce.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_mwcc.h"\
+	"..\..\..\pjlib\include\pj\compat\ctype.h"\
+	"..\..\..\pjlib\include\pj\compat\errno.h"\
+	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
+	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
+	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
+	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_symbian.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
+	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
+	"..\..\..\pjlib\include\pj\compat\size_t.h"\
+	"..\..\..\pjlib\include\pj\compat\stdarg.h"\
+	"..\..\..\pjlib\include\pj\compat\string.h"\
+	"..\..\..\pjlib\include\pj\config.h"\
+	"..\..\..\pjlib\include\pj\config_site.h"\
+	"..\..\..\pjlib\include\pj\config_site_sample.h"\
+	"..\..\..\pjlib\include\pj\ctype.h"\
+	"..\..\..\pjlib\include\pj\errno.h"\
+	"..\..\..\pjlib\include\pj\except.h"\
+	"..\..\..\pjlib\include\pj\fifobuf.h"\
+	"..\..\..\pjlib\include\pj\file_access.h"\
+	"..\..\..\pjlib\include\pj\file_io.h"\
+	"..\..\..\pjlib\include\pj\guid.h"\
+	"..\..\..\pjlib\include\pj\hash.h"\
+	"..\..\..\pjlib\include\pj\ioqueue.h"\
+	"..\..\..\pjlib\include\pj\ip_helper.h"\
+	"..\..\..\pjlib\include\pj\list.h"\
+	"..\..\..\pjlib\include\pj\list_i.h"\
+	"..\..\..\pjlib\include\pj\lock.h"\
+	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
+	"..\..\..\pjlib\include\pj\os.h"\
+	"..\..\..\pjlib\include\pj\pool.h"\
+	"..\..\..\pjlib\include\pj\pool_alt.h"\
+	"..\..\..\pjlib\include\pj\pool_buf.h"\
+	"..\..\..\pjlib\include\pj\pool_i.h"\
+	"..\..\..\pjlib\include\pj\rand.h"\
+	"..\..\..\pjlib\include\pj\rbtree.h"\
+	"..\..\..\pjlib\include\pj\sock.h"\
+	"..\..\..\pjlib\include\pj\sock_select.h"\
+	"..\..\..\pjlib\include\pj\string.h"\
+	"..\..\..\pjlib\include\pj\string_i.h"\
+	"..\..\..\pjlib\include\pj\timer.h"\
+	"..\..\..\pjlib\include\pj\types.h"\
+	"..\..\..\pjlib\include\pj\unicode.h"\
+	"..\..\..\pjlib\include\pjlib.h"\
+	"..\..\include\pjnath\config.h"\
+	"..\..\include\pjnath\errno.h"\
+	"..\..\include\pjnath\stun_auth.h"\
+	"..\..\include\pjnath\stun_config.h"\
+	"..\..\include\pjnath\stun_msg.h"\
+	"..\..\include\pjnath\stun_session.h"\
+	"..\..\include\pjnath\stun_sock.h"\
+	"..\..\include\pjnath\stun_transaction.h"\
+	"..\..\include\pjnath\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_STUN_SO=\
+	"..\..\..\pjlib-util\include\pjlib-util\config.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\dns.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\srv_resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\types.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
+	"..\..\..\pjlib\include\pj\addr_resolv.h"\
+	"..\..\..\pjlib\include\pj\array.h"\
+	"..\..\..\pjlib\include\pj\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_armcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_codew.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcce.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_mwcc.h"\
+	"..\..\..\pjlib\include\pj\compat\ctype.h"\
+	"..\..\..\pjlib\include\pj\compat\errno.h"\
+	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
+	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
+	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
+	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_symbian.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
+	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
+	"..\..\..\pjlib\include\pj\compat\size_t.h"\
+	"..\..\..\pjlib\include\pj\compat\stdarg.h"\
+	"..\..\..\pjlib\include\pj\compat\string.h"\
+	"..\..\..\pjlib\include\pj\config.h"\
+	"..\..\..\pjlib\include\pj\config_site.h"\
+	"..\..\..\pjlib\include\pj\config_site_sample.h"\
+	"..\..\..\pjlib\include\pj\ctype.h"\
+	"..\..\..\pjlib\include\pj\errno.h"\
+	"..\..\..\pjlib\include\pj\except.h"\
+	"..\..\..\pjlib\include\pj\fifobuf.h"\
+	"..\..\..\pjlib\include\pj\file_access.h"\
+	"..\..\..\pjlib\include\pj\file_io.h"\
+	"..\..\..\pjlib\include\pj\guid.h"\
+	"..\..\..\pjlib\include\pj\hash.h"\
+	"..\..\..\pjlib\include\pj\ioqueue.h"\
+	"..\..\..\pjlib\include\pj\ip_helper.h"\
+	"..\..\..\pjlib\include\pj\list.h"\
+	"..\..\..\pjlib\include\pj\list_i.h"\
+	"..\..\..\pjlib\include\pj\lock.h"\
+	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
+	"..\..\..\pjlib\include\pj\os.h"\
+	"..\..\..\pjlib\include\pj\pool.h"\
+	"..\..\..\pjlib\include\pj\pool_alt.h"\
+	"..\..\..\pjlib\include\pj\pool_buf.h"\
+	"..\..\..\pjlib\include\pj\pool_i.h"\
+	"..\..\..\pjlib\include\pj\rand.h"\
+	"..\..\..\pjlib\include\pj\rbtree.h"\
+	"..\..\..\pjlib\include\pj\sock.h"\
+	"..\..\..\pjlib\include\pj\sock_select.h"\
+	"..\..\..\pjlib\include\pj\string.h"\
+	"..\..\..\pjlib\include\pj\string_i.h"\
+	"..\..\..\pjlib\include\pj\timer.h"\
+	"..\..\..\pjlib\include\pj\types.h"\
+	"..\..\..\pjlib\include\pj\unicode.h"\
+	"..\..\..\pjlib\include\pjlib.h"\
+	"..\..\include\pjnath\config.h"\
+	"..\..\include\pjnath\errno.h"\
+	"..\..\include\pjnath\stun_auth.h"\
+	"..\..\include\pjnath\stun_config.h"\
+	"..\..\include\pjnath\stun_msg.h"\
+	"..\..\include\pjnath\stun_session.h"\
+	"..\..\include\pjnath\stun_sock.h"\
+	"..\..\include\pjnath\stun_transaction.h"\
+	"..\..\include\pjnath\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_STUN_SO=\
+	"..\..\..\pjlib-util\include\pjlib-util\config.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\dns.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\srv_resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\types.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
+	"..\..\..\pjlib\include\pj\addr_resolv.h"\
+	"..\..\..\pjlib\include\pj\array.h"\
+	"..\..\..\pjlib\include\pj\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_armcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_codew.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcce.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_mwcc.h"\
+	"..\..\..\pjlib\include\pj\compat\ctype.h"\
+	"..\..\..\pjlib\include\pj\compat\errno.h"\
+	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
+	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
+	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
+	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_symbian.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
+	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
+	"..\..\..\pjlib\include\pj\compat\size_t.h"\
+	"..\..\..\pjlib\include\pj\compat\stdarg.h"\
+	"..\..\..\pjlib\include\pj\compat\string.h"\
+	"..\..\..\pjlib\include\pj\config.h"\
+	"..\..\..\pjlib\include\pj\config_site.h"\
+	"..\..\..\pjlib\include\pj\config_site_sample.h"\
+	"..\..\..\pjlib\include\pj\ctype.h"\
+	"..\..\..\pjlib\include\pj\errno.h"\
+	"..\..\..\pjlib\include\pj\except.h"\
+	"..\..\..\pjlib\include\pj\fifobuf.h"\
+	"..\..\..\pjlib\include\pj\file_access.h"\
+	"..\..\..\pjlib\include\pj\file_io.h"\
+	"..\..\..\pjlib\include\pj\guid.h"\
+	"..\..\..\pjlib\include\pj\hash.h"\
+	"..\..\..\pjlib\include\pj\ioqueue.h"\
+	"..\..\..\pjlib\include\pj\ip_helper.h"\
+	"..\..\..\pjlib\include\pj\list.h"\
+	"..\..\..\pjlib\include\pj\list_i.h"\
+	"..\..\..\pjlib\include\pj\lock.h"\
+	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
+	"..\..\..\pjlib\include\pj\os.h"\
+	"..\..\..\pjlib\include\pj\pool.h"\
+	"..\..\..\pjlib\include\pj\pool_alt.h"\
+	"..\..\..\pjlib\include\pj\pool_buf.h"\
+	"..\..\..\pjlib\include\pj\pool_i.h"\
+	"..\..\..\pjlib\include\pj\rand.h"\
+	"..\..\..\pjlib\include\pj\rbtree.h"\
+	"..\..\..\pjlib\include\pj\sock.h"\
+	"..\..\..\pjlib\include\pj\sock_select.h"\
+	"..\..\..\pjlib\include\pj\string.h"\
+	"..\..\..\pjlib\include\pj\string_i.h"\
+	"..\..\..\pjlib\include\pj\timer.h"\
+	"..\..\..\pjlib\include\pj\types.h"\
+	"..\..\..\pjlib\include\pj\unicode.h"\
+	"..\..\..\pjlib\include\pjlib.h"\
+	"..\..\include\pjnath\config.h"\
+	"..\..\include\pjnath\errno.h"\
+	"..\..\include\pjnath\stun_auth.h"\
+	"..\..\include\pjnath\stun_config.h"\
+	"..\..\include\pjnath\stun_msg.h"\
+	"..\..\include\pjnath\stun_session.h"\
+	"..\..\include\pjnath\stun_sock.h"\
+	"..\..\include\pjnath\stun_transaction.h"\
+	"..\..\include\pjnath\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4T) Release"
+
+DEP_CPP_STUN_SO=\
+	"..\..\..\pjlib-util\include\pjlib-util\config.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\dns.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\srv_resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\types.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
+	"..\..\..\pjlib\include\pj\addr_resolv.h"\
+	"..\..\..\pjlib\include\pj\array.h"\
+	"..\..\..\pjlib\include\pj\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_armcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_codew.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcce.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_mwcc.h"\
+	"..\..\..\pjlib\include\pj\compat\ctype.h"\
+	"..\..\..\pjlib\include\pj\compat\errno.h"\
+	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
+	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
+	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
+	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_symbian.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
+	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
+	"..\..\..\pjlib\include\pj\compat\size_t.h"\
+	"..\..\..\pjlib\include\pj\compat\stdarg.h"\
+	"..\..\..\pjlib\include\pj\compat\string.h"\
+	"..\..\..\pjlib\include\pj\config.h"\
+	"..\..\..\pjlib\include\pj\config_site.h"\
+	"..\..\..\pjlib\include\pj\config_site_sample.h"\
+	"..\..\..\pjlib\include\pj\ctype.h"\
+	"..\..\..\pjlib\include\pj\errno.h"\
+	"..\..\..\pjlib\include\pj\except.h"\
+	"..\..\..\pjlib\include\pj\fifobuf.h"\
+	"..\..\..\pjlib\include\pj\file_access.h"\
+	"..\..\..\pjlib\include\pj\file_io.h"\
+	"..\..\..\pjlib\include\pj\guid.h"\
+	"..\..\..\pjlib\include\pj\hash.h"\
+	"..\..\..\pjlib\include\pj\ioqueue.h"\
+	"..\..\..\pjlib\include\pj\ip_helper.h"\
+	"..\..\..\pjlib\include\pj\list.h"\
+	"..\..\..\pjlib\include\pj\list_i.h"\
+	"..\..\..\pjlib\include\pj\lock.h"\
+	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
+	"..\..\..\pjlib\include\pj\os.h"\
+	"..\..\..\pjlib\include\pj\pool.h"\
+	"..\..\..\pjlib\include\pj\pool_alt.h"\
+	"..\..\..\pjlib\include\pj\pool_buf.h"\
+	"..\..\..\pjlib\include\pj\pool_i.h"\
+	"..\..\..\pjlib\include\pj\rand.h"\
+	"..\..\..\pjlib\include\pj\rbtree.h"\
+	"..\..\..\pjlib\include\pj\sock.h"\
+	"..\..\..\pjlib\include\pj\sock_select.h"\
+	"..\..\..\pjlib\include\pj\string.h"\
+	"..\..\..\pjlib\include\pj\string_i.h"\
+	"..\..\..\pjlib\include\pj\timer.h"\
+	"..\..\..\pjlib\include\pj\types.h"\
+	"..\..\..\pjlib\include\pj\unicode.h"\
+	"..\..\..\pjlib\include\pjlib.h"\
+	"..\..\include\pjnath\config.h"\
+	"..\..\include\pjnath\errno.h"\
+	"..\..\include\pjnath\stun_auth.h"\
+	"..\..\include\pjnath\stun_config.h"\
+	"..\..\include\pjnath\stun_msg.h"\
+	"..\..\include\pjnath\stun_session.h"\
+	"..\..\include\pjnath\stun_sock.h"\
+	"..\..\include\pjnath\stun_transaction.h"\
+	"..\..\include\pjnath\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4T) Debug"
+
+DEP_CPP_STUN_SO=\
+	"..\..\..\pjlib-util\include\pjlib-util\config.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\dns.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\srv_resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\types.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
+	"..\..\..\pjlib\include\pj\addr_resolv.h"\
+	"..\..\..\pjlib\include\pj\array.h"\
+	"..\..\..\pjlib\include\pj\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_armcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_codew.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcce.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_mwcc.h"\
+	"..\..\..\pjlib\include\pj\compat\ctype.h"\
+	"..\..\..\pjlib\include\pj\compat\errno.h"\
+	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
+	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
+	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
+	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_symbian.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
+	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
+	"..\..\..\pjlib\include\pj\compat\size_t.h"\
+	"..\..\..\pjlib\include\pj\compat\stdarg.h"\
+	"..\..\..\pjlib\include\pj\compat\string.h"\
+	"..\..\..\pjlib\include\pj\config.h"\
+	"..\..\..\pjlib\include\pj\config_site.h"\
+	"..\..\..\pjlib\include\pj\config_site_sample.h"\
+	"..\..\..\pjlib\include\pj\ctype.h"\
+	"..\..\..\pjlib\include\pj\errno.h"\
+	"..\..\..\pjlib\include\pj\except.h"\
+	"..\..\..\pjlib\include\pj\fifobuf.h"\
+	"..\..\..\pjlib\include\pj\file_access.h"\
+	"..\..\..\pjlib\include\pj\file_io.h"\
+	"..\..\..\pjlib\include\pj\guid.h"\
+	"..\..\..\pjlib\include\pj\hash.h"\
+	"..\..\..\pjlib\include\pj\ioqueue.h"\
+	"..\..\..\pjlib\include\pj\ip_helper.h"\
+	"..\..\..\pjlib\include\pj\list.h"\
+	"..\..\..\pjlib\include\pj\list_i.h"\
+	"..\..\..\pjlib\include\pj\lock.h"\
+	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
+	"..\..\..\pjlib\include\pj\os.h"\
+	"..\..\..\pjlib\include\pj\pool.h"\
+	"..\..\..\pjlib\include\pj\pool_alt.h"\
+	"..\..\..\pjlib\include\pj\pool_buf.h"\
+	"..\..\..\pjlib\include\pj\pool_i.h"\
+	"..\..\..\pjlib\include\pj\rand.h"\
+	"..\..\..\pjlib\include\pj\rbtree.h"\
+	"..\..\..\pjlib\include\pj\sock.h"\
+	"..\..\..\pjlib\include\pj\sock_select.h"\
+	"..\..\..\pjlib\include\pj\string.h"\
+	"..\..\..\pjlib\include\pj\string_i.h"\
+	"..\..\..\pjlib\include\pj\timer.h"\
+	"..\..\..\pjlib\include\pj\types.h"\
+	"..\..\..\pjlib\include\pj\unicode.h"\
+	"..\..\..\pjlib\include\pjlib.h"\
+	"..\..\include\pjnath\config.h"\
+	"..\..\include\pjnath\errno.h"\
+	"..\..\include\pjnath\stun_auth.h"\
+	"..\..\include\pjnath\stun_config.h"\
+	"..\..\include\pjnath\stun_msg.h"\
+	"..\..\include\pjnath\stun_session.h"\
+	"..\..\include\pjnath\stun_sock.h"\
+	"..\..\include\pjnath\stun_transaction.h"\
+	"..\..\include\pjnath\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE x86) Release"
+
+DEP_CPP_STUN_SO=\
+	"..\..\..\pjlib-util\include\pjlib-util\config.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\dns.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\srv_resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\types.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
+	"..\..\..\pjlib\include\pj\addr_resolv.h"\
+	"..\..\..\pjlib\include\pj\array.h"\
+	"..\..\..\pjlib\include\pj\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_armcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_codew.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcce.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_mwcc.h"\
+	"..\..\..\pjlib\include\pj\compat\ctype.h"\
+	"..\..\..\pjlib\include\pj\compat\errno.h"\
+	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
+	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
+	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
+	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_symbian.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
+	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
+	"..\..\..\pjlib\include\pj\compat\size_t.h"\
+	"..\..\..\pjlib\include\pj\compat\stdarg.h"\
+	"..\..\..\pjlib\include\pj\compat\string.h"\
+	"..\..\..\pjlib\include\pj\config.h"\
+	"..\..\..\pjlib\include\pj\config_site.h"\
+	"..\..\..\pjlib\include\pj\config_site_sample.h"\
+	"..\..\..\pjlib\include\pj\ctype.h"\
+	"..\..\..\pjlib\include\pj\errno.h"\
+	"..\..\..\pjlib\include\pj\except.h"\
+	"..\..\..\pjlib\include\pj\fifobuf.h"\
+	"..\..\..\pjlib\include\pj\file_access.h"\
+	"..\..\..\pjlib\include\pj\file_io.h"\
+	"..\..\..\pjlib\include\pj\guid.h"\
+	"..\..\..\pjlib\include\pj\hash.h"\
+	"..\..\..\pjlib\include\pj\ioqueue.h"\
+	"..\..\..\pjlib\include\pj\ip_helper.h"\
+	"..\..\..\pjlib\include\pj\list.h"\
+	"..\..\..\pjlib\include\pj\list_i.h"\
+	"..\..\..\pjlib\include\pj\lock.h"\
+	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
+	"..\..\..\pjlib\include\pj\os.h"\
+	"..\..\..\pjlib\include\pj\pool.h"\
+	"..\..\..\pjlib\include\pj\pool_alt.h"\
+	"..\..\..\pjlib\include\pj\pool_buf.h"\
+	"..\..\..\pjlib\include\pj\pool_i.h"\
+	"..\..\..\pjlib\include\pj\rand.h"\
+	"..\..\..\pjlib\include\pj\rbtree.h"\
+	"..\..\..\pjlib\include\pj\sock.h"\
+	"..\..\..\pjlib\include\pj\sock_select.h"\
+	"..\..\..\pjlib\include\pj\string.h"\
+	"..\..\..\pjlib\include\pj\string_i.h"\
+	"..\..\..\pjlib\include\pj\timer.h"\
+	"..\..\..\pjlib\include\pj\types.h"\
+	"..\..\..\pjlib\include\pj\unicode.h"\
+	"..\..\..\pjlib\include\pjlib.h"\
+	"..\..\include\pjnath\config.h"\
+	"..\..\include\pjnath\errno.h"\
+	"..\..\include\pjnath\stun_auth.h"\
+	"..\..\include\pjnath\stun_config.h"\
+	"..\..\include\pjnath\stun_msg.h"\
+	"..\..\include\pjnath\stun_session.h"\
+	"..\..\include\pjnath\stun_sock.h"\
+	"..\..\include\pjnath\stun_transaction.h"\
+	"..\..\include\pjnath\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE x86) Debug"
+
+DEP_CPP_STUN_SO=\
+	"..\..\..\pjlib-util\include\pjlib-util\config.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\dns.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\srv_resolver.h"\
+	"..\..\..\pjlib-util\include\pjlib-util\types.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
+	"..\..\..\pjlib\include\pj\addr_resolv.h"\
+	"..\..\..\pjlib\include\pj\array.h"\
+	"..\..\..\pjlib\include\pj\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_armcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_codew.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcce.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_mwcc.h"\
+	"..\..\..\pjlib\include\pj\compat\ctype.h"\
+	"..\..\..\pjlib\include\pj\compat\errno.h"\
+	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
+	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
+	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
+	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_symbian.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
+	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
+	"..\..\..\pjlib\include\pj\compat\size_t.h"\
+	"..\..\..\pjlib\include\pj\compat\stdarg.h"\
+	"..\..\..\pjlib\include\pj\compat\string.h"\
+	"..\..\..\pjlib\include\pj\config.h"\
+	"..\..\..\pjlib\include\pj\config_site.h"\
+	"..\..\..\pjlib\include\pj\config_site_sample.h"\
+	"..\..\..\pjlib\include\pj\ctype.h"\
+	"..\..\..\pjlib\include\pj\errno.h"\
+	"..\..\..\pjlib\include\pj\except.h"\
+	"..\..\..\pjlib\include\pj\fifobuf.h"\
+	"..\..\..\pjlib\include\pj\file_access.h"\
+	"..\..\..\pjlib\include\pj\file_io.h"\
+	"..\..\..\pjlib\include\pj\guid.h"\
+	"..\..\..\pjlib\include\pj\hash.h"\
+	"..\..\..\pjlib\include\pj\ioqueue.h"\
+	"..\..\..\pjlib\include\pj\ip_helper.h"\
+	"..\..\..\pjlib\include\pj\list.h"\
+	"..\..\..\pjlib\include\pj\list_i.h"\
+	"..\..\..\pjlib\include\pj\lock.h"\
+	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
+	"..\..\..\pjlib\include\pj\os.h"\
+	"..\..\..\pjlib\include\pj\pool.h"\
+	"..\..\..\pjlib\include\pj\pool_alt.h"\
+	"..\..\..\pjlib\include\pj\pool_buf.h"\
+	"..\..\..\pjlib\include\pj\pool_i.h"\
+	"..\..\..\pjlib\include\pj\rand.h"\
+	"..\..\..\pjlib\include\pj\rbtree.h"\
+	"..\..\..\pjlib\include\pj\sock.h"\
+	"..\..\..\pjlib\include\pj\sock_select.h"\
+	"..\..\..\pjlib\include\pj\string.h"\
+	"..\..\..\pjlib\include\pj\string_i.h"\
+	"..\..\..\pjlib\include\pj\timer.h"\
+	"..\..\..\pjlib\include\pj\types.h"\
+	"..\..\..\pjlib\include\pj\unicode.h"\
+	"..\..\..\pjlib\include\pjlib.h"\
+	"..\..\include\pjnath\config.h"\
+	"..\..\include\pjnath\errno.h"\
+	"..\..\include\pjnath\stun_auth.h"\
+	"..\..\include\pjnath\stun_config.h"\
+	"..\..\include\pjnath\stun_msg.h"\
+	"..\..\include\pjnath\stun_session.h"\
+	"..\..\include\pjnath\stun_sock.h"\
+	"..\..\include\pjnath\stun_transaction.h"\
+	"..\..\include\pjnath\types.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\pjnath\stun_transaction.c
+
+!IF  "$(CFG)" == "pjnath_wince - Win32 (WCE emulator) Release"
+
+DEP_CPP_STUN_T=\
+	"..\..\..\pjlib\include\pj\addr_resolv.h"\
+	"..\..\..\pjlib\include\pj\array.h"\
+	"..\..\..\pjlib\include\pj\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
+	"..\..\..\pjlib\include\pj\compat\ctype.h"\
+	"..\..\..\pjlib\include\pj\compat\errno.h"\
+	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
+	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
+	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
+	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
+	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
+	"..\..\..\pjlib\include\pj\compat\size_t.h"\
+	"..\..\..\pjlib\include\pj\compat\string.h"\
+	"..\..\..\pjlib\include\pj\config.h"\
+	"..\..\..\pjlib\include\pj\config_site.h"\
+	"..\..\..\pjlib\include\pj\config_site_sample.h"\
+	"..\..\..\pjlib\include\pj\ctype.h"\
+	"..\..\..\pjlib\include\pj\errno.h"\
+	"..\..\..\pjlib\include\pj\except.h"\
+	"..\..\..\pjlib\include\pj\fifobuf.h"\
+	"..\..\..\pjlib\include\pj\file_access.h"\
+	"..\..\..\pjlib\include\pj\file_io.h"\
+	"..\..\..\pjlib\include\pj\guid.h"\
+	"..\..\..\pjlib\include\pj\hash.h"\
+	"..\..\..\pjlib\include\pj\ioqueue.h"\
+	"..\..\..\pjlib\include\pj\ip_helper.h"\
+	"..\..\..\pjlib\include\pj\list.h"\
+	"..\..\..\pjlib\include\pj\list_i.h"\
+	"..\..\..\pjlib\include\pj\lock.h"\
+	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\os.h"\
+	"..\..\..\pjlib\include\pj\pool.h"\
+	"..\..\..\pjlib\include\pj\pool_alt.h"\
+	"..\..\..\pjlib\include\pj\pool_buf.h"\
+	"..\..\..\pjlib\include\pj\pool_i.h"\
+	"..\..\..\pjlib\include\pj\rand.h"\
+	"..\..\..\pjlib\include\pj\rbtree.h"\
+	"..\..\..\pjlib\include\pj\sock.h"\
+	"..\..\..\pjlib\include\pj\sock_select.h"\
+	"..\..\..\pjlib\include\pj\string.h"\
+	"..\..\..\pjlib\include\pj\string_i.h"\
+	"..\..\..\pjlib\include\pj\timer.h"\
+	"..\..\..\pjlib\include\pj\types.h"\
+	"..\..\..\pjlib\include\pj\unicode.h"\
+	"..\..\..\pjlib\include\pjlib.h"\
+	"..\..\include\pjnath\config.h"\
+	"..\..\include\pjnath\errno.h"\
+	"..\..\include\pjnath\stun_config.h"\
+	"..\..\include\pjnath\stun_msg.h"\
+	"..\..\include\pjnath\stun_transaction.h"\
+	"..\..\include\pjnath\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE emulator) Debug"
+
+DEP_CPP_STUN_T=\
+	"..\..\..\pjlib\include\pj\addr_resolv.h"\
+	"..\..\..\pjlib\include\pj\array.h"\
+	"..\..\..\pjlib\include\pj\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_armcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_codew.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcce.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_mwcc.h"\
+	"..\..\..\pjlib\include\pj\compat\ctype.h"\
+	"..\..\..\pjlib\include\pj\compat\errno.h"\
+	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
+	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
+	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
+	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_symbian.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
+	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
+	"..\..\..\pjlib\include\pj\compat\size_t.h"\
+	"..\..\..\pjlib\include\pj\compat\stdarg.h"\
+	"..\..\..\pjlib\include\pj\compat\string.h"\
+	"..\..\..\pjlib\include\pj\config.h"\
+	"..\..\..\pjlib\include\pj\config_site.h"\
+	"..\..\..\pjlib\include\pj\config_site_sample.h"\
+	"..\..\..\pjlib\include\pj\ctype.h"\
+	"..\..\..\pjlib\include\pj\errno.h"\
+	"..\..\..\pjlib\include\pj\except.h"\
+	"..\..\..\pjlib\include\pj\fifobuf.h"\
+	"..\..\..\pjlib\include\pj\file_access.h"\
+	"..\..\..\pjlib\include\pj\file_io.h"\
+	"..\..\..\pjlib\include\pj\guid.h"\
+	"..\..\..\pjlib\include\pj\hash.h"\
+	"..\..\..\pjlib\include\pj\ioqueue.h"\
+	"..\..\..\pjlib\include\pj\ip_helper.h"\
+	"..\..\..\pjlib\include\pj\list.h"\
+	"..\..\..\pjlib\include\pj\list_i.h"\
+	"..\..\..\pjlib\include\pj\lock.h"\
+	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\os.h"\
+	"..\..\..\pjlib\include\pj\pool.h"\
+	"..\..\..\pjlib\include\pj\pool_alt.h"\
+	"..\..\..\pjlib\include\pj\pool_buf.h"\
+	"..\..\..\pjlib\include\pj\pool_i.h"\
+	"..\..\..\pjlib\include\pj\rand.h"\
+	"..\..\..\pjlib\include\pj\rbtree.h"\
+	"..\..\..\pjlib\include\pj\sock.h"\
+	"..\..\..\pjlib\include\pj\sock_select.h"\
+	"..\..\..\pjlib\include\pj\string.h"\
+	"..\..\..\pjlib\include\pj\string_i.h"\
+	"..\..\..\pjlib\include\pj\timer.h"\
+	"..\..\..\pjlib\include\pj\types.h"\
+	"..\..\..\pjlib\include\pj\unicode.h"\
+	"..\..\..\pjlib\include\pjlib.h"\
+	"..\..\include\pjnath\config.h"\
+	"..\..\include\pjnath\errno.h"\
+	"..\..\include\pjnath\stun_config.h"\
+	"..\..\include\pjnath\stun_msg.h"\
+	"..\..\include\pjnath\stun_transaction.h"\
+	"..\..\include\pjnath\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4I) Release"
+
+DEP_CPP_STUN_T=\
+	"..\..\..\pjlib\include\pj\addr_resolv.h"\
+	"..\..\..\pjlib\include\pj\array.h"\
+	"..\..\..\pjlib\include\pj\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
+	"..\..\..\pjlib\include\pj\compat\ctype.h"\
+	"..\..\..\pjlib\include\pj\compat\errno.h"\
+	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
+	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
+	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
+	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
+	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
+	"..\..\..\pjlib\include\pj\compat\size_t.h"\
+	"..\..\..\pjlib\include\pj\compat\string.h"\
+	"..\..\..\pjlib\include\pj\config.h"\
+	"..\..\..\pjlib\include\pj\config_site.h"\
+	"..\..\..\pjlib\include\pj\config_site_sample.h"\
+	"..\..\..\pjlib\include\pj\ctype.h"\
+	"..\..\..\pjlib\include\pj\errno.h"\
+	"..\..\..\pjlib\include\pj\except.h"\
+	"..\..\..\pjlib\include\pj\fifobuf.h"\
+	"..\..\..\pjlib\include\pj\file_access.h"\
+	"..\..\..\pjlib\include\pj\file_io.h"\
+	"..\..\..\pjlib\include\pj\guid.h"\
+	"..\..\..\pjlib\include\pj\hash.h"\
+	"..\..\..\pjlib\include\pj\ioqueue.h"\
+	"..\..\..\pjlib\include\pj\ip_helper.h"\
+	"..\..\..\pjlib\include\pj\list.h"\
+	"..\..\..\pjlib\include\pj\list_i.h"\
+	"..\..\..\pjlib\include\pj\lock.h"\
+	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\os.h"\
+	"..\..\..\pjlib\include\pj\pool.h"\
+	"..\..\..\pjlib\include\pj\pool_alt.h"\
+	"..\..\..\pjlib\include\pj\pool_buf.h"\
+	"..\..\..\pjlib\include\pj\pool_i.h"\
+	"..\..\..\pjlib\include\pj\rand.h"\
+	"..\..\..\pjlib\include\pj\rbtree.h"\
+	"..\..\..\pjlib\include\pj\sock.h"\
+	"..\..\..\pjlib\include\pj\sock_select.h"\
+	"..\..\..\pjlib\include\pj\string.h"\
+	"..\..\..\pjlib\include\pj\string_i.h"\
+	"..\..\..\pjlib\include\pj\timer.h"\
+	"..\..\..\pjlib\include\pj\types.h"\
+	"..\..\..\pjlib\include\pj\unicode.h"\
+	"..\..\..\pjlib\include\pjlib.h"\
+	"..\..\include\pjnath\config.h"\
+	"..\..\include\pjnath\errno.h"\
+	"..\..\include\pjnath\stun_config.h"\
+	"..\..\include\pjnath\stun_msg.h"\
+	"..\..\include\pjnath\stun_transaction.h"\
+	"..\..\include\pjnath\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4I) Debug"
+
+DEP_CPP_STUN_T=\
+	"..\..\..\pjlib\include\pj\addr_resolv.h"\
+	"..\..\..\pjlib\include\pj\array.h"\
+	"..\..\..\pjlib\include\pj\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
+	"..\..\..\pjlib\include\pj\compat\ctype.h"\
+	"..\..\..\pjlib\include\pj\compat\errno.h"\
+	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
+	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
+	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
+	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
+	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
+	"..\..\..\pjlib\include\pj\compat\size_t.h"\
+	"..\..\..\pjlib\include\pj\compat\string.h"\
+	"..\..\..\pjlib\include\pj\config.h"\
+	"..\..\..\pjlib\include\pj\config_site.h"\
+	"..\..\..\pjlib\include\pj\config_site_sample.h"\
+	"..\..\..\pjlib\include\pj\ctype.h"\
+	"..\..\..\pjlib\include\pj\errno.h"\
+	"..\..\..\pjlib\include\pj\except.h"\
+	"..\..\..\pjlib\include\pj\fifobuf.h"\
+	"..\..\..\pjlib\include\pj\file_access.h"\
+	"..\..\..\pjlib\include\pj\file_io.h"\
+	"..\..\..\pjlib\include\pj\guid.h"\
+	"..\..\..\pjlib\include\pj\hash.h"\
+	"..\..\..\pjlib\include\pj\ioqueue.h"\
+	"..\..\..\pjlib\include\pj\ip_helper.h"\
+	"..\..\..\pjlib\include\pj\list.h"\
+	"..\..\..\pjlib\include\pj\list_i.h"\
+	"..\..\..\pjlib\include\pj\lock.h"\
+	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\os.h"\
+	"..\..\..\pjlib\include\pj\pool.h"\
+	"..\..\..\pjlib\include\pj\pool_alt.h"\
+	"..\..\..\pjlib\include\pj\pool_buf.h"\
+	"..\..\..\pjlib\include\pj\pool_i.h"\
+	"..\..\..\pjlib\include\pj\rand.h"\
+	"..\..\..\pjlib\include\pj\rbtree.h"\
+	"..\..\..\pjlib\include\pj\sock.h"\
+	"..\..\..\pjlib\include\pj\sock_select.h"\
+	"..\..\..\pjlib\include\pj\string.h"\
+	"..\..\..\pjlib\include\pj\string_i.h"\
+	"..\..\..\pjlib\include\pj\timer.h"\
+	"..\..\..\pjlib\include\pj\types.h"\
+	"..\..\..\pjlib\include\pj\unicode.h"\
+	"..\..\..\pjlib\include\pjlib.h"\
+	"..\..\include\pjnath\config.h"\
+	"..\..\include\pjnath\errno.h"\
+	"..\..\include\pjnath\stun_config.h"\
+	"..\..\include\pjnath\stun_msg.h"\
+	"..\..\include\pjnath\stun_transaction.h"\
+	"..\..\include\pjnath\types.h"\
+	
+
+!ELSEIF  "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_STUN_T=\
+	"..\..\..\pjlib\include\pj\activesock.h"\
+	"..\..\..\pjlib\include\pj\addr_resolv.h"\
+	"..\..\..\pjlib\include\pj\array.h"\
+	"..\..\..\pjlib\include\pj\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\assert.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_armcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_codew.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_gcce.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_msvc.h"\
+	"..\..\..\pjlib\include\pj\compat\cc_mwcc.h"\
+	"..\..\..\pjlib\include\pj\compat\ctype.h"\
+	"..\..\..\pjlib\include\pj\compat\errno.h"\
+	"..\..\..\pjlib\include\pj\compat\high_precision.h"\
+	"..\..\..\pjlib\include\pj\compat\m_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_auto.h"\
+	"..\..\..\pjlib\include\pj\compat\os_darwinos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux.h"\
+	"..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\
+	"..\..\..\pjlib\include\pj\compat\os_palmos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_rtems.h"\
+	"..\..\..\pjlib\include\pj\compat\os_sunos.h"\
+	"..\..\..\pjlib\include\pj\compat\os_symbian.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32.h"\
+	"..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\
+	"..\..\..\pjlib\include\pj\compat\setjmp.h"\
+	"..\..\..\pjlib\include\pj\compat\size_t.h"\
+	"..\..\..\pjlib\include\pj\compat\stdarg.h"\
+	"..\..\..\pjlib\include\pj\compat\string.h"\
+	"..\..\..\pjlib\include\pj\config.h"\
+	"..\..\..\pjlib\include\pj\config_site.h"\
+	"..\..\..\pjlib\include\pj\config_site_sample.h"\
+	"..\..\..\pjlib\include\pj\ctype.h"\
+	"..\..\..\pjlib\include\pj\errno.h"\
+	"..\..\..\pjlib\include\pj\except.h"\
+	"..\..\..\pjlib\include\pj\fifobuf.h"\
+	"..\..\..\pjlib\include\pj\file_access.h"\
+	"..\..\..\pjlib\include\pj\file_io.h"\
+	"..\..\..\pjlib\include\pj\guid.h"\
+	"..\..\..\pjlib\include\pj\hash.h"\
+	"..\..\..\pjlib\include\pj\ioqueue.h"\
+	"..\..\..\pjlib\include\pj\ip_helper.h"\
+	"..\..\..\pjlib\include\pj\list.h"\
+	"..\..\..\pjlib\include\pj\list_i.h"\
+	"..\..\..\pjlib\include\pj\lock.h"\
+	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
 	"..\..\..\pjlib\include\pj\os.h"\
 	"..\..\..\pjlib\include\pj\pool.h"\
@@ -6927,4 +7765,5 @@
 	"..\..\..\pjlib-util\include\pjlib-util\srv_resolver.h"\
 	"..\..\..\pjlib-util\include\pjlib-util\types.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
 	"..\..\..\pjlib\include\pj\addr_resolv.h"\
 	"..\..\..\pjlib\include\pj\array.h"\
@@ -6972,4 +7811,5 @@
 	"..\..\..\pjlib\include\pj\lock.h"\
 	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
 	"..\..\..\pjlib\include\pj\os.h"\
 	"..\..\..\pjlib\include\pj\pool.h"\
@@ -7719,4 +8559,5 @@
 	"..\..\..\pjlib-util\include\pjlib-util\resolver.h"\
 	"..\..\..\pjlib-util\include\pjlib-util\types.h"\
+	"..\..\..\pjlib\include\pj\activesock.h"\
 	"..\..\..\pjlib\include\pj\addr_resolv.h"\
 	"..\..\..\pjlib\include\pj\array.h"\
@@ -7764,4 +8605,5 @@
 	"..\..\..\pjlib\include\pj\lock.h"\
 	"..\..\..\pjlib\include\pj\log.h"\
+	"..\..\..\pjlib\include\pj\math.h"\
 	"..\..\..\pjlib\include\pj\os.h"\
 	"..\..\..\pjlib\include\pj\pool.h"\
Index: pjproject/trunk/pjnath/include/pjnath.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath.h	(revision 1987)
+++ pjproject/trunk/pjnath/include/pjnath.h	(revision 1988)
@@ -27,4 +27,5 @@
 #include <pjnath/stun_msg.h>
 #include <pjnath/stun_session.h>
+#include <pjnath/stun_sock.h>
 #include <pjnath/stun_transaction.h>
 #include <pjnath/turn_session.h>
Index: pjproject/trunk/pjnath/include/pjnath/config.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/config.h	(revision 1987)
+++ pjproject/trunk/pjnath/include/pjnath/config.h	(revision 1988)
@@ -143,4 +143,82 @@
 
 /* **************************************************************************
+ * STUN TRANSPORT CONFIGURATION
+ */
+
+/**
+ * The packet buffer size for the STUN transport.
+ */
+#ifndef PJ_STUN_SOCK_PKT_LEN
+#   define PJ_STUN_SOCK_PKT_LEN			    2000
+#endif
+
+
+/**
+ * The duration of the STUN keep-alive period, in seconds.
+ */
+#ifndef PJ_STUN_KEEP_ALIVE_SEC
+#   define PJ_STUN_KEEP_ALIVE_SEC		    15
+#endif
+
+
+/* **************************************************************************
+ * TURN CONFIGURATION
+ */
+
+/**
+ * Maximum DNS SRV entries to be processed in the DNS SRV response
+ */
+#ifndef PJ_TURN_MAX_DNS_SRV_CNT
+#   define PJ_TURN_MAX_DNS_SRV_CNT		    4
+#endif
+
+
+/**
+ * Maximum TURN packet size to be supported.
+ */
+#ifndef PJ_TURN_MAX_PKT_LEN
+#   define PJ_TURN_MAX_PKT_LEN			    3000
+#endif
+
+
+/**
+ * The TURN permission lifetime setting. This value should be taken from the
+ * TURN protocol specification.
+ */
+#ifndef PJ_TURN_PERM_TIMEOUT
+#   define PJ_TURN_PERM_TIMEOUT			    300
+#endif
+
+
+/**
+ * The TURN channel binding lifetime. This value should be taken from the
+ * TURN protocol specification.
+ */
+#ifndef PJ_TURN_CHANNEL_TIMEOUT
+#   define PJ_TURN_CHANNEL_TIMEOUT		    600
+#endif
+
+
+/**
+ * Number of seconds to refresh the permission/channel binding before the 
+ * permission/channel binding expires. This value should be greater than 
+ * PJ_TURN_PERM_TIMEOUT setting.
+ */
+#ifndef PJ_TURN_REFRESH_SEC_BEFORE
+#   define PJ_TURN_REFRESH_SEC_BEFORE		    60
+#endif
+
+
+/**
+ * The TURN session timer heart beat interval. When this timer occurs, the 
+ * TURN session will scan all the permissions/channel bindings to see which
+ * need to be refreshed.
+ */
+#ifndef PJ_TURN_KEEP_ALIVE_SEC
+#   define PJ_TURN_KEEP_ALIVE_SEC		    15
+#endif
+
+
+/* **************************************************************************
  * ICE CONFIGURATION
  */
Index: pjproject/trunk/pjnath/include/pjnath/errno.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/errno.h	(revision 1987)
+++ pjproject/trunk/pjnath/include/pjnath/errno.h	(revision 1988)
@@ -122,4 +122,13 @@
 
 
+/************************************************************
+ * STUN SESSION/TRANSPORT ERROR CODES
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * STUN object has been destoyed.
+ */
+#define PJNATH_ESTUNDESTROYED	    (PJNATH_ERRNO_START+60) /* 370060 */
+
 
 /************************************************************
Index: pjproject/trunk/pjnath/include/pjnath/ice_session.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/ice_session.h	(revision 1987)
+++ pjproject/trunk/pjnath/include/pjnath/ice_session.h	(revision 1988)
@@ -130,4 +130,9 @@
 
 /**
+ * Forward declaration for checklist.
+ */
+typedef struct pj_ice_sess_checklist pj_ice_sess_checklist;
+
+/**
  * This enumeration describes the type of an ICE candidate.
  */
@@ -192,4 +197,22 @@
 
 } pj_ice_sess_comp;
+
+
+/**
+ * Data structure to be attached to internal message processing.
+ */
+typedef struct pj_ice_msg_data
+{
+    unsigned	transport_id;
+    pj_bool_t	has_req_data;
+
+    union data {
+	struct request_data {
+	    pj_ice_sess		    *ice;
+	    pj_ice_sess_checklist   *clist;
+	    unsigned		     ckid;
+	} req;
+    } data;
+} pj_ice_msg_data;
 
 
@@ -205,14 +228,32 @@
 {
     /**
+     * The candidate type, as described in #pj_ice_cand_type enumeration.
+     */
+    pj_ice_cand_type	 type;
+
+    /** 
+     * Status of this candidate. The value will be PJ_SUCCESS if candidate
+     * address has been resolved successfully, PJ_EPENDING when the address
+     * resolution process is in progress, or other value when the address 
+     * resolution has completed with failure.
+     */
+    pj_status_t		 status;
+
+    /**
      * The component ID of this candidate. Note that component IDs starts
      * with one for RTP and two for RTCP. In other words, it's not zero
      * based.
      */
-    pj_uint32_t		 comp_id;
-
-    /**
-     * The candidate type, as described in #pj_ice_cand_type enumeration.
-     */
-    pj_ice_cand_type	 type;
+    pj_uint8_t		 comp_id;
+
+    /**
+     * Transport ID to be used to send packets for this candidate.
+     */
+    pj_uint8_t		 transport_id;
+
+    /**
+     * Local preference value, which typically is 65535.
+     */
+    pj_uint16_t		 local_pref;
 
     /**
@@ -384,5 +425,5 @@
  * candidate pairs that an agent will use to generate checks.
  */
-typedef struct pj_ice_sess_checklist
+struct pj_ice_sess_checklist
 {
     /**
@@ -406,5 +447,5 @@
     pj_timer_entry	     timer;
 
-} pj_ice_sess_checklist;
+};
 
 
@@ -431,4 +472,5 @@
      * @param ice	    The ICE session.
      * @param comp_id	    ICE component ID.
+     * @param transport_id  Transport ID.
      * @param pkt	    The STUN packet.
      * @param size	    The size of the packet.
@@ -437,4 +479,5 @@
      */
     pj_status_t (*on_tx_pkt)(pj_ice_sess *ice, unsigned comp_id, 
+			     unsigned transport_id,
 			     const void *pkt, pj_size_t size,
 			     const pj_sockaddr_t *dst_addr,
@@ -447,4 +490,5 @@
      * @param ice	    The ICE session.
      * @param comp_id	    ICE component ID.
+     * @param transport_id  Transport ID.
      * @param pkt	    The whole packet.
      * @param size	    Size of the packet.
@@ -454,4 +498,5 @@
      */
     void	(*on_rx_data)(pj_ice_sess *ice, unsigned comp_id,
+			      unsigned transport_id, 
 			      void *pkt, pj_size_t size,
 			      const pj_sockaddr_t *src_addr,
@@ -497,4 +542,5 @@
 
     unsigned		 comp_id;	/**< Component ID.		*/
+    unsigned		 transport_id;	/**< Transport ID.		*/
 
     pj_sockaddr		 src_addr;	/**< Source address of request	*/
@@ -553,4 +599,7 @@
     unsigned		 rcand_cnt;		    /**< # of remote cand.  */
     pj_ice_sess_cand	 rcand[PJ_ICE_MAX_CAND];    /**< Array of cand.	    */
+
+    /* Array of transport datas */
+    pj_ice_msg_data	 tp_data[4];
 
     /* List of eearly checks */
@@ -580,4 +629,15 @@
  */
 PJ_DECL(const char*) pj_ice_get_cand_type_name(pj_ice_cand_type type);
+
+
+/**
+ * This is a utility function to retrieve the string name for the
+ * particular role type.
+ *
+ * @param role		Role type.
+ *
+ * @return		The string representation of the role.
+ */
+PJ_DECL(const char*) pj_ice_sess_role_name(pj_ice_sess_role role);
 
 
@@ -686,4 +746,6 @@
  * @param ice		ICE session instance.
  * @param comp_id	Component ID of this candidate.
+ * @param transport_id	Transport ID to be used to send packets for this
+ *			candidate.
  * @param type		Candidate type.
  * @param local_pref	Local preference for this candidate, which
@@ -700,4 +762,5 @@
 PJ_DECL(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice,
 					  unsigned comp_id,
+					  unsigned transport_id,
 					  pj_ice_cand_type type,
 					  pj_uint16_t local_pref,
@@ -798,4 +861,7 @@
  * @param ice		The ICE session.
  * @param comp_id	Component ID.
+ * @param transport_id	Number to identify where this packet was received
+ *			from. This parameter will be returned back to
+ *			application in \a on_tx_pkt() callback.
  * @param pkt		Incoming packet.
  * @param pkt_size	Size of incoming packet.
@@ -807,4 +873,5 @@
 PJ_DECL(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
 					   unsigned comp_id,
+					   unsigned transport_id,
 					   void *pkt,
 					   pj_size_t pkt_size,
Index: pjproject/trunk/pjnath/include/pjnath/ice_strans.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/ice_strans.h	(revision 1987)
+++ pjproject/trunk/pjnath/include/pjnath/ice_strans.h	(revision 1988)
@@ -26,4 +26,6 @@
  */
 #include <pjnath/ice_session.h>
+#include <pjnath/stun_sock.h>
+#include <pjnath/turn_sock.h>
 #include <pjlib-util/resolver.h>
 #include <pj/ioqueue.h>
@@ -45,5 +47,5 @@
  *
  * ICE stream transport, as represented by #pj_ice_strans structure, is an ICE
- * capable component for transporting media streams within a media session. 
+ * capable class for transporting media streams within a media session. 
  * It consists of one or more transport sockets (typically two for RTP
  * based communication - one for RTP and one for RTCP), and an 
@@ -51,128 +53,19 @@
  * various candidates of the transport addresses.
  *
- * \section PJNATH_ICE_STREAM_TRANSPORT_USING Using the ICE Stream Transport
- *
- * Application may use the ICE stream transport in two ways:
- *  - it can create the ICE stream transports once during application 
- *    initialization and keep them alive throughout application lifetime, or
- *  - it can create and destroy the ICE stream transport as needed everytime 
- *     a call is made and destroyed. 
- *
- * Keeping the ICE stream transport alive throughout
- * application's lifetime is normally preferable, as initializing the
- * ICE stream transport may incur delay because the ICE stream transport
- * would need to communicate with the STUN/TURN server to get the
- * server reflexive and relayed candidates for the transports.
- *
- * Regardless of which usage scenario is being used, the ICE stream
- * transport is capable for restarting the ICE session being used and to
- * send STUN keep-alives for its STUN server reflexive and relayed
- * candidates. When ICE stream transport detects that the STUN mapped
- * address has changed in the keep-alive response, it will automatically
- * update its address to the new address, and notify the application via
- * \a on_addr_change() function of the #pj_ice_strans_cb callback.
- *
- * \subsection PJNATH_ICE_ST_TRA_INIT Initialization
- *
- * Application creates the ICE stream transport by calling 
- * #pj_ice_strans_create() function. Among other things, application needs
- * to specify:
- *	- STUN configuration (pj_stun_config), containing STUN settings
- *	  such as timeout values and the instances of timer heap and
- *	  ioqueue.
- *	- Session name, useful for identifying this session in the log.
- *	- Number of ICE components.
- *	- Arbitrary user data, useful when associating the ICE session
- *	  with some application's data structure.
- *	- A callback (#pj_ice_strans_cb) to receive events from the ICE
- *	  stream transport. Two of the most important fields in this
- *	  callback structure are \a on_rx_data() to notify application
- *	  about incoming data (perhaps RTP or RTCP packet), and
- *	  \a on_ice_complete() to notify application that ICE negotiation
- *	  has completed, either successfully or with failure.
- *
- * After the ICE stream transport is created, application may set up the
- * STUN servers to be used to obtain STUN server reflexive and relayed
- * candidate, by calling #pj_ice_strans_set_stun_domain() or 
- * #pj_ice_strans_set_stun_srv().
- *
- * Application then creates each component by calling 
- * #pj_ice_strans_create_comp(); this would create an actual socket
- * which listens to the specified local address, and it would also
- * perform lookup to find various transport address candidates for this
- * socket.
- *
- * Adding component may involve contacting STUN and TURN servers to get
- * STUN mapped address and allocate TURN relay channel, and this process
- * may take some time to complete. Once application has added all
- * components, it can check whether server reflexive and relayed 
- * candidates have been acquired, by calling #pj_ice_strans_get_comps_status().
- * 
- * \subsection PJNATH_ICE_ST_TRA_INIT_ICE Starting ICE Session
- *
- * When application is about to send an offer containing ICE capability,
- * or when it receives an offer containing ICE capability, it would
- * create the ICE session by calling #pj_ice_strans_init_ice(). This would
- * register all transport address aliases for each component to the ICE
- * session as candidates. After this application can enumerate all local
- * candidates by calling #pj_ice_strans_enum_cands(), and encode these
- * candidates in the SDP to be sent to remote agent.
- *
- * \subsection PJNATH_ICE_ST_TRA_START Starting Connectivity Checks
- *
- * Once application receives the SDP from remote, it pairs local candidates
- * with remote candidates, and can start ICE connectivity checks. This is
- * done by calling #pj_ice_strans_start_ice(), specifying
- * the remote candidate list, and remote username and password. If the
- * pairing process is successful, ICE connectivity checks will begin
- * immediately. The ICE session/transport will then notify the application 
- * via the callback when ICE connectivity checks completes, either 
- * successfully or with failure.
- *
- * \subsection PJNATH_ICE_ST_TRA_SEND_RECV Sending and Receiving Data
- *
- * Application can send data (normally RTP or RTCP packets) at any time
- * by calling #pj_ice_strans_sendto(). This function takes a destination
- * address as one of the arguments, and this destination address should
- * be taken from the default transport address of the component (that is
- * the address in SDP c= and m= lines, or in a=rtcp attribute). 
- * If ICE negotiation is in progress, this function will send the data 
- * to the destination address. Otherwise if ICE negotiation has completed
- * successfully, this function will send the data to the nominated remote 
- * address, as negotiated by ICE.
- *
- * Upon receiving incoming data (that is a non-STUN  message), the ICE
- * stream transport will notify the application by calling \a on_rx_data()
- * of the #pj_ice_strans_cb callback.
- *
- * \subsection PJNATH_ICE_ST_TRA_STOP Stopping ICE Session
- *
- * Once the call is terminated, application no longer needs to keep the
- * ICE session, so it should call #pj_ice_strans_stop_ice() to destroy the
- * ICE session within this ICE stream transport. Note that this WILL NOT
- * destroy the sockets/transports, it only destroys the ICE session
- * within this ICE stream transport. It is recommended that application 
- * retains the ICE stream transport to speed up the process of setting up
- * the next call. The ICE stream transport will continue to send STUN 
- * keep-alive packets to keep the NAT binding open and to detect change 
- * in STUN mapped address.
- *
- * \subsection PJNATH_ICE_ST_TRA_RESTART Restarting ICE Session
- *
- * When a new call is made, application can repeat the above
- * #pj_ice_strans_init_ice() to #pj_ice_strans_stop_ice() cycle for 
- * the new call, using this same ICE stream transport.
- *
- * \subsection PJNATH_ICE_ST_TRA_DESTROY Destroying ICE Stream Transport
- *
- * Finally, when the ICE stream transport itself is no longer needed,
- * for example when the application quits, application should call
- * #pj_ice_strans_destroy() to release back all resources allocated by this
- * ICE stream transport.
- *
  */
 
 /** Forward declaration for ICE stream transport. */
 typedef struct pj_ice_strans pj_ice_strans;
+
+/** Transport operation types to be reported on \a on_status() callback */
+typedef enum pj_ice_strans_op
+{
+    /** Initialization (candidate gathering) */
+    PJ_ICE_STRANS_OP_INIT,
+
+    /** Negotiation */
+    PJ_ICE_STRANS_OP_NEGOTIATION
+
+} pj_ice_strans_op;
 
 /** 
@@ -201,193 +94,198 @@
 
     /**
-     * This callback will be called when ICE checks have completed.
-     * This callback is optional.
+     * Callback to report status.
      * 
      * @param ice_st	    The ICE stream transport.
-     * @param status	    The ICE connectivity check status.
+     * @param op	    The operation
+     * @param status	    Operation status.
      */
     void    (*on_ice_complete)(pj_ice_strans *ice_st, 
+			       pj_ice_strans_op op,
 			       pj_status_t status);
 
-    /**
-     * This callback will be called when ICE transport has detected that
-     * the STUN mapped address of a candidate has changed.
+} pj_ice_strans_cb;
+
+
+/**
+ * This structure describes ICE stream transport configuration. Application
+ * should initialize the structure by calling #pj_ice_strans_cfg_default()
+ * before changing the settings.
+ */
+typedef struct pj_ice_strans_cfg
+{
+    /**
+     * Address family, IPv4 or IPv6. Currently only pj_AF_INET() (IPv4)
+     * is supported, and this is the default value.
+     */
+    int			af;
+
+    /**
+     * STUN configuration which contains the timer heap and
+     * ioqueue instance to be used, and STUN retransmission
+     * settings. This setting is mandatory.
      *
-     * @param ice_st	    The ICE stream transport.
-     * @param comp_id	    Component ID.
-     * @param cand_id	    Candidate ID.
-     */
-    void    (*on_addr_change)(pj_ice_strans *ice_st, 
-			      unsigned comp_id, 
-			      unsigned cand_id);
-			      
-} pj_ice_strans_cb;
-
-
-/**
- * Various flags that can be specified when creating a component with
- * #pj_ice_strans_create_comp(). These options may be combined together
- * with bitmask operation.
- */
-enum pj_ice_strans_option
-{
-    /**
-     * If this option is specified, only a listening socket will be
-     * created for the component, and no candidate will be added to
-     * the component. Application must add the component manually
-     * by inspecting the socket and transport address of the component.
-     */
-    PJ_ICE_ST_OPT_DONT_ADD_CAND = 1,
-
-    /**
-     * If this option is specified, then no STUN reflexive candidate
-     * will be added to the component.
-     */
-    PJ_ICE_ST_OPT_DISABLE_STUN	= 2,
-
-    /**
-     * If this option is specified, then no STUN relay candidate
-     * will be added to the component.
-     */
-    PJ_ICE_ST_OPT_DISABLE_RELAY	= 4,
-
-    /**
-     * If this option is specified, then when the function fails to
-     * bind the socket to the specified port, it WILL NOT try to
-     * bind the socket to the next available port.
+     * The default value is all zero. Application must initialize
+     * this setting with #pj_stun_config_init().
+     */
+    pj_stun_config	 stun_cfg;
+
+    /**
+     * DNS resolver to be used to resolve servers. If DNS SRV
+     * resolution is required, the resolver must be set.
      *
-     * If this option is NOT specified, then the function will try to
-     * bind the socket to next port+2, repetitively until the socket
-     * is bound successfully.
-     */
-    PJ_ICE_ST_OPT_NO_PORT_RETRY	= 8,
-};
-
-
-/**
- * This structure describes ICE stream transport candidate. A "candidate"
- * in ICE stream transport can be viewed as alias transport address
- * for the socket.
- */
-typedef struct pj_ice_strans_cand
-{
-    /**
-     * Candidate type.
-     */
-    pj_ice_cand_type	type;
-
-    /** 
-     * Status of this candidate. This status is useful for ICE reflexive
-     * and relay candidate, where the address needs to be resolved 
-     * asynchronously by sending STUN request to STUN server.
-     *
-     * The value will be PJ_SUCCESS if candidate address has been resolved
-     * successfully, PJ_EPENDING when the address resolution process is
-     * in progress, or other value when the address resolution has
-     * completed with failure.
-     */
-    pj_status_t		status;
-
-    /**
-     * The candidate transport address.
-     */
-    pj_sockaddr		addr;
-
-    /**
-     * The ICE session candidate ID after this candidate has been registered
-     * to an ICE session. Before ICE session is created, or after ICE
-     * session has been destroyed, the value will be -1.
-     */
-    int			ice_cand_id;
-
-    /**
-     * Local preference value, which typically is 65535.
-     */
-    pj_uint16_t		local_pref;
-
-    /**
-     * Foundation associated with this candidate, which value normally will be
-     * calculated by the function.
-     */
-    pj_str_t		foundation;
-
-} pj_ice_strans_cand;
-
-
-/**
- * This structure describes an ICE stream transport component. A component
- * in ICE stream transport typically corresponds to a single socket created
- * for this component, and bound to a specific transport address. This
- * component may have multiple alias addresses, for example one alias 
- * address for each interfaces in multi-homed host, another for server
- * reflexive alias, and another for relayed alias. For each transport
- * address alias, an ICE stream transport candidate (#pj_ice_strans_cand) will
- * be created, and these candidates will eventually registered to the ICE
- * session.
- */
-typedef struct pj_ice_strans_comp
-{
-    pj_ice_strans	*ice_st;	/**< ICE stream transport.	*/
-    unsigned		 comp_id;	/**< Component ID.		*/
-    pj_uint32_t		 options;	/**< Option flags.		*/
-    pj_sock_t		 sock;		/**< Socket descriptor.		*/
-
-    pj_stun_session	*stun_sess;	/**< STUN session.		*/
-    pj_uint8_t		 ka_tsx_id[12];	/**< ID for keep STUN alives	*/
-
-    pj_sockaddr		 local_addr;	/**< Local/base address.	*/
-
-    unsigned		 pending_cnt;	/**< Pending resolution cnt.	*/
-    pj_status_t		 last_status;	/**< Last status.		*/
-
-    unsigned		 cand_cnt;	/**< # of candidates/aliaes.	*/
-    pj_ice_strans_cand	 cand_list[PJ_ICE_ST_MAX_CAND];	/**< Cand array	*/
-    int			 default_cand;	/**< Default candidate selected	*/
-
-    pj_ioqueue_key_t	*key;		/**< ioqueue key.		*/
-    pj_uint8_t		 pkt[1500];	/**< Incoming packet buffer.	*/
-    pj_ioqueue_op_key_t	 read_op;	/**< ioqueue read operation key	*/
-    pj_ioqueue_op_key_t	 write_op;	/**< ioqueue write op. key	*/
-    pj_sockaddr		 src_addr;	/**< source packet address buf.	*/
-    int			 src_addr_len;	/**< length of src addr. buf.	*/
-
-} pj_ice_strans_comp;
-
-
-/**
- * This structure represents the ICE stream transport.
- */
-struct pj_ice_strans
-{
-    char		     obj_name[PJ_MAX_OBJ_NAME];	/**< Log ID.	*/
-
-    pj_pool_t		    *pool;	/**< Pool used by this object.	*/
-    void		    *user_data;	/**< Application data.		*/
-    pj_stun_config	     stun_cfg;	/**< STUN settings.		*/
-    pj_ice_strans_cb	     cb;	/**< Application callback.	*/
-
-    pj_ice_sess		    *ice;	/**< ICE session.		*/
-
-    unsigned		     comp_cnt;	/**< Number of components.	*/
-    pj_ice_strans_comp	   **comp;	/**< Components array.		*/
-
-    pj_dns_resolver	    *resolver;	/**< The resolver instance.	*/
-    pj_bool_t		     has_rjob;	/**< Has pending resolve?	*/
-    pj_sockaddr_in	     stun_srv;	/**< STUN server address.	*/
-    pj_sockaddr_in	     turn_srv;	/**< TURN server address.	*/
-
-    pj_timer_entry	     ka_timer;	/**< STUN keep-alive timer.	*/
-};
-
-
-/**
- * Create the ICE stream transport containing the specified number of
- * components. After the ICE stream transport is created, application
- * may initialize the STUN server settings, and after that it has to 
- * initialize each components by calling #pj_ice_strans_create_comp()
- * function.
- *
- * @param stun_cfg	The STUN settings.
+     * The default value is NULL.
+     */
+    pj_dns_resolver	*resolver;
+
+    /**
+     * STUN and local transport settings. This specifies the 
+     * settings for local UDP socket, which will be resolved
+     * to get the STUN mapped address.
+     */
+    struct {
+	/**
+	 * Optional configuration for STUN transport. The default
+	 * value will be initialized with #pj_stun_sock_cfg_default().
+	 */
+	pj_stun_sock_cfg     cfg;
+
+	/**
+	 * Disable host candidates. When this option is set, no
+	 * host candidates will be added.
+	 *
+	 * Default: PJ_FALSE
+	 */
+	pj_bool_t	     no_host_cands;
+
+	/**
+	 * Include loopback addresses in the host candidates.
+	 *
+	 * Default: PJ_FALSE
+	 */
+	pj_bool_t	     loop_addr;
+
+	/**
+	 * Specify the STUN server domain or hostname or IP address.
+	 * If DNS SRV resolution is required, application must fill
+	 * in this setting with the domain name of the STUN server 
+	 * and set the resolver instance in the \a resolver field.
+	 * Otherwise if the \a resolver setting is not set, this
+	 * field will be resolved with hostname resolution and in
+	 * this case the \a port field must be set.
+	 *
+	 * The \a port field should also be set even when DNS SRV
+	 * resolution is used, in case the DNS SRV resolution fails.
+	 *
+	 * When this field is empty, STUN mapped address resolution
+	 * will not be performed. In this case only ICE host candidates
+	 * will be added to the ICE transport, unless if \a no_host_cands
+	 * field is set. In this case, both host and srflx candidates 
+	 * are disabled.
+	 *
+	 * The default value is empty.
+	 */
+	pj_str_t	     server;
+
+	/**
+	 * The port number of the STUN server, when \a server
+	 * field specifies a hostname rather than domain name. This
+	 * field should also be set even when the \a server
+	 * specifies a domain name, to allow DNS SRV resolution
+	 * to fallback to DNS A/AAAA resolution when the DNS SRV
+	 * resolution fails.
+	 *
+	 * The default value is PJ_STUN_PORT.
+	 */
+	pj_uint16_t	     port;
+
+    } stun;
+
+    /**
+     * TURN specific settings.
+     */
+    struct {
+	/**
+	 * Specify the TURN server domain or hostname or IP address.
+	 * If DNS SRV resolution is required, application must fill
+	 * in this setting with the domain name of the TURN server 
+	 * and set the resolver instance in the \a resolver field.
+	 * Otherwise if the \a resolver setting is not set, this
+	 * field will be resolved with hostname resolution and in
+	 * this case the \a port field must be set.
+	 *
+	 * The \a port field should also be set even when DNS SRV
+	 * resolution is used, in case the DNS SRV resolution fails.
+	 *
+	 * When this field is empty, relay candidate will not be
+	 * created.
+	 *
+	 * The default value is empty.
+	 */
+	pj_str_t	     server;
+
+	/**
+	 * The port number of the TURN server, when \a server
+	 * field specifies a hostname rather than domain name. This
+	 * field should also be set even when the \a server
+	 * specifies a domain name, to allow DNS SRV resolution
+	 * to fallback to DNS A/AAAA resolution when the DNS SRV
+	 * resolution fails.
+	 *
+	 * Default is zero.
+	 */
+	pj_uint16_t	     port;
+
+	/**
+	 * Type of connection to the TURN server.
+	 *
+	 * Default is PJ_TURN_TP_UDP.
+	 */
+	pj_turn_tp_type	     conn_type;
+
+	/**
+	 * Credential to be used for the TURN session. This setting
+	 * is mandatory.
+	 *
+	 * Default is to have no credential.
+	 */
+	pj_stun_auth_cred    auth_cred;
+
+	/**
+	 * Optional TURN Allocate parameter. The default value will be
+	 * initialized by #pj_turn_alloc_param_default().
+	 */
+	pj_turn_alloc_param  alloc_param;
+
+    } turn;
+
+} pj_ice_strans_cfg;
+
+
+/** 
+ * Initialize ICE transport configuration with default values.
+ *
+ * @param cfg		The configuration to be initialized.
+ */
+PJ_DECL(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg);
+
+
+/**
+ * Copy configuration.
+ *
+ * @param pool		Pool.
+ * @param dst		Destination.
+ * @param src		Source.
+ */
+PJ_DECL(void) pj_ice_strans_cfg_copy(pj_pool_t *pool,
+				     pj_ice_strans_cfg *dst,
+				     const pj_ice_strans_cfg *src);
+
+
+/**
+ * Create and initialize the ICE stream transport with the specified
+ * parameters. 
+ *
  * @param name		Optional name for logging identification.
+ * @param cfg		Configuration.
  * @param comp_cnt	Number of components.
  * @param user_data	Arbitrary user data to be associated with this
@@ -400,6 +298,6 @@
  *			successfully.
  */
-PJ_DECL(pj_status_t) pj_ice_strans_create(pj_stun_config *stun_cfg,
-					  const char *name,
+PJ_DECL(pj_status_t) pj_ice_strans_create(const char *name,
+					  const pj_ice_strans_cfg *cfg,
 					  unsigned comp_cnt,
 					  void *user_data,
@@ -420,131 +318,22 @@
 
 /**
- * Set the domain to be used when resolving the STUN servers. If application
- * wants to utillize STUN, then STUN server must be specified, either by
- * calling this function or by calling #pj_ice_strans_set_stun_srv().
- *
- * If application calls this function, then the STUN/TURN servers will
- * be resolved by querying DNS SRV records for the specified domain.
- *
- * @param ice_st	The ICE stream transport.
- * @param resolver	The resolver instance that will be used to
- *			resolve the STUN/TURN servers.
- * @param domain	The target domain.
- *
- * @return		PJ_SUCCESS if DNS SRV resolution job can be
- *			started. The resolution process itself will
- *			complete asynchronously.
- */
-PJ_DECL(pj_status_t) pj_ice_strans_set_stun_domain(pj_ice_strans *ice_st,
-						   pj_dns_resolver *resolver,
-						   const pj_str_t *domain);
-
-/**
- * Set the STUN and TURN server addresses. If application
- * wants to utillize STUN, then STUN server must be specified, either by
- * calling this function or by calling #pj_ice_strans_set_stun_domain().
- *
- * With this function, the STUN and TURN server addresses will be 
- * assigned immediately, that is no DNS resolution will need to be 
- * performed.
- *
- * @param ice_st	The ICE stream transport.
- * @param stun_srv	The STUN server address, or NULL if STUN
- *			reflexive candidate is not to be used.
- * @param turn_srv	The TURN server address, or NULL if STUN
- *			relay candidate is not to be used.
- *
- * @return		PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) 
-pj_ice_strans_set_stun_srv( pj_ice_strans *ice_st,
-			    const pj_sockaddr_in *stun_srv,
-			    const pj_sockaddr_in *turn_srv);
-
-/**
- * Create and initialize the specified component. This function will
- * instantiate the socket descriptor for this component, optionally
- * bind the socket to the specified address (or bind to any address/port
- * if the \a addr parameter is NULL), and start finding all alias
- * addresses for this socket. For each alias addresses that if finds,
- * it will add an ICE stream transport candidate for this component.
- *
- * After all components have been initialized, application should poll
- * the #pj_ice_strans_get_comps_status() peridically to check if STUN
- * server reflexive and relayed candidates have been obtained
- * successfully.
- *
- * @param ice_st	The ICE stream transport.
- * @param comp_id	The component ID, which value must be greater than
- *			zero and less than or equal to the number of 
- *			components in this ICE stream transport.
- * @param options	Options, see #pj_ice_strans_option.
- * @param addr		Local address where socket will be bound to. This
- *			address will be used as follows:
- *			- if the value is NULL, then socket will be bound
- *			  to any available port.
- *			- if the value is not NULL, then if the port number
- *			  is not zero, it will used as the starting port 
- *			  where the socket will be bound to. If bind() to
- *			  this port fails, this function will try to bind
- *			  to port+2, repeatedly until it succeeded.
- *			  If application doesn't want this function to 
- *			  retry binding the socket to other port, it can
- *			  specify PJ_ICE_ST_OPT_NO_PORT_RETRY option.
- *			- if the value is not NULL, then if the address
- *			  is not INADDR_ANY, this function will bind the
- *			  socket to this particular interface only, and
- *			  no other host candidates will be added for this
- *			  socket.
- *			
- *
- * @return		PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_ice_strans_create_comp(pj_ice_strans *ice_st,
-					       unsigned comp_id,
-					       pj_uint32_t options,
-					       const pj_sockaddr_in *addr);
-
-/**
- * Manually add a candidate (transport address alias) for the specified
- * component. Normally application shouldn't need to use this function,
- * as candidates will be added automatically when component is created
- * with #pj_ice_strans_create_comp().
- *
- * @param ice_st	ICE stream transport.
- * @param comp_id	The component ID.
- * @param type		The candidate type.
- * @param local_pref	The local preference for this candidate
- *			(typically the value is 65535).
- * @param addr		The candidate address.
- * @param set_default	Set to non-zero to make this candidate the 
- *			default candidate for this component.
- *
- * @return		PJ_SUCCESS, or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_ice_strans_add_cand(pj_ice_strans *ice_st,
-					    unsigned comp_id,
-					    pj_ice_cand_type type,
-					    pj_uint16_t local_pref,
-					    const pj_sockaddr_in *addr,
-					    pj_bool_t set_default);
-
-/**
- * Get the status of components in the ICE stream transports. Since
- * some IP address candidates have to be obtained asynchronously (for
- * example, the STUN reflexive or relay candidate), application can
- * use this function to know whether the address resolution has 
- * completed.
- *
- * @param ice_st	The ICE stream transport.
- *
- * @return		PJ_SUCCESS if all candidates have been resolved
- *			successfully, PJ_EPENDING if transport resolution
- *			is still in progress, or other status on failure.
- */
-PJ_DECL(pj_status_t) pj_ice_strans_get_comps_status(pj_ice_strans *ice_st);
+ * Get the user data associated with the ICE stream transport.
+ *
+ * @param ice_st	The ICE stream transport.
+ *
+ * @return		The user data.
+ */
+PJ_DECL(void*) pj_ice_strans_get_user_data(pj_ice_strans *ice_st);
+
 
 /**
  * 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
+ * call this function to initialize the internal ICE session. This would
+ * register all transport address aliases for each component to the ICE
+ * session as candidates. Then application can enumerate all local
+ * candidates by calling #pj_ice_strans_enum_cands(), and encode these
+ * candidates in the SDP to be sent to remote agent.
  *
  * @param ice_st	The ICE stream transport.
@@ -561,8 +350,8 @@
 
 /**
- * Enumerate the local candidates. This function can only be called
- * after the ICE session has been created in the ICE stream transport.
- *
- * @param ice_st	The ICE stream transport.
+ * Enumerate the local candidates for the specified component.
+ *
+ * @param ice_st	The ICE stream transport.
+ * @param comp_id	Component ID.
  * @param count		On input, it specifies the maximum number of
  *			elements. On output, it will be filled with
@@ -574,16 +363,62 @@
  */
 PJ_DECL(pj_status_t) pj_ice_strans_enum_cands(pj_ice_strans *ice_st,
+					      unsigned comp_id,
 					      unsigned *count,
 					      pj_ice_sess_cand cand[]);
 
 /**
+ * Get the default candidate for the specified component. When this
+ * function is called before ICE negotiation completes, the default
+ * candidate is selected according to local preference criteria. When
+ * this function is called after ICE negotiation completes, the
+ * default candidate is the candidate that forms the valid pair.
+ *
+ * @param ice_st	The ICE stream transport.
+ * @param comp_id	Component ID.
+ * @param cand		Pointer to receive the default candidate
+ *			information.
+ */
+PJ_DECL(pj_status_t) pj_ice_strans_get_def_cand(pj_ice_strans *ice_st,
+						unsigned comp_id,
+						pj_ice_sess_cand *cand);
+
+/**
+ * Get the current ICE role. ICE session must have been initialized
+ * before this function can be called.
+ *
+ * @param ice_st	The ICE stream transport.
+ *
+ * @return		Current ICE role.
+ */
+PJ_DECL(pj_ice_sess_role) pj_ice_strans_get_role(pj_ice_strans *ice_st);
+
+
+/**
+ * Change session role. This happens for example when ICE session was
+ * created with controlled role when receiving an offer, but it turns out
+ * that the offer contains "a=ice-lite" attribute when the SDP gets
+ * inspected. ICE session must have been initialized before this function
+ * can be called.
+ *
+ * @param ice_st	The ICE stream transport.
+ * @param new_role	The new role to be set.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate error.
+ */
+PJ_DECL(pj_status_t) pj_ice_strans_change_role(pj_ice_strans *ice_st,
+					       pj_ice_sess_role new_role);
+
+
+/**
  * Start ICE connectivity checks. This function can only be called
- * after the ICE session has been created in the ICE stream transport.
- *
- * This function will pair the local and remote candidates to create 
- * check list. Once the check list is created and sorted based on the
- * priority, ICE periodic checks will be started. This function will 
- * return immediately, and application will be notified about the 
- * connectivity check status in the callback.
+ * after the ICE session has been created in the ICE stream transport
+ * with #pj_ice_strans_init_ice().
+ *
+ * This function must be called once application has received remote
+ * candidate list (typically from the remote SDP). This function pairs
+ * local candidates with remote candidates, and starts ICE connectivity
+ * checks. The ICE session/transport will then notify the application 
+ * via the callback when ICE connectivity checks completes, either 
+ * successfully or with failure.
  *
  * @param ice_st	The ICE stream transport.
@@ -592,18 +427,47 @@
  * @param rem_passwd	Remote password, as seen in the SDP received from
  *			the remote agent.
- * @param rem_cand_cnt	Number of remote candidates.
- * @param rem_cand	Remote candidate array.
+ * @param rcand_cnt	Number of remote candidates in the array.
+ * @param rcand		Remote candidates array.
  *
  * @return		PJ_SUCCESS, or the appropriate error code.
  */
-PJ_DECL(pj_status_t) 
-pj_ice_strans_start_ice( pj_ice_strans *ice_st,
-			 const pj_str_t *rem_ufrag,
-			 const pj_str_t *rem_passwd,
-			 unsigned rem_cand_cnt,
-			 const pj_ice_sess_cand rem_cand[]);
-
-/**
- * Stop and destroy the ICE session inside this media transport.
+PJ_DECL(pj_status_t) pj_ice_strans_start_ice(pj_ice_strans *ice_st,
+					     const pj_str_t *rem_ufrag,
+					     const pj_str_t *rem_passwd,
+					     unsigned rcand_cnt,
+					     const pj_ice_sess_cand rcand[]);
+
+/**
+ * Retrieve the candidate pair that has been nominated and successfully
+ * checked for the specified component. If ICE negotiation is still in
+ * progress or it has failed, this function will return NULL.
+ *
+ * @param ice_st	The ICE stream transport.
+ * @param comp_id	Component ID.
+ *
+ * @return		The valid pair as ICE checklist structure if the
+ *			pair exist.
+ */
+PJ_DECL(const pj_ice_sess_check*) 
+pj_ice_strans_get_valid_pair(const pj_ice_strans *ice_st,
+			     unsigned comp_id);
+
+/**
+ * Stop and destroy the ICE session inside this media transport. Application
+ * needs to call this function once the media session is over (the call has
+ * been disconnected).
+ *
+ * Application MAY reuse this ICE stream transport for subsequent calls.
+ * In this case, it must call #pj_ice_strans_stop_ice() when the call is
+ * disconnected, and reinitialize the ICE stream transport for subsequent
+ * call with #pj_ice_strans_init_ice()/#pj_ice_strans_start_ice(). In this
+ * case, the ICE stream transport will maintain the internal sockets and
+ * continue to send STUN keep-alive packets and TURN Refresh request to 
+ * keep the NAT binding/TURN allocation open and to detect change in STUN
+ * mapped address.
+ *
+ * If application does not want to reuse the ICE stream transport for
+ * subsequent calls, it must call #pj_ice_strans_destroy() to destroy the
+ * ICE stream transport altogether.
  *
  * @param ice_st	The ICE stream transport.
@@ -615,9 +479,14 @@
 
 /**
- * Send outgoing packet using this transport. If ICE checks have not 
- * produced a valid check for the specified component ID, this function 
- * send to the destination address. Otherwise it will send the packet to
- * remote destination using the nominated local candidate as have been checked
- * previously.
+ * Send outgoing packet using this transport. 
+ * Application can send data (normally RTP or RTCP packets) at any time
+ * by calling this function. This function takes a destination
+ * address as one of the arguments, and this destination address should
+ * be taken from the default transport address of the component (that is
+ * the address in SDP c= and m= lines, or in a=rtcp attribute). 
+ * If ICE negotiation is in progress, this function will send the data 
+ * to the destination address. Otherwise if ICE negotiation has completed
+ * successfully, this function will send the data to the nominated remote 
+ * address, as negotiated by ICE.
  *
  * @param ice_st	The ICE stream transport.
Index: pjproject/trunk/pjnath/include/pjnath/nat_detect.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/nat_detect.h	(revision 1987)
+++ pjproject/trunk/pjnath/include/pjnath/nat_detect.h	(revision 1988)
@@ -33,5 +33,4 @@
  * @defgroup PJNATH_NAT_DETECT NAT Classification/Detection Tool
  * @brief NAT Classification/Detection Tool
- * @ingroup PJNATH_ICE
  * @{
  *
Index: pjproject/trunk/pjnath/include/pjnath/stun_config.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/stun_config.h	(revision 1987)
+++ pjproject/trunk/pjnath/include/pjnath/stun_config.h	(revision 1988)
@@ -26,4 +26,6 @@
 
 #include <pjnath/stun_msg.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
 #include <pj/string.h>
 
@@ -104,4 +106,15 @@
 
 /**
+ * Check that STUN config is valid.
+ */
+PJ_INLINE(pj_status_t) pj_stun_config_check_valid(const pj_stun_config *cfg)
+{
+    PJ_ASSERT_RETURN(cfg->ioqueue && cfg->pf && cfg->timer_heap &&
+		     cfg->rto_msec && cfg->res_cache_msec, PJ_EINVAL);
+    return PJ_SUCCESS;
+}
+
+
+/**
  * @}
  */
Index: pjproject/trunk/pjnath/include/pjnath/stun_msg.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/stun_msg.h	(revision 1987)
+++ pjproject/trunk/pjnath/include/pjnath/stun_msg.h	(revision 1988)
@@ -35,6 +35,6 @@
 /**
  * @defgroup PJNATH_STUN_MSG STUN Message Representation and Parsing
+ * @ingroup PJNATH_STUN
  * @brief Low-level representation and parsing of STUN messages.
- * @ingroup PJNATH_STUN
  * @{
  */
@@ -956,7 +956,6 @@
  * This describes the TURN REQUESTED-PROPS attribute, encoded as
  * STUN 32bit integer attribute. Few macros are provided to manipulate
- * the values in this attribute: #PJ_STUN_GET_RPP_BITS(), 
- * #PJ_STUN_SET_RPP_BITS(), #PJ_STUN_GET_RPP_PORT(), and
- * #PJ_STUN_SET_RPP_PORT().
+ * the values in this attribute: #PJ_STUN_GET_PROP_TYPE(), and
+ * #PJ_STUN_SET_PROP_TYPE().
  * 
  * This attribute allows the client to request certain properties for
@@ -1165,5 +1164,5 @@
      * #pj_stun_msg_check() and #pj_stun_msg_decode() to disable the 
      * verification of FINGERPRINT, for example when the STUN usage says when
-     * FINGERPRINT mechanism shall not * be used.
+     * FINGERPRINT mechanism shall not be used.
      */
     PJ_STUN_NO_FINGERPRINT_CHECK = 8
@@ -1293,5 +1292,5 @@
  *
  * If application wants to apply credential to the message, it MUST
- * include a blank MESSAGE-INTEGRITY attribute in the message, as the
+ * include a blank MESSAGE-INTEGRITY attribute in the message as the
  * last attribute or the attribute before FINGERPRINT. This function will
  * calculate the HMAC digest from the message using  the supplied key in
@@ -1321,8 +1320,8 @@
 PJ_DECL(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg,
 				        pj_uint8_t *pkt_buf,
-				        unsigned buf_size,
+				        pj_size_t buf_size,
 				        unsigned options,
 					const pj_str_t *key,
-				        unsigned *p_msg_len);
+				        pj_size_t *p_msg_len);
 
 /**
@@ -1346,5 +1345,5 @@
  */
 PJ_DECL(pj_status_t) pj_stun_msg_check(const pj_uint8_t *pdu, 
-				       unsigned pdu_len, unsigned options);
+				       pj_size_t pdu_len, unsigned options);
 
 
@@ -1372,8 +1371,8 @@
 PJ_DECL(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool,
 				        const pj_uint8_t *pdu,
-				        unsigned pdu_len,
+				        pj_size_t pdu_len,
 				        unsigned options,
 				        pj_stun_msg **p_msg,
-					unsigned *p_parsed_len,
+					pj_size_t *p_parsed_len,
 				        pj_stun_msg **p_response);
 
Index: pjproject/trunk/pjnath/include/pjnath/stun_session.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/stun_session.h	(revision 1987)
+++ pjproject/trunk/pjnath/include/pjnath/stun_session.h	(revision 1988)
@@ -214,4 +214,5 @@
 struct pj_stun_tx_data
 {
+    /** PJLIB list interface */
     PJ_DECL_LIST_MEMBER(struct pj_stun_tx_data);
 
@@ -238,4 +239,19 @@
     pj_timer_entry	 res_timer;	/**< Response cache timer.	    */
 };
+
+
+/**
+ * These are the flags to control the message logging in the STUN session.
+ */
+typedef enum pj_stun_sess_msg_log_flag
+{
+    PJ_STUN_SESS_LOG_TX_REQ=1,	/**< Log outgoing STUN requests.    */
+    PJ_STUN_SESS_LOG_TX_RES=2,	/**< Log outgoing STUN responses.   */
+    PJ_STUN_SESS_LOG_TX_IND=4,	/**< Log outgoing STUN indications. */
+
+    PJ_STUN_SESS_LOG_RX_REQ=8,	/**< Log incoming STUN requests.    */
+    PJ_STUN_SESS_LOG_RX_RES=16,	/**< Log incoming STUN responses    */
+    PJ_STUN_SESS_LOG_RX_IND=32	/**< Log incoming STUN indications  */
+} pj_stun_sess_msg_log_flag;
 
 
@@ -259,9 +275,14 @@
 
 /**
- * Destroy the STUN session.
- *
- * @param sess	    The STUN session instance.
- *
- * @return	    PJ_SUCCESS on success, or the appropriate error code.
+ * Destroy the STUN session and all objects created in the context of
+ * this session.
+ *
+ * @param sess	    The STUN session instance.
+ *
+ * @return	    PJ_SUCCESS on success, or the appropriate error code.
+ *		    This function will return PJ_EPENDING if the operation
+ *		    cannot be performed immediately because callbacks are
+ *		    being called; in this case the session will be destroyed
+ *		    as soon as the last callback returns.
  */
 PJ_DECL(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess);
@@ -334,4 +355,12 @@
 						pj_stun_auth_type auth_type,
 						const pj_stun_auth_cred *cred);
+/**
+ * Configure message logging. By default all flags are enabled.
+ *
+ * @param sess	    The STUN session instance.
+ * @param flags	    Bitmask combination of #pj_stun_sess_msg_log_flag
+ */
+PJ_DECL(void) pj_stun_session_set_log(pj_stun_session *sess,
+				      unsigned flags);
 
 /**
@@ -382,5 +411,5 @@
  *
  * @param sess	    The STUN session instance.
- * @param req	    The STUN request where the response is to be created.
+ * @param rdata	    The STUN request where the response is to be created.
  * @param err_code  Error code to be set in the response, if error response
  *		    is to be created, according to pj_stun_status enumeration.
@@ -433,4 +462,7 @@
  *
  * @return	    PJ_SUCCESS on success, or the appropriate error code.
+ *		    This function will return PJNATH_ESTUNDESTROYED if 
+ *		    application has destroyed the session in 
+ *		    \a on_send_msg() callback.
  */
 PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess,
@@ -450,5 +482,5 @@
  * @param sess	    The STUN session instance.
  * @param rdata	    The STUN request message to be responded.
- * @param err_code  Error code to be set in the response, if error response
+ * @param code	    Error code to be set in the response, if error response
  *		    is to be created, according to pj_stun_status enumeration.
  *		    This argument MUST be zero if successful response is
@@ -473,4 +505,7 @@
  *
  * @return	    PJ_SUCCESS on success, or the appropriate error code.
+ *		    This function will return PJNATH_ESTUNDESTROYED if 
+ *		    application has destroyed the session in 
+ *		    \a on_send_msg() callback.
  */
 PJ_DECL(pj_status_t) pj_stun_session_respond(pj_stun_session *sess, 
@@ -497,4 +532,7 @@
  *
  * @return	    PJ_SUCCESS if transaction is successfully cancelled.
+ *		    This function will return PJNATH_ESTUNDESTROYED if 
+ *		    application has destroyed the session in 
+ *		    \a on_request_complete() callback.
  */
 PJ_DECL(pj_status_t) pj_stun_session_cancel_req(pj_stun_session *sess,
@@ -512,4 +550,7 @@
  *
  * @return	    PJ_SUCCESS on success, or the appropriate error.
+ *		    This function will return PJNATH_ESTUNDESTROYED if 
+ *		    application has destroyed the session in \a on_send_msg()
+ *		    callback.
  */
 PJ_DECL(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess,
@@ -549,4 +590,7 @@
  *
  * @return		PJ_SUCCESS on success, or the appropriate error code.
+ *			This function will return PJNATH_ESTUNDESTROYED if 
+ *			application has destroyed the session in one of the
+ *			callback.
  */
 PJ_DECL(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
Index: pjproject/trunk/pjnath/include/pjnath/stun_sock.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/stun_sock.h	(revision 1988)
+++ pjproject/trunk/pjnath/include/pjnath/stun_sock.h	(revision 1988)
@@ -0,0 +1,403 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#ifndef __PJNATH_STUN_SOCK_H__
+#define __PJNATH_STUN_SOCK_H__
+
+/**
+ * @file stun_sock.h
+ * @brief STUN aware socket transport
+ */
+#include <pjnath/stun_config.h>
+#include <pjlib-util/resolver.h>
+#include <pj/ioqueue.h>
+#include <pj/sock.h>
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJNATH_STUN_SOCK STUN aware socket transport
+ * @brief STUN aware socket transport
+ * @ingroup PJNATH_STUN
+ * @{
+ * The STUN transport provides asynchronous UDP like socket transport
+ * with the additional capability to query the publicly mapped transport
+ * address (using STUN resolution), to refresh the NAT binding, and to
+ * demultiplex internal STUN messages from application data (the 
+ * application data may be a STUN message as well).
+ */
+
+/**
+ * Opaque type to represent a STUN transport.
+ */
+typedef struct pj_stun_sock pj_stun_sock;
+
+/**
+ * Types of operation being reported in \a on_status() callback of
+ * pj_stun_sock_cb. Application may retrieve the string representation
+ * of these constants with pj_stun_sock_op_name().
+ */
+typedef enum pj_stun_sock_op
+{
+    /**
+     * Asynchronous DNS resolution.
+     */
+    PJ_STUN_SOCK_DNS_OP		= 1,
+
+    /**
+     * Initial STUN Binding request.
+     */
+    PJ_STUN_SOCK_BINDING_OP,
+
+    /**
+     * Subsequent STUN Binding request for keeping the binding
+     * alive.
+     */
+    PJ_STUN_SOCK_KEEP_ALIVE_OP,
+
+} pj_stun_sock_op;
+
+
+/**
+ * This structure contains callbacks that will be called by the STUN
+ * transport to notify application about various events.
+ */
+typedef struct pj_stun_sock_cb
+{
+    /**
+     * Notification when incoming packet has been received.
+     *
+     * @param stun_sock	The STUN transport.
+     * @param data	The packet.
+     * @param data_len	Length of the packet.
+     * @param src_addr	The source address of the packet.
+     * @param addr_len	The length of the source address.
+     *
+     * @return		Application should normally return PJ_TRUE to let
+     *			the STUN transport continue its operation. However
+     *			it must return PJ_FALSE if it has destroyed the
+     *			STUN transport in this callback.
+     */
+    pj_bool_t (*on_rx_data)(pj_stun_sock *stun_sock,
+			    void *pkt,
+			    unsigned pkt_len,
+			    const pj_sockaddr_t *src_addr,
+			    unsigned addr_len);
+
+    /**
+     * Notifification when asynchronous send operation has completed.
+     *
+     * @param stun_sock	The STUN transport.
+     * @param send_key	The send operation key that was given in
+     *			#pj_stun_sock_sendto().
+     * @param sent	If value is positive non-zero it indicates the
+     *			number of data sent. When the value is negative,
+     *			it contains the error code which can be retrieved
+     *			by negating the value (i.e. status=-sent).
+     *
+     * @return		Application should normally return PJ_TRUE to let
+     *			the STUN transport continue its operation. However
+     *			it must return PJ_FALSE if it has destroyed the
+     *			STUN transport in this callback.
+     */
+    pj_bool_t (*on_data_sent)(pj_stun_sock *stun_sock,
+			      pj_ioqueue_op_key_t *send_key,
+			      pj_ssize_t sent);
+
+    /**
+     * Notification when the status of the STUN transport has changed. This
+     * callback may be called for the following conditions:
+     *	- the first time the publicly mapped address has been resolved from
+     *	  the STUN server, this callback will be called with \a op argument
+     *    set to PJ_STUN_SOCK_BINDING_OP \a status  argument set to 
+     *    PJ_SUCCESS.
+     *  - anytime when the transport has detected that the publicly mapped
+     *    address has changed, this callback will be called with \a op
+     *    argument set to PJ_STUN_SOCK_KEEP_ALIVE_OP and \a status
+     *    argument set to PJ_SUCCESS. On this case and the case above,
+     *    application will get the resolved public address in the
+     *    #pj_stun_sock_info structure.
+     *  - for any terminal error (such as STUN time-out, DNS resolution
+     *    failure, or keep-alive failure), this callback will be called 
+     *	  with the \a status argument set to non-PJ_SUCCESS.
+     *
+     * @param stun_sock	The STUN transport.
+     * @param op	The operation that triggers the callback.
+     * @param status	The status.
+     *
+     * @return		Must return PJ_FALSE if it has destroyed the
+     *			STUN transport in this callback. Application should
+     *			normally destroy the socket and return PJ_FALSE
+     *			upon encountering terminal error, otherwise it
+     *			should return PJ_TRUE to let the STUN socket operation
+     *			continues.
+     */
+    pj_bool_t	(*on_status)(pj_stun_sock *stun_sock, 
+			     pj_stun_sock_op op,
+			     pj_status_t status);
+
+} pj_stun_sock_cb;
+
+
+/**
+ * This structure contains information about the STUN transport. Application
+ * may query this information by calling #pj_stun_sock_get_info().
+ */
+typedef struct pj_stun_sock_info
+{
+    /**
+     * The bound address of the socket.
+     */
+    pj_sockaddr	    bound_addr;
+
+    /**
+     * IP address of the STUN server.
+     */
+    pj_sockaddr	    srv_addr;
+
+    /**
+     * The publicly mapped address. It may contain zero address when the
+     * mapped address has not been resolved. Application may query whether
+     * this field contains valid address with pj_sockaddr_has_addr().
+     */
+    pj_sockaddr	    mapped_addr;
+
+    /**
+     * Number of interface address aliases. The interface address aliases
+     * are list of all interface addresses in this host.
+     */
+    unsigned	    alias_cnt;
+
+    /**
+     * Array of interface address aliases.
+     */
+    pj_sockaddr	    aliases[PJ_ICE_ST_MAX_CAND];
+
+} pj_stun_sock_info;
+
+
+/**
+ * This describe the settings to be given to the STUN transport during its
+ * creation. Application should initialize this structure by calling
+ * #pj_stun_sock_cfg_default().
+ */
+typedef struct pj_stun_sock_cfg
+{
+    /**
+     * Packet buffer size. Default value is PJ_STUN_SOCK_PKT_LEN.
+     */
+    unsigned max_pkt_size;
+
+    /**
+     * Specify the number of simultaneous asynchronous read operations to
+     * be invoked to the ioqueue. Having more than one read operations will
+     * increase performance on multiprocessor systems since the application
+     * will be able to process more than one incoming packets simultaneously.
+     * Default value is 1.
+     */
+    unsigned async_cnt;
+
+    /**
+     * Specify the interface where the socket should be bound to. If the
+     * address is zero, socket will be bound to INADDR_ANY. If the address
+     * is non-zero, socket will be bound to this address only, and the
+     * transport will have only one address alias (the \a alias_cnt field
+     * in #pj_stun_sock_info structure.
+     */
+    pj_sockaddr bound_addr;
+
+    /**
+     * Specify the STUN keep-alive duration, in seconds. The STUN transport
+     * does keep-alive by sending STUN Binding request to the STUN server. 
+     * If this value is zero, the PJ_STUN_KEEP_ALIVE_SEC value will be used.
+     * If the value is negative, it will disable STUN keep-alive.
+     */
+    int ka_interval;
+
+} pj_stun_sock_cfg;
+
+
+
+/**
+ * Retrieve the name representing the specified operation.
+ */
+PJ_DECL(const char*) pj_stun_sock_op_name(pj_stun_sock_op op);
+
+
+/**
+ * Initialize the STUN transport setting with its default values.
+ *
+ * @param cfg	The STUN transport config.
+ */
+PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg);
+
+
+/**
+ * Create the STUN transport using the specified configuration. Once
+ * the STUN transport has been create, application should call
+ * #pj_stun_sock_start() to start the transport.
+ *
+ * @param stun_cfg	The STUN configuration which contains among other
+ *			things the ioqueue and timer heap instance for
+ *			the operation of this transport.
+ * @param af		Address family of socket. Currently pj_AF_INET()
+ *			and pj_AF_INET6() are supported. 
+ * @param name		Optional name to be given to this transport to
+ *			assist debugging.
+ * @param cb		Callback to receive events/data from the transport.
+ * @param cfg		Optional transport settings.
+ * @param user_data	Arbitrary application data to be associated with
+ *			this transport.
+ * @param p_sock	Pointer to receive the created transport instance.
+ *
+ * @restun		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_stun_sock_create(pj_stun_config *stun_cfg,
+					 const char *name,
+					 int af,
+					 const pj_stun_sock_cb *cb,
+					 const pj_stun_sock_cfg *cfg,
+					 void *user_data,
+					 pj_stun_sock **p_sock);
+
+
+/**
+ * Start the STUN transport. This will start the DNS SRV resolution for
+ * the STUN server (if desired), and once the server is resolved, STUN
+ * Binding request will be sent to resolve the publicly mapped address.
+ * Once the initial STUN Binding response is received, the keep-alive
+ * timer will be started.
+ *
+ * @param stun_sock	The STUN transport instance.
+ * @param domain	The domain, hostname, or IP address of the TURN
+ *			server. When this parameter contains domain name,
+ *			the \a resolver parameter must be set to activate
+ *			DNS SRV resolution.
+ * @param default_port	The default STUN port number to use when DNS SRV
+ *			resolution is not used. If DNS SRV resolution is
+ *			used, the server port number will be set from the
+ *			DNS SRV records. The recommended value for this
+ *			parameter is PJ_STUN_PORT.
+ * @param resolver	If this parameter is not NULL, then the \a domain
+ *			parameter will be first resolved with DNS SRV and
+ *			then fallback to using DNS A/AAAA resolution when
+ *			DNS SRV resolution fails. If this parameter is
+ *			NULL, the \a domain parameter will be resolved as
+ *			hostname.
+ *
+ * @return		PJ_SUCCESS if the operation has been successfully
+ *			queued, or the appropriate error code on failure.
+ *			When this function returns PJ_SUCCESS, the final
+ *			result of the allocation process will be notified
+ *			to application in \a on_state() callback.
+ */
+PJ_DECL(pj_status_t) pj_stun_sock_start(pj_stun_sock *stun_sock,
+				        const pj_str_t *domain,
+				        pj_uint16_t default_port,
+				        pj_dns_resolver *resolver);
+
+/**
+ * Destroy the STUN transport.
+ *
+ * @param sock		The STUN transport socket.
+ *
+ * @restun		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *sock);
+
+
+/**
+ * Associate a user data with this STUN transport. The user data may then
+ * be retrieved later with #pj_stun_sock_get_user_data().
+ *
+ * @param stun_sock	The STUN transport instance.
+ * @param user_data	Arbitrary data.
+ *
+ * @restun		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_stun_sock_set_user_data(pj_stun_sock *stun_sock,
+					        void *user_data);
+
+/**
+ * Retrieve the previously assigned user data associated with this STUN
+ * transport.
+ *
+ * @param stun_sock	The STUN transport instance.
+ *
+ * @restun		The user/application data.
+ */
+PJ_DECL(void*) pj_stun_sock_get_user_data(pj_stun_sock *stun_sock);
+
+
+/**
+ * Get the STUN transport info. The transport info contains, among other
+ * things, the allocated relay address.
+ *
+ * @param stun_sock	The STUN transport instance.
+ * @param info		Pointer to be filled with STUN transport info.
+ *
+ * @restun		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_stun_sock_get_info(pj_stun_sock *stun_sock,
+					   pj_stun_sock_info *info);
+
+
+/**
+ * Send a data to the specified address. This function may complete
+ * asynchronously and in this case \a on_data_sent() will be called.
+ *
+ * @param stun_sock	The STUN transport instance.
+ * @param op_key	Optional send key for sending the packet down to
+ *			the ioqueue. This value will be given back to
+ *			\a on_data_sent() callback
+ * @param pkt		The data/packet to be sent to peer.
+ * @param pkt_len	Length of the data.
+ * @param flag		pj_ioqueue_sendto() flag.
+ * @param dst_addr	The remote address.
+ * @param addr_len	Length of the address.
+ *
+ * @return		PJ_SUCCESS if data has been sent immediately, or
+ *			PJ_EPENDING if data cannot be sent immediately. In
+ *			this case the \a on_data_sent() callback will be
+ *			called when data is actually sent. Any other return
+ *			value indicates error condition.
+ */ 
+PJ_DECL(pj_status_t) pj_stun_sock_sendto(pj_stun_sock *stun_sock,
+					 pj_ioqueue_op_key_t *send_key,
+					 const void *pkt,
+					 unsigned pkt_len,
+					 unsigned flag,
+					 const pj_sockaddr_t *dst_addr,
+					 unsigned addr_len);
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+
+#endif	/* __PJNATH_STUN_SOCK_H__ */
+
Index: pjproject/trunk/pjnath/include/pjnath/stun_transaction.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/stun_transaction.h	(revision 1987)
+++ pjproject/trunk/pjnath/include/pjnath/stun_transaction.h	(revision 1988)
@@ -90,5 +90,7 @@
      *
      * @return		    If return value of the callback is not PJ_SUCCESS,
-     *			    the transaction will fail.
+     *			    the transaction will fail. Application MUST return
+     *			    PJNATH_ESTUNDESTROYED if it has destroyed the
+     *			    transaction in this callback.
      */
     pj_status_t (*on_send_msg)(pj_stun_client_tsx *tsx,
@@ -162,5 +164,6 @@
  * @param tsx		The STUN transaction.
  *
- * @return		PJ_SUCCESS on success, or the appropriate error code.
+ * @return		PJ_SUCCESS on success or PJ_EINVAL if the parameter
+ *			is NULL.
  */
 PJ_DECL(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx);
@@ -215,5 +218,8 @@
  * @param pkt_len	Length of STUN packet.
  *
- * @return		PJ_SUCCESS on success or the appropriate error code.
+ * @return		PJ_SUCCESS on success, or PJNATH_ESTUNDESTROYED 
+ *			when the user has destroyed the transaction in 
+ *			\a on_send_msg() callback, or any other error code
+ *			as returned by \a on_send_msg() callback.
  */
 PJ_DECL(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx,
@@ -229,5 +235,8 @@
  * @param tsx		The STUN client transaction instance.
  *
- * @return		PJ_SUCCESS on success or the appropriate error code.
+ * @return		PJ_SUCCESS on success, or PJNATH_ESTUNDESTROYED 
+ *			when the user has destroyed the transaction in 
+ *			\a on_send_msg() callback, or any other error code
+ *			as returned by \a on_send_msg() callback.
  */
 PJ_DECL(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx);
Index: pjproject/trunk/pjnath/include/pjnath/turn_session.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/turn_session.h	(revision 1987)
+++ pjproject/trunk/pjnath/include/pjnath/turn_session.h	(revision 1988)
@@ -30,10 +30,23 @@
 PJ_BEGIN_DECL
 
+/**
+ * @defgroup PJNATH_TURN TURN Client Library
+ */
+
+
 /* **************************************************************************/
 /**
- * @defgroup PJNATH_TURN_SESSION TURN client session
+ * @defgroup PJNATH_TURN_SESSION Transport independent TURN client session
  * @brief Transport independent TURN client session
- * @ingroup PJNATH_STUN
+ * @ingroup PJNATH_TURN
  * @{
+ *
+ * This module describes the transport independent TURN client session. This
+ * interface is provided for implementors of a TURN client transport, and
+ * application usually will want to use \ref PJNATH_TURN_SOCK instead.
+ *
+ * The transport independent TURN client session is created to facilitate
+ * the creation of different types of transports between the client and the
+ * TURN server.
  */
 
@@ -42,16 +55,4 @@
  */
 typedef struct pj_turn_session pj_turn_session;
-
-
-#define PJ_TURN_INVALID_CHANNEL	    0xFFFF
-#define PJ_TURN_CHANNEL_MIN	    0x4000
-#define PJ_TURN_CHANNEL_MAX	    0xFFFE  /* inclusive */
-#define PJ_TURN_NO_TIMEOUT	    ((long)0x7FFFFFFF)
-#define PJ_TURN_MAX_PKT_LEN	    3000
-#define PJ_TURN_PERM_TIMEOUT	    300	/* Must be greater than REFRESH_SEC_BEFORE */
-#define PJ_TURN_CHANNEL_TIMEOUT	    600	/* Must be greater than REFRESH_SEC_BEFORE */
-#define PJ_TURN_REFRESH_SEC_BEFORE  60
-#define PJ_TURN_KEEP_ALIVE_SEC	    15
-#define PJ_TURN_PEER_HTABLE_SIZE    8
 
 
@@ -134,11 +135,18 @@
 
 
-/* ChannelData header */
+#pragma pack(1)
+
+/**
+ * This structure ChannelData header. All the fields are in network byte
+ * order when it's on the wire.
+ */
 typedef struct pj_turn_channel_data
 {
-    pj_uint16_t ch_number;
-    pj_uint16_t length;
+    pj_uint16_t ch_number;	/**< Channel number.    */
+    pj_uint16_t length;		/**< Payload length.	*/
 } pj_turn_channel_data;
 
+
+#pragma pack()
 
 
@@ -152,4 +160,13 @@
      * needs to send outgoing message. Since the TURN session doesn't
      * have a socket on its own, this callback must be implemented.
+     *
+     * @param sess	The TURN session.
+     * @param pkt	The packet/data to be sent.
+     * @param pkt_len	Length of the packet/data.
+     * @param dst_addr	Destination address of the packet.
+     * @param addr_len	Length of the destination address.
+     *
+     * @return		The callback should return the status of the
+     *			send operation. 
      */
     pj_status_t (*on_send_pkt)(pj_turn_session *sess,
@@ -157,5 +174,5 @@
 			       unsigned pkt_len,
 			       const pj_sockaddr_t *dst_addr,
-			       unsigned dst_addr_len);
+			       unsigned addr_len);
 
     /**
@@ -163,5 +180,11 @@
      * a channel number.
      *
-     * This callback is optional.
+     * This callback is optional since the nature of this callback is
+     * for information only.
+     *
+     * @param sess	The TURN session.
+     * @param peer_addr	The peer address.
+     * @param addr_len	Length of the peer address.
+     * @param ch_num	The channel number associated with this peer address.
      */
     void (*on_channel_bound)(pj_turn_session *sess,
@@ -174,8 +197,14 @@
      * Data indication or ChannelData message from the TURN server.
      *
-     * This callback is optional.
+     * @param sess	The TURN session.
+     * @param pkt	The data/payload of the Data Indication or ChannelData
+     *			packet.
+     * @param pkt_len	Length of the data/payload.
+     * @param peer_addr	Peer address where this payload was received by
+     *			the TURN server.
+     * @param addr_len	Length of the peer address.
      */
     void (*on_rx_data)(pj_turn_session *sess,
-		       const pj_uint8_t *pkt,
+		       void *pkt,
 		       unsigned pkt_len,
 		       const pj_sockaddr_t *peer_addr,
@@ -186,6 +215,11 @@
      * implement this callback at least to know that the TURN session is
      * going to be destroyed.
-     */
-    void (*on_state)(pj_turn_session *sess, pj_turn_state_t old_state,
+     *
+     * @param sess	The TURN session.
+     * @param old_state	The previous state of the session.
+     * @param new_state	The current state of the session.
+     */
+    void (*on_state)(pj_turn_session *sess, 
+		     pj_turn_state_t old_state,
 		     pj_turn_state_t new_state);
 
@@ -194,16 +228,35 @@
 
 /**
- * Allocate parameter.
+ * Allocation parameter, which can be given when application calls 
+ * pj_turn_session_alloc() to allocate relay address in the TURN server.
+ * Application should call pj_turn_alloc_param_default() to initialize
+ * this structure with the default values.
  */
 typedef struct pj_turn_alloc_param
 {
+    /**
+     * The requested BANDWIDTH. Default is zero to not request any
+     * specific bandwidth.
+     */
     int	    bandwidth;
+
+    /**
+     * The requested LIFETIME. Default is zero to not request any
+     * explicit allocation lifetime.
+     */
     int	    lifetime;
+
+    /**
+     * If set to non-zero, the TURN session will periodically send blank
+     * Send Indication every PJ_TURN_KEEP_ALIVE_SEC to refresh local
+     * NAT bindings. Default is zero.
+     */
     int	    ka_interval;
+
 } pj_turn_alloc_param;
 
 
 /**
- * TURN session info.
+ * This structure describes TURN session info.
  */
 typedef struct pj_turn_session_info
@@ -215,7 +268,22 @@
 
     /**
+     * Last error (if session was terminated because of error)
+     */
+    pj_status_t	    last_status;
+
+    /**
      * Type of connection to the TURN server.
      */
-    pj_turn_tp_type tp_type;
+    pj_turn_tp_type conn_type;
+
+    /**
+     * The selected TURN server address.
+     */
+    pj_sockaddr	    server;
+
+    /**
+     * Mapped address, as reported by the TURN server.
+     */
+    pj_sockaddr	    mapped_addr;
 
     /**
@@ -225,9 +293,4 @@
 
     /**
-     * The selected TURN server address.
-     */
-    pj_sockaddr	    server;
-
-    /**
      * Current seconds before allocation expires.
      */
@@ -238,10 +301,17 @@
 
 /**
- * Create default pj_turn_alloc_param.
+ * Initialize pj_turn_alloc_param with the default values.
+ *
+ * @param prm	The TURN allocation parameter to be initialized.
  */
 PJ_DECL(void) pj_turn_alloc_param_default(pj_turn_alloc_param *prm);
 
+
 /**
  * Duplicate pj_turn_alloc_param.
+ *
+ * @param pool	Pool to allocate memory (currently not used)
+ * @param dst	Destination parameter.
+ * @param src	Source parameter.
  */
 PJ_DECL(void) pj_turn_alloc_param_copy(pj_pool_t *pool, 
@@ -250,5 +320,9 @@
 
 /**
- * Get TURN state name.
+ * Get string representation for the given TURN state.
+ *
+ * @param state	The TURN session state.
+ *
+ * @return	The state name as NULL terminated string.
  */
 PJ_DECL(const char*) pj_turn_state_name(pj_turn_state_t state);
@@ -256,18 +330,43 @@
 
 /**
- * Create TURN client session.
- */
-PJ_DECL(pj_status_t) pj_turn_session_create(pj_stun_config *cfg,
+ * Create a TURN session instance with the specified address family and
+ * connection type. Once TURN session instance is created, application
+ * must call pj_turn_session_alloc() to allocate a relay address in the TURN
+ * server.
+ *
+ * @param cfg		The STUN configuration which contains among other
+ *			things the ioqueue and timer heap instance for
+ *			the operation of this session.
+ * @param name		Optional name to identify this session in the log.
+ * @param af		Address family of the client connection. Currently
+ *			pj_AF_INET() and pj_AF_INET6() are supported.
+ * @param conn_type	Connection type to the TURN server. 
+ * @param cb		Callback to receive events from the TURN session.
+ * @param options	Option flags, currently this value must be zero.
+ * @param user_data	Arbitrary application data to be associated with
+ *			this transport.
+ * @param p_sess	Pointer to receive the created instance of the
+ *			TURN session.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_turn_session_create(const pj_stun_config *cfg,
 					    const char *name,
 					    int af,
 					    pj_turn_tp_type conn_type,
 					    const pj_turn_session_cb *cb,
+					    unsigned options,
 					    void *user_data,
-					    unsigned options,
 					    pj_turn_session **p_sess);
 
-
-/**
- * Shutdown TURN client session.
+/**
+ * Shutdown TURN client session. This will gracefully deallocate and
+ * destroy the client session.
+ *
+ * @param sess		The TURN client session.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
  */
 PJ_DECL(pj_status_t) pj_turn_session_shutdown(pj_turn_session *sess);
@@ -275,5 +374,12 @@
 
 /**
- * Forcefully destroy the TURN session.
+ * Forcefully destroy the TURN session. This will destroy the session
+ * immediately. If there is an active allocation, the server will not
+ * be notified about the client destruction.
+ *
+ * @param sess		The TURN client session.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
  */
 PJ_DECL(pj_status_t) pj_turn_session_destroy(pj_turn_session *sess);
@@ -281,5 +387,13 @@
 
 /**
- * Get TURN session info.
+ * Get the information about this TURN session and the allocation, if
+ * any.
+ *
+ * @param sess		The TURN client session.
+ * @param info		The structure to be initialized with the TURN
+ *			session info.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
  */
 PJ_DECL(pj_status_t) pj_turn_session_get_info(pj_turn_session *sess,
@@ -287,5 +401,12 @@
 
 /**
- * Re-assign user data.
+ * Associate a user data with this TURN session. The user data may then
+ * be retrieved later with pj_turn_session_get_user_data().
+ *
+ * @param sess		The TURN client session.
+ * @param user_data	Arbitrary data.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
  */
 PJ_DECL(pj_status_t) pj_turn_session_set_user_data(pj_turn_session *sess,
@@ -293,10 +414,58 @@
 
 /**
- * Retrieve user data.
+ * Retrieve the previously assigned user data associated with this TURN
+ * session.
+ *
+ * @param sess		The TURN client session.
+ *
+ * @return		The user/application data.
  */
 PJ_DECL(void*) pj_turn_session_get_user_data(pj_turn_session *sess);
 
-/**
- * Set the server or domain name of the server.
+
+/**
+ * Configure message logging. By default all flags are enabled.
+ *
+ * @param sess		The TURN client session.
+ * @param flags		Bitmask combination of #pj_stun_sess_msg_log_flag
+ */
+PJ_DECL(void) pj_turn_session_set_log(pj_turn_session *sess,
+				      unsigned flags);
+
+
+/**
+ * Set the server or domain name of the server. Before the application
+ * can send Allocate request (with pj_turn_session_alloc()), it must first
+ * resolve the server address(es) using this function. This function will
+ * resolve the TURN server using DNS SRV resolution if the \a resolver
+ * is set. The server resolution process will complete asynchronously,
+ * and application will be notified in \a on_state() callback with the
+ * session state set to PJ_TURN_STATE_RESOLVED.
+ *
+ * Application may call with pj_turn_session_alloc() before the server
+ * resolution completes. In this case, the operation will be queued by
+ * the session, and it will be sent once the server resolution completes.
+ *
+ * @param sess		The TURN client session.
+ * @param domain	The domain, hostname, or IP address of the TURN
+ *			server. When this parameter contains domain name,
+ *			the \a resolver parameter must be set to activate
+ *			DNS SRV resolution.
+ * @param default_port	The default TURN port number to use when DNS SRV
+ *			resolution is not used. If DNS SRV resolution is
+ *			used, the server port number will be set from the
+ *			DNS SRV records.
+ * @param resolver	If this parameter is not NULL, then the \a domain
+ *			parameter will be first resolved with DNS SRV and
+ *			then fallback to using DNS A/AAAA resolution when
+ *			DNS SRV resolution fails. If this parameter is
+ *			NULL, the \a domain parameter will be resolved as
+ *			hostname.
+ *
+ * @return		PJ_SUCCESS if the operation has been successfully
+ *			queued, or the appropriate error code on failure.
+ *			When this function returns PJ_SUCCESS, the final
+ *			result of the resolution process will be notified
+ *			to application in \a on_state() callback.
  */
 PJ_DECL(pj_status_t) pj_turn_session_set_server(pj_turn_session *sess,
@@ -307,5 +476,13 @@
 
 /**
- * Set credential to be used by the session.
+ * Set credential to be used to authenticate against TURN server. 
+ * Application must call this function before sending Allocate request
+ * with pj_turn_session_alloc().
+ *
+ * @param sess		The TURN client session
+ * @param cred		STUN credential to be used.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
  */
 PJ_DECL(pj_status_t) pj_turn_session_set_credential(pj_turn_session *sess,
@@ -314,5 +491,24 @@
 
 /**
- * Create TURN allocation.
+ * Allocate a relay address/resource in the TURN server by sending TURN
+ * Allocate request. Application must first initiate the server resolution
+ * process with pj_turn_session_set_server() and set the credential to be
+ * used with pj_turn_session_set_credential() before calling this function.
+ *
+ * This function will complete asynchronously, and the application will be
+ * notified about the allocation result in \a on_state() callback. The 
+ * TURN session state will move to PJ_TURN_STATE_READY if allocation is
+ * successful, and PJ_TURN_STATE_DEALLOCATING or greater state if allocation
+ * has failed.
+ *
+ * Once allocation has been successful, the TURN session will keep this
+ * allocation alive until the session is destroyed, by sending periodic
+ * allocation refresh to the TURN server.
+ *
+ * @param sess		The TURN client session.
+ * @param param		Optional TURN allocation parameter.
+ *
+ * @return		PJ_SUCCESS if the operation has been successfully
+ *			initiated or the appropriate error code on failure.
  */
 PJ_DECL(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess,
@@ -321,14 +517,50 @@
 
 /**
- * Relay data to the specified peer through the session.
+ * Send a data to the specified peer address via the TURN relay. This 
+ * function will encapsulate the data as STUN Send Indication or TURN
+ * ChannelData packet and send the message to the TURN server. The TURN
+ * server then will send the data to the peer.
+ *
+ * The allocation (pj_turn_session_alloc()) must have been successfully
+ * created before application can relay any data.
+ *
+ * Since TURN session is transport independent, this function will
+ * ultimately call \a on_send_pkt() callback to request the application 
+ * to actually send the packet containing the data to the TURN server.
+ *
+ * @param sess		The TURN client session.
+ * @param pkt		The data/packet to be sent to peer.
+ * @param pkt_len	Length of the data.
+ * @param peer_addr	The remote peer address (the ultimate destination
+ *			of the data, and not the TURN server address).
+ * @param addr_len	Length of the address.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
  */
 PJ_DECL(pj_status_t) pj_turn_session_sendto(pj_turn_session *sess,
 					    const pj_uint8_t *pkt,
 					    unsigned pkt_len,
-					    const pj_sockaddr_t *addr,
+					    const pj_sockaddr_t *peer_addr,
 					    unsigned addr_len);
 
 /**
- * Bind a peer address to a channel number.
+ * Optionally establish channel binding for the specified a peer address.
+ * This function will assign a unique channel number for the peer address
+ * and request channel binding to the TURN server for this address. When
+ * a channel has been bound to a peer, the TURN client and TURN server
+ * will exchange data using ChannelData encapsulation format, which has
+ * lower bandwidth overhead than Send Indication (the default format used
+ * when peer address is not bound to a channel).
+ *
+ * This function will complete asynchronously, and application will be
+ * notified about the result in \a on_channel_bound() callback.
+ *
+ * @param sess		The TURN client session.
+ * @param peer		The remote peer address.
+ * @param addr_len	Length of the address.
+ *
+ * @return		PJ_SUCCESS if the operation has been successfully
+ *			initiated, or the appropriate error code on failure.
  */
 PJ_DECL(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess,
@@ -337,11 +569,25 @@
 
 /**
- * Notify TURN client session upon receiving a packet from server.
- * The packet maybe a STUN packet or ChannelData packet.
+ * Notify TURN client session upon receiving a packet from server. Since
+ * the TURN session is transport independent, it does not read packet from
+ * any sockets, and rather relies on application giving it packets that
+ * are received from the TURN server. The session then processes this packet
+ * and decides whether it is part of TURN protocol exchange or if it is a
+ * data to be reported back to user, which in this case it will call the
+ * \a on_rx_data() callback.
+ *
+ * @param sess		The TURN client session.
+ * @param pkt		The packet as received from the TURN server. This
+ *			should contain either STUN encapsulated message or
+ *			a ChannelData packet.
+ * @param pkt_len	The length of the packet.
+ *
+ * @return		The function may return non-PJ_SUCCESS if it receives
+ *			non-STUN and non-ChannelData packet, or if the
+ *			\a on_rx_data() returns non-PJ_SUCCESS;
  */
 PJ_DECL(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
-					       const pj_uint8_t *pkt,
-					       unsigned pkt_len,
-					       pj_bool_t is_datagram);
+					       void *pkt,
+					       unsigned pkt_len);
 
 
Index: pjproject/trunk/pjnath/include/pjnath/turn_sock.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/turn_sock.h	(revision 1987)
+++ pjproject/trunk/pjnath/include/pjnath/turn_sock.h	(revision 1988)
@@ -17,6 +17,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  */
-#ifndef __PJNATH_turn_sock_H__
-#define __PJNATH_turn_sock_H__
+#ifndef __PJNATH_TURN_SOCK_H__
+#define __PJNATH_TURN_SOCK_H__
 
 /**
@@ -32,27 +32,44 @@
 /* **************************************************************************/
 /**
- * @defgroup PJNATH_TURN_UDP TURN TCP client
- * @brief TURN relay using TCP client as transport protocol
- * @ingroup PJNATH_STUN
+ * @defgroup PJNATH_TURN_SOCK TURN client transport
+ * @brief Client transport utilizing TURN relay
+ * @ingroup PJNATH_TURN
  * @{
+ *
+ * The TURN relay client transport can be used to relay data from the client
+ * to peer via a TURN relay. The application establishes TURN connection to
+ * the TURN server using UDP or TCP as the transport, then creates a relay
+ * address in the TURN server to be advertised to remote peer(s) as the 
+ * transport address. When application sends data to a remote address via
+ * this transport, the data will be sent via the TURN relay, and vice versa.
  */
 
 
 /** 
- * Opaque declaration for TURN TCP client.
+ * Opaque declaration for TURN client.
  */
 typedef struct pj_turn_sock pj_turn_sock;
 
-
+/**
+ * This structure contains callbacks that will be called by the TURN
+ * transport.
+ */
 typedef struct pj_turn_sock_cb
 {
     /**
-     * Notification when incoming data has been received, either through
-     * Data indication or ChannelData message from the TURN server.
+     * Notification when incoming data has been received from the remote
+     * peer via the TURN server. The data reported in this callback will
+     * be the exact data as sent by the peer (e.g. the TURN encapsulation
+     * such as Data Indication or ChannelData will be removed before this
+     * function is called).
      *
-     * This callback is mandatory.
+     * @param turn_sock	    The TURN client transport.
+     * @param data	    The data as received from the peer.    
+     * @param data_len	    Length of the data.
+     * @param peer_addr	    The peer address.
+     * @param addr_len	    The length of the peer address.
      */
     void (*on_rx_data)(pj_turn_sock *turn_sock,
-		       const pj_uint8_t *pkt,
+		       void *pkt,
 		       unsigned pkt_len,
 		       const pj_sockaddr_t *peer_addr,
@@ -61,8 +78,12 @@
     /**
      * Notification when TURN session state has changed. Application should
-     * implement this callback to know that the TURN session is no longer
-     * available.
+     * implement this callback to monitor the progress of the TURN session.
+     *
+     * @param turn_sock	    The TURN client transport.
+     * @param old_state	    Previous state.
+     * @param new_state	    Current state.
      */
-    void (*on_state)(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
+    void (*on_state)(pj_turn_sock *turn_sock, 
+		     pj_turn_state_t old_state,
 		     pj_turn_state_t new_state);
 
@@ -71,5 +92,25 @@
 
 /**
- * Create.
+ * Create a TURN transport instance with the specified address family and
+ * connection type. Once TURN transport instance is created, application
+ * must call pj_turn_sock_alloc() to allocate a relay address in the TURN
+ * server.
+ *
+ * @param cfg		The STUN configuration which contains among other
+ *			things the ioqueue and timer heap instance for
+ *			the operation of this transport.
+ * @param af		Address family of the client connection. Currently
+ *			pj_AF_INET() and pj_AF_INET6() are supported.
+ * @param conn_type	Connection type to the TURN server. Both TCP and
+ *			UDP are supported.
+ * @param cb		Callback to receive events from the TURN transport.
+ * @param options	Option flags, currently this value must be zero.
+ * @param user_data	Arbitrary application data to be associated with
+ *			this transport.
+ * @param p_turn_sock	Pointer to receive the created instance of the
+ *			TURN transport.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
  */
 PJ_DECL(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg,
@@ -82,16 +123,35 @@
 
 /**
- * Destroy.
+ * Destroy the TURN transport instance. This will gracefully close the
+ * connection between the client and the TURN server. Although this
+ * function will return immediately, the TURN socket deletion may continue
+ * in the background and the application may still get state changes
+ * notifications from this transport.
+ *
+ * @param turn_sock	The TURN transport instance.
  */
 PJ_DECL(void) pj_turn_sock_destroy(pj_turn_sock *turn_sock);
 
-/**
- * Set user data.
+
+/**
+ * Associate a user data with this TURN transport. The user data may then
+ * be retrieved later with #pj_turn_sock_get_user_data().
+ *
+ * @param turn_sock	The TURN transport instance.
+ * @param user_data	Arbitrary data.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
  */
 PJ_DECL(pj_status_t) pj_turn_sock_set_user_data(pj_turn_sock *turn_sock,
-					       void *user_data);
-
-/**
- * Get user data.
+					        void *user_data);
+
+/**
+ * Retrieve the previously assigned user data associated with this TURN
+ * transport.
+ *
+ * @param turn_sock	The TURN transport instance.
+ *
+ * @return		The user/application data.
  */
 PJ_DECL(void*) pj_turn_sock_get_user_data(pj_turn_sock *turn_sock);
@@ -99,34 +159,136 @@
 
 /**
- * Get info.
+ * Get the TURN transport info. The transport info contains, among other
+ * things, the allocated relay address.
+ *
+ * @param turn_sock	The TURN transport instance.
+ * @param info		Pointer to be filled with TURN transport info.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
  */
 PJ_DECL(pj_status_t) pj_turn_sock_get_info(pj_turn_sock *turn_sock,
-					  pj_turn_session_info *info);
-
-/**
- * Initialize.
- */
-PJ_DECL(pj_status_t) pj_turn_sock_init(pj_turn_sock *turn_sock,
-				       const pj_str_t *domain,
-				       int default_port,
-				       pj_dns_resolver *resolver,
-				       const pj_stun_auth_cred *cred,
-				       const pj_turn_alloc_param *param);
-
-/**
- * Send packet.
+					   pj_turn_session_info *info);
+
+/**
+ * Acquire the internal mutex of the TURN transport. Application may need
+ * to call this function to synchronize access to other objects alongside 
+ * the TURN transport, to avoid deadlock.
+ *
+ * @param turn_sock	The TURN transport instance.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_turn_sock_lock(pj_turn_sock *turn_sock);
+
+
+/**
+ * Release the internal mutex previously held with pj_turn_sock_lock().
+ *
+ * @param turn_sock	The TURN transport instance.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
+ */
+PJ_DECL(pj_status_t) pj_turn_sock_unlock(pj_turn_sock *turn_sock);
+
+
+/**
+ * Set STUN message logging for this TURN session. 
+ * See #pj_stun_session_set_log().
+ *
+ * @param turn_sock	The TURN transport instance.
+ * @param flags		Bitmask combination of #pj_stun_sess_msg_log_flag
+ */
+PJ_DECL(void) pj_turn_sock_set_log(pj_turn_sock *turn_sock,
+				   unsigned flags);
+
+/**
+ * Allocate a relay address/resource in the TURN server. This function
+ * will resolve the TURN server using DNS SRV (if desired) and send TURN
+ * \a Allocate request using the specified credential to allocate a relay
+ * address in the server. This function completes asynchronously, and
+ * application will be notified when the allocation process has been
+ * successful in the \a on_state() callback when the state is set to
+ * PJ_TURN_STATE_READY. If the allocation fails, the state will be set
+ * to PJ_TURN_STATE_DEALLOCATING or greater.
+ *
+ * @param turn_sock	The TURN transport instance.
+ * @param domain	The domain, hostname, or IP address of the TURN
+ *			server. When this parameter contains domain name,
+ *			the \a resolver parameter must be set to activate
+ *			DNS SRV resolution.
+ * @param default_port	The default TURN port number to use when DNS SRV
+ *			resolution is not used. If DNS SRV resolution is
+ *			used, the server port number will be set from the
+ *			DNS SRV records.
+ * @param resolver	If this parameter is not NULL, then the \a domain
+ *			parameter will be first resolved with DNS SRV and
+ *			then fallback to using DNS A/AAAA resolution when
+ *			DNS SRV resolution fails. If this parameter is
+ *			NULL, the \a domain parameter will be resolved as
+ *			hostname.
+ * @param cred		The STUN credential to be used for the TURN server.
+ * @param param		Optional TURN allocation parameter.
+ *
+ * @return		PJ_SUCCESS if the operation has been successfully
+ *			queued, or the appropriate error code on failure.
+ *			When this function returns PJ_SUCCESS, the final
+ *			result of the allocation process will be notified
+ *			to application in \a on_state() callback.
+ *			
+ */
+PJ_DECL(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock,
+				        const pj_str_t *domain,
+				        int default_port,
+				        pj_dns_resolver *resolver,
+				        const pj_stun_auth_cred *cred,
+				        const pj_turn_alloc_param *param);
+
+/**
+ * Send a data to the specified peer address via the TURN relay. This 
+ * function will encapsulate the data as STUN Send Indication or TURN
+ * ChannelData packet and send the message to the TURN server. The TURN
+ * server then will send the data to the peer.
+ *
+ * The allocation (pj_turn_sock_alloc()) must have been successfully
+ * created before application can relay any data.
+ *
+ * @param turn_sock	The TURN transport instance.
+ * @param pkt		The data/packet to be sent to peer.
+ * @param pkt_len	Length of the data.
+ * @param peer_addr	The remote peer address (the ultimate destination
+ *			of the data, and not the TURN server address).
+ * @param addr_len	Length of the address.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
  */ 
 PJ_DECL(pj_status_t) pj_turn_sock_sendto(pj_turn_sock *turn_sock,
 					const pj_uint8_t *pkt,
 					unsigned pkt_len,
-					const pj_sockaddr_t *addr,
+					const pj_sockaddr_t *peer_addr,
 					unsigned addr_len);
 
 /**
- * Bind a peer address to a channel number.
+ * Optionally establish channel binding for the specified a peer address.
+ * This function will assign a unique channel number for the peer address
+ * and request channel binding to the TURN server for this address. When
+ * a channel has been bound to a peer, the TURN transport and TURN server
+ * will exchange data using ChannelData encapsulation format, which has
+ * lower bandwidth overhead than Send Indication (the default format used
+ * when peer address is not bound to a channel).
+ *
+ * @param turn_sock	The TURN transport instance.
+ * @param peer		The remote peer address.
+ * @param addr_len	Length of the address.
+ *
+ * @return		PJ_SUCCESS if the operation has been successful,
+ *			or the appropriate error code on failure.
  */
 PJ_DECL(pj_status_t) pj_turn_sock_bind_channel(pj_turn_sock *turn_sock,
-					      const pj_sockaddr_t *peer,
-					      unsigned addr_len);
+					       const pj_sockaddr_t *peer,
+					       unsigned addr_len);
 
 
@@ -139,4 +301,4 @@
 
 
-#endif	/* __PJNATH_turn_sock_H__ */
-
+#endif	/* __PJNATH_TURN_SOCK_H__ */
+
Index: pjproject/trunk/pjnath/include/pjnath/types.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/types.h	(revision 1987)
+++ pjproject/trunk/pjnath/include/pjnath/types.h	(revision 1988)
@@ -36,4 +36,11 @@
 
 /**
+ * This constant describes a number to be used to identify an invalid TURN
+ * channel number.
+ */
+#define PJ_TURN_INVALID_CHANNEL	    0xFFFF
+
+
+/**
  * Initialize pjnath library.
  *
@@ -44,5 +51,5 @@
 
 /**
- * Display error to the log.
+ * Display error to the log. 
  *
  * @param sender    The sender name.
@@ -68,165 +75,214 @@
 
 /**
- * @mainpage PJNATH - Open Source ICE, STUN, and TURN Library
- *
- * \n
- * This is the documentation of PJNATH, an Open Source library providing
- * NAT traversal helper functionalities by using standard based protocols.
- *
- * \n
-
- * \section PJNATH_STUN STUN Protocol Library
- *
- * Session Traversal Utilities (STUN, or previously known as Simple 
- * Traversal of User Datagram Protocol (UDP) Through Network Address 
- * Translators (NAT)s), is a lightweight protocol that serves as a tool for
- * application protocols in dealing with NAT traversal. It allows a client
- * to determine the IP address and port allocated to them by a NAT and to 
- * keep NAT bindings open.
- * 
- * The PJNATH library provides facilities to support both the core 
- * <B>STUN-bis</B> specification and the <B>TURN</B> usage of STUN, 
- * as well as other STUN usages. Please see #pj_stun_attr_type for 
- * list of STUN attributes supported by this library.
- *
- * 
- * The following are some design principles that have been utilized
- * when implementing the STUN library in PJNATH:
- *
- *  - layered architecture, with \ref PJNATH_STUN_MSG as the lowest
- *    layer and \ref PJNATH_STUN_SESSION as the highest abstraction
- *    layer, to accommodate various usage scenario of the library.
- *
- *  - no transport -- the STUN library is pretty much transport
- *    independent and all sending and receiving functionalities will
- *    have to be implemented by application or higher level
- *    abstraction (such as ICE). This helps facilitating an even
- *    more usage scenarios of the library.
- *
- *  - common functionalities for both STUN client and server
- *    development. All STUN components can be used to develop both
- *    STUN client and STUN server application, and in fact, in ICE,
- *    both STUN client and server functionality exist in a single
- *    ICE session.
- *
- * \n
- *
- * \subsection PJNATH_STUN_ARCH STUN Library Organization
- *
- * \image html stun-arch.jpg "STUN Library Architecture"
- *
- * The STUN library is organized as follows:
- *
- *  - for both client and server, the highest abstraction is
- *    \ref PJNATH_STUN_SESSION, which provides management of incoming
- *    and outgoing messages and association of STUN credential to
- *    a STUN session. 
- *
- *  - for client, the next layer below is \ref PJNATH_STUN_TRANSACTION,
- *    which manages retransmissions of STUN request. Server side STUN
- *    transaction is handled in \ref PJNATH_STUN_SESSION layer above.
- *
- *  - \ref PJNATH_STUN_AUTH provides mechanism to verify STUN
- *    credential in incoming STUN messages.
- *
- *  - the lowest layer of the library is \ref PJNATH_STUN_MSG. This layer
- *    provides STUN message representation, validation, parsing, 
- *    encoding MESSAGE-INTEGRITY for outgoing messages, and
- *    debugging (dump to log) of STUN messages.
- *
- * All STUN library components are independent of any transports. 
- * Application gives incoming packet to the STUN components for processing,
- * and it must supply the STUN components with callback to send outgoing 
- * messages.
- * 
- *
- * \n
- *
- * \subsection PJNATH_STUN_CLASSES PJNATH Class Diagram
- *
- * 
- * \image html UML-class-diagram.png "Class Diagram"
- *
- * TBD: write descriptions.
- *
- * \subsection PJNATH_STUN_USING Using STUN Library
- *
- * [The developers guide documentation can certainly be improved here]
- *
- * For a sample STUN and TURN client, please see <tt>pjstun-client</tt>
- * project under <tt>pjnath/src</tt> directory.
- *
- * For a sample STUN and TURN server, please see <tt>pjstun-srv-test</tt>
- * project under <tt>pjnath/src</tt> directory.
- *
- *
- * \subsection PJNATH_STUN_REF STUN Reference
- *
- * References for STUN:
- *
- *  - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-rfc3489bis-15.txt">
- *    <B>draft-ietf-behave-rfc3489bis-15</b></A>: Session Traversal 
- *     Utilities for (NAT) (STUN),
- *  - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-turn-07.txt">
- *    <B>draft-ietf-behave-turn-07</B></A>: Obtaining Relay Addresses 
- *    from Simple Traversal Underneath NAT (STUN)
- *  - Obsoleted: <A HREF="http://www.ietf.org/rfc/rfc3489.txt">RFC 3489</A>.
- *
- * \n
- *
- * \section PJNATH_ICE ICE Implementation
- *
- * Interactive Connectivity Establishment (ICE) is a standard based 
- * methodology for traversing Network Address Translator (NAT), and
- * is described in 
- * <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-mmusic-ice-19.txt">
- * <B>draft-ietf-mmusic-ice-19.txt</B></A> draft. The PJNATH ICE
- * implementation is aimed to provide a usable and generic ICE transports
- * for different types of application, including but not limited to
- * the usage of ICE in SIP/SDP offer/answer.
- * 
- *
- * \subsection PJNATH_ICE_ARCH ICE Library Organization
- * 
- * \image html ice-arch.jpg "ICE Architecture"
- *
- * The ICE library is organized as follows:
- *
- *  - the highest abstraction is ICE media transport, which maintains
- *    ICE stream transport and provides SDP translations to be used
- *    for SIP offer/answer exchanges. ICE media transport is part
- *    of PJMEDIA library.
- *
- *  - higher in the hierarchy is \ref PJNATH_ICE_STREAM_TRANSPORT,
- *    which binds ICE with UDP sockets, and provides STUN binding
- *    and relay/TURN allocation for the sockets. This component can
- *    be directly used by application, although normally application
- *    should use the next higher abstraction since it provides
- *    SDP translations and better integration with other PJ libraries
- *    such as PJSIP and PJMEDIA.
- *
- *  - the lowest layer is \ref PJNATH_ICE_SESSION, which provides 
- *    ICE management and negotiation in a transport-independent way.
- *    This layer contains the state machines to perform ICE
- *    negotiation, and provides the most flexibility to control all
- *    aspects of ICE session. This layer normally is only usable for
- *    ICE implementors.
- *
- * \subsection PJNATH_ICE_USING Using the ICE Library
- *
- * For ICE implementation that has been integrated with socket transport,
- * please see \ref PJNATH_ICE_STREAM_TRANSPORT_USING.
- *
- * For ICE implementation that has not been integrated with socket
- * transport, please see \ref pj_ice_sess_using_sec.
- *
- * \subsection PJNATH_ICE_REF Reference
- *
- * References for ICE:
- *  - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-mmusic-ice-19.txt">
- *    <B>draft-ietf-mmusic-ice-19.txt</B></A>: Interactive Connectivity 
- *    Establishment (ICE): A Methodology for Network Address Translator 
- *    (NAT) Traversal for Offer/Answer Protocols
- */
+
+@mainpage PJNATH - Open Source ICE, STUN, and TURN Library
+
+\n
+This is the documentation of PJNATH, an Open Source library providing
+NAT traversal helper functionalities by using standard based protocols
+such as STUN, TURN, and ICE.
+
+\n
+\n
+
+\section lib_comps Library Components
+
+\subsection comp_stun STUN
+
+Session Traversal Utilities (STUN, or previously known as Simple 
+Traversal of User Datagram Protocol (UDP) Through Network Address 
+Translators (NAT)s), is a lightweight protocol that serves as a tool for
+application protocols in dealing with NAT traversal. It allows a client
+to determine the IP address and port allocated to them by a NAT and to 
+keep NAT bindings open.
+
+This version of PJNATH implements the following STUN-bis draft:
+- <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-rfc3489bis-15.txt">
+  <B>draft-ietf-behave-rfc3489bis-15</b></A>: Session Traversal 
+    Utilities for (NAT) (STUN),
+
+
+\subsection comp_turn TURN
+
+Traversal Using Relays around NAT (TURN) allows the host to control the
+operation of the relay and to exchange packets with its peers using the relay.
+
+This version of PJNATH implements both TCP and UDP client transport and it
+complies with the following TURN draft:
+ - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-turn-07.txt">
+   <B>draft-ietf-behave-turn-07</B></A>: Obtaining Relay Addresses 
+   from Simple Traversal Underneath NAT (STUN)
+
+
+\subsection comp_ice ICE
+
+Interactive Connectivity Establishment (ICE) is a standard based 
+methodology for traversing Network Address Translator (NAT). This
+implementation is aimed to provide a usable and generic ICE transports
+for different types of application, including but not limited to
+the usage of ICE in SIP/SDP offer/answer.
+
+
+This version of PJNATH implements the following ICE draft:
+ - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-mmusic-ice-19.txt">
+   <B>draft-ietf-mmusic-ice-19.txt</B></A> draft. The PJNATH ICE
+
+
+\subsection comp_natck NAT Classification Utility
+
+The PJNATH library also provides NAT classification utility as 
+described in <A HREF="http://www.ietf.org/rfc/rfc3489.txt">RFC 3489</A>.
+While the practice to detect the NAT type to assist NAT traversal
+has been deprecated in favor of ICE, the information may still be
+useful for troubleshooting purposes, hence the utility is provided.
+
+
+\n
+\n
+
+\section lib_org Library Organization
+
+The PJNATH library consists of many components with each providing
+specific functionality that may or may not be of the interests of 
+applications (or application developers). This section attempts to 
+give brief overview on the components provided by PJNATH.
+
+The PJNATH components from the highest layer to the lower layer are
+as follows.
+
+
+\n
+
+\subsection user_comp High-level Transport Objects
+
+PJNATH library provides some high-level objects that may be used
+by applications:
+
+
+\subsubsection stun_sock STUN Transport
+
+The \ref PJNATH_STUN_SOCK provides asynchronous UDP like socket transport
+with the additional capability to query the publicly mapped transport
+address (using STUN resolution), to refresh the NAT binding, and to
+demultiplex internal STUN messages from application data (the 
+application data may be a STUN message as well).
+
+
+\subsubsection turn_sock TURN Client Transport
+
+The \ref PJNATH_TURN_SOCK may be used by the application to send and
+receive data via TURN server. For more information please see the
+documentation of \ref PJNATH_TURN_SOCK.
+
+
+\subsubsection ice_strans ICE Stream Transport
+
+The \ref PJNATH_ICE_STREAM_TRANSPORT provides transport interface to
+send and receive data through connection that is negotiated
+with ICE protocol. The \ref PJNATH_ICE_STREAM_TRANSPORT naturally 
+contains both STUN Transport and \ref PJNATH_TURN_SOCK.
+
+The \ref PJNATH_ICE_STREAM_TRANSPORT interface is suitable for both
+SIP or non-SIP use. For SIP use, application may prefer to use the
+ICE media transport in PJMEDIA instead where it has been integrated
+with the SDP offer and answer mechanism.
+
+
+\subsubsection natck NAT Classification Utility
+
+PJNATH also provides \a PJNATH_NAT_DETECT to assist troubleshooting
+of problems related to NAT traversal.
+
+
+
+\n
+
+
+\subsection sessions Transport Independent Sessions Layer
+
+Right below the high level transports objects are the transport
+independent sessions. These sessions don't have access to sockets,
+so higher level objects (such as transports) must give incoming
+packets to the sessions and provide callback to be called by
+sessions to send outgoing packets.
+
+
+\subsubsection ice_sess ICE Session
+
+The \ref PJNATH_ICE_SESSION is used by the \ref PJNATH_ICE_STREAM_TRANSPORT
+and contains the actual logic of the ICE negotiation.
+
+
+\subsubsection turn_sess TURN Session
+
+The \ref PJNATH_TURN_SESSION is used by the \ref PJNATH_TURN_SOCK
+and it contains TURN protocol logic. Implementors may implement
+other types of TURN client connection (such as TURN TLS client)
+by utilizing this session.
+
+
+\subsubsection stun_sess STUN Session
+
+The \ref PJNATH_STUN_SESSION manages STUN message exchange between
+a client and server (or vice versa). It manages \ref PJNATH_STUN_TRANSACTION
+for sending or receiving requests and \ref PJNATH_STUN_AUTH for both
+both incoming and outgoing STUN messages. 
+
+The \ref PJNATH_STUN_SESSION is naturally used by the \ref PJNATH_TURN_SESSION
+and \ref PJNATH_ICE_SESSION
+
+
+\n
+
+\subsection stun_tsx STUN Transaction Layer
+
+The \ref PJNATH_STUN_TRANSACTION is a thin layer to manage retransmission
+of STUN requests.
+
+
+\n
+
+
+\subsection stun_msg STUN Messaging Layer
+
+At the very bottom of the PJNATH components is the \ref PJNATH_STUN_MSG
+layer. The API contains various representation of STUN messaging components
+and it provides API to encode and decode STUN messages.
+
+
+
+\n
+\n
+
+\section class_dia Class Diagram
+
+
+The following class diagram shows the interactions between objects in
+PJNATH:
+
+\image html UML-class-diagram.png "Class Diagram"
+\image latex UML-class-diagram.png "Class Diagram"
+
+
+
+\n
+\n
+
+\section samples Sample Applications
+
+
+Some sample applications have been provided with PJNATH, and it's available
+under <tt>pjnath/src</tt> directory:
+
+   - <b>pjturn-client</b>: this is a stand-alone, console based TURN client
+     application to be used as a demonstration for PJNATH TURN client 
+     transport API and for simple testing against TURN server implementations.
+     The client supports both UDP and TCP connection to the TURN server.
+
+   - <b>pjturn-srv</b>: this is a simple TURN server to be used for testing
+     purposes. It supports both UDP and TCP connections to the clients.
+
+
+*/
 
 /**
Index: pjproject/trunk/pjnath/src/pjnath-test/ice_test.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath-test/ice_test.c	(revision 1987)
+++ pjproject/trunk/pjnath/src/pjnath-test/ice_test.c	(revision 1988)
@@ -18,91 +18,404 @@
  */
 #include "test.h"
-
-#define THIS_FILE   "ice_test.c"
-
-
-struct ice_data
-{
-    const char	   *obj_name;
-    pj_bool_t	    complete;
-    pj_status_t	    err_code;
-    unsigned	    rx_rtp_cnt;
-    unsigned	    rx_rtcp_cnt;
-
-    unsigned	    rx_rtp_count;
-    char	    last_rx_rtp_data[32];
-    unsigned	    rx_rtcp_count;
-    char	    last_rx_rtcp_data[32];
+#include "server.h"
+
+enum
+{
+    NO	= 0,
+    YES	= 1,
+    SRV	= 3,
 };
 
-static pj_stun_config stun_cfg;
-
-static void on_ice_complete(pj_ice_strans *icest, 
-			    pj_status_t status)
-{
-    struct ice_data *id = (struct ice_data*) icest->user_data;
-    id->complete = PJ_TRUE;
-    id->err_code = status;
-    PJ_LOG(3,(THIS_FILE, "     ICE %s complete %s", id->obj_name,
-	      (status==PJ_SUCCESS ? "successfully" : "with failure")));
+#define NODELAY		0xFFFFFFFF
+#define SRV_DOMAIN	"pjsip.lab.domain"
+
+#define INDENT		"    "
+
+/* Client flags */
+enum
+{
+    WRONG_TURN	= 1,
+    DEL_ON_ERR	= 2,
+};
+
+
+/* Test results */
+struct test_result
+{
+    pj_status_t	init_status;	/* init successful?		*/
+    pj_status_t	nego_status;	/* negotiation successful?	*/
+    unsigned	rx_cnt[4];	/* Number of data received	*/
+};
+
+
+/* Test session configuration */
+struct test_cfg
+{
+    pj_ice_sess_role role;	/* Role.			*/
+    unsigned	comp_cnt;	/* Component count		*/
+    unsigned    enable_host;	/* Enable host candidates	*/
+    unsigned    enable_stun;	/* Enable srflx candidates	*/
+    unsigned    enable_turn;	/* Enable turn candidates	*/
+    unsigned	client_flag;	/* Client flags			*/
+
+    unsigned    answer_delay;	/* Delay before sending SDP	*/
+    unsigned    send_delay;	/* Delay before sending data	*/
+    unsigned    destroy_delay;	/* Delay before destroy()	*/
+
+    struct test_result expected;/* Expected result		*/
+};
+
+/* ICE endpoint state */
+struct ice_ept
+{
+    struct test_cfg	 cfg;	/* Configuratino.		*/
+    pj_ice_strans	*ice;	/* ICE stream transport		*/
+    struct test_result	 result;/* Test result.			*/
+
+    pj_str_t		 ufrag;	/* username fragment.		*/
+    pj_str_t		 pass;	/* password			*/
+};
+
+/* The test session */
+struct test_sess
+{
+    pj_pool_t		*pool;
+    pj_stun_config	*stun_cfg;
+    pj_dns_resolver	*resolver;
+
+    test_server		*server;
+
+    unsigned		 server_flag;
+    struct ice_ept	 caller;
+    struct ice_ept	 callee;
+};
+
+
+static void ice_on_rx_data(pj_ice_strans *ice_st,
+			   unsigned comp_id, 
+			   void *pkt, pj_size_t size,
+			   const pj_sockaddr_t *src_addr,
+			   unsigned src_addr_len);
+static void ice_on_ice_complete(pj_ice_strans *ice_st, 
+			        pj_ice_strans_op op,
+			        pj_status_t status);
+static void destroy_sess(struct test_sess *sess, unsigned wait_msec);
+
+/* Create ICE stream transport */
+static int create_ice_strans(struct test_sess *test_sess,
+			     struct ice_ept *ept,
+			     pj_ice_strans **p_ice)
+{
+    pj_ice_strans *ice;
+    pj_ice_strans_cb ice_cb;
+    pj_ice_strans_cfg ice_cfg;
+    pj_sockaddr hostip;
+    char serverip[PJ_INET6_ADDRSTRLEN];
+    pj_status_t status;
+
+    status = pj_gethostip(pj_AF_INET(), &hostip);
+    if (status != PJ_SUCCESS)
+	return -1030;
+
+    pj_sockaddr_print(&hostip, serverip, sizeof(serverip), 0);
+
+    /* Init callback structure */
+    pj_bzero(&ice_cb, sizeof(ice_cb));
+    ice_cb.on_rx_data = &ice_on_rx_data;
+    ice_cb.on_ice_complete = &ice_on_ice_complete;
+
+    /* Init ICE stream transport configuration structure */
+    pj_ice_strans_cfg_default(&ice_cfg);
+    pj_memcpy(&ice_cfg.stun_cfg, test_sess->stun_cfg, sizeof(pj_stun_config));
+    if ((ept->cfg.enable_stun & SRV)==SRV || (ept->cfg.enable_turn & SRV)==SRV)
+	ice_cfg.resolver = test_sess->resolver;
+
+    if (ept->cfg.enable_stun & YES) {
+	if ((ept->cfg.enable_stun & SRV) == SRV) {
+	    ice_cfg.stun.server = pj_str(SRV_DOMAIN);
+	} else {
+	    ice_cfg.stun.server = pj_str(serverip);
+	}
+	ice_cfg.stun.port = STUN_SERVER_PORT;
+    }
+
+    if (ept->cfg.enable_host == 0) {
+	ice_cfg.stun.no_host_cands = PJ_TRUE;
+    } else {
+	ice_cfg.stun.no_host_cands = PJ_FALSE;
+	ice_cfg.stun.loop_addr = PJ_TRUE;
+    }
+
+
+    if (ept->cfg.enable_turn & YES) {
+	if ((ept->cfg.enable_turn & SRV) == SRV) {
+	    ice_cfg.turn.server = pj_str(SRV_DOMAIN);
+	} else {
+	    ice_cfg.turn.server = pj_str(serverip);
+	}
+	ice_cfg.turn.port = TURN_SERVER_PORT;
+	ice_cfg.turn.conn_type = PJ_TURN_TP_UDP;
+	ice_cfg.turn.auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
+	ice_cfg.turn.auth_cred.data.static_cred.realm = pj_str(SRV_DOMAIN);
+	if (ept->cfg.client_flag & WRONG_TURN)
+	    ice_cfg.turn.auth_cred.data.static_cred.username = pj_str("xxx");
+	else
+	    ice_cfg.turn.auth_cred.data.static_cred.username = pj_str(TURN_USERNAME);
+	ice_cfg.turn.auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
+	ice_cfg.turn.auth_cred.data.static_cred.data = pj_str(TURN_PASSWD);
+    }
+
+    /* Create ICE stream transport */
+    status = pj_ice_strans_create(NULL, &ice_cfg, ept->cfg.comp_cnt,
+				  (void*)ept, &ice_cb,
+				  &ice);
+    if (status != PJ_SUCCESS) {
+	app_perror(INDENT "err: pj_ice_strans_create()", status);
+	return status;
+    }
+
+    pj_create_unique_string(test_sess->pool, &ept->ufrag);
+    pj_create_unique_string(test_sess->pool, &ept->pass);
+
+    /* Looks alright */
+    *p_ice = ice;
+    return PJ_SUCCESS;
 }
-
-
-static void on_rx_data(pj_ice_strans *icest, unsigned comp_id, 
-		       void *pkt, pj_size_t size,
-		       const pj_sockaddr_t *src_addr,
-		       unsigned src_addr_len)
-{
-    struct ice_data *id = (struct ice_data*) icest->user_data;
-
-    if (comp_id == 1) {
-	id->rx_rtp_cnt++;
-	pj_memcpy(id->last_rx_rtp_data, pkt, size);
-	id->last_rx_rtp_data[size] = '\0';
-    } else if (comp_id == 2) {
-	id->rx_rtcp_cnt++;
-	pj_memcpy(id->last_rx_rtcp_data, pkt, size);
-	id->last_rx_rtcp_data[size] = '\0';
-    } else {
-	pj_assert(!"Invalid component ID");
-    }
-
+			     
+/* Create test session */
+static int create_sess(pj_stun_config *stun_cfg,
+		       unsigned server_flag,
+		       struct test_cfg *caller_cfg,
+		       struct test_cfg *callee_cfg,
+		       struct test_sess **p_sess)
+{
+    pj_pool_t *pool;
+    struct test_sess *sess;
+    pj_str_t ns_ip;
+    pj_uint16_t ns_port;
+    unsigned flags;
+    pj_status_t status;
+
+    /* Create session structure */
+    pool = pj_pool_create(mem, "testsess", 512, 512, NULL);
+    sess = PJ_POOL_ZALLOC_T(pool, struct test_sess);
+    sess->pool = pool;
+    sess->stun_cfg = stun_cfg;
+
+    pj_memcpy(&sess->caller.cfg, caller_cfg, sizeof(*caller_cfg));
+    sess->caller.result.init_status = sess->caller.result.nego_status = PJ_EPENDING;
+
+    pj_memcpy(&sess->callee.cfg, callee_cfg, sizeof(*callee_cfg));
+    sess->callee.result.init_status = sess->callee.result.nego_status = PJ_EPENDING;
+
+    /* Create server */
+    flags = server_flag;
+    status = create_test_server(stun_cfg, flags, SRV_DOMAIN, &sess->server);
+    if (status != PJ_SUCCESS) {
+	app_perror(INDENT "error: create_test_server()", status);
+	destroy_sess(sess, 500);
+	return -10;
+    }
+    sess->server->turn_respond_allocate = 
+	sess->server->turn_respond_refresh = PJ_TRUE;
+
+    /* Create resolver */
+    status = pj_dns_resolver_create(mem, NULL, 0, stun_cfg->timer_heap,
+				    stun_cfg->ioqueue, &sess->resolver);
+    if (status != PJ_SUCCESS) {
+	app_perror(INDENT "error: pj_dns_resolver_create()", status);
+	destroy_sess(sess, 500);
+	return -20;
+    }
+
+    ns_ip = pj_str("127.0.0.1");
+    ns_port = (pj_uint16_t)DNS_SERVER_PORT;
+    status = pj_dns_resolver_set_ns(sess->resolver, 1, &ns_ip, &ns_port);
+    if (status != PJ_SUCCESS) {
+	app_perror( INDENT "error: pj_dns_resolver_set_ns()", status);
+	destroy_sess(sess, 500);
+	return -21;
+    }
+
+    /* Create caller ICE stream transport */
+    status = create_ice_strans(sess, &sess->caller, &sess->caller.ice);
+    if (status != PJ_SUCCESS) {
+	destroy_sess(sess, 500);
+	return -30;
+    }
+
+    /* Create callee ICE stream transport */
+    status = create_ice_strans(sess, &sess->callee, &sess->callee.ice);
+    if (status != PJ_SUCCESS) {
+	destroy_sess(sess, 500);
+	return -40;
+    }
+
+    *p_sess = sess;
+    return 0;
+}
+
+/* Destroy test session */
+static void destroy_sess(struct test_sess *sess, unsigned wait_msec)
+{
+    if (sess->caller.ice) {
+	pj_ice_strans_destroy(sess->caller.ice);
+	sess->caller.ice = NULL;
+    }
+
+    if (sess->callee.ice) {
+	pj_ice_strans_destroy(sess->callee.ice);
+	sess->callee.ice = NULL;
+    }
+
+    poll_events(sess->stun_cfg, wait_msec, PJ_FALSE);
+
+    if (sess->resolver) {
+	pj_dns_resolver_destroy(sess->resolver, PJ_FALSE);
+	sess->resolver = NULL;
+    }
+
+    if (sess->server) {
+	destroy_test_server(sess->server);
+	sess->server = NULL;
+    }
+
+    if (sess->pool) {
+	pj_pool_t *pool = sess->pool;
+	sess->pool = NULL;
+	pj_pool_release(pool);
+    }
+}
+
+static void ice_on_rx_data(pj_ice_strans *ice_st,
+			   unsigned comp_id, 
+			   void *pkt, pj_size_t size,
+			   const pj_sockaddr_t *src_addr,
+			   unsigned src_addr_len)
+{
+    struct ice_ept *ept;
+
+    PJ_UNUSED_ARG(pkt);
+    PJ_UNUSED_ARG(size);
     PJ_UNUSED_ARG(src_addr);
     PJ_UNUSED_ARG(src_addr_len);
+
+    ept = (struct ice_ept*) pj_ice_strans_get_user_data(ice_st);
+    ept->result.rx_cnt[comp_id]++;
 }
 
 
-static void handle_events(unsigned msec_timeout)
-{
-    pj_time_val delay;
-
-    pj_timer_heap_poll(stun_cfg.timer_heap, NULL);
-
-    delay.sec = 0;
-    delay.msec = msec_timeout;
-    pj_time_val_normalize(&delay);
-
-    pj_ioqueue_poll(stun_cfg.ioqueue, &delay);
+static void ice_on_ice_complete(pj_ice_strans *ice_st, 
+			        pj_ice_strans_op op,
+			        pj_status_t status)
+{
+    struct ice_ept *ept;
+
+    ept = (struct ice_ept*) pj_ice_strans_get_user_data(ice_st);
+    switch (op) {
+    case PJ_ICE_STRANS_OP_INIT:
+	ept->result.init_status = status;
+	if (status != PJ_SUCCESS && (ept->cfg.client_flag & DEL_ON_ERR)) {
+	    pj_ice_strans_destroy(ice_st);
+	    ept->ice = NULL;
+	}
+	break;
+    case PJ_ICE_STRANS_OP_NEGOTIATION:
+	ept->result.nego_status = status;
+	break;
+    default:
+	pj_assert(!"Unknown op");
+    }
 }
 
 
-/* Basic create and destroy test */
-static int ice_basic_create_destroy_test()
-{
-    pj_ice_strans *im;
-    pj_ice_strans_cb icest_cb;
+/* Start ICE negotiation on the endpoint, based on parameter from
+ * the other endpoint.
+ */
+static pj_status_t start_ice(struct ice_ept *ept, const struct ice_ept *remote)
+{
+    pj_ice_sess_cand rcand[32];
+    unsigned i, rcand_cnt = 0;
     pj_status_t status;
 
-    PJ_LOG(3,(THIS_FILE, "...basic create/destroy"));
-
-    pj_bzero(&icest_cb, sizeof(icest_cb));
-    icest_cb.on_ice_complete = &on_ice_complete;
-    icest_cb.on_rx_data = &on_rx_data;
-
-    status = pj_ice_strans_create(&stun_cfg, "icetest", 2, NULL, &icest_cb, &im);
-    if (status != PJ_SUCCESS)
-	return -10;
-
-    pj_ice_strans_destroy(im);
+    /* Enum remote candidates */
+    for (i=0; i<remote->cfg.comp_cnt; ++i) {
+	unsigned cnt = PJ_ARRAY_SIZE(rcand) - rcand_cnt;
+	status = pj_ice_strans_enum_cands(remote->ice, i+1, &cnt, rcand+rcand_cnt);
+	if (status != PJ_SUCCESS) {
+	    app_perror(INDENT "err: pj_ice_strans_enum_cands()", status);
+	    return status;
+	}
+	rcand_cnt += cnt;
+    }
+
+    status = pj_ice_strans_start_ice(ept->ice, &remote->ufrag, &remote->pass,
+				     rcand_cnt, rcand);
+    if (status != PJ_SUCCESS) {
+	app_perror(INDENT "err: pj_ice_strans_start_ice()", status);
+	return status;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/* Check that the pair in both agents are matched */
+static int check_pair(const struct ice_ept *ept1, const struct ice_ept *ept2,
+		      int start_err)
+{
+    unsigned i, min_cnt, max_cnt;
+
+    if (ept1->cfg.comp_cnt < ept2->cfg.comp_cnt) {
+	min_cnt = ept1->cfg.comp_cnt;
+	max_cnt = ept2->cfg.comp_cnt;
+    } else {
+	min_cnt = ept2->cfg.comp_cnt;
+	max_cnt = ept1->cfg.comp_cnt;
+    }
+
+    /* Must have valid pair for common components */
+    for (i=0; i<min_cnt; ++i) {
+	const pj_ice_sess_check *c1;
+	const pj_ice_sess_check *c2;
+
+	c1 = pj_ice_strans_get_valid_pair(ept1->ice, i+1);
+	if (c1 == NULL) {
+	    PJ_LOG(3,("", INDENT "err: unable to get valid pair for ice1 "
+			  "component %d", i+1));
+	    return start_err - 2;
+	}
+
+	c2 = pj_ice_strans_get_valid_pair(ept2->ice, i+1);
+	if (c2 == NULL) {
+	    PJ_LOG(3,("", INDENT "err: unable to get valid pair for ice2 "
+			  "component %d", i+1));
+	    return start_err - 4;
+	}
+
+	if (pj_sockaddr_cmp(&c1->rcand->addr, &c2->lcand->addr) != 0) {
+	    PJ_LOG(3,("", INDENT "err: candidate pair does not match "
+			  "for component %d", i+1));
+	    return start_err - 6;
+	}
+    }
+
+    /* Extra components must not have valid pair */
+    for (; i<max_cnt; ++i) {
+	if (ept1->cfg.comp_cnt>i &&
+	    pj_ice_strans_get_valid_pair(ept1->ice, i+1) != NULL) 
+	{
+	    PJ_LOG(3,("", INDENT "err: ice1 shouldn't have valid pair "
+		          "for component %d", i+1));
+	    return start_err - 8;
+	}
+	if (ept2->cfg.comp_cnt>i &&
+	    pj_ice_strans_get_valid_pair(ept2->ice, i+1) != NULL) 
+	{
+	    PJ_LOG(3,("", INDENT "err: ice2 shouldn't have valid pair "
+		          "for component %d", i+1));
+	    return start_err - 9;
+	}
+    }
 
     return 0;
@@ -110,63 +423,168 @@
 
 
-static pj_status_t start_ice(pj_ice_strans *ist, pj_ice_strans *remote)
-{
-    unsigned count;
-    pj_ice_sess_cand cand[PJ_ICE_MAX_CAND];
-    pj_status_t status;
-
-    count = PJ_ARRAY_SIZE(cand);
-    status = pj_ice_strans_enum_cands(remote, &count, cand);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    return pj_ice_strans_start_ice(ist, &remote->ice->rx_ufrag, &remote->ice->rx_pass,
-			       count, cand);
-}
-
-
-struct dummy_cand
-{
-    unsigned		 comp_id;
-    pj_ice_cand_type	 type;
-    const char		*addr;
-    unsigned		 port;
-};
-
-static int init_ice_st(pj_ice_strans *ice_st,
-		       pj_bool_t add_valid_comp,
-		       unsigned dummy_cnt,
-		       struct dummy_cand cand[])
-{
-    pj_str_t a;
-    pj_status_t status;
-    unsigned i;
-
-    /* Create components */
-    for (i=0; i<ice_st->comp_cnt; ++i) {
-	status = pj_ice_strans_create_comp(ice_st, i+1, PJ_ICE_ST_OPT_DONT_ADD_CAND, NULL);
-	if (status != PJ_SUCCESS)
-	    return -21;
-    }
-
-    /* Add dummy candidates */
-    for (i=0; i<dummy_cnt; ++i) {
-	pj_sockaddr_in addr;
-
-	pj_sockaddr_in_init(&addr, pj_cstr(&a, cand[i].addr), (pj_uint16_t)cand[i].port);
-	status = pj_ice_strans_add_cand(ice_st, cand[i].comp_id, cand[i].type,
-				    65535, &addr, PJ_FALSE);
-	if (status != PJ_SUCCESS)
-	    return -22;
-    }
-
-    /* Add the real candidate */
-    if (add_valid_comp) {
-	for (i=0; i<ice_st->comp_cnt; ++i) {
-	    status = pj_ice_strans_add_cand(ice_st, i+1, PJ_ICE_CAND_TYPE_HOST, 65535,
-					&ice_st->comp[i]->local_addr.ipv4, PJ_TRUE);
-	    if (status != PJ_SUCCESS)
-		return -23;
-	}
+#define WAIT_UNTIL(timeout,expr, RC)  { \
+				pj_time_val t0, t; \
+				pj_gettimeofday(&t0); \
+				RC = -1; \
+				for (;;) { \
+				    poll_events(stun_cfg, 10, PJ_FALSE); \
+				    pj_gettimeofday(&t); \
+				    if (expr) { \
+					rc = PJ_SUCCESS; \
+					break; \
+				    } \
+				    if (t.sec - t0.sec > (timeout)) break; \
+				} \
+			    }
+
+
+static int perform_test(const char *title,
+			pj_stun_config *stun_cfg,
+			unsigned server_flag,
+		        struct test_cfg *caller_cfg,
+		        struct test_cfg *callee_cfg)
+{
+    pjlib_state pjlib_state;
+    struct test_sess *sess;
+    int rc;
+
+    PJ_LOG(3,("", INDENT "%s", title));
+
+    capture_pjlib_state(stun_cfg, &pjlib_state);
+
+    rc = create_sess(stun_cfg, server_flag, caller_cfg, callee_cfg, &sess);
+    if (rc != 0)
+	return rc;
+
+#define ALL_READY   (sess->caller.result.init_status!=PJ_EPENDING && \
+		     sess->callee.result.init_status!=PJ_EPENDING)
+
+    /* Wait until both ICE transports are initialized */
+    WAIT_UNTIL(30, ALL_READY, rc);
+
+    if (!ALL_READY) {
+	PJ_LOG(3,("", INDENT "err: init timed-out"));
+	destroy_sess(sess, 500);
+	return -100;
+    }
+
+    if (sess->caller.result.init_status != sess->caller.cfg.expected.init_status) {
+	app_perror(INDENT "err: caller init", sess->caller.result.init_status);
+	destroy_sess(sess, 500);
+	return -102;
+    }
+    if (sess->callee.result.init_status != sess->callee.cfg.expected.init_status) {
+	app_perror(INDENT "err: callee init", sess->callee.result.init_status);
+	destroy_sess(sess, 500);
+	return -104;
+    }
+
+    /* Failure condition */
+    if (sess->caller.result.init_status != PJ_SUCCESS ||
+	sess->callee.result.init_status != PJ_SUCCESS)
+    {
+	rc = 0;
+	goto on_return;
+    }
+
+    /* Init ICE on caller */
+    rc = pj_ice_strans_init_ice(sess->caller.ice, sess->caller.cfg.role, 
+				&sess->caller.ufrag, &sess->caller.pass);
+    if (rc != PJ_SUCCESS) {
+	app_perror(INDENT "err: caller pj_ice_strans_init_ice()", rc);
+	destroy_sess(sess, 500);
+	return -100;
+    }
+
+    /* Init ICE on callee */
+    rc = pj_ice_strans_init_ice(sess->callee.ice, sess->callee.cfg.role, 
+				&sess->callee.ufrag, &sess->callee.pass);
+    if (rc != PJ_SUCCESS) {
+	app_perror(INDENT "err: callee pj_ice_strans_init_ice()", rc);
+	destroy_sess(sess, 500);
+	return -110;
+    }
+
+    /* Start ICE on callee */
+    rc = start_ice(&sess->callee, &sess->caller);
+    if (rc != PJ_SUCCESS) {
+	destroy_sess(sess, 500);
+	return -120;
+    }
+
+    /* Wait for callee's answer_delay */
+    poll_events(stun_cfg, sess->callee.cfg.answer_delay, PJ_FALSE);
+
+    /* Start ICE on caller */
+    rc = start_ice(&sess->caller, &sess->callee);
+    if (rc != PJ_SUCCESS) {
+	destroy_sess(sess, 500);
+	return -130;
+    }
+
+    /* Wait until negotiation is complete on both endpoints */
+#define ALL_DONE    (sess->caller.result.nego_status!=PJ_EPENDING && \
+		     sess->callee.result.nego_status!=PJ_EPENDING)
+    WAIT_UNTIL(30, ALL_DONE, rc);
+
+    if (!ALL_DONE) {
+	PJ_LOG(3,("", INDENT "err: negotiation timed-out"));
+	destroy_sess(sess, 500);
+	return -140;
+    }
+
+    if (sess->caller.result.nego_status != sess->caller.cfg.expected.nego_status) {
+	app_perror(INDENT "err: caller negotiation failed", sess->caller.result.nego_status);
+	destroy_sess(sess, 500);
+	return -150;
+    }
+
+    if (sess->callee.result.nego_status != sess->callee.cfg.expected.nego_status) {
+	app_perror(INDENT "err: callee negotiation failed", sess->callee.result.nego_status);
+	destroy_sess(sess, 500);
+	return -160;
+    }
+
+    /* Verify that both agents have agreed on the same pair */
+    rc = check_pair(&sess->caller, &sess->callee, -170);
+    if (rc != 0) {
+	destroy_sess(sess, 500);
+	return rc;
+    }
+    rc = check_pair(&sess->callee, &sess->caller, -180);
+    if (rc != 0) {
+	destroy_sess(sess, 500);
+	return rc;
+    }
+
+    /* Looks like everything is okay */
+
+    /* Destroy ICE stream transports first to let it de-allocate
+     * TURN relay (otherwise there'll be timer/memory leak, unless
+     * we wait for long time in the last poll_events() below).
+     */
+    if (sess->caller.ice) {
+	pj_ice_strans_destroy(sess->caller.ice);
+	sess->caller.ice = NULL;
+    }
+
+    if (sess->callee.ice) {
+	pj_ice_strans_destroy(sess->callee.ice);
+	sess->callee.ice = NULL;
+    }
+
+on_return:
+    /* Wait.. */
+    poll_events(stun_cfg, 500, PJ_FALSE);
+
+    /* Now destroy everything */
+    destroy_sess(sess, 500);
+
+    /* Flush events */
+    poll_events(stun_cfg, 100, PJ_FALSE);
+
+    rc = check_pjlib_state(stun_cfg, &pjlib_state);
+    if (rc != 0) {
+	return rc;
     }
 
@@ -174,348 +592,282 @@
 }
 
-
-/* When ICE completes, both agents should agree on the same candidate pair.
- * Check that the remote address selected by agent1 is equal to the
- * local address of selected by agent 2.
- */
-static int verify_address(pj_ice_strans *agent1, pj_ice_strans *agent2,
-			  unsigned comp_id)
-{
-    pj_ice_sess_cand *rcand, *lcand;
-    int lcand_id;
-
-    if (agent1->ice->comp[comp_id-1].valid_check == NULL) {
-	PJ_LOG(3,(THIS_FILE, "....error: valid_check not set for comp_id %d", comp_id));
-	return -60;
-    }
-
-    /* Get default remote candidate of agent 1 */
-    rcand = agent1->ice->comp[comp_id-1].valid_check->rcand;
-
-    /* Get default local candidate of agent 2 */
-    pj_ice_sess_find_default_cand(agent2->ice, comp_id, &lcand_id);
-    if (lcand_id < 0)
-	return -62;
-
-    lcand = &agent2->ice->lcand[lcand_id];
-
-    if (pj_memcmp(&rcand->addr, &lcand->addr, sizeof(pj_sockaddr_in))!=0) {
-	PJ_LOG(3,(THIS_FILE, "....error: the selected addresses are incorrect for comp_id %d", comp_id));
-	return -64;
-    }
-
-    return 0;
-}
-
-
-/* Perform ICE test with the following parameters:
- *
- * - title:	The title of the test
- * - ocand_cnt,
- *   ocand	Additional candidates to be added to offerer
- * - acand_cnt,
- *   acand	Additional candidates to be added to answerer
- *
- * The additional candidates are normally invalid candidates, meaning 
- * they won't be reachable by the agents. They are used to "confuse"
- * ICE processing.
- */
-static int perform_ice_test(const char *title,
-			    pj_bool_t expected_success,
-			    unsigned comp_cnt,
-			    pj_bool_t add_valid_comp,
-			    unsigned wait_before_send,
-			    unsigned max_total_time,
-			    unsigned ocand_cnt,
-			    struct dummy_cand ocand[],
-			    unsigned acand_cnt,
-			    struct dummy_cand acand[])
-{
-    pj_ice_strans *im1, *im2;
-    pj_ice_strans_cb icest_cb;
-    struct ice_data *id1, *id2;
-    pj_timestamp t_start, t_end;
+#define ROLE1	PJ_ICE_SESS_ROLE_CONTROLLED
+#define ROLE2	PJ_ICE_SESS_ROLE_CONTROLLING
+
+int ice_test(void)
+{
+    pj_pool_t *pool;
+    pj_stun_config stun_cfg;
     unsigned i;
-    pj_str_t data_from_offerer, data_from_answerer;
-    pj_status_t status;
-
-#define CHECK_COMPLETE()    if (id1->complete && id2->complete) { \
-				if (t_end.u32.lo==0) pj_get_timestamp(&t_end); \
-			    } else {}
-
-    PJ_LOG(3,(THIS_FILE, "...%s", title));
-
-    pj_bzero(&t_end, sizeof(t_end));
-
-    pj_bzero(&icest_cb, sizeof(icest_cb));
-    icest_cb.on_ice_complete = &on_ice_complete;
-    icest_cb.on_rx_data = &on_rx_data;
-
-    /* Create first ICE */
-    status = pj_ice_strans_create(&stun_cfg, "offerer", comp_cnt, NULL, &icest_cb, &im1);
-    if (status != PJ_SUCCESS)
-	return -20;
-
-    id1 = PJ_POOL_ZALLOC_T(im1->pool, struct ice_data);
-    id1->obj_name = "offerer";
-    im1->user_data = id1;
-
-    /* Init components */
-    status = init_ice_st(im1, add_valid_comp, ocand_cnt, ocand);
-    if (status != 0)
-	return status;
-
-    /* Create second ICE */
-    status = pj_ice_strans_create(&stun_cfg, "answerer", comp_cnt, NULL, &icest_cb, &im2);
-    if (status != PJ_SUCCESS)
-	return -25;
-
-    id2 = PJ_POOL_ZALLOC_T(im2->pool, struct ice_data);
-    id2->obj_name = "answerer";
-    im2->user_data = id2;
-
-    /* Init components */
-    status = init_ice_st(im2, add_valid_comp, acand_cnt, acand);
-    if (status != 0)
-	return status;
-
-
-    /* Init ICE on im1 */
-    status = pj_ice_strans_init_ice(im1, PJ_ICE_SESS_ROLE_CONTROLLING, NULL, NULL);
-    if (status != PJ_SUCCESS)
-	return -29;
-
-    /* Init ICE on im2 */
-    status = pj_ice_strans_init_ice(im2, PJ_ICE_SESS_ROLE_CONTROLLED, NULL, NULL);
-    if (status != PJ_SUCCESS)
-	return -29;
-
-    /* Start ICE on im2 */
-    status = start_ice(im2, im1);
-    if (status != PJ_SUCCESS) {
-	app_perror("   error starting ICE", status);
-	return -30;
-    }
-
-    /* Start ICE on im1 */
-    status = start_ice(im1, im2);
-    if (status != PJ_SUCCESS)
-	return -35;
-
-    /* Apply delay to let other checks commence */
-    pj_thread_sleep(40);
-
-    /* Mark start time */
-    pj_get_timestamp(&t_start);
-
-    /* Poll for wait_before_send msecs before we send the first data */
-    if (expected_success) {
-	for (;;) {
-	    pj_timestamp t_now;
-
-	    handle_events(1);
-
-	    CHECK_COMPLETE();
-
-	    pj_get_timestamp(&t_now);
-	    if (pj_elapsed_msec(&t_start, &t_now) >= wait_before_send)
-		break;
-	}
-
-	/* Send data. It must be successful! */
-	data_from_offerer = pj_str("from offerer");
-	status = pj_ice_sess_send_data(im1->ice, 1, data_from_offerer.ptr, data_from_offerer.slen);
-	if (status != PJ_SUCCESS)
-	    return -47;
-
-	data_from_answerer = pj_str("from answerer");
-	status = pj_ice_sess_send_data(im2->ice, 1, data_from_answerer.ptr, data_from_answerer.slen);
-	if (status != PJ_SUCCESS) {
-	    app_perror("   error sending packet", status);
-	    return -48;
-	}
-
-	/* Poll to allow data to be received */
-	for (;;) {
-	    pj_timestamp t_now;
-	    handle_events(1);
-	    CHECK_COMPLETE();
-	    pj_get_timestamp(&t_now);
-	    if (pj_elapsed_msec(&t_start, &t_now) >= (wait_before_send + 200))
-		break;
-	}
-    }
-
-    /* Just wait until both completes, or timed out */
-    while (!id1->complete || !id2->complete) {
-	pj_timestamp t_now;
-
-	handle_events(1);
-
-	CHECK_COMPLETE();
-	pj_get_timestamp(&t_now);
-	if (pj_elapsed_msec(&t_start, &t_now) >= max_total_time) {
-	    PJ_LOG(3,(THIS_FILE, "....error: timed-out"));
-	    return -50;
-	}
-    }
-
-    /* Mark end-time */
-    CHECK_COMPLETE();
-
-    /* If expected to fail, then just check that both fail */
-    if (!expected_success) {
-	/* Check status */
-	if (id1->err_code == PJ_SUCCESS)
-	    return -51;
-	if (id2->err_code == PJ_SUCCESS)
-	    return -52;
-	goto on_return;
-    }
-
-    /* Check status */
-    if (id1->err_code != PJ_SUCCESS)
-	return -53;
-    if (id2->err_code != PJ_SUCCESS)
-	return -56;
-
-    /* Verify that offerer gets answerer's transport address */
-    for (i=0; i<comp_cnt; ++i) {
-	status = verify_address(im1, im2, i+1);
-	if (status != 0)
-	    return status;
-    }
-
-    /* And the other way around */
-    for (i=0; i<comp_cnt; ++i) {
-	status = verify_address(im2, im1, i+1);
-	if (status != 0)
-	    return status;
-    }
-
-    /* Check that data is received in offerer */
-    if (id1->rx_rtp_cnt != 1) {
-	PJ_LOG(3,(THIS_FILE, "....error: data not received in offerer"));
-	return -80;
-    }
-    if (pj_strcmp2(&data_from_answerer, id1->last_rx_rtp_data) != 0) {
-	PJ_LOG(3,(THIS_FILE, "....error: data mismatch in offerer"));
-	return -82;
-    }
-
-    /* And the same in answerer */
-    if (id2->rx_rtp_cnt != 1) {
-	PJ_LOG(3,(THIS_FILE, "....error: data not received in answerer"));
-	return -84;
-    }
-    if (pj_strcmp2(&data_from_offerer, id2->last_rx_rtp_data) != 0) {
-	PJ_LOG(3,(THIS_FILE, "....error: data mismatch in answerer"));
-	return -82;
-    }
-
+    int rc;
+    struct sess_cfg_t {
+	const char	*title;
+	unsigned	 server_flag;
+	struct test_cfg	 ua1;
+	struct test_cfg	 ua2;
+    } sess_cfg[] = 
+    {
+	/*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
+	{
+	    "hosts candidates only",
+	    0xFFFF,
+	    {ROLE1, 1,	    YES,    NO,	    NO,	    NO,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}},
+	    {ROLE2, 1,	    YES,    NO,	    NO,	    NO,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}}
+	},
+	{
+	    "host and srflxes",
+	    0xFFFF,
+	    {ROLE1, 1,	    YES,    YES,    NO,	    NO,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}},
+	    {ROLE2, 1,	    YES,    YES,    NO,	    NO,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}}
+	},
+	{
+	    "host vs relay",
+	    0xFFFF,
+	    {ROLE1, 1,	    YES,    NO,    NO,	    NO,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}},
+	    {ROLE2, 1,	    NO,     NO,    YES,	    NO,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}}
+	},
+	{
+	    "relay vs host",
+	    0xFFFF,
+	    {ROLE1, 1,	    NO,	    NO,   YES,	    NO,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}},
+	    {ROLE2, 1,	   YES,     NO,    NO,	    NO,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}}
+	},
+	{
+	    "relay vs relay",
+	    0xFFFF,
+	    {ROLE1, 1,	    NO,	    NO,   YES,	    NO,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}},
+	    {ROLE2, 1,	    NO,     NO,   YES,	    NO,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}}
+	},
+	{
+	    "all candidates",
+	    0xFFFF,
+	    {ROLE1, 1,	   YES,	   YES,   YES,	    NO,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}},
+	    {ROLE2, 1,	   YES,    YES,   YES,	    NO,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}}
+	},
+    };
+
+    pool = pj_pool_create(mem, NULL, 512, 512, NULL);
+    rc = create_stun_config(pool, &stun_cfg);
+    if (rc != PJ_SUCCESS) {
+	pj_pool_release(pool);
+	return -7;
+    }
+
+    /* Simple test first with host candidate */
+    if (1) {
+	struct sess_cfg_t cfg = 
+	{
+	    "Basic with host candidates",
+	    0x0,
+	    /*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
+	    {ROLE1,	1,	YES,     NO,	    NO,	    0,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}},
+	    {ROLE2,	1,	YES,     NO,	    NO,	    0,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}}
+	};
+
+	rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, 
+			  &cfg.ua1, &cfg.ua2);
+	if (rc != 0)
+	    goto on_return;
+
+	cfg.ua1.comp_cnt = 4;
+	cfg.ua2.comp_cnt = 4;
+	rc = perform_test("Basic with host candidates, 4 components", 
+			  &stun_cfg, cfg.server_flag, 
+			  &cfg.ua1, &cfg.ua2);
+	if (rc != 0)
+	    goto on_return;
+    }
+
+    /* Simple test first with srflx candidate */
+    if (1) {
+	struct sess_cfg_t cfg = 
+	{
+	    "Basic with srflx candidates",
+	    0xFFFF,
+	    /*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
+	    {ROLE1,	1,	YES,    YES,	    NO,	    0,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}},
+	    {ROLE2,	1,	YES,    YES,	    NO,	    0,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}}
+	};
+
+	rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, 
+			  &cfg.ua1, &cfg.ua2);
+	if (rc != 0)
+	    goto on_return;
+
+	cfg.ua1.comp_cnt = 4;
+	cfg.ua2.comp_cnt = 4;
+
+	rc = perform_test("Basic with srflx candidates, 4 components", 
+			  &stun_cfg, cfg.server_flag, 
+			  &cfg.ua1, &cfg.ua2);
+	if (rc != 0)
+	    goto on_return;
+    }
+
+    /* Simple test with relay candidate */
+    if (1) {
+	struct sess_cfg_t cfg = 
+	{
+	    "Basic with relay candidates",
+	    0xFFFF,
+	    /*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
+	    {ROLE1,	1,	 NO,     NO,	  YES,	    0,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}},
+	    {ROLE2,	1,	 NO,     NO,	  YES,	    0,	    0,	    0,	    0, {PJ_SUCCESS, PJ_SUCCESS}}
+	};
+
+	rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, 
+			  &cfg.ua1, &cfg.ua2);
+	if (rc != 0)
+	    goto on_return;
+
+	cfg.ua1.comp_cnt = 4;
+	cfg.ua2.comp_cnt = 4;
+
+	rc = perform_test("Basic with relay candidates, 4 components", 
+			  &stun_cfg, cfg.server_flag, 
+			  &cfg.ua1, &cfg.ua2);
+	if (rc != 0)
+	    goto on_return;
+    }
+
+    /* Failure test with STUN resolution */
+    if (1) {
+	struct sess_cfg_t cfg = 
+	{
+	    "STUN resolution failure",
+	    0x0,
+	    /*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
+	    {ROLE1,	2,	 NO,    YES,	    NO,	    0,	    0,	    0,	    0, {PJNATH_ESTUNTIMEDOUT, -1}},
+	    {ROLE2,	2,	 NO,    YES,	    NO,	    0,	    0,	    0,	    0, {PJNATH_ESTUNTIMEDOUT, -1}}
+	};
+
+	rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, 
+			  &cfg.ua1, &cfg.ua2);
+	if (rc != 0)
+	    goto on_return;
+
+	cfg.ua1.client_flag |= DEL_ON_ERR;
+	cfg.ua2.client_flag |= DEL_ON_ERR;
+
+	rc = perform_test("STUN resolution failure with destroy on callback", 
+			  &stun_cfg, cfg.server_flag, 
+			  &cfg.ua1, &cfg.ua2);
+	if (rc != 0)
+	    goto on_return;
+    }
+
+    /* Failure test with TURN resolution */
+    if (1) {
+	struct sess_cfg_t cfg = 
+	{
+	    "TURN allocation failure",
+	    0xFFFF,
+	    /*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
+	    {ROLE1,	4,	 NO,    NO,	YES, WRONG_TURN,    0,	    0,	    0, {PJ_STATUS_FROM_STUN_CODE(401), -1}},
+	    {ROLE2,	4,	 NO,    NO,	YES, WRONG_TURN,    0,	    0,	    0, {PJ_STATUS_FROM_STUN_CODE(401), -1}}
+	};
+
+	rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, 
+			  &cfg.ua1, &cfg.ua2);
+	if (rc != 0)
+	    goto on_return;
+
+	cfg.ua1.client_flag |= DEL_ON_ERR;
+	cfg.ua2.client_flag |= DEL_ON_ERR;
+
+	rc = perform_test("TURN allocation failure with destroy on callback", 
+			  &stun_cfg, cfg.server_flag, 
+			  &cfg.ua1, &cfg.ua2);
+	if (rc != 0)
+	    goto on_return;
+    }
+
+    /* STUN failure, testing TURN deallocation */
+    if (1) {
+	struct sess_cfg_t cfg = 
+	{
+	    "STUN failure, testing TURN deallocation",
+	    0xFFFF & (~(CREATE_STUN_SERVER)),
+	    /*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
+	    {ROLE1,	2,	 YES,    YES,	YES,	0,    0,	    0,	    0, {PJNATH_ESTUNTIMEDOUT, -1}},
+	    {ROLE2,	2,	 YES,    YES,	YES,	0,    0,	    0,	    0, {PJNATH_ESTUNTIMEDOUT, -1}}
+	};
+
+	rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, 
+			  &cfg.ua1, &cfg.ua2);
+	if (rc != 0)
+	    goto on_return;
+
+	cfg.ua1.client_flag |= DEL_ON_ERR;
+	cfg.ua2.client_flag |= DEL_ON_ERR;
+
+	rc = perform_test("STUN failure, testing TURN deallocation (cb)", 
+			  &stun_cfg, cfg.server_flag, 
+			  &cfg.ua1, &cfg.ua2);
+	if (rc != 0)
+	    goto on_return;
+    }
+
+    rc = 0;
+    /* Iterate each test item */
+    for (i=0; i<PJ_ARRAY_SIZE(sess_cfg); ++i) {
+	struct sess_cfg_t *cfg = &sess_cfg[i];
+	unsigned delay[] = { 50, 2000 };
+	unsigned d;
+
+	PJ_LOG(3,("", "  %s", cfg->title));
+
+	/* For each test item, test with various answer delay */
+	for (d=0; d<PJ_ARRAY_SIZE(delay); ++d) {
+	    struct role_t {
+		pj_ice_sess_role	ua1;
+		pj_ice_sess_role	ua2;
+	    } role[] = 
+	    {
+		{ ROLE1, ROLE2},
+		{ ROLE2, ROLE1},
+		{ ROLE1, ROLE1},
+		{ ROLE2, ROLE2}
+	    };
+	    unsigned j;
+
+	    cfg->ua1.answer_delay = delay[d];
+	    cfg->ua2.answer_delay = delay[d];
+
+	    /* For each test item, test with role conflict scenarios */
+	    for (j=0; j<PJ_ARRAY_SIZE(role); ++j) {
+		unsigned k1;
+
+		cfg->ua1.role = role[j].ua1;
+		cfg->ua2.role = role[j].ua2;
+
+		/* For each test item, test with different number of components */
+		for (k1=1; k1<=2; ++k1) {
+		    unsigned k2;
+
+		    cfg->ua1.comp_cnt = k1;
+
+		    for (k2=1; k2<=2; ++k2) {
+			char title[120];
+
+			sprintf(title, 
+				"%s/%s, %dms answer delay, %d vs %d components", 
+				pj_ice_sess_role_name(role[j].ua1),
+				pj_ice_sess_role_name(role[j].ua2),
+				delay[d], k1, k2);
+
+			cfg->ua2.comp_cnt = k2;
+			rc = perform_test(title, &stun_cfg, cfg->server_flag, 
+					  &cfg->ua1, &cfg->ua2);
+			if (rc != 0)
+			    goto on_return;
+		    }
+		}
+	    }
+	}
+    }
 
 on_return:
-
-    /* Done */
-    PJ_LOG(3,(THIS_FILE, "....success: ICE completed in %d msec, waiting..", 
-	      pj_elapsed_msec(&t_start, &t_end)));
-
-    /* Wait for some more time */
-    for (;;) {
-	pj_timestamp t_now;
-
-	pj_get_timestamp(&t_now);
-	if (pj_elapsed_msec(&t_start, &t_now) > max_total_time)
-	    break;
-
-	handle_events(1);
-    }
-
-
-    pj_ice_strans_destroy(im1);
-    pj_ice_strans_destroy(im2);
-    handle_events(100);
-    return 0;
-}
-
-
-int ice_test(void)
-{
-    int rc = 0;
-    pj_pool_t *pool;
-    pj_ioqueue_t *ioqueue;
-    pj_timer_heap_t *timer_heap;
-    enum { D1=500, D2=5000, D3=15000 };
-    struct dummy_cand ocand[] = 
-    {
-	{1, PJ_ICE_CAND_TYPE_SRFLX, "127.1.1.1", 65534 },
-	{2, PJ_ICE_CAND_TYPE_SRFLX, "127.1.1.1", 65535 },
-    };
-    struct dummy_cand acand[] =
-    {
-	{1, PJ_ICE_CAND_TYPE_SRFLX, "127.2.2.2", 65534 },
-	{2, PJ_ICE_CAND_TYPE_SRFLX, "127.2.2.2", 65535 },
-    };
-
-    pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
-    pj_ioqueue_create(pool, 12, &ioqueue);
-    pj_timer_heap_create(pool, 100, &timer_heap);
-    
-    pj_stun_config_init(&stun_cfg, mem, 0, ioqueue, timer_heap);
-
-#if 0
-    pj_log_set_level(5);
-#endif
-
-    //goto test;
-
-    /* Basic create/destroy */
-    rc = ice_basic_create_destroy_test();
-    if (rc != 0)
-	goto on_return;
-
-    /* Direct communication */
-    rc = perform_ice_test("Simple test (1 component)", PJ_TRUE, 1, PJ_TRUE, D1, D2, 0, NULL, 0, NULL);
-    if (rc != 0)
-	goto on_return;
-
-    /* Failure case (all checks fail) */
-#if 0
-    /* Cannot just add an SRFLX candidate; it needs a base */
-    rc = perform_ice_test("Failure case (all checks fail)", PJ_FALSE, 1, PJ_FALSE, D3, D3, 1, ocand, 1, acand);
-    if (rc != 0)
-	goto on_return;
-#endif
-
-    /* Direct communication with invalid address */
-    rc = perform_ice_test("With 1 unreachable address", PJ_TRUE, 1, PJ_TRUE, D1, D2, 1, ocand, 0, NULL);
-    if (rc != 0)
-	goto on_return;
-
-    /* Direct communication with invalid address */
-    rc = perform_ice_test("With 2 unreachable addresses (one each)", PJ_TRUE, 1, PJ_TRUE, D1, D2, 1, ocand, 1, acand);
-    if (rc != 0)
-	goto on_return;
-
-    /* Direct communication with two components */
-//test:
-    rc = perform_ice_test("With two components (RTP and RTCP)", PJ_TRUE, 2, PJ_TRUE, D1, D2, 0, NULL, 0, NULL);
-    if (rc != 0)
-	goto on_return;
-
-    goto on_return;
-
-    /* Direct communication with mismatch number of components */
-
-    /* Direct communication with 2 components and 2 invalid address */
-    rc = perform_ice_test("With 2 two components and 2 unreachable address", PJ_TRUE, 2, PJ_TRUE, D1, D2, 1, ocand, 1, acand);
-    if (rc != 0)
-	goto on_return;
-
-
-
-on_return:
-    pj_log_set_level(3);
-    pj_ioqueue_destroy(stun_cfg.ioqueue);
+    destroy_stun_config(&stun_cfg);
     pj_pool_release(pool);
     return rc;
Index: pjproject/trunk/pjnath/src/pjnath-test/server.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath-test/server.c	(revision 1988)
+++ pjproject/trunk/pjnath/src/pjnath-test/server.c	(revision 1988)
@@ -0,0 +1,652 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "server.h"
+#include "test.h"
+
+#define THIS_FILE	"server.c"
+#define MAX_STUN_PKT	1500
+#define TURN_NONCE	"thenonce"
+
+static pj_bool_t stun_on_data_recvfrom(pj_activesock_t *asock,
+				       void *data,
+				       pj_size_t size,
+				       const pj_sockaddr_t *src_addr,
+				       int addr_len,
+				       pj_status_t status);
+static pj_bool_t turn_on_data_recvfrom(pj_activesock_t *asock,
+				       void *data,
+				       pj_size_t size,
+				       const pj_sockaddr_t *src_addr,
+				       int addr_len,
+				       pj_status_t status);
+static pj_bool_t alloc_on_data_recvfrom(pj_activesock_t *asock,
+				       void *data,
+				       pj_size_t size,
+				       const pj_sockaddr_t *src_addr,
+				       int addr_len,
+				       pj_status_t status);
+
+pj_status_t create_test_server(pj_stun_config *stun_cfg,
+			       pj_uint32_t flags,
+			       const char *domain,
+			       test_server **p_test_srv)
+{
+    pj_pool_t *pool;
+    test_server *test_srv;
+    pj_sockaddr hostip;
+    char strbuf[100];
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(stun_cfg && domain && p_test_srv, PJ_EINVAL);
+
+    status = pj_gethostip(pj_AF_INET(), &hostip);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pool = pj_pool_create(mem, THIS_FILE, 512, 512, NULL);
+    test_srv = (test_server*) PJ_POOL_ZALLOC_T(pool, test_server);
+    test_srv->pool = pool;
+    test_srv->flags = flags;
+    test_srv->stun_cfg = stun_cfg;
+
+    pj_strdup2(pool, &test_srv->domain, domain);
+    test_srv->username = pj_str(TURN_USERNAME);
+    test_srv->passwd = pj_str(TURN_PASSWD);
+
+    pj_ioqueue_op_key_init(&test_srv->send_key, sizeof(test_srv->send_key));
+
+    if (flags & CREATE_DNS_SERVER) {
+	status = pj_dns_server_create(mem, test_srv->stun_cfg->ioqueue,
+				      pj_AF_INET(), DNS_SERVER_PORT,
+				      0, &test_srv->dns_server);
+	if (status != PJ_SUCCESS) {
+	    destroy_test_server(test_srv);
+	    return status;
+	}
+
+	/* Add DNS A record for the domain, for fallback */
+	if (flags & CREATE_A_RECORD_FOR_DOMAIN) {
+	    pj_dns_parsed_rr rr;
+	    pj_str_t res_name;
+	    pj_in_addr ip_addr;
+
+	    pj_strdup2(pool, &res_name, domain);
+	    ip_addr = hostip.ipv4.sin_addr;
+	    pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr);
+	    pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
+	}
+
+    }
+
+    if (flags & CREATE_STUN_SERVER) {
+	pj_activesock_cb stun_sock_cb;
+	pj_sockaddr bound_addr;
+
+	pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb));
+	stun_sock_cb.on_data_recvfrom = &stun_on_data_recvfrom;
+
+	pj_sockaddr_in_init(&bound_addr.ipv4, NULL, STUN_SERVER_PORT);
+
+	status = pj_activesock_create_udp(pool, &bound_addr, NULL, 
+					  test_srv->stun_cfg->ioqueue,
+					  &stun_sock_cb, test_srv, 
+					  &test_srv->stun_sock, NULL);
+	if (status != PJ_SUCCESS) {
+	    destroy_test_server(test_srv);
+	    return status;
+	}
+
+	status = pj_activesock_start_recvfrom(test_srv->stun_sock, pool,
+					      MAX_STUN_PKT, 0);
+	if (status != PJ_SUCCESS) {
+	    destroy_test_server(test_srv);
+	    return status;
+	}
+
+	if (test_srv->dns_server && (flags & CREATE_STUN_SERVER_DNS_SRV)) {
+	    pj_str_t res_name, target;
+	    pj_dns_parsed_rr rr;
+	    pj_in_addr ip_addr;
+
+	    /* Add DNS entries:
+	     *  _stun._udp.domain 60 IN SRV 0 0 PORT stun.domain.
+	     *  stun.domain IN A 127.0.0.1
+	     */
+	    pj_ansi_snprintf(strbuf, sizeof(strbuf),
+			     "_stun._udp.%s", domain);
+	    pj_strdup2(pool, &res_name, strbuf);
+	    pj_ansi_snprintf(strbuf, sizeof(strbuf),
+			     "stun.%s", domain);
+	    pj_strdup2(pool, &target, strbuf);
+	    pj_dns_init_srv_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, 0, 0, 
+			       STUN_SERVER_PORT, &target);
+	    pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
+
+	    res_name = target;
+	    ip_addr = hostip.ipv4.sin_addr;
+	    pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr);
+	    pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
+	}
+
+    }
+
+    if (flags & CREATE_TURN_SERVER) {
+	pj_activesock_cb turn_sock_cb;
+	pj_sockaddr bound_addr;
+
+	pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb));
+	turn_sock_cb.on_data_recvfrom = &turn_on_data_recvfrom;
+
+	pj_sockaddr_in_init(&bound_addr.ipv4, NULL, TURN_SERVER_PORT);
+
+	status = pj_activesock_create_udp(pool, &bound_addr, NULL, 
+					  test_srv->stun_cfg->ioqueue,
+					  &turn_sock_cb, test_srv,
+					  &test_srv->turn_sock, NULL);
+	if (status != PJ_SUCCESS) {
+	    destroy_test_server(test_srv);
+	    return status;
+	}
+
+	status = pj_activesock_start_recvfrom(test_srv->turn_sock, pool,
+					      MAX_STUN_PKT, 0);
+	if (status != PJ_SUCCESS) {
+	    destroy_test_server(test_srv);
+	    return status;
+	}
+
+	if (test_srv->dns_server && (flags & CREATE_TURN_SERVER_DNS_SRV)) {
+	    pj_str_t res_name, target;
+	    pj_dns_parsed_rr rr;
+	    pj_in_addr ip_addr;
+
+	    /* Add DNS entries:
+	     *  _turn._udp.domain 60 IN SRV 0 0 PORT turn.domain.
+	     *  turn.domain IN A 127.0.0.1
+	     */
+	    pj_ansi_snprintf(strbuf, sizeof(strbuf),
+			     "_turn._udp.%s", domain);
+	    pj_strdup2(pool, &res_name, strbuf);
+	    pj_ansi_snprintf(strbuf, sizeof(strbuf),
+			     "turn.%s", domain);
+	    pj_strdup2(pool, &target, strbuf);
+	    pj_dns_init_srv_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, 0, 0, 
+			       TURN_SERVER_PORT, &target);
+	    pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
+
+	    res_name = target;
+	    ip_addr = hostip.ipv4.sin_addr;
+	    pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr);
+	    pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
+	}
+    }
+
+    *p_test_srv = test_srv;
+    return PJ_SUCCESS;
+}
+
+void destroy_test_server(test_server *test_srv)
+{
+    unsigned i;
+
+    PJ_ASSERT_ON_FAIL(test_srv, return);
+
+    for (i=0; i<test_srv->turn_alloc_cnt; ++i) {
+	pj_activesock_close(test_srv->turn_alloc[i].sock);
+	pj_pool_release(test_srv->turn_alloc[i].pool);
+    }
+    test_srv->turn_alloc_cnt = 0;
+
+    if (test_srv->turn_sock) {
+	pj_activesock_close(test_srv->turn_sock);
+	test_srv->turn_sock = NULL;
+    }
+
+    if (test_srv->stun_sock) {
+	pj_activesock_close(test_srv->stun_sock);
+	test_srv->stun_sock = NULL;
+    }
+
+    if (test_srv->dns_server) {
+	pj_dns_server_destroy(test_srv->dns_server);
+	test_srv->dns_server = NULL;
+    }
+
+    if (test_srv->pool) {
+	pj_pool_t *pool = test_srv->pool;
+	test_srv->pool = NULL;
+	pj_pool_release(pool);
+    }
+}
+
+static pj_bool_t stun_on_data_recvfrom(pj_activesock_t *asock,
+				       void *data,
+				       pj_size_t size,
+				       const pj_sockaddr_t *src_addr,
+				       int addr_len,
+				       pj_status_t status)
+{
+    test_server *test_srv;
+    pj_stun_msg *req, *resp = NULL;
+    pj_pool_t *pool;
+    pj_ssize_t len;
+
+    if (status != PJ_SUCCESS)
+	return PJ_TRUE;
+
+    test_srv = (test_server*) pj_activesock_get_user_data(asock);
+    pool = pj_pool_create(test_srv->stun_cfg->pf, NULL, 512, 512, NULL);
+
+    status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size, 
+				PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, 
+				&req, NULL, NULL);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    if (req->hdr.type != PJ_STUN_BINDING_REQUEST) {
+	pj_stun_msg_create_response(pool, req, PJ_STUN_SC_BAD_REQUEST, 
+				    NULL, &resp);
+	goto send_pkt;
+    }
+
+    status = pj_stun_msg_create_response(pool, req, 0, NULL, &resp);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_XOR_MAPPED_ADDR,
+				  PJ_TRUE, src_addr, addr_len);
+
+send_pkt:
+    status = pj_stun_msg_encode(resp, (pj_uint8_t*)data, MAX_STUN_PKT, 
+				0, NULL, &size);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    len = size;
+    status = pj_activesock_sendto(asock, &test_srv->send_key, data, &len,
+				  0, src_addr, addr_len);
+
+on_return:
+    pj_pool_release(pool);
+    return PJ_TRUE;
+}
+
+
+static pj_stun_msg* create_success_response(test_server *test_srv,
+					    turn_allocation *alloc,
+					    pj_stun_msg *req,
+					    pj_pool_t *pool,
+					    unsigned lifetime,
+					    pj_str_t *auth_key)
+{
+    pj_stun_msg *resp;
+    pj_str_t tmp;
+    pj_status_t status;
+
+    /* Create response */
+    status = pj_stun_msg_create_response(pool, req, 0, NULL, &resp);
+    if (status != PJ_SUCCESS) {
+	return NULL;
+    }
+    /* Add TURN_NONCE */
+    pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, TURN_NONCE));
+    /* Add LIFETIME */
+    pj_stun_msg_add_uint_attr(pool, resp, PJ_STUN_ATTR_LIFETIME, lifetime);
+    if (lifetime != 0) {
+	/* Add RELAY-ADDRESS */
+	pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_RELAY_ADDR, PJ_TRUE, &alloc->alloc_addr,
+				      pj_sockaddr_get_len(&alloc->alloc_addr));
+	/* Add XOR-MAPPED-ADDRESS */
+	pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &alloc->client_addr,
+				      pj_sockaddr_get_len(&alloc->client_addr));
+    }
+
+    /* Add blank MESSAGE-INTEGRITY */
+    pj_stun_msg_add_msgint_attr(pool, resp);
+
+    /* Set auth key */
+    pj_stun_create_key(pool, auth_key, &test_srv->domain, &test_srv->username,
+		       PJ_STUN_PASSWD_PLAIN, &test_srv->passwd);
+
+    return resp;
+}
+
+
+static pj_bool_t turn_on_data_recvfrom(pj_activesock_t *asock,
+				       void *data,
+				       pj_size_t size,
+				       const pj_sockaddr_t *src_addr,
+				       int addr_len,
+				       pj_status_t status)
+{
+    test_server *test_srv;
+    pj_pool_t *pool;
+    turn_allocation *alloc;
+    pj_stun_msg *req, *resp = NULL;
+    pj_str_t auth_key = { NULL, 0 };
+    char client_info[PJ_INET6_ADDRSTRLEN+10];
+    unsigned i;
+    pj_ssize_t len;
+
+    if (status != PJ_SUCCESS)
+	return PJ_TRUE;
+
+    pj_sockaddr_print(src_addr, client_info, sizeof(client_info), 3);
+
+    test_srv = (test_server*) pj_activesock_get_user_data(asock);
+    pool = pj_pool_create(test_srv->stun_cfg->pf, NULL, 512, 512, NULL);
+
+    status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size, 
+				PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET |
+				    PJ_STUN_NO_FINGERPRINT_CHECK, 
+				&req, NULL, NULL);
+    if (status != PJ_SUCCESS) {
+	char errmsg[PJ_ERR_MSG_SIZE];
+	pj_strerror(status, errmsg, sizeof(errmsg));
+	PJ_LOG(1,("", "STUN message decode error from client %s: %s", client_info, errmsg));
+	goto on_return;
+    }
+
+    /* Find the client */
+    for (i=0; i<test_srv->turn_alloc_cnt; i++) {
+	if (pj_sockaddr_cmp(&test_srv->turn_alloc[i].client_addr, src_addr)==0)
+	    break;
+    }
+
+    if (i==test_srv->turn_alloc_cnt) {
+	/* New client */
+	//pj_str_t ip_addr;
+	pj_stun_username_attr *uname;
+	pj_activesock_cb alloc_sock_cb;
+	turn_allocation *alloc;
+
+	/* Must be Allocate request */
+	if (req->hdr.type != PJ_STUN_ALLOCATE_REQUEST) {
+	    PJ_LOG(1,(THIS_FILE, "Invalid %s %s from client %s",
+		      pj_stun_get_method_name(req->hdr.type),
+		      pj_stun_get_class_name(req->hdr.type),
+		      client_info));
+
+	    if (PJ_STUN_IS_REQUEST(req->hdr.type))
+		pj_stun_msg_create_response(pool, req, PJ_STUN_SC_BAD_REQUEST, NULL, &resp);
+	    goto send_pkt;
+	}
+
+	test_srv->turn_stat.rx_allocate_cnt++;
+
+	/* Skip if we're not responding to Allocate request */
+	if (!test_srv->turn_respond_allocate)
+	    return PJ_TRUE;
+
+	/* Check if we have too many clients */
+	if (test_srv->turn_alloc_cnt == MAX_TURN_ALLOC) {
+	    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_INSUFFICIENT_CAPACITY, NULL, &resp);
+	    goto send_pkt;
+	}
+
+	/* Get USERNAME attribute */
+	uname = (pj_stun_username_attr*)
+		pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0);
+
+	/* Reject if it doesn't have MESSAGE-INTEGRITY or USERNAME attributes or
+	 * the user is incorrect
+	 */
+	if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0) == NULL ||
+	    uname==NULL || pj_stricmp2(&uname->value, TURN_USERNAME) != 0) 
+	{
+	    pj_str_t tmp;
+
+	    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_UNAUTHORIZED, NULL, &resp);
+	    pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_REALM, &test_srv->domain);
+	    pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, TURN_NONCE));
+	    goto send_pkt;
+	}
+
+	pj_bzero(&alloc_sock_cb, sizeof(alloc_sock_cb));
+	alloc_sock_cb.on_data_recvfrom = &alloc_on_data_recvfrom;
+
+	/* Create allocation */
+	alloc = &test_srv->turn_alloc[test_srv->turn_alloc_cnt];
+	alloc->perm_cnt = 0;
+	alloc->test_srv = test_srv;
+	pj_memcpy(&alloc->client_addr, src_addr, addr_len);
+	pj_ioqueue_op_key_init(&alloc->send_key, sizeof(alloc->send_key));
+
+	alloc->pool = pj_pool_create(test_srv->stun_cfg->pf, "alloc", 512, 512, NULL);
+
+	/* Create relay socket */
+	pj_sockaddr_in_init(&alloc->alloc_addr.ipv4, NULL, 0);
+	pj_gethostip(pj_AF_INET(), &alloc->alloc_addr);
+
+	status = pj_activesock_create_udp(alloc->pool, &alloc->alloc_addr, NULL, 
+					  test_srv->stun_cfg->ioqueue,
+					  &alloc_sock_cb, alloc, 
+					  &alloc->sock, &alloc->alloc_addr);
+	if (status != PJ_SUCCESS) {
+	    pj_pool_release(alloc->pool);
+	    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp);
+	    goto send_pkt;
+	}
+	//pj_sockaddr_set_str_addr(pj_AF_INET(), &alloc->alloc_addr, &ip_addr);
+
+	pj_activesock_set_user_data(alloc->sock, alloc);
+
+	status = pj_activesock_start_recvfrom(alloc->sock, alloc->pool, 1500, 0);
+	if (status != PJ_SUCCESS) {
+	    pj_activesock_close(alloc->sock);
+	    pj_pool_release(alloc->pool);
+	    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp);
+	    goto send_pkt;
+	}
+
+	/* Create Data indication */
+	status = pj_stun_msg_create(alloc->pool, PJ_STUN_DATA_INDICATION,
+				    PJ_STUN_MAGIC, NULL, &alloc->data_ind);
+	if (status != PJ_SUCCESS) {
+	    pj_activesock_close(alloc->sock);
+	    pj_pool_release(alloc->pool);
+	    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp);
+	    goto send_pkt;
+	}
+	pj_stun_msg_add_sockaddr_attr(alloc->pool, alloc->data_ind, 
+				      PJ_STUN_ATTR_PEER_ADDR, PJ_TRUE,
+				      &alloc->alloc_addr,
+				      pj_sockaddr_get_len(&alloc->alloc_addr));
+	pj_stun_msg_add_binary_attr(alloc->pool, alloc->data_ind,
+				    PJ_STUN_ATTR_DATA, (pj_uint8_t*)"", 1);
+
+	/* Create response */
+	resp = create_success_response(test_srv, alloc, req, pool, 600, &auth_key);
+	if (resp == NULL) {
+	    pj_activesock_close(alloc->sock);
+	    pj_pool_release(alloc->pool);
+	    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp);
+	    goto send_pkt;
+	}
+
+	++test_srv->turn_alloc_cnt;
+
+    } else {
+	alloc = &test_srv->turn_alloc[i];
+
+	if (req->hdr.type == PJ_STUN_ALLOCATE_REQUEST) {
+
+	    test_srv->turn_stat.rx_allocate_cnt++;
+
+	    /* Skip if we're not responding to Allocate request */
+	    if (!test_srv->turn_respond_allocate)
+		return PJ_TRUE;
+
+	    resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key);
+
+	} else if (req->hdr.type == PJ_STUN_REFRESH_REQUEST) {
+	    pj_stun_lifetime_attr *lf_attr;
+
+	    test_srv->turn_stat.rx_refresh_cnt++;
+
+	    /* Skip if we're not responding to Refresh request */
+	    if (!test_srv->turn_respond_refresh)
+		return PJ_TRUE;
+
+	    lf_attr = (pj_stun_lifetime_attr*)
+		      pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0);
+	    if (lf_attr && lf_attr->value != 0) {
+		resp = create_success_response(test_srv, alloc, req, pool, 600, &auth_key);
+		pj_array_erase(test_srv->turn_alloc, sizeof(test_srv->turn_alloc[0]),
+			       test_srv->turn_alloc_cnt, i);
+		--test_srv->turn_alloc_cnt;
+	    } else
+		resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key);
+	} else if (req->hdr.type == PJ_STUN_SEND_INDICATION) {
+	    pj_stun_peer_addr_attr *pa;
+	    pj_stun_data_attr *da;
+
+	    test_srv->turn_stat.rx_send_ind_cnt++;
+
+	    pa = (pj_stun_peer_addr_attr*)
+		 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_PEER_ADDR, 0);
+	    da = (pj_stun_data_attr*)
+		 pj_stun_msg_find_attr(req, PJ_STUN_ATTR_DATA, 0);
+	    if (pa && da) {
+		unsigned j;
+		char peer_info[PJ_INET6_ADDRSTRLEN];
+		pj_ssize_t sent;
+
+		pj_sockaddr_print(&pa->sockaddr, peer_info, sizeof(peer_info), 3);
+
+		for (j=0; j<alloc->perm_cnt; ++j) {
+		    if (pj_sockaddr_cmp(&alloc->perm[j], &pa->sockaddr)==0)
+			break;
+		}
+
+		if (j==alloc->perm_cnt && alloc->perm_cnt < MAX_TURN_PERM) {
+		    pj_sockaddr_cp(&alloc->perm[alloc->perm_cnt], &pa->sockaddr);
+		    ++alloc->perm_cnt;
+
+		    PJ_LOG(5,("", "Permission %s added to client %s, perm_cnt=%d", 
+			          peer_info, client_info, alloc->perm_cnt));
+		}
+
+		PJ_LOG(5,(THIS_FILE, "Relaying %d bytes data from client %s to peer %s, "
+				     "perm_cnt=%d", 
+			  da->length, client_info, peer_info, alloc->perm_cnt));
+
+		sent = da->length;
+		pj_activesock_sendto(alloc->sock, &alloc->send_key,
+				     da->data, &sent, 0,
+				     &pa->sockaddr,
+				     pj_sockaddr_get_len(&pa->sockaddr));
+	    } else {
+		PJ_LOG(1,(THIS_FILE, "Invalid Send Indication from %s", client_info));
+	    }
+	} else if (PJ_STUN_IS_REQUEST(req->hdr.type)) {
+	    pj_stun_msg_create_response(pool, req, PJ_STUN_SC_BAD_REQUEST, NULL, &resp);
+	}
+    }
+
+
+send_pkt:
+    if (resp) {
+	status = pj_stun_msg_encode(resp, (pj_uint8_t*)data, MAX_STUN_PKT, 
+				    0, &auth_key, &size);
+	if (status != PJ_SUCCESS)
+	    goto on_return;
+
+	len = size;
+	status = pj_activesock_sendto(asock, &test_srv->send_key, data, &len,
+				      0, src_addr, addr_len);
+    }
+
+on_return:
+    pj_pool_release(pool);
+    return PJ_TRUE;
+}
+
+/* On received data from peer */
+static pj_bool_t alloc_on_data_recvfrom(pj_activesock_t *asock,
+				       void *data,
+				       pj_size_t size,
+				       const pj_sockaddr_t *src_addr,
+				       int addr_len,
+				       pj_status_t status)
+{
+    turn_allocation *alloc;
+    pj_stun_peer_addr_attr *pa;
+    pj_stun_data_attr *da;
+    char peer_info[PJ_INET6_ADDRSTRLEN+10];
+    char client_info[PJ_INET6_ADDRSTRLEN+10];
+    pj_uint8_t buffer[1500];
+    pj_ssize_t sent;
+    unsigned i;
+
+    if (status != PJ_SUCCESS)
+	return PJ_TRUE;
+
+    alloc = (turn_allocation*) pj_activesock_get_user_data(asock);
+
+    pj_sockaddr_print(&alloc->client_addr, client_info, sizeof(client_info), 3);
+    pj_sockaddr_print(src_addr, peer_info, sizeof(peer_info), 3);
+
+    /* Check that this peer has a permission */
+    for (i=0; i<alloc->perm_cnt; ++i) {
+	if (pj_sockaddr_get_len(&alloc->perm[i]) == (unsigned)addr_len &&
+	    pj_memcmp(pj_sockaddr_get_addr(&alloc->perm[i]),
+		      pj_sockaddr_get_addr(src_addr),
+		      addr_len) == 0)
+	{
+	    break;
+	}
+    }
+    if (i==alloc->perm_cnt) {
+	PJ_LOG(5,("", "Client %s received %d bytes unauthorized data from peer %s", 
+		      client_info, size, peer_info));
+	if (alloc->perm_cnt == 0)
+	    PJ_LOG(5,("", "Client %s has no permission", client_info));
+	return PJ_TRUE;
+    }
+
+    /* Format a Data indication */
+    pa = (pj_stun_peer_addr_attr*)
+	 pj_stun_msg_find_attr(alloc->data_ind, PJ_STUN_ATTR_PEER_ADDR, 0);
+    da = (pj_stun_data_attr*)
+	 pj_stun_msg_find_attr(alloc->data_ind, PJ_STUN_ATTR_DATA, 0);
+    pj_assert(pa && da);
+
+    pj_sockaddr_cp(&pa->sockaddr, src_addr);
+    da->data = data;
+    da->length = size;
+
+    /* Encode Data indication */
+    status = pj_stun_msg_encode(alloc->data_ind, buffer, sizeof(buffer), 0,
+				NULL, &size);
+    if (status != PJ_SUCCESS)
+	return PJ_TRUE;
+
+    /* Send */
+    sent = size;
+    PJ_LOG(5,("", "Forwarding %d bytes data from peer %s to client %s", 
+		   sent, peer_info, client_info));
+
+    pj_activesock_sendto(alloc->test_srv->turn_sock, &alloc->send_key, buffer,
+			 &sent, 0, &alloc->client_addr,
+			 pj_sockaddr_get_len(&alloc->client_addr));
+
+    return PJ_TRUE;
+}
+
Index: pjproject/trunk/pjnath/src/pjnath-test/server.h
===================================================================
--- pjproject/trunk/pjnath/src/pjnath-test/server.h	(revision 1988)
+++ pjproject/trunk/pjnath/src/pjnath-test/server.h	(revision 1988)
@@ -0,0 +1,108 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#ifndef __PJNATH_TEST_SERVER_H__
+#define __PJNATH_TEST_SERVER_H__
+
+#include <pjnath.h>
+#include <pjlib-util.h>
+#include <pjlib.h>
+
+#define DNS_SERVER_PORT	    55533
+#define STUN_SERVER_PORT    33478
+#define TURN_SERVER_PORT    33479
+
+#define TURN_USERNAME	"auser"
+#define TURN_PASSWD	"apass"
+
+#define MAX_TURN_ALLOC	    16
+#define MAX_TURN_PERM	    16
+
+enum test_server_flags
+{
+    CREATE_DNS_SERVER		= (1 << 0),
+    CREATE_A_RECORD_FOR_DOMAIN	= (1 << 1),
+
+    CREATE_STUN_SERVER		= (1 << 5),
+    CREATE_STUN_SERVER_DNS_SRV	= (1 << 6),
+
+    CREATE_TURN_SERVER		= (1 << 10),
+    CREATE_TURN_SERVER_DNS_SRV	= (1 << 11),
+
+};
+
+typedef struct test_server test_server;
+
+/* TURN allocation */
+typedef struct turn_allocation
+{
+    test_server		*test_srv;
+    pj_pool_t		*pool;
+    pj_activesock_t	*sock;
+    pj_ioqueue_op_key_t	 send_key;
+    pj_sockaddr		 client_addr;
+    pj_sockaddr		 alloc_addr;
+    unsigned		 perm_cnt;
+    pj_sockaddr		 perm[MAX_TURN_PERM];
+    pj_stun_msg		*data_ind;
+} turn_allocation;
+
+/*
+ * Server installation for testing.
+ * This comprises of DNS server, STUN server, and TURN server.
+ */
+struct test_server
+{
+    pj_pool_t		*pool;
+    pj_uint32_t		 flags;
+    pj_stun_config	*stun_cfg;
+    pj_ioqueue_op_key_t	 send_key;
+
+    pj_dns_server	*dns_server;
+
+    pj_activesock_t	*stun_sock;
+
+    pj_activesock_t	*turn_sock;
+    unsigned		 turn_alloc_cnt;
+    turn_allocation	 turn_alloc[MAX_TURN_ALLOC];
+    pj_bool_t		 turn_respond_allocate;
+    pj_bool_t		 turn_respond_refresh;
+
+    struct turn_stat {
+	unsigned	 rx_allocate_cnt;
+	unsigned	 rx_refresh_cnt;
+	unsigned	 rx_send_ind_cnt;
+    } turn_stat;
+
+    pj_str_t		 domain;
+    pj_str_t		 username;
+    pj_str_t		 passwd;
+
+};
+
+
+pj_status_t create_test_server(pj_stun_config *stun_cfg,
+			       pj_uint32_t flags,
+			       const char *domain,
+			       test_server **p_test_srv);
+void        destroy_test_server(test_server *test_srv);
+void        test_server_poll_events(test_server *test_srv);
+
+
+#endif	/* __PJNATH_TEST_SERVER_H__ */
+
Index: pjproject/trunk/pjnath/src/pjnath-test/sess_auth.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath-test/sess_auth.c	(revision 1987)
+++ pjproject/trunk/pjnath/src/pjnath-test/sess_auth.c	(revision 1988)
@@ -1099,4 +1099,9 @@
     /* If REALM doesn't match, server must respond with 401
      */
+#if 0
+    // STUN session now will just use the realm sent in the
+    // response, so this test will fail because it will
+    // authenticate successfully.
+
     rc = run_client_test("Invalid REALM (long term)",  // title
 			 PJ_TRUE,	    // server responding
@@ -1117,4 +1122,5 @@
 	goto done;
     }
+#endif
 
     /* Invalid HMAC */
Index: pjproject/trunk/pjnath/src/pjnath-test/stun_sock_test.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath-test/stun_sock_test.c	(revision 1988)
+++ pjproject/trunk/pjnath/src/pjnath-test/stun_sock_test.c	(revision 1988)
@@ -0,0 +1,844 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+#define THIS_FILE   "stun_sock_test.c"
+
+enum {
+    RESPOND_STUN    = 1,
+    WITH_MAPPED	    = 2,
+    WITH_XOR_MAPPED = 4,
+
+    ECHO	    = 8
+};
+
+/*
+ * Simple STUN server
+ */
+struct stun_srv
+{
+    pj_activesock_t	*asock;
+    unsigned		 flag;
+    pj_sockaddr		 addr;
+    unsigned		 rx_cnt;
+    pj_ioqueue_op_key_t	 send_key;
+    pj_str_t		 ip_to_send;
+    pj_uint16_t		 port_to_send;
+};
+
+static pj_bool_t srv_on_data_recvfrom(pj_activesock_t *asock,
+				      void *data,
+				      pj_size_t size,
+				      const pj_sockaddr_t *src_addr,
+				      int addr_len,
+				      pj_status_t status)
+{
+    struct stun_srv *srv;
+    pj_ssize_t sent;
+
+    srv = pj_activesock_get_user_data(asock);
+
+    /* Ignore error */
+    if (status != PJ_SUCCESS)
+	return PJ_TRUE;
+
+    ++srv->rx_cnt;
+
+    /* Ignore if we're not responding */
+    if (srv->flag & RESPOND_STUN) {
+	pj_pool_t *pool;
+	pj_stun_msg *req_msg, *res_msg;
+
+	pool = pj_pool_create(mem, "stunsrv", 512, 512, NULL);
+    
+	/* Parse request */
+	status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size, 
+				    PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET,
+				    &req_msg, NULL, NULL);
+	if (status != PJ_SUCCESS) {
+	    app_perror("   pj_stun_msg_decode()", status);
+	    pj_pool_release(pool);
+	    return PJ_TRUE;
+	}
+
+	/* Create response */
+	status = pj_stun_msg_create(pool, PJ_STUN_BINDING_RESPONSE, PJ_STUN_MAGIC,
+				    req_msg->hdr.tsx_id, &res_msg);
+	if (status != PJ_SUCCESS) {
+	    app_perror("   pj_stun_msg_create()", status);
+	    pj_pool_release(pool);
+	    return PJ_TRUE;
+	}
+
+	/* Add MAPPED-ADDRESS or XOR-MAPPED-ADDRESS (or don't add) */
+	if (srv->flag & WITH_MAPPED) {
+	    pj_sockaddr_in addr;
+
+	    pj_sockaddr_in_init(&addr, &srv->ip_to_send, srv->port_to_send);
+	    pj_stun_msg_add_sockaddr_attr(pool, res_msg, PJ_STUN_ATTR_MAPPED_ADDR,
+					  PJ_FALSE, &addr, sizeof(addr));
+	} else if (srv->flag & WITH_XOR_MAPPED) {
+	    pj_sockaddr_in addr;
+
+	    pj_sockaddr_in_init(&addr, &srv->ip_to_send, srv->port_to_send);
+	    pj_stun_msg_add_sockaddr_attr(pool, res_msg, 
+					  PJ_STUN_ATTR_XOR_MAPPED_ADDR,
+					  PJ_TRUE, &addr, sizeof(addr));
+	}
+
+	/* Encode */
+	status = pj_stun_msg_encode(res_msg, (pj_uint8_t*)data, 100, 0, 
+				    NULL, &size);
+	if (status != PJ_SUCCESS) {
+	    app_perror("   pj_stun_msg_encode()", status);
+	    pj_pool_release(pool);
+	    return PJ_TRUE;
+	}
+
+	/* Send back */
+	sent = size;
+	pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0, 
+			     src_addr, addr_len);
+
+	pj_pool_release(pool);
+
+    } else if (srv->flag & ECHO) {
+	/* Send back */
+	sent = size;
+	pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0, 
+			     src_addr, addr_len);
+
+    }
+
+    return PJ_TRUE;
+}
+
+static pj_status_t create_server(pj_pool_t *pool,
+				 pj_ioqueue_t *ioqueue,
+				 unsigned flag,
+				 struct stun_srv **p_srv)
+{
+    struct stun_srv *srv;
+    pj_activesock_cb activesock_cb;
+    pj_status_t status;
+
+    srv = PJ_POOL_ZALLOC_T(pool, struct stun_srv);
+    srv->flag = flag;
+    srv->ip_to_send = pj_str("1.1.1.1");
+    srv->port_to_send = 1000;
+
+    status = pj_sockaddr_in_init(&srv->addr.ipv4, NULL, 0);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_bzero(&activesock_cb, sizeof(activesock_cb));
+    activesock_cb.on_data_recvfrom = &srv_on_data_recvfrom;
+    status = pj_activesock_create_udp(pool, &srv->addr, NULL, ioqueue,
+				      &activesock_cb, srv, &srv->asock, 
+				      &srv->addr);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_ioqueue_op_key_init(&srv->send_key, sizeof(srv->send_key));
+
+    status = pj_activesock_start_recvfrom(srv->asock, pool, 512, 0);
+    if (status != PJ_SUCCESS) {
+	pj_activesock_close(srv->asock);
+	return status;
+    }
+
+    *p_srv = srv;
+    return PJ_SUCCESS;
+}
+
+static void destroy_server(struct stun_srv *srv)
+{
+    pj_activesock_close(srv->asock);
+}
+
+
+struct stun_client
+{
+    pj_pool_t		*pool;
+    pj_stun_sock	*sock;
+
+    pj_ioqueue_op_key_t	 send_key;
+    pj_bool_t		 destroy_on_err;
+
+    unsigned		 on_status_cnt;
+    pj_stun_sock_op	 last_op;
+    pj_status_t		 last_status;
+
+    unsigned		 on_rx_data_cnt;
+};
+
+static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, 
+				     pj_stun_sock_op op,
+				     pj_status_t status)
+{
+    struct stun_client *client;
+
+    client = pj_stun_sock_get_user_data(stun_sock);
+    client->on_status_cnt++;
+    client->last_op = op;
+    client->last_status = status;
+
+    if (status != PJ_SUCCESS && client->destroy_on_err) {
+	pj_stun_sock_destroy(client->sock);
+	client->sock = NULL;
+	return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
+}
+
+static pj_bool_t stun_sock_on_rx_data(pj_stun_sock *stun_sock,
+				      void *pkt,
+				      unsigned pkt_len,
+				      const pj_sockaddr_t *src_addr,
+				      unsigned addr_len)
+{
+    struct stun_client *client;
+
+    PJ_UNUSED_ARG(pkt);
+    PJ_UNUSED_ARG(pkt_len);
+    PJ_UNUSED_ARG(src_addr);
+    PJ_UNUSED_ARG(addr_len);
+
+    client = pj_stun_sock_get_user_data(stun_sock);
+    client->on_rx_data_cnt++;
+
+    return PJ_TRUE;
+}
+
+static pj_status_t create_client(pj_stun_config *cfg,
+				 struct stun_client **p_client,
+				 pj_bool_t destroy_on_err)
+{
+    pj_pool_t *pool;
+    struct stun_client *client;
+    pj_stun_sock_cfg sock_cfg;
+    pj_stun_sock_cb cb;
+    pj_status_t status;
+
+    pool = pj_pool_create(mem, "test", 512, 512, NULL);
+    client = PJ_POOL_ZALLOC_T(pool, struct stun_client);
+    client->pool = pool;
+
+    pj_stun_sock_cfg_default(&sock_cfg);
+
+    pj_bzero(&cb, sizeof(cb));
+    cb.on_status = &stun_sock_on_status;
+    cb.on_rx_data = &stun_sock_on_rx_data;
+    status = pj_stun_sock_create(cfg, NULL, pj_AF_INET(), &cb,
+				 &sock_cfg, client, &client->sock);
+    if (status != PJ_SUCCESS) {
+	app_perror("   pj_stun_sock_create()", status);
+	pj_pool_release(pool);
+	return status;
+    }
+
+    pj_stun_sock_set_user_data(client->sock, client);
+
+    pj_ioqueue_op_key_init(&client->send_key, sizeof(client->send_key));
+
+    client->destroy_on_err = destroy_on_err;
+
+    *p_client = client;
+
+    return PJ_SUCCESS;
+}
+
+
+static void destroy_client(struct stun_client *client)
+{
+    if (client->sock) {
+	pj_stun_sock_destroy(client->sock);
+	client->sock = NULL;
+    }
+    pj_pool_release(client->pool);
+}
+
+static void handle_events(pj_stun_config *cfg, unsigned msec_delay)
+{
+    pj_time_val delay;
+
+    pj_timer_heap_poll(cfg->timer_heap, NULL);
+
+    delay.sec = 0;
+    delay.msec = msec_delay;
+    pj_time_val_normalize(&delay);
+
+    pj_ioqueue_poll(cfg->ioqueue, &delay);
+}
+
+/*
+ * Timeout test: scenario when no response is received from server
+ */
+static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
+{
+    struct stun_srv *srv;
+    struct stun_client *client;
+    pj_str_t srv_addr;
+    pj_time_val timeout, t;
+    int ret = 0;
+    pj_status_t status;
+
+    PJ_LOG(3,(THIS_FILE, "  timeout test [%d]", destroy_on_err));
+
+    status =  create_client(cfg, &client, destroy_on_err);
+    if (status != PJ_SUCCESS)
+	return -10;
+
+    status = create_server(client->pool, cfg->ioqueue, 0, &srv);
+    if (status != PJ_SUCCESS) {
+	destroy_client(client);
+	return -20;
+    }
+
+    srv_addr = pj_str("127.0.0.1");
+    status = pj_stun_sock_start(client->sock, &srv_addr, 
+				pj_ntohs(srv->addr.ipv4.sin_port), NULL);
+    if (status != PJ_SUCCESS) {
+	destroy_server(srv);
+	destroy_client(client);
+	return -30;
+    }
+
+    /* Wait until on_status() callback is called with the failure */
+    pj_gettimeofday(&timeout);
+    timeout.sec += 60;
+    do {
+	handle_events(cfg, 100);
+	pj_gettimeofday(&t);
+    } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+    /* Check that callback with correct operation is called */
+    if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting Binding operation status"));
+	ret = -40;
+	goto on_return;
+    }
+    /* .. and with the correct status */
+    if (client->last_status != PJNATH_ESTUNTIMEDOUT) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting PJNATH_ESTUNTIMEDOUT"));
+	ret = -50;
+	goto on_return;
+    }
+    /* Check that server received correct retransmissions */
+    if (srv->rx_cnt != PJ_STUN_MAX_TRANSMIT_COUNT) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting %d retransmissions, got %d",
+		   PJ_STUN_MAX_TRANSMIT_COUNT, srv->rx_cnt));
+	ret = -60;
+	goto on_return;
+    }
+    /* Check that client doesn't receive anything */
+    if (client->on_rx_data_cnt != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
+	ret = -70;
+	goto on_return;
+    }
+
+on_return:
+    destroy_server(srv);
+    destroy_client(client);
+    return ret;
+}
+
+
+/*
+ * Invalid response scenario: when server returns no MAPPED-ADDRESS or
+ * XOR-MAPPED-ADDRESS attribute.
+ */
+static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
+{
+    struct stun_srv *srv;
+    struct stun_client *client;
+    pj_str_t srv_addr;
+    pj_time_val timeout, t;
+    int ret = 0;
+    pj_status_t status;
+
+    PJ_LOG(3,(THIS_FILE, "  missing attribute test [%d]", destroy_on_err));
+
+    status =  create_client(cfg, &client, destroy_on_err);
+    if (status != PJ_SUCCESS)
+	return -110;
+
+    status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN, &srv);
+    if (status != PJ_SUCCESS) {
+	destroy_client(client);
+	return -120;
+    }
+
+    srv_addr = pj_str("127.0.0.1");
+    status = pj_stun_sock_start(client->sock, &srv_addr, 
+				pj_ntohs(srv->addr.ipv4.sin_port), NULL);
+    if (status != PJ_SUCCESS) {
+	destroy_server(srv);
+	destroy_client(client);
+	return -130;
+    }
+
+    /* Wait until on_status() callback is called with the failure */
+    pj_gettimeofday(&timeout);
+    timeout.sec += 60;
+    do {
+	handle_events(cfg, 100);
+	pj_gettimeofday(&t);
+    } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+    /* Check that callback with correct operation is called */
+    if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting Binding operation status"));
+	ret = -140;
+	goto on_return;
+    }
+    if (client->last_status != PJNATH_ESTUNNOMAPPEDADDR) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting PJNATH_ESTUNNOMAPPEDADDR"));
+	ret = -150;
+	goto on_return;
+    }
+    /* Check that client doesn't receive anything */
+    if (client->on_rx_data_cnt != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
+	ret = -170;
+	goto on_return;
+    }
+
+on_return:
+    destroy_server(srv);
+    destroy_client(client);
+    return ret;
+}
+
+/*
+ * Keep-alive test.
+ */
+static int keep_alive_test(pj_stun_config *cfg)
+{
+    struct stun_srv *srv;
+    struct stun_client *client;
+    pj_sockaddr_in mapped_addr;
+    pj_stun_sock_info info;
+    pj_str_t srv_addr;
+    pj_time_val timeout, t;
+    int ret = 0;
+    pj_status_t status;
+
+    PJ_LOG(3,(THIS_FILE, "  normal operation"));
+
+    status =  create_client(cfg, &client, PJ_TRUE);
+    if (status != PJ_SUCCESS)
+	return -310;
+
+    status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN|WITH_XOR_MAPPED, &srv);
+    if (status != PJ_SUCCESS) {
+	destroy_client(client);
+	return -320;
+    }
+
+    /*
+     * Part 1: initial Binding resolution.
+     */
+    PJ_LOG(3,(THIS_FILE, "    initial Binding request"));
+    srv_addr = pj_str("127.0.0.1");
+    status = pj_stun_sock_start(client->sock, &srv_addr, 
+				pj_ntohs(srv->addr.ipv4.sin_port), NULL);
+    if (status != PJ_SUCCESS) {
+	destroy_server(srv);
+	destroy_client(client);
+	return -330;
+    }
+
+    /* Wait until on_status() callback is called with success status */
+    pj_gettimeofday(&timeout);
+    timeout.sec += 60;
+    do {
+	handle_events(cfg, 100);
+	pj_gettimeofday(&t);
+    } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+    /* Check that callback with correct operation is called */
+    if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting Binding operation status"));
+	ret = -340;
+	goto on_return;
+    }
+    if (client->last_status != PJ_SUCCESS) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting PJ_SUCCESS status"));
+	ret = -350;
+	goto on_return;
+    }
+    /* Check that client doesn't receive anything */
+    if (client->on_rx_data_cnt != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
+	ret = -370;
+	goto on_return;
+    }
+
+    /* Get info */
+    pj_bzero(&info, sizeof(info));
+    pj_stun_sock_get_info(client->sock, &info);
+
+    /* Check that we have server address */
+    if (!pj_sockaddr_has_addr(&info.srv_addr)) {
+	PJ_LOG(3,(THIS_FILE, "    error: missing server address"));
+	ret = -380;
+	goto on_return;
+    }
+    /* .. and bound address port must not be zero */
+    if (pj_sockaddr_get_port(&info.bound_addr)==0) {
+	PJ_LOG(3,(THIS_FILE, "    error: bound address is zero"));
+	ret = -381;
+	goto on_return;
+    }
+    /* .. and mapped address */
+    if (!pj_sockaddr_has_addr(&info.mapped_addr)) {
+	PJ_LOG(3,(THIS_FILE, "    error: missing mapped address"));
+	ret = -382;
+	goto on_return;
+    }
+    /* verify the mapped address */
+    pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send);
+    if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: mapped address mismatched"));
+	ret = -383;
+	goto on_return;
+    }
+
+    /* .. and at least one alias */
+    if (info.alias_cnt == 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: must have at least one alias"));
+	ret = -384;
+	goto on_return;
+    }
+    if (!pj_sockaddr_has_addr(&info.aliases[0])) {
+	PJ_LOG(3,(THIS_FILE, "    error: missing alias"));
+	ret = -386;
+	goto on_return;
+    }
+
+
+    /*
+     * Part 2: sending and receiving data
+     */
+    PJ_LOG(3,(THIS_FILE, "    sending/receiving data"));
+
+    /* Change server operation mode to echo back data */
+    srv->flag = ECHO;
+
+    /* Reset server */
+    srv->rx_cnt = 0;
+
+    /* Client sending data to echo server */
+    {
+	char txt[100];
+	PJ_LOG(3,(THIS_FILE, "     sending to %s", pj_sockaddr_print(&info.srv_addr, txt, sizeof(txt), 3)));
+    }
+    status = pj_stun_sock_sendto(client->sock, NULL, &ret, sizeof(ret),
+				 0, &info.srv_addr, 
+				 pj_sockaddr_get_len(&info.srv_addr));
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	app_perror("    error: server sending data", status);
+	ret = -390;
+	goto on_return;
+    }
+
+    /* Wait for a short period until client receives data. We can't wait for
+     * too long otherwise the keep-alive will kick in.
+     */
+    pj_gettimeofday(&timeout);
+    timeout.sec += 1;
+    do {
+	handle_events(cfg, 100);
+	pj_gettimeofday(&t);
+    } while (client->on_rx_data_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+    /* Check that data is received in server */
+    if (srv->rx_cnt == 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: server didn't receive data"));
+	ret = -395;
+	goto on_return;
+    }
+
+    /* Check that status is still OK */
+    if (client->last_status != PJ_SUCCESS) {
+	app_perror("    error: client has failed", client->last_status);
+	ret = -400;
+	goto on_return;
+    }
+    /* Check that data has been received */
+    if (client->on_rx_data_cnt == 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: client doesn't receive data"));
+	ret = -410;
+	goto on_return;
+    }
+
+    /*
+     * Part 3: Successful keep-alive,
+     */
+    PJ_LOG(3,(THIS_FILE, "    successful keep-alive scenario"));
+
+    /* Change server operation mode to normal mode */
+    srv->flag = RESPOND_STUN | WITH_XOR_MAPPED;
+
+    /* Reset server */
+    srv->rx_cnt = 0;
+
+    /* Reset client */
+    client->on_status_cnt = 0;
+    client->last_status = PJ_SUCCESS;
+    client->on_rx_data_cnt = 0;
+
+    /* Wait for keep-alive duration to see if client actually sends the
+     * keep-alive.
+     */
+    pj_gettimeofday(&timeout);
+    timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1);
+    do {
+	handle_events(cfg, 100);
+	pj_gettimeofday(&t);
+    } while (PJ_TIME_VAL_LT(t, timeout));
+
+    /* Check that server receives some packets */
+    if (srv->rx_cnt == 0) {
+	PJ_LOG(3, (THIS_FILE, "    error: no keep-alive was received"));
+	ret = -420;
+	goto on_return;
+    }
+    /* Check that client status is still okay and on_status() callback is NOT
+     * called
+     */
+    if (client->on_status_cnt != 0) {
+	PJ_LOG(3, (THIS_FILE, "    error: on_status() must not be called on successful"
+			      "keep-alive when mapped-address does not change"));
+	ret = -430;
+	goto on_return;
+    }
+    /* Check that client doesn't receive anything */
+    if (client->on_rx_data_cnt != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
+	ret = -440;
+	goto on_return;
+    }
+
+
+    /*
+     * Part 4: Successful keep-alive with IP address change
+     */
+    PJ_LOG(3,(THIS_FILE, "    mapped IP address change"));
+
+    /* Change server operation mode to normal mode */
+    srv->flag = RESPOND_STUN | WITH_XOR_MAPPED;
+
+    /* Change mapped address in the response */
+    srv->ip_to_send = pj_str("2.2.2.2");
+    srv->port_to_send++;
+
+    /* Reset server */
+    srv->rx_cnt = 0;
+
+    /* Reset client */
+    client->on_status_cnt = 0;
+    client->last_status = PJ_SUCCESS;
+    client->on_rx_data_cnt = 0;
+
+    /* Wait for keep-alive duration to see if client actually sends the
+     * keep-alive.
+     */
+    pj_gettimeofday(&timeout);
+    timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1);
+    do {
+	handle_events(cfg, 100);
+	pj_gettimeofday(&t);
+    } while (PJ_TIME_VAL_LT(t, timeout));
+
+    /* Check that server receives some packets */
+    if (srv->rx_cnt == 0) {
+	PJ_LOG(3, (THIS_FILE, "    error: no keep-alive was received"));
+	ret = -450;
+	goto on_return;
+    }
+    /* Check that on_status() callback is called (because mapped address
+     * has changed)
+     */
+    if (client->on_status_cnt != 1) {
+	PJ_LOG(3, (THIS_FILE, "    error: on_status() was not called"));
+	ret = -460;
+	goto on_return;
+    }
+    /* Check that callback was called with correct operation */
+    if (client->last_op != PJ_STUN_SOCK_KEEP_ALIVE_OP) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting keep-alive operation status"));
+	ret = -470;
+	goto on_return;
+    }
+    /* Check that last status is still success */
+    if (client->last_status != PJ_SUCCESS) {
+	PJ_LOG(3, (THIS_FILE, "    error: expecting successful status"));
+	ret = -480;
+	goto on_return;
+    }
+    /* Check that client doesn't receive anything */
+    if (client->on_rx_data_cnt != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
+	ret = -490;
+	goto on_return;
+    }
+
+    /* Get info */
+    pj_bzero(&info, sizeof(info));
+    pj_stun_sock_get_info(client->sock, &info);
+
+    /* Check that we have server address */
+    if (!pj_sockaddr_has_addr(&info.srv_addr)) {
+	PJ_LOG(3,(THIS_FILE, "    error: missing server address"));
+	ret = -500;
+	goto on_return;
+    }
+    /* .. and mapped address */
+    if (!pj_sockaddr_has_addr(&info.mapped_addr)) {
+	PJ_LOG(3,(THIS_FILE, "    error: missing mapped address"));
+	ret = -510;
+	goto on_return;
+    }
+    /* verify the mapped address */
+    pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send);
+    if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: mapped address mismatched"));
+	ret = -520;
+	goto on_return;
+    }
+
+    /* .. and at least one alias */
+    if (info.alias_cnt == 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: must have at least one alias"));
+	ret = -530;
+	goto on_return;
+    }
+    if (!pj_sockaddr_has_addr(&info.aliases[0])) {
+	PJ_LOG(3,(THIS_FILE, "    error: missing alias"));
+	ret = -540;
+	goto on_return;
+    }
+
+
+    /*
+     * Part 5: Failed keep-alive
+     */
+    PJ_LOG(3,(THIS_FILE, "    failed keep-alive scenario"));
+    
+    /* Change server operation mode to respond without attribute */
+    srv->flag = RESPOND_STUN;
+
+    /* Reset server */
+    srv->rx_cnt = 0;
+
+    /* Reset client */
+    client->on_status_cnt = 0;
+    client->last_status = PJ_SUCCESS;
+    client->on_rx_data_cnt = 0;
+
+    /* Wait until on_status() is called with failure. */
+    pj_gettimeofday(&timeout);
+    timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + PJ_STUN_TIMEOUT_VALUE + 5);
+    do {
+	handle_events(cfg, 100);
+	pj_gettimeofday(&t);
+    } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+    /* Check that callback with correct operation is called */
+    if (client->last_op != PJ_STUN_SOCK_KEEP_ALIVE_OP) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting keep-alive operation status"));
+	ret = -600;
+	goto on_return;
+    }
+    if (client->last_status == PJ_SUCCESS) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting failed keep-alive"));
+	ret = -610;
+	goto on_return;
+    }
+    /* Check that client doesn't receive anything */
+    if (client->on_rx_data_cnt != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
+	ret = -620;
+	goto on_return;
+    }
+
+
+on_return:
+    destroy_server(srv);
+    destroy_client(client);
+    return ret;
+}
+
+
+#define DO_TEST(expr)	    \
+	    capture_pjlib_state(&stun_cfg, &pjlib_state); \
+	    ret = expr; \
+	    if (ret != 0) goto on_return; \
+	    ret = check_pjlib_state(&stun_cfg, &pjlib_state); \
+	    if (ret != 0) goto on_return;
+
+
+int stun_sock_test(void)
+{
+    struct pjlib_state pjlib_state;
+    pj_stun_config stun_cfg;
+    pj_ioqueue_t *ioqueue = NULL;
+    pj_timer_heap_t *timer_heap = NULL;
+    pj_pool_t *pool = NULL;
+    pj_status_t status;
+    int ret = 0;
+
+    pool = pj_pool_create(mem, NULL, 512, 512, NULL);
+
+    status = pj_ioqueue_create(pool, 12, &ioqueue);
+    if (status != PJ_SUCCESS) {
+	app_perror("   pj_ioqueue_create()", status);
+	ret = -4;
+	goto on_return;
+    }
+
+    status = pj_timer_heap_create(pool, 100, &timer_heap);
+    if (status != PJ_SUCCESS) {
+	app_perror("   pj_timer_heap_create()", status);
+	ret = -8;
+	goto on_return;
+    }
+    
+    pj_stun_config_init(&stun_cfg, mem, 0, ioqueue, timer_heap);
+
+    DO_TEST(timeout_test(&stun_cfg, PJ_FALSE));
+    DO_TEST(timeout_test(&stun_cfg, PJ_TRUE));
+
+    DO_TEST(missing_attr_test(&stun_cfg, PJ_FALSE));
+    DO_TEST(missing_attr_test(&stun_cfg, PJ_TRUE));
+
+    DO_TEST(keep_alive_test(&stun_cfg));
+
+on_return:
+    if (timer_heap) pj_timer_heap_destroy(timer_heap);
+    if (ioqueue) pj_ioqueue_destroy(ioqueue);
+    if (pool) pj_pool_release(pool);
+    return ret;
+}
+
+
Index: pjproject/trunk/pjnath/src/pjnath-test/test.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath-test/test.c	(revision 1987)
+++ pjproject/trunk/pjnath/src/pjnath-test/test.c	(revision 1988)
@@ -30,4 +30,107 @@
 }
 
+pj_status_t create_stun_config(pj_pool_t *pool, pj_stun_config *stun_cfg)
+{
+    pj_ioqueue_t *ioqueue;
+    pj_timer_heap_t *timer_heap;
+    pj_status_t status;
+
+    status = pj_ioqueue_create(pool, 64, &ioqueue);
+    if (status != PJ_SUCCESS) {
+	app_perror("   pj_ioqueue_create()", status);
+	return status;
+    }
+
+    status = pj_timer_heap_create(pool, 256, &timer_heap);
+    if (status != PJ_SUCCESS) {
+	app_perror("   pj_timer_heap_create()", status);
+	pj_ioqueue_destroy(ioqueue);
+	return status;
+    }
+
+    pj_stun_config_init(stun_cfg, mem, 0, ioqueue, timer_heap);
+
+    return PJ_SUCCESS;
+}
+
+void destroy_stun_config(pj_stun_config *stun_cfg)
+{
+    if (stun_cfg->timer_heap) {
+	pj_timer_heap_destroy(stun_cfg->timer_heap);
+	stun_cfg->timer_heap = NULL;
+    }
+    if (stun_cfg->ioqueue) {
+	pj_ioqueue_destroy(stun_cfg->ioqueue);
+	stun_cfg->ioqueue = NULL;
+    }
+}
+
+void poll_events(pj_stun_config *stun_cfg, unsigned msec,
+		 pj_bool_t first_event_only)
+{
+    pj_time_val stop_time;
+    int count = 0;
+
+    pj_gettimeofday(&stop_time);
+    stop_time.msec += msec;
+    pj_time_val_normalize(&stop_time);
+
+    /* Process all events for the specified duration. */
+    for (;;) {
+	pj_time_val timeout = {0, 1}, now;
+	int c;
+
+	c = pj_timer_heap_poll( stun_cfg->timer_heap, NULL );
+	if (c > 0)
+	    count += c;
+
+	//timeout.sec = timeout.msec = 0;
+	c = pj_ioqueue_poll( stun_cfg->ioqueue, &timeout);
+	if (c > 0)
+	    count += c;
+
+	pj_gettimeofday(&now);
+	if (PJ_TIME_VAL_GTE(now, stop_time))
+	    break;
+
+	if (first_event_only && count >= 0)
+	    break;
+    }
+}
+
+void capture_pjlib_state(pj_stun_config *cfg, struct pjlib_state *st)
+{
+    pj_caching_pool *cp;
+
+    st->timer_cnt = pj_timer_heap_count(cfg->timer_heap);
+    
+    cp = (pj_caching_pool*)mem;
+    st->pool_used_cnt = cp->used_count;
+}
+
+int check_pjlib_state(pj_stun_config *cfg, 
+		      const struct pjlib_state *initial_st)
+{
+    struct pjlib_state current_state;
+    int rc = 0;
+
+    capture_pjlib_state(cfg, &current_state);
+
+    if (current_state.timer_cnt > initial_st->timer_cnt) {
+	PJ_LOG(3,("", "    error: possibly leaking timer"));
+	rc |= ERR_TIMER_LEAK;
+    }
+
+    if (current_state.pool_used_cnt > initial_st->pool_used_cnt) {
+	PJ_LOG(3,("", "    error: possibly leaking memory"));
+	PJ_LOG(3,("", "    dumping memory pool:"));
+	pj_pool_factory_dump(mem, PJ_TRUE);
+	rc |= ERR_MEMORY_LEAK;
+    }
+
+    return rc;
+}
+
+
 #define DO_TEST(test)	do { \
 			    PJ_LOG(3, ("test", "Running %s...", #test));  \
@@ -65,4 +168,5 @@
     pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 );
 
+    pjlib_util_init();
     pjnath_init();
 
@@ -74,4 +178,12 @@
 #if INCLUDE_ICE_TEST
     DO_TEST(ice_test());
+#endif
+
+#if INCLUDE_STUN_SOCK_TEST
+    DO_TEST(stun_sock_test());
+#endif
+
+#if INCLUDE_TURN_SOCK_TEST
+    DO_TEST(turn_sock_test());
 #endif
 
Index: pjproject/trunk/pjnath/src/pjnath-test/test.h
===================================================================
--- pjproject/trunk/pjnath/src/pjnath-test/test.h	(revision 1987)
+++ pjproject/trunk/pjnath/src/pjnath-test/test.h	(revision 1988)
@@ -23,7 +23,11 @@
 #define INCLUDE_STUN_TEST	    1
 #define INCLUDE_ICE_TEST	    1
+#define INCLUDE_STUN_SOCK_TEST	    1
+#define INCLUDE_TURN_SOCK_TEST	    1
 
 int stun_test(void);
 int sess_auth_test(void);
+int stun_sock_test(void);
+int turn_sock_test(void);
 int ice_test(void);
 int test_main(void);
@@ -32,2 +36,27 @@
 extern pj_pool_factory *mem;
 
+////////////////////////////////////
+/*
+ * Utilities
+ */
+pj_status_t create_stun_config(pj_pool_t *pool, pj_stun_config *stun_cfg);
+void destroy_stun_config(pj_stun_config *stun_cfg);
+
+void poll_events(pj_stun_config *stun_cfg, unsigned msec,
+		 pj_bool_t first_event_only);
+
+typedef struct pjlib_state
+{
+    unsigned	timer_cnt;	/* Number of timer entries */
+    unsigned	pool_used_cnt;	/* Number of app pools	    */
+} pjlib_state;
+
+
+void capture_pjlib_state(pj_stun_config *cfg, struct pjlib_state *st);
+int check_pjlib_state(pj_stun_config *cfg, 
+		      const struct pjlib_state *initial_st);
+
+
+#define ERR_MEMORY_LEAK	    1
+#define ERR_TIMER_LEAK	    2
+
Index: pjproject/trunk/pjnath/src/pjnath-test/turn_sock_test.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath-test/turn_sock_test.c	(revision 1988)
+++ pjproject/trunk/pjnath/src/pjnath-test/turn_sock_test.c	(revision 1988)
@@ -0,0 +1,515 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include "server.h"
+
+#define SRV_DOMAIN	"pjsip.lab.domain"
+#define KA_INTERVAL	50
+
+struct test_result
+{
+    unsigned    state_called;
+    unsigned    rx_data_cnt;
+};
+
+struct test_session
+{
+    pj_pool_t		*pool;
+    pj_stun_config	*stun_cfg;
+    pj_turn_sock	*turn_sock;
+    pj_dns_resolver	*resolver;
+    test_server		*test_srv;
+
+    pj_bool_t		 destroy_called;
+    int			 destroy_on_state;
+    struct test_result	 result;
+};
+
+struct test_session_cfg
+{
+    struct {
+	pj_bool_t	enable_dns_srv;
+	int		destroy_on_state;
+    } client;
+
+    struct {
+	pj_uint32_t	flags;
+	pj_bool_t	respond_allocate;
+	pj_bool_t	respond_refresh;
+    } srv;
+};
+
+static void turn_on_rx_data(pj_turn_sock *turn_sock,
+			    void *pkt,
+			    unsigned pkt_len,
+			    const pj_sockaddr_t *peer_addr,
+			    unsigned addr_len);
+static void turn_on_state(pj_turn_sock *turn_sock, 
+			  pj_turn_state_t old_state,
+			  pj_turn_state_t new_state);
+
+static void destroy_session(struct test_session *sess)
+{
+    if (sess->resolver) {
+	pj_dns_resolver_destroy(sess->resolver, PJ_TRUE);
+	sess->resolver = NULL;
+    }
+
+    if (sess->turn_sock) {
+	if (!sess->destroy_called) {
+	    sess->destroy_called = PJ_TRUE;
+	    pj_turn_sock_destroy(sess->turn_sock);
+	}
+	sess->turn_sock = NULL;
+    }
+
+    if (sess->test_srv) {
+	destroy_test_server(sess->test_srv);
+	sess->test_srv = NULL;
+    }
+
+    if (sess->pool) {
+	pj_pool_release(sess->pool);
+    }
+}
+
+
+
+static int create_test_session(pj_stun_config  *stun_cfg,
+			       const struct test_session_cfg *cfg,
+			       struct test_session **p_sess)
+{
+    struct test_session *sess;
+    pj_pool_t *pool;
+    pj_turn_sock_cb turn_sock_cb;
+    pj_turn_alloc_param alloc_param;
+    pj_stun_auth_cred cred;
+    pj_status_t status;
+
+    /* Create client */
+    pool = pj_pool_create(mem, "turnclient", 512, 512, NULL);
+    sess = PJ_POOL_ZALLOC_T(pool, struct test_session);
+    sess->pool = pool;
+    sess->stun_cfg = stun_cfg;
+    sess->destroy_on_state = cfg->client.destroy_on_state;
+
+    pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb));
+    turn_sock_cb.on_rx_data = &turn_on_rx_data;
+    turn_sock_cb.on_state = &turn_on_state;
+    status = pj_turn_sock_create(sess->stun_cfg, pj_AF_INET(), PJ_TURN_TP_UDP,
+				 &turn_sock_cb, 0, sess, &sess->turn_sock);
+    if (status != PJ_SUCCESS) {
+	destroy_session(sess);
+	return -20;
+    }
+
+    /* Create test server */
+    status = create_test_server(sess->stun_cfg, cfg->srv.flags,
+				SRV_DOMAIN, &sess->test_srv);
+    if (status != PJ_SUCCESS) {
+	destroy_session(sess);
+	return -30;
+    }
+
+    sess->test_srv->turn_respond_allocate = cfg->srv.respond_allocate;
+    sess->test_srv->turn_respond_refresh = cfg->srv.respond_refresh;
+
+    /* Create client resolver */
+    status = pj_dns_resolver_create(mem, "resolver", 0, sess->stun_cfg->timer_heap,
+				    sess->stun_cfg->ioqueue, &sess->resolver);
+    if (status != PJ_SUCCESS) {
+	destroy_session(sess);
+	return -40;
+
+    } else {
+	pj_str_t dns_srv = pj_str("127.0.0.1");
+	pj_uint16_t dns_srv_port = (pj_uint16_t) DNS_SERVER_PORT;
+	status = pj_dns_resolver_set_ns(sess->resolver, 1, &dns_srv, &dns_srv_port);
+
+	if (status != PJ_SUCCESS) {
+	    destroy_session(sess);
+	    return -50;
+	}
+    }
+
+    /* Init TURN credential */
+    pj_bzero(&cred, sizeof(cred));
+    cred.type = PJ_STUN_AUTH_CRED_STATIC;
+    cred.data.static_cred.realm = pj_str(SRV_DOMAIN);
+    cred.data.static_cred.username = pj_str(TURN_USERNAME);
+    cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
+    cred.data.static_cred.data = pj_str(TURN_PASSWD);
+
+    /* Init TURN allocate parameter */
+    pj_turn_alloc_param_default(&alloc_param);
+    alloc_param.ka_interval = KA_INTERVAL;
+
+    /* Start the client */
+    if (cfg->client.enable_dns_srv) {
+	/* Use DNS SRV to resolve server, may fallback to DNS A */
+	pj_str_t domain = pj_str(SRV_DOMAIN);
+	status = pj_turn_sock_alloc(sess->turn_sock, &domain, TURN_SERVER_PORT,
+				    sess->resolver, &cred, &alloc_param);
+
+    } else {
+	/* Explicitly specify server address */
+	pj_str_t host = pj_str("127.0.0.1");
+	status = pj_turn_sock_alloc(sess->turn_sock, &host, TURN_SERVER_PORT,
+				    NULL, &cred, &alloc_param);
+
+    }
+
+    if (status != PJ_SUCCESS) {
+	if (cfg->client.destroy_on_state >= PJ_TURN_STATE_READY) {
+	    destroy_session(sess);
+	    return -70;
+	}
+    }
+
+    *p_sess = sess;
+    return 0;
+}
+
+
+static void turn_on_rx_data(pj_turn_sock *turn_sock,
+			    void *pkt,
+			    unsigned pkt_len,
+			    const pj_sockaddr_t *peer_addr,
+			    unsigned addr_len)
+{
+    struct test_session *sess;
+
+    PJ_UNUSED_ARG(pkt);
+    PJ_UNUSED_ARG(pkt_len);
+    PJ_UNUSED_ARG(peer_addr);
+    PJ_UNUSED_ARG(addr_len);
+
+    sess = (struct test_session*) pj_turn_sock_get_user_data(turn_sock);
+    if (sess == NULL)
+	return;
+
+    sess->result.rx_data_cnt++;
+}
+
+
+static void turn_on_state(pj_turn_sock *turn_sock, 
+			  pj_turn_state_t old_state,
+			  pj_turn_state_t new_state)
+{
+    struct test_session *sess;
+    unsigned i, mask;
+
+    PJ_UNUSED_ARG(old_state);
+
+    sess = (struct test_session*) pj_turn_sock_get_user_data(turn_sock);
+    if (sess == NULL)
+	return;
+
+    /* This state must not be called before */
+    pj_assert((sess->result.state_called & (1<<new_state)) == 0);
+
+    /* new_state must be greater than old_state */
+    pj_assert(new_state > old_state);
+
+    /* must not call any greater state before */
+    mask = 0;
+    for (i=new_state+1; i<31; ++i) mask |= (1 << i);
+
+    pj_assert((sess->result.state_called & mask) == 0);
+
+    sess->result.state_called |= (1 << new_state);
+
+    if (new_state >= sess->destroy_on_state && !sess->destroy_called) {
+	sess->destroy_called = PJ_TRUE;
+	pj_turn_sock_destroy(turn_sock);
+    }
+
+    if (new_state >= PJ_TURN_STATE_DESTROYING) {
+	pj_turn_sock_set_user_data(sess->turn_sock, NULL);
+	sess->turn_sock = NULL;
+    }
+}
+
+
+/////////////////////////////////////////////////////////////////////
+
+static int state_progression_test(pj_stun_config  *stun_cfg)
+{
+    struct test_session_cfg test_cfg = 
+    {
+	{   /* Client cfg */
+	    /* DNS SRV */   /* Destroy on state */
+	    PJ_TRUE,	    0xFFFF
+	},
+	{   /* Server cfg */
+	    0xFFFFFFFF,	    /* flags */
+	    PJ_TRUE,	    /* respond to allocate  */
+	    PJ_TRUE	    /* respond to refresh   */
+	}
+    };
+    struct test_session *sess;
+    unsigned i;
+    int rc;
+
+    PJ_LOG(3,("", "  state progression tests"));
+
+    for (i=0; i<=1; ++i) {
+	enum { TIMEOUT = 60 };
+	pjlib_state pjlib_state;
+	pj_turn_session_info info;
+	struct test_result result;
+	pj_time_val tstart;
+
+	PJ_LOG(3,("", "   %s DNS SRV resolution",
+	              (i==0? "without" : "with")));
+
+	capture_pjlib_state(stun_cfg, &pjlib_state);
+
+	test_cfg.client.enable_dns_srv = i;
+
+	rc = create_test_session(stun_cfg, &test_cfg, &sess);
+	if (rc != 0)
+	    return rc;
+
+	pj_bzero(&info, sizeof(info));
+
+	/* Wait until state is READY */
+	pj_gettimeofday(&tstart);
+	while (sess->turn_sock) {
+	    pj_time_val now;
+
+	    poll_events(stun_cfg, 10, PJ_FALSE);
+	    rc = pj_turn_sock_get_info(sess->turn_sock, &info);
+	    if (rc!=PJ_SUCCESS)
+		break;
+
+	    if (info.state >= PJ_TURN_STATE_READY)
+		break;
+
+	    pj_gettimeofday(&now);
+	    if (now.sec - tstart.sec > TIMEOUT) {
+		PJ_LOG(3,("", "    timed-out"));
+		break;
+	    }
+	}
+
+	if (info.state != PJ_TURN_STATE_READY) {
+	    PJ_LOG(3,("", "    error: state is not READY"));
+	    destroy_session(sess);
+	    return -130;
+	}
+
+	/* Deallocate */
+	pj_turn_sock_destroy(sess->turn_sock);
+
+	/* Wait for couple of seconds.
+	 * We can't poll the session info since the session may have
+	 * been destroyed
+	 */
+	poll_events(stun_cfg, 2000, PJ_FALSE);
+	sess->turn_sock = NULL;
+	pj_memcpy(&result, &sess->result, sizeof(result));
+	destroy_session(sess);
+
+	/* Check the result */
+	if ((result.state_called & (1<<PJ_TURN_STATE_RESOLVING)) == 0) {
+	    PJ_LOG(3,("", "    error: PJ_TURN_STATE_RESOLVING is not called"));
+	    return -140;
+	}
+
+	if ((result.state_called & (1<<PJ_TURN_STATE_RESOLVED)) == 0) {
+	    PJ_LOG(3,("", "    error: PJ_TURN_STATE_RESOLVED is not called"));
+	    return -150;
+	}
+
+	if ((result.state_called & (1<<PJ_TURN_STATE_ALLOCATING)) == 0) {
+	    PJ_LOG(3,("", "    error: PJ_TURN_STATE_ALLOCATING is not called"));
+	    return -155;
+	}
+
+	if ((result.state_called & (1<<PJ_TURN_STATE_READY)) == 0) {
+	    PJ_LOG(3,("", "    error: PJ_TURN_STATE_READY is not called"));
+	    return -160;
+	}
+
+	if ((result.state_called & (1<<PJ_TURN_STATE_DEALLOCATING)) == 0) {
+	    PJ_LOG(3,("", "    error: PJ_TURN_STATE_DEALLOCATING is not called"));
+	    return -170;
+	}
+
+	if ((result.state_called & (1<<PJ_TURN_STATE_DEALLOCATED)) == 0) {
+	    PJ_LOG(3,("", "    error: PJ_TURN_STATE_DEALLOCATED is not called"));
+	    return -180;
+	}
+
+	if ((result.state_called & (1<<PJ_TURN_STATE_DESTROYING)) == 0) {
+	    PJ_LOG(3,("", "    error: PJ_TURN_STATE_DESTROYING is not called"));
+	    return -190;
+	}
+
+	poll_events(stun_cfg, 500, PJ_FALSE);
+	rc = check_pjlib_state(stun_cfg, &pjlib_state);
+	if (rc != 0) {
+	    PJ_LOG(3,("", "    error: memory/timer-heap leak detected"));
+	    return rc;
+	}
+    }
+
+    return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////
+
+static int destroy_test(pj_stun_config  *stun_cfg,
+			pj_bool_t with_dns_srv,
+			pj_bool_t in_callback)
+{
+    struct test_session_cfg test_cfg = 
+    {
+	{   /* Client cfg */
+	    /* DNS SRV */   /* Destroy on state */
+	    PJ_TRUE,	    0xFFFF
+	},
+	{   /* Server cfg */
+	    0xFFFFFFFF,	    /* flags */
+	    PJ_TRUE,	    /* respond to allocate  */
+	    PJ_TRUE	    /* respond to refresh   */
+	}
+    };
+    struct test_session *sess;
+    int target_state;
+    int rc;
+
+    PJ_LOG(3,("", "  destroy test %s %s",
+	          (in_callback? "in callback" : ""),
+		  (with_dns_srv? "with DNS srv" : "")
+		  ));
+
+    test_cfg.client.enable_dns_srv = with_dns_srv;
+
+    for (target_state=PJ_TURN_STATE_RESOLVING; target_state<=PJ_TURN_STATE_READY; ++target_state) {
+	enum { TIMEOUT = 60 };
+	pjlib_state pjlib_state;
+	pj_turn_session_info info;
+	pj_time_val tstart;
+
+	capture_pjlib_state(stun_cfg, &pjlib_state);
+
+	PJ_LOG(3,("", "   %s", pj_turn_state_name((pj_turn_state_t)target_state)));
+
+	if (in_callback)
+	    test_cfg.client.destroy_on_state = target_state;
+
+	rc = create_test_session(stun_cfg, &test_cfg, &sess);
+	if (rc != 0)
+	    return rc;
+
+	if (in_callback) {
+	    pj_gettimeofday(&tstart);
+	    rc = 0;
+	    while (sess->turn_sock) {
+		pj_time_val now;
+
+		poll_events(stun_cfg, 100, PJ_FALSE);
+
+		pj_gettimeofday(&now);
+		if (now.sec - tstart.sec > TIMEOUT) {
+		    rc = -7;
+		    break;
+		}
+	    }
+
+	} else {
+	    pj_gettimeofday(&tstart);
+	    rc = 0;
+	    while (sess->turn_sock) {
+		pj_time_val now;
+
+		poll_events(stun_cfg, 1, PJ_FALSE);
+
+		pj_turn_sock_get_info(sess->turn_sock, &info);
+		
+		if (info.state >= target_state) {
+		    pj_turn_sock_destroy(sess->turn_sock);
+		    break;
+		}
+
+		pj_gettimeofday(&now);
+		if (now.sec - tstart.sec > TIMEOUT) {
+		    rc = -8;
+		    break;
+		}
+	    }
+	}
+
+
+	if (rc != 0) {
+	    PJ_LOG(3,("", "    error: timeout"));
+	    return rc;
+	}
+
+	poll_events(stun_cfg, 1000, PJ_FALSE);
+	destroy_session(sess);
+
+	rc = check_pjlib_state(stun_cfg, &pjlib_state);
+	if (rc != 0) {
+	    PJ_LOG(3,("", "    error: memory/timer-heap leak detected"));
+	    return rc;
+	}
+    }
+
+    return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////
+
+int turn_sock_test(void)
+{
+    pj_pool_t *pool;
+    pj_stun_config stun_cfg;
+    int i, rc = 0;
+
+    pool = pj_pool_create(mem, "turntest", 512, 512, NULL);
+    rc = create_stun_config(pool, &stun_cfg);
+    if (rc != PJ_SUCCESS) {
+	pj_pool_release(pool);
+	return -2;
+    }
+
+    rc = state_progression_test(&stun_cfg);
+    if (rc != 0) 
+	goto on_return;
+
+    for (i=0; i<=1; ++i) {
+	int j;
+	for (j=0; j<=1; ++j) {
+	    rc = destroy_test(&stun_cfg, i, j);
+	    if (rc != 0)
+		goto on_return;
+	}
+    }
+
+on_return:
+    destroy_stun_config(&stun_cfg);
+    pj_pool_release(pool);
+    return rc;
+}
+
Index: pjproject/trunk/pjnath/src/pjnath/errno.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/errno.c	(revision 1987)
+++ pjproject/trunk/pjnath/src/pjnath/errno.c	(revision 1988)
@@ -53,4 +53,6 @@
     PJ_BUILD_ERR( PJNATH_ESTUNINSERVER,	    "Invalid STUN server or server not configured"),
 
+    PJ_BUILD_ERR( PJNATH_ESTUNDESTROYED,    "STUN object has been destoyed"),
+
     /* ICE related errors */
     PJ_BUILD_ERR( PJNATH_ENOICE,	    "ICE session not available"),
Index: pjproject/trunk/pjnath/src/pjnath/ice_session.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 1987)
+++ pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 1988)
@@ -22,4 +22,5 @@
 #include <pj/assert.h>
 #include <pj/guid.h>
+#include <pj/hash.h>
 #include <pj/log.h>
 #include <pj/os.h>
@@ -100,4 +101,9 @@
     pj_ice_sess_checklist   *clist;
 } timer_data;
+
+
+/* This is the data that will be attached as token to outgoing
+ * STUN messages.
+ */
 
 
@@ -170,4 +176,19 @@
 
 
+PJ_DEF(const char*) pj_ice_sess_role_name(pj_ice_sess_role role)
+{
+    switch (role) {
+    case PJ_ICE_SESS_ROLE_UNKNOWN:
+	return "Unknown";
+    case PJ_ICE_SESS_ROLE_CONTROLLED:
+	return "Controlled";
+    case PJ_ICE_SESS_ROLE_CONTROLLING:
+	return "Controlling";
+    default:
+	return "??";
+    }
+}
+
+
 /* Get the prefix for the foundation */
 static int get_type_prefix(pj_ice_cand_type type)
@@ -184,5 +205,10 @@
 }
 
-/* Calculate foundation */
+/* Calculate foundation:
+ * Two candidates have the same foundation when they are "similar" - of
+ * the same type and obtained from the same host candidate and STUN
+ * server using the same protocol.  Otherwise, their foundation is
+ * different.
+ */
 PJ_DEF(void) pj_ice_calc_foundation(pj_pool_t *pool,
 				    pj_str_t *foundation,
@@ -191,8 +217,14 @@
 {
     char buf[64];
-
+    pj_uint32_t val;
+
+    if (base_addr->addr.sa_family == pj_AF_INET()) {
+	val = pj_ntohl(base_addr->ipv4.sin_addr.s_addr);
+    } else {
+	val = pj_hash_calc(0, pj_sockaddr_get_addr(base_addr),
+			   pj_sockaddr_get_addr_len(base_addr));
+    }
     pj_ansi_snprintf(buf, sizeof(buf), "%c%x",
-		     get_type_prefix(type),
-		     (int)pj_ntohl(base_addr->ipv4.sin_addr.s_addr));
+		     get_type_prefix(type), val);
     pj_strdup2(pool, foundation, buf);
 }
@@ -264,5 +296,5 @@
 
     if (name == NULL)
-	name = "ice%p";
+	name = "icess%p";
 
     pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_SESS, 
@@ -299,4 +331,10 @@
 	    return status;
 	}
+    }
+
+    /* Initialize transport datas */
+    for (i=0; i<PJ_ARRAY_SIZE(ice->tp_data); ++i) {
+	ice->tp_data[i].transport_id = i;
+	ice->tp_data[i].has_req_data = PJ_FALSE;
     }
 
@@ -552,4 +590,5 @@
 PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice,
 					 unsigned comp_id,
+					 unsigned transport_id,
 					 pj_ice_cand_type type,
 					 pj_uint16_t local_pref,
@@ -577,5 +616,6 @@
 
     lcand = &ice->lcand[ice->lcand_cnt];
-    lcand->comp_id = comp_id;
+    lcand->comp_id = (pj_uint8_t)comp_id;
+    lcand->transport_id = (pj_uint8_t)transport_id;
     lcand->type = type;
     pj_strdup(ice->pool, &lcand->foundation, foundation);
@@ -583,9 +623,5 @@
     pj_memcpy(&lcand->addr, addr, addr_len);
     pj_memcpy(&lcand->base_addr, base_addr, addr_len);
-    if (rel_addr)
-	pj_memcpy(&lcand->rel_addr, rel_addr, addr_len);
-    else
-	pj_bzero(&lcand->rel_addr, sizeof(lcand->rel_addr));
-
+    pj_memcpy(&lcand->rel_addr, rel_addr, addr_len);
 
     pj_ansi_strcpy(ice->tmp.txt, pj_inet_ntoa(lcand->addr.ipv4.sin_addr));
@@ -1323,7 +1359,11 @@
 
     /* Disable our components which don't have matching component */
-    if (ice->comp_cnt==2 && highest_comp==1) {
-	ice->comp_cnt = 1;
-    }
+    for (i=highest_comp; i<ice->comp_cnt; ++i) {
+	if (ice->comp[i].stun_sess) {
+	    pj_stun_session_destroy(ice->comp[i].stun_sess);
+	    pj_bzero(&ice->comp[i], sizeof(ice->comp[i]));
+	}
+    }
+    ice->comp_cnt = highest_comp;
 
     /* Init timer entry in the checklist. Initially the timer ID is FALSE
@@ -1346,17 +1386,4 @@
 }
 
-
-/* This is the data that will be attached as user data to outgoing
- * STUN requests, and it will be given back when we receive completion
- * status of the request.
- */
-struct req_data
-{
-    pj_ice_sess		    *ice;
-    pj_ice_sess_checklist   *clist;
-    unsigned		     ckid;
-};
-
-
 /* Perform check on the specified candidate pair */
 static pj_status_t perform_check(pj_ice_sess *ice, 
@@ -1365,5 +1392,5 @@
 {
     pj_ice_sess_comp *comp;
-    struct req_data *rd;
+    pj_ice_msg_data *msg_data;
     pj_ice_sess_check *check;
     const pj_ice_sess_cand *lcand;
@@ -1393,8 +1420,10 @@
      * completes and on_stun_request_complete() callback is called.
      */
-    rd = PJ_POOL_ZALLOC_T(check->tdata->pool, struct req_data);
-    rd->ice = ice;
-    rd->clist = clist;
-    rd->ckid = check_id;
+    msg_data = PJ_POOL_ZALLOC_T(check->tdata->pool, pj_ice_msg_data);
+    msg_data->transport_id = lcand->transport_id;
+    msg_data->has_req_data = PJ_TRUE;
+    msg_data->data.req.ice = ice;
+    msg_data->data.req.clist = clist;
+    msg_data->data.req.ckid = check_id;
 
     /* Add PRIORITY */
@@ -1428,5 +1457,5 @@
 
     /* Initiate STUN transaction to send the request */
-    status = pj_stun_session_send_msg(comp->stun_sess, (void*)rd, PJ_FALSE, 
+    status = pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE, 
 				      PJ_TRUE, &rcand->addr, 
 				      sizeof(pj_sockaddr_in), check->tdata);
@@ -1656,10 +1685,8 @@
     stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess);
     pj_ice_sess *ice = sd->ice;
-
-    PJ_UNUSED_ARG(token);
-
-    return (*ice->cb.on_tx_pkt)(ice, sd->comp_id, 
-				pkt, pkt_size, 
-				dst_addr, addr_len);
+    pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token;
+    
+    return (*ice->cb.on_tx_pkt)(ice, sd->comp_id, msg_data->transport_id,
+				pkt, pkt_size, dst_addr, addr_len);
 }
 
@@ -1674,5 +1701,5 @@
 				     unsigned src_addr_len)
 {
-    struct req_data *rd = (struct req_data*) token;
+    pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token;
     pj_ice_sess *ice;
     pj_ice_sess_check *check, *new_check;
@@ -1685,7 +1712,10 @@
     PJ_UNUSED_ARG(src_addr_len);
 
-    ice = rd->ice;
-    check = &rd->clist->checks[rd->ckid];
-    clist = rd->clist;
+    pj_assert(msg_data->has_req_data);
+
+    ice = msg_data->data.req.ice;
+    clist = msg_data->data.req.clist;
+    check = &clist->checks[msg_data->data.req.ckid];
+    
 
     /* Mark STUN transaction as complete */
@@ -1740,5 +1770,5 @@
 	    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, rd->ckid);
+	    perform_check(ice, clist, msg_data->data.req.ckid);
 	    pj_mutex_unlock(ice->mutex);
 	    return;
@@ -1847,4 +1877,5 @@
 	/* Add new peer reflexive candidate */
 	status = pj_ice_sess_add_cand(ice, check->lcand->comp_id, 
+				      msg_data->transport_id,
 				      PJ_ICE_CAND_TYPE_PRFLX,
 				      65535, &foundation,
@@ -1920,4 +1951,5 @@
     stun_data *sd;
     const pj_stun_msg *msg = rdata->msg;
+    pj_ice_msg_data *msg_data;
     pj_ice_sess *ice;
     pj_stun_priority_attr *prio_attr;
@@ -1930,10 +1962,9 @@
     PJ_UNUSED_ARG(pkt);
     PJ_UNUSED_ARG(pkt_len);
-    PJ_UNUSED_ARG(token);
-
+    
     /* Reject any requests except Binding request */
     if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) {
 	pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, 
-				NULL, NULL, PJ_TRUE, 
+				NULL, token, PJ_TRUE, 
 				src_addr, src_addr_len);
 	return PJ_SUCCESS;
@@ -2002,5 +2033,5 @@
 	    /* Generate 487 response */
 	    pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, 
-				    NULL, NULL, PJ_TRUE, 
+				    NULL, token, PJ_TRUE, 
 				    src_addr, src_addr_len);
 	    pj_mutex_unlock(ice->mutex);
@@ -2014,5 +2045,5 @@
 	    /* Generate 487 response */
 	    pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, 
-				    NULL, NULL, PJ_TRUE, 
+				    NULL, token, PJ_TRUE, 
 				    src_addr, src_addr_len);
 	    pj_mutex_unlock(ice->mutex);
@@ -2035,9 +2066,16 @@
     }
 
+    /* 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);
 
-    status = pj_stun_session_send_msg(sess, NULL, PJ_TRUE, PJ_TRUE,
+    /* Create a msg_data to be associated with this response */
+    msg_data = PJ_POOL_ZALLOC_T(tdata->pool, pj_ice_msg_data);
+    msg_data->transport_id = ((pj_ice_msg_data*)token)->transport_id;
+    msg_data->has_req_data = PJ_FALSE;
+
+    /* Send the response */
+    status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, PJ_TRUE,
 				      src_addr, src_addr_len, tdata);
 
@@ -2059,4 +2097,5 @@
     /* Init rcheck */
     rcheck->comp_id = sd->comp_id;
+    rcheck->transport_id = ((pj_ice_msg_data*)token)->transport_id;
     rcheck->src_addr_len = src_addr_len;
     pj_memcpy(&rcheck->src_addr, src_addr, src_addr_len);
@@ -2091,5 +2130,4 @@
     pj_ice_sess_cand *rcand;
     unsigned i;
-    pj_bool_t is_relayed;
 
     comp = find_comp(ice, rcheck->comp_id);
@@ -2110,5 +2148,5 @@
     if (i == ice->rcand_cnt) {
 	rcand = &ice->rcand[ice->rcand_cnt++];
-	rcand->comp_id = rcheck->comp_id;
+	rcand->comp_id = (pj_uint8_t)rcheck->comp_id;
 	rcand->type = PJ_ICE_CAND_TYPE_PRFLX;
 	rcand->prio = rcheck->priority;
@@ -2148,10 +2186,12 @@
     }
 #else
-    /* Just get candidate with the highest priority for the specified 
-     * component ID in the checklist.
+    /* Just get candidate with the highest priority and same transport ID
+     * for the specified  component ID in the checklist.
      */
     for (i=0; i<ice->clist.count; ++i) {
 	pj_ice_sess_check *c = &ice->clist.checks[i];
-	if (c->lcand->comp_id == rcheck->comp_id) {
+	if (c->lcand->comp_id == rcheck->comp_id &&
+	    c->lcand->transport_id == rcheck->transport_id) 
+	{
 	    lcand = c->lcand;
 	    break;
@@ -2171,9 +2211,4 @@
      * Create candidate pair for this request. 
      */
-    /* First check if the source address is the source address of the 
-     * STUN relay, to determine if local candidate is relayed candidate.
-     */
-    PJ_TODO(DETERMINE_IF_REQUEST_COMES_FROM_RELAYED_CANDIDATE);
-    is_relayed = PJ_FALSE;
 
     /* 
@@ -2310,4 +2345,5 @@
     pj_status_t status = PJ_SUCCESS;
     pj_ice_sess_comp *comp;
+    pj_ice_sess_cand *cand;
 
     PJ_ASSERT_RETURN(ice && comp_id, PJ_EINVAL);
@@ -2333,5 +2369,7 @@
     }
 
-    status = (*ice->cb.on_tx_pkt)(ice, comp_id, data, data_len, 
+    cand = comp->valid_check->lcand;
+    status = (*ice->cb.on_tx_pkt)(ice, comp_id, cand->transport_id, 
+				  data, data_len, 
 				  &comp->valid_check->rcand->addr, 
 				  sizeof(pj_sockaddr_in));
@@ -2345,4 +2383,5 @@
 PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
 					  unsigned comp_id,
+					  unsigned transport_id,
 					  void *pkt,
 					  pj_size_t pkt_size,
@@ -2352,4 +2391,6 @@
     pj_status_t status = PJ_SUCCESS;
     pj_ice_sess_comp *comp;
+    pj_ice_msg_data *msg_data = NULL;
+    unsigned i;
     pj_status_t stun_status;
 
@@ -2364,9 +2405,22 @@
     }
 
+    /* Find transport */
+    for (i=0; i<PJ_ARRAY_SIZE(ice->tp_data); ++i) {
+	if (ice->tp_data[i].transport_id == transport_id) {
+	    msg_data = &ice->tp_data[i];
+	    break;
+	}
+    }
+    if (msg_data == NULL) {
+	pj_assert(!"Invalid transport ID");
+	status = PJ_EINVAL;
+	goto on_return;
+    }
+
     stun_status = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_size, 
     				    PJ_STUN_IS_DATAGRAM);
     if (stun_status == PJ_SUCCESS) {
 	status = pj_stun_session_on_rx_pkt(comp->stun_sess, pkt, pkt_size,
-					   PJ_STUN_IS_DATAGRAM, NULL,
+					   PJ_STUN_IS_DATAGRAM, msg_data,
 					   NULL, src_addr, src_addr_len);
 	if (status != PJ_SUCCESS) {
@@ -2376,5 +2430,5 @@
 	}
     } else {
-	(*ice->cb.on_rx_data)(ice, comp_id, pkt, pkt_size, 
+	(*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size, 
 			      src_addr, src_addr_len);
     }
Index: pjproject/trunk/pjnath/src/pjnath/ice_strans.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/ice_strans.c	(revision 1987)
+++ pjproject/trunk/pjnath/src/pjnath/ice_strans.c	(revision 1988)
@@ -20,7 +20,9 @@
 #include <pjnath/errno.h>
 #include <pj/addr_resolv.h>
+#include <pj/array.h>
 #include <pj/assert.h>
 #include <pj/ip_helper.h>
 #include <pj/log.h>
+#include <pj/os.h>
 #include <pj/pool.h>
 #include <pj/rand.h>
@@ -36,4 +38,17 @@
 
 
+/* Transport IDs */
+enum tp_type
+{
+    TP_NONE,
+    TP_STUN,
+    TP_TURN
+};
+
+/* Candidate preference default values */
+#define SRFLX_PREF  65535
+#define HOST_PREF   65530
+#define RELAY_PREF  65525
+
 
 /* ICE callbacks */
@@ -41,49 +56,347 @@
 static pj_status_t ice_tx_pkt(pj_ice_sess *ice, 
 			      unsigned comp_id,
+			      unsigned transport_id,
 			      const void *pkt, pj_size_t size,
 			      const pj_sockaddr_t *dst_addr,
 			      unsigned dst_addr_len);
 static void	   ice_rx_data(pj_ice_sess *ice, 
-			      unsigned comp_id, 
-			      void *pkt, pj_size_t size,
-			      const pj_sockaddr_t *src_addr,
-			      unsigned src_addr_len);
-
-/* Ioqueue callback */
-static void on_read_complete(pj_ioqueue_key_t *key, 
-                             pj_ioqueue_op_key_t *op_key, 
-                             pj_ssize_t bytes_read);
-
-static void destroy_component(pj_ice_strans_comp *comp);
-static void destroy_ice_st(pj_ice_strans *ice_st, pj_status_t reason);
-
-
-/* STUN session callback */
-static pj_status_t stun_on_send_msg(pj_stun_session *sess,
-				    void *token,
-				    const void *pkt,
-				    pj_size_t pkt_size,
-				    const pj_sockaddr_t *dst_addr,
-				    unsigned addr_len);
-static void stun_on_request_complete(pj_stun_session *sess,
-				     pj_status_t status,
-				     void *token,
-				     pj_stun_tx_data *tdata,
-				     const pj_stun_msg *response,
-				     const pj_sockaddr_t *src_addr,
-				     unsigned src_addr_len);
-
-/* Keep-alive timer */
-static void start_ka_timer(pj_ice_strans *ice_st);
-static void stop_ka_timer(pj_ice_strans *ice_st);
-
-/* Utility: print error */
+			       unsigned comp_id, 
+			       unsigned transport_id,
+			       void *pkt, pj_size_t size,
+			       const pj_sockaddr_t *src_addr,
+			       unsigned src_addr_len);
+
+
+/* STUN socket callbacks */
+/* Notification when incoming packet has been received. */
+static pj_bool_t stun_on_rx_data(pj_stun_sock *stun_sock,
+				 void *pkt,
+				 unsigned pkt_len,
+				 const pj_sockaddr_t *src_addr,
+				 unsigned addr_len);
+/* Notifification when asynchronous send operation has completed. */
+static pj_bool_t stun_on_data_sent(pj_stun_sock *stun_sock,
+				   pj_ioqueue_op_key_t *send_key,
+				   pj_ssize_t sent);
+/* Notification when the status of the STUN transport has changed. */
+static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, 
+				pj_stun_sock_op op,
+				pj_status_t status);
+
+
+/* TURN callbacks */
+static void turn_on_rx_data(pj_turn_sock *turn_sock,
+			    void *pkt,
+			    unsigned pkt_len,
+			    const pj_sockaddr_t *peer_addr,
+			    unsigned addr_len);
+static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
+			  pj_turn_state_t new_state);
+
+
+
+/* Forward decls */
+static void destroy_ice_st(pj_ice_strans *ice_st);
 #define ice_st_perror(ice_st,msg,rc) pjnath_perror(ice_st->obj_name,msg,rc)
+static void sess_init_update(pj_ice_strans *ice_st);
+
+static void sess_add_ref(pj_ice_strans *ice_st);
+static pj_bool_t sess_dec_ref(pj_ice_strans *ice_st);
+
+/**
+ * This structure describes an ICE stream transport component. A component
+ * in ICE stream transport typically corresponds to a single socket created
+ * for this component, and bound to a specific transport address. This
+ * component may have multiple alias addresses, for example one alias 
+ * address for each interfaces in multi-homed host, another for server
+ * reflexive alias, and another for relayed alias. For each transport
+ * address alias, an ICE stream transport candidate (#pj_ice_sess_cand) will
+ * be created, and these candidates will eventually registered to the ICE
+ * session.
+ */
+typedef struct pj_ice_strans_comp
+{
+    pj_ice_strans	*ice_st;	/**< ICE stream transport.	*/
+    unsigned		 comp_id;	/**< Component ID.		*/
+
+    pj_stun_sock	*stun_sock;	/**< STUN transport.		*/
+    pj_turn_sock	*turn_sock;	/**< TURN relay transport.	*/
+
+    unsigned		 cand_cnt;	/**< # of candidates/aliaes.	*/
+    pj_ice_sess_cand	 cand_list[PJ_ICE_ST_MAX_CAND];	/**< Cand array	*/
+
+    unsigned		 default_cand;	/**< Default candidate.		*/
+
+} pj_ice_strans_comp;
+
+
+/**
+ * This structure represents the ICE stream transport.
+ */
+struct pj_ice_strans
+{
+    char		    *obj_name;	/**< Log ID.			*/
+    pj_pool_t		    *pool;	/**< Pool used by this object.	*/
+    void		    *user_data;	/**< Application data.		*/
+    pj_ice_strans_cfg	     cfg;	/**< Configuration.		*/
+    pj_ice_strans_cb	     cb;	/**< Application callback.	*/
+
+    pj_ice_sess		    *ice;	/**< ICE session.		*/
+    pj_time_val		     start_time;/**< Time when ICE was started	*/
+
+    unsigned		     comp_cnt;	/**< Number of components.	*/
+    pj_ice_strans_comp	   **comp;	/**< Components array.		*/
+
+    pj_timer_entry	     ka_timer;	/**< STUN keep-alive timer.	*/
+
+    pj_atomic_t		    *busy_cnt;	/**< To prevent destroy		*/
+    pj_bool_t		     destroy_req;/**< Destroy has been called?	*/
+    pj_bool_t		     cb_called;	/**< Init error callback called?*/
+};
+
+
+/* Validate configuration */
+static pj_status_t pj_ice_strans_cfg_check_valid(const pj_ice_strans_cfg *cfg)
+{
+    pj_status_t status;
+
+    status = pj_stun_config_check_valid(&cfg->stun_cfg);
+    if (!status)
+	return status;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Initialize ICE transport configuration with default values.
+ */
+PJ_DEF(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg)
+{
+    pj_bzero(cfg, sizeof(*cfg));
+
+    pj_stun_config_init(&cfg->stun_cfg, NULL, 0, NULL, NULL);
+    pj_stun_sock_cfg_default(&cfg->stun.cfg);
+    pj_turn_alloc_param_default(&cfg->turn.alloc_param);
+
+    cfg->af = pj_AF_INET();
+    cfg->stun.port = PJ_STUN_PORT;
+    cfg->turn.conn_type = PJ_TURN_TP_UDP;
+}
+
+
+/*
+ * Copy configuration.
+ */
+PJ_DEF(void) pj_ice_strans_cfg_copy( pj_pool_t *pool,
+				     pj_ice_strans_cfg *dst,
+				     const pj_ice_strans_cfg *src)
+{
+    pj_memcpy(dst, src, sizeof(*src));
+
+    if (src->stun.server.slen)
+	pj_strdup(pool, &dst->stun.server, &src->stun.server);
+    if (src->turn.server.slen)
+	pj_strdup(pool, &dst->turn.server, &src->turn.server);
+    pj_stun_auth_cred_dup(pool, &dst->turn.auth_cred,
+			  &src->turn.auth_cred);
+}
+
+/*
+ * Create the component.
+ */
+static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id)
+{
+    pj_ice_strans_comp *comp = NULL;
+    pj_status_t status;
+
+    /* Verify arguments */
+    PJ_ASSERT_RETURN(ice_st && comp_id, PJ_EINVAL);
+
+    /* Check that component ID present */
+    PJ_ASSERT_RETURN(comp_id <= ice_st->comp_cnt, PJNATH_EICEINCOMPID);
+
+    /* Create component */
+    comp = PJ_POOL_ZALLOC_T(ice_st->pool, pj_ice_strans_comp);
+    comp->ice_st = ice_st;
+    comp->comp_id = comp_id;
+
+    ice_st->comp[comp_id-1] = comp;
+
+    /* Initialize default candidate */
+    comp->default_cand = 0;
+
+    /* Create STUN transport if configured */
+    if (ice_st->cfg.stun.server.slen || !ice_st->cfg.stun.no_host_cands) {
+	pj_stun_sock_cb stun_sock_cb;
+	pj_ice_sess_cand *cand;
+
+	pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb));
+	stun_sock_cb.on_rx_data = &stun_on_rx_data;
+	stun_sock_cb.on_status = &stun_on_status;
+	stun_sock_cb.on_data_sent = &stun_on_data_sent;
+	
+	/* Create the STUN transport */
+	status = pj_stun_sock_create(&ice_st->cfg.stun_cfg, NULL,
+				     ice_st->cfg.af, &stun_sock_cb,
+				     &ice_st->cfg.stun.cfg,
+				     comp, &comp->stun_sock);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	/* Start STUN Binding resolution and add srflx candidate 
+	 * only if server is set 
+	 */
+	if (ice_st->cfg.stun.server.slen) {
+	    pj_stun_sock_info stun_sock_info;
+
+	    /* Add pending job */
+	    ///sess_add_ref(ice_st);
+
+	    /* Start Binding resolution */
+	    status = pj_stun_sock_start(comp->stun_sock, 
+					&ice_st->cfg.stun.server,
+					ice_st->cfg.stun.port, 
+					ice_st->cfg.resolver);
+	    if (status != PJ_SUCCESS) {
+		///sess_dec_ref(ice_st);
+		return status;
+	    }
+
+	    /* Enumerate addresses */
+	    status = pj_stun_sock_get_info(comp->stun_sock, &stun_sock_info);
+	    if (status != PJ_SUCCESS) {
+		///sess_dec_ref(ice_st);
+		return status;
+	    }
+
+	    /* Add srflx candidate with pending status */
+	    cand = &comp->cand_list[comp->cand_cnt++];
+	    cand->type = PJ_ICE_CAND_TYPE_SRFLX;
+	    cand->status = PJ_EPENDING;
+	    cand->local_pref = SRFLX_PREF;
+	    cand->transport_id = TP_STUN;
+	    cand->comp_id = (pj_uint8_t) comp_id;
+	    pj_sockaddr_cp(&cand->base_addr, &stun_sock_info.aliases[0]);
+	    pj_sockaddr_cp(&cand->rel_addr, &cand->base_addr);
+	    pj_ice_calc_foundation(ice_st->pool, &cand->foundation,
+				   cand->type, &cand->base_addr);
+	    PJ_LOG(4,(ice_st->obj_name, 
+		      "Comp %d: srflx candidate starts Binding discovery",
+		      comp_id));
+
+	    /* Set default candidate to srflx */
+	    comp->default_cand = cand - comp->cand_list;
+	}
+
+	/* Add local addresses to host candidates, unless no_host_cands
+	 * flag is set.
+	 */
+	if (ice_st->cfg.stun.no_host_cands == PJ_FALSE) {
+	    pj_stun_sock_info stun_sock_info;
+	    unsigned i;
+
+	    /* Enumerate addresses */
+	    status = pj_stun_sock_get_info(comp->stun_sock, &stun_sock_info);
+	    if (status != PJ_SUCCESS)
+		return status;
+
+	    for (i=0; i<stun_sock_info.alias_cnt; ++i) {
+		char addrinfo[PJ_INET6_ADDRSTRLEN+10];
+		const pj_sockaddr *addr = &stun_sock_info.aliases[i];
+
+		/* Leave one candidate for relay */
+		if (comp->cand_cnt >= PJ_ICE_ST_MAX_CAND-1) {
+		    PJ_LOG(4,(ice_st->obj_name, "Too many host candidates"));
+		    break;
+		}
+
+		/* Ignore loopback addresses unless cfg->stun.loop_addr 
+		 * is set 
+		 */
+		if ((pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127) {
+		    if (ice_st->cfg.stun.loop_addr==PJ_FALSE)
+			continue;
+		}
+
+		cand = &comp->cand_list[comp->cand_cnt++];
+
+		cand->type = PJ_ICE_CAND_TYPE_HOST;
+		cand->status = PJ_SUCCESS;
+		cand->local_pref = HOST_PREF;
+		cand->transport_id = TP_STUN;
+		cand->comp_id = (pj_uint8_t) comp_id;
+		pj_sockaddr_cp(&cand->addr, addr);
+		pj_sockaddr_cp(&cand->base_addr, addr);
+		pj_bzero(&cand->rel_addr, sizeof(cand->rel_addr));
+		pj_ice_calc_foundation(ice_st->pool, &cand->foundation,
+				       cand->type, &cand->base_addr);
+
+		PJ_LOG(4,(ice_st->obj_name, 
+			  "Comp %d: host candidate %s added",
+			  comp_id, pj_sockaddr_print(&cand->addr, addrinfo,
+						     sizeof(addrinfo), 3)));
+	    }
+	}
+    }
+
+    /* Create TURN relay if configured. */
+    if (ice_st->cfg.turn.server.slen) {
+	pj_turn_sock_cb turn_sock_cb;
+	pj_ice_sess_cand *cand;
+
+	/* Init TURN socket */
+	pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb));
+	turn_sock_cb.on_rx_data = &turn_on_rx_data;
+	turn_sock_cb.on_state = &turn_on_state;
+
+	status = pj_turn_sock_create(&ice_st->cfg.stun_cfg, ice_st->cfg.af,
+				     ice_st->cfg.turn.conn_type,
+				     &turn_sock_cb, 0, comp, 
+				     &comp->turn_sock);
+	if (status != PJ_SUCCESS) {
+	    return status;
+	}
+
+	/* Add pending job */
+	///sess_add_ref(ice_st);
+
+	/* Start allocation */
+	status=pj_turn_sock_alloc(comp->turn_sock,  
+				  &ice_st->cfg.turn.server,
+				  ice_st->cfg.turn.port,
+				  ice_st->cfg.resolver, 
+				  &ice_st->cfg.turn.auth_cred, 
+				  &ice_st->cfg.turn.alloc_param);
+	if (status != PJ_SUCCESS) {
+	    ///sess_dec_ref(ice_st);
+	    return status;
+	}
+
+	/* Add relayed candidate with pending status */
+	cand = &comp->cand_list[comp->cand_cnt++];
+	cand->type = PJ_ICE_CAND_TYPE_RELAYED;
+	cand->status = PJ_EPENDING;
+	cand->local_pref = RELAY_PREF;
+	cand->transport_id = TP_TURN;
+	cand->comp_id = (pj_uint8_t) comp_id;
+
+	PJ_LOG(4,(ice_st->obj_name, 
+		      "Comp %d: TURN relay candidate waiting for allocation",
+		      comp_id));
+
+	/* Set default candidate to relay */
+	comp->default_cand = cand - comp->cand_list;
+    }
+
+    return PJ_SUCCESS;
+}
+
 
 /* 
  * Create ICE stream transport 
  */
-PJ_DEF(pj_status_t) pj_ice_strans_create( pj_stun_config *stun_cfg,
-					  const char *name,
+PJ_DEF(pj_status_t) pj_ice_strans_create( const char *name,
+					  const pj_ice_strans_cfg *cfg,
 					  unsigned comp_cnt,
 					  void *user_data,
@@ -93,25 +406,50 @@
     pj_pool_t *pool;
     pj_ice_strans *ice_st;
-
-    PJ_ASSERT_RETURN(stun_cfg && comp_cnt && cb && p_ice_st, PJ_EINVAL);
-    PJ_ASSERT_RETURN(stun_cfg->ioqueue && stun_cfg->timer_heap, PJ_EINVAL);
+    unsigned i;
+    pj_status_t status;
+
+    status = pj_ice_strans_cfg_check_valid(cfg);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    PJ_ASSERT_RETURN(comp_cnt && cb && p_ice_st, PJ_EINVAL);
 
     if (name == NULL)
-	name = "icstr%p";
-
-    pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_STRANS, 
+	name = "ice%p";
+
+    pool = pj_pool_create(cfg->stun_cfg.pf, name, PJNATH_POOL_LEN_ICE_STRANS,
 			  PJNATH_POOL_INC_ICE_STRANS, NULL);
     ice_st = PJ_POOL_ZALLOC_T(pool, pj_ice_strans);
     ice_st->pool = pool;
-    pj_memcpy(ice_st->obj_name, pool->obj_name, PJ_MAX_OBJ_NAME);
+    ice_st->obj_name = pool->obj_name;
     ice_st->user_data = user_data;
+
+    PJ_LOG(4,(ice_st->obj_name, 
+	      "Creating ICE stream transport with %d component(s)",
+	      comp_cnt));
+
+    pj_ice_strans_cfg_copy(pool, &ice_st->cfg, cfg);
+    pj_memcpy(&ice_st->cb, cb, sizeof(*cb));
     
+    status = pj_atomic_create(pool, 0, &ice_st->busy_cnt);
+    if (status != PJ_SUCCESS) {
+	destroy_ice_st(ice_st);
+	return status;
+    }
+
     ice_st->comp_cnt = comp_cnt;
-    ice_st->comp = (pj_ice_strans_comp**) pj_pool_calloc(pool, comp_cnt, 
-						     sizeof(void*));
-
-    pj_memcpy(&ice_st->cb, cb, sizeof(*cb));
-    pj_memcpy(&ice_st->stun_cfg, stun_cfg, sizeof(*stun_cfg));
-
+    ice_st->comp = (pj_ice_strans_comp**) 
+		   pj_pool_calloc(pool, comp_cnt, sizeof(pj_ice_strans_comp*));
+
+    for (i=0; i<comp_cnt; ++i) {
+	status = create_comp(ice_st, i+1);
+	if (status != PJ_SUCCESS) {
+	    destroy_ice_st(ice_st);
+	    return status;
+	}
+    }
+
+    /* Check if all candidates are ready (this may call callback) */
+    sess_init_update(ice_st);
 
     PJ_LOG(4,(ice_st->obj_name, "ICE stream transport created"));
@@ -122,16 +460,7 @@
 
 /* Destroy ICE */
-static void destroy_ice_st(pj_ice_strans *ice_st, pj_status_t reason)
+static void destroy_ice_st(pj_ice_strans *ice_st)
 {
     unsigned i;
-    char obj_name[PJ_MAX_OBJ_NAME];
-
-    if (reason == PJ_SUCCESS) {
-	pj_memcpy(obj_name, ice_st->obj_name, PJ_MAX_OBJ_NAME);
-	PJ_LOG(4,(obj_name, "ICE stream transport shutting down"));
-    }
-
-    /* Kill keep-alive timer, if any */
-    stop_ka_timer(ice_st);
 
     /* Destroy ICE if we have ICE */
@@ -144,16 +473,74 @@
     for (i=0; i<ice_st->comp_cnt; ++i) {
 	if (ice_st->comp[i]) {
-	    destroy_component(ice_st->comp[i]);
-	    ice_st->comp[i] = NULL;
+	    if (ice_st->comp[i]->stun_sock) {
+		pj_stun_sock_set_user_data(ice_st->comp[i]->stun_sock, NULL);
+		pj_stun_sock_destroy(ice_st->comp[i]->stun_sock);
+		ice_st->comp[i]->stun_sock = NULL;
+	    }
+	    if (ice_st->comp[i]->turn_sock) {
+		pj_turn_sock_set_user_data(ice_st->comp[i]->turn_sock, NULL);
+		pj_turn_sock_destroy(ice_st->comp[i]->turn_sock);
+		ice_st->comp[i]->turn_sock = NULL;
+	    }
 	}
     }
     ice_st->comp_cnt = 0;
+
+    /* Destroy reference counter */
+    if (ice_st->busy_cnt) {
+	pj_assert(pj_atomic_get(ice_st->busy_cnt)==0);
+	pj_atomic_destroy(ice_st->busy_cnt);
+	ice_st->busy_cnt = NULL;
+    }
 
     /* Done */
     pj_pool_release(ice_st->pool);
-
-    if (reason == PJ_SUCCESS) {
-	PJ_LOG(4,(obj_name, "ICE stream transport destroyed"));
-    }
+}
+
+/* Notification about failure */
+static void sess_fail(pj_ice_strans *ice_st, pj_ice_strans_op op,
+		      const char *title, pj_status_t status)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+
+    pj_strerror(status, errmsg, sizeof(errmsg));
+    PJ_LOG(4,(ice_st->obj_name, "%s: %s", title, errmsg));
+
+    if (op==PJ_ICE_STRANS_OP_INIT && ice_st->cb_called)
+	return;
+
+    ice_st->cb_called = PJ_TRUE;
+
+    if (ice_st->cb.on_ice_complete)
+	(*ice_st->cb.on_ice_complete)(ice_st, op, status);
+}
+
+/* Update initialization status */
+static void sess_init_update(pj_ice_strans *ice_st)
+{
+    unsigned i;
+
+    /* Ignore if init callback has been called */
+    if (ice_st->cb_called)
+	return;
+
+    /* Notify application when all candidates have been gathered */
+    for (i=0; i<ice_st->comp_cnt; ++i) {
+	unsigned j;
+	pj_ice_strans_comp *comp = ice_st->comp[i];
+
+	for (j=0; j<comp->cand_cnt; ++j) {
+	    pj_ice_sess_cand *cand = &comp->cand_list[j];
+
+	    if (cand->status == PJ_EPENDING)
+		return;
+	}
+    }
+
+    /* All candidates have been gathered */
+    ice_st->cb_called = PJ_TRUE;
+    if (ice_st->cb.on_ice_complete)
+	(*ice_st->cb.on_ice_complete)(ice_st, PJ_ICE_STRANS_OP_INIT, 
+				      PJ_SUCCESS);
 }
 
@@ -163,662 +550,56 @@
 PJ_DEF(pj_status_t) pj_ice_strans_destroy(pj_ice_strans *ice_st)
 {
-    destroy_ice_st(ice_st, PJ_SUCCESS);
+    char obj_name[PJ_MAX_OBJ_NAME];
+
+    PJ_ASSERT_RETURN(ice_st, PJ_EINVAL);
+
+    ice_st->destroy_req = PJ_TRUE;
+    if (pj_atomic_get(ice_st->busy_cnt) > 0) {
+	PJ_LOG(5,(ice_st->obj_name, 
+		  "ICE strans object is busy, will destroy later"));
+	return PJ_EPENDING;
+    }
+    
+    pj_memcpy(obj_name, ice_st->obj_name, PJ_MAX_OBJ_NAME);
+    destroy_ice_st(ice_st);
+
+    PJ_LOG(4,(obj_name, "ICE stream transport destroyed"));
     return PJ_SUCCESS;
 }
 
-/*
- * Resolve STUN server
- */
-PJ_DEF(pj_status_t) pj_ice_strans_set_stun_domain(pj_ice_strans *ice_st,
-						  pj_dns_resolver *resolver,
-						  const pj_str_t *domain)
-{
-    /* Yeah, TODO */
-    PJ_UNUSED_ARG(ice_st);
-    PJ_UNUSED_ARG(resolver);
-    PJ_UNUSED_ARG(domain);
-    return -1;
-}
-
-/*
- * Set STUN server address.
- */
-PJ_DEF(pj_status_t) pj_ice_strans_set_stun_srv( pj_ice_strans *ice_st,
-						const pj_sockaddr_in *stun_srv,
-						const pj_sockaddr_in *turn_srv)
-{
-    PJ_ASSERT_RETURN(ice_st, PJ_EINVAL);
-    /* Must not have pending resolver job */
-    PJ_ASSERT_RETURN(ice_st->has_rjob==PJ_FALSE, PJ_EINVALIDOP);
-
-    if (stun_srv) {
-	pj_memcpy(&ice_st->stun_srv, stun_srv, sizeof(pj_sockaddr_in));
+
+/*
+ * Increment busy counter.
+ */
+static void sess_add_ref(pj_ice_strans *ice_st)
+{
+    pj_atomic_inc(ice_st->busy_cnt);
+}
+
+/*
+ * Decrement busy counter. If the counter has reached zero and destroy
+ * has been requested, destroy the object and return FALSE.
+ */
+static pj_bool_t sess_dec_ref(pj_ice_strans *ice_st)
+{
+    int count = pj_atomic_dec_and_get(ice_st->busy_cnt);
+    pj_assert(count >= 0);
+    if (count==0 && ice_st->destroy_req) {
+	pj_ice_strans_destroy(ice_st);
+	return PJ_FALSE;
     } else {
-	pj_bzero(&ice_st->stun_srv, sizeof(pj_sockaddr_in));
-    }
-
-    if (turn_srv) {
-	pj_memcpy(&ice_st->turn_srv, turn_srv, sizeof(pj_sockaddr_in));
-    } else {
-	pj_bzero(&ice_st->turn_srv, sizeof(pj_sockaddr_in));
-    }
-
-    return PJ_SUCCESS;
-}
-
-/* Add new candidate */
-static pj_status_t add_cand( pj_ice_strans *ice_st,
-			     pj_ice_strans_comp *comp,
-			     unsigned comp_id,
-			     pj_ice_cand_type type,
-			     pj_uint16_t local_pref,
-			     const pj_sockaddr_in *addr,
-			     pj_bool_t set_default)
-{
-    pj_ice_strans_cand *cand;
-    unsigned i;
-
-    PJ_ASSERT_RETURN(ice_st && comp && addr, PJ_EINVAL);
-    PJ_ASSERT_RETURN(comp->cand_cnt < PJ_ICE_ST_MAX_CAND, PJ_ETOOMANY);
-
-    /* Check that we don't have candidate with the same
-     * address.
-     */
-    for (i=0; i<comp->cand_cnt; ++i) {
-	if (pj_memcmp(addr, &comp->cand_list[i].addr, 
-		      sizeof(pj_sockaddr_in))==0)
-	{
-	    /* Duplicate */
-	    PJ_LOG(5,(ice_st->obj_name, "Duplicate candidate not added"));
-	    return PJ_SUCCESS;
-	}
-    }
-
-    cand = &comp->cand_list[comp->cand_cnt];
-
-    pj_bzero(cand, sizeof(*cand));
-    cand->type = type;
-    cand->status = PJ_SUCCESS;
-    pj_memcpy(&cand->addr, addr, sizeof(pj_sockaddr_in));
-    cand->ice_cand_id = -1;
-    cand->local_pref = local_pref;
-    pj_ice_calc_foundation(ice_st->pool, &cand->foundation, type, 
-			   &comp->local_addr);
-
-    if (set_default) 
-	comp->default_cand = comp->cand_cnt;
-
-    PJ_LOG(5,(ice_st->obj_name, 
-	      "Candidate %s:%d (type=%s) added to component %d",
-	      pj_inet_ntoa(addr->sin_addr),
-	      (int)pj_ntohs(addr->sin_port), 
-	      pj_ice_get_cand_type_name(type),
-	      comp_id));
-    
-    comp->cand_cnt++;
-    return PJ_SUCCESS;
-}
-
-/*  Create new component (i.e. socket)  */
-static pj_status_t create_component(pj_ice_strans *ice_st,
-				    unsigned comp_id,
-				    pj_uint32_t options,
-				    const pj_sockaddr_in *addr,
-				    pj_ice_strans_comp **p_comp)
-{
-    enum { MAX_RETRY=100, PORT_INC=2 };
-    pj_ioqueue_callback ioqueue_cb;
-    pj_ice_strans_comp *comp;
-    int retry, addr_len;
-    struct {
-	pj_uint32_t a1, a2, a3;
-    } tsx_id;
-    pj_status_t status;
-
-    comp = PJ_POOL_ZALLOC_T(ice_st->pool, pj_ice_strans_comp);
-    comp->ice_st = ice_st;
-    comp->comp_id = comp_id;
-    comp->options = options;
-    comp->sock = PJ_INVALID_SOCKET;
-    comp->last_status = PJ_SUCCESS;
-
-    /* Create transaction ID for STUN keep alives */
-    tsx_id.a1 = 0;
-    tsx_id.a2 = comp_id;
-    tsx_id.a3 = (pj_uint32_t) (unsigned long) ice_st;
-    pj_memcpy(comp->ka_tsx_id, &tsx_id, sizeof(comp->ka_tsx_id));
-
-    /* Create socket */
-    status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &comp->sock);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    /* Init address */
-    if (addr) 
-	pj_memcpy(&comp->local_addr, addr, sizeof(pj_sockaddr_in));
-    else 
-	pj_sockaddr_in_init(&comp->local_addr.ipv4, NULL, 0);
-
-    /* Retry binding socket */
-    for (retry=0; retry<MAX_RETRY; ++retry) {
-	pj_uint16_t port;
-
-	status = pj_sock_bind(comp->sock, &comp->local_addr, 
-			      sizeof(pj_sockaddr_in));
-	if (status == PJ_SUCCESS)
-	    break;
-
-	if (options & PJ_ICE_ST_OPT_NO_PORT_RETRY)
-	    goto on_error;
-
-	port = pj_ntohs(comp->local_addr.ipv4.sin_port);
-	port += PORT_INC;
-	comp->local_addr.ipv4.sin_port = pj_htons(port);
-    }
-
-    /* Get the actual port where the socket is bound to.
-     * (don't care about the address, it will be retrieved later)
-     */
-    addr_len = sizeof(comp->local_addr);
-    status = pj_sock_getsockname(comp->sock, &comp->local_addr, &addr_len);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Register to ioqueue */
-    pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb));
-    ioqueue_cb.on_read_complete = &on_read_complete;
-    status = pj_ioqueue_register_sock(ice_st->pool, ice_st->stun_cfg.ioqueue, 
-				      comp->sock, comp, &ioqueue_cb, 
-				      &comp->key);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Disable concurrency */
-    status = pj_ioqueue_set_concurrency(comp->key, PJ_FALSE);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    pj_ioqueue_op_key_init(&comp->read_op, sizeof(comp->read_op));
-    pj_ioqueue_op_key_init(&comp->write_op, sizeof(comp->write_op));
-
-    /* Kick start reading the socket */
-    on_read_complete(comp->key, &comp->read_op, 0);
-
-    /* If the socket is bound to INADDR_ANY, then lookup all interfaces in
-     * the host and add them into cand_list. Otherwise if the socket is bound
-     * to a specific interface, then only add that specific interface to
-     * cand_list.
-     */
-    if (((options & PJ_ICE_ST_OPT_DONT_ADD_CAND)==0) &&
-	comp->local_addr.ipv4.sin_addr.s_addr == 0) 
-    {
-	/* Socket is bound to INADDR_ANY */
-	unsigned i, ifs_cnt;
-	pj_sockaddr ifs[PJ_ICE_ST_MAX_CAND-2];
-
-	/* Reset default candidate */
-	comp->default_cand = -1;
-
-	/* Enum all IP interfaces in the host */
-	ifs_cnt = PJ_ARRAY_SIZE(ifs);
-	status = pj_enum_ip_interface(pj_AF_INET(), &ifs_cnt, ifs);
-	if (status != PJ_SUCCESS)
-	    goto on_error;
-
-	/* Set default IP interface as the base address */
-	status = pj_gethostip(pj_AF_INET(), &comp->local_addr);
-	if (status != PJ_SUCCESS)
-	    goto on_error;
-
-	/* Add candidate entry for each interface */
-	for (i=0; i<ifs_cnt; ++i) {
-	    pj_sockaddr_in cand_addr;
-	    pj_bool_t set_default;
-	    pj_uint16_t local_pref;
-
-	    /* Ignore 127.0.0.0/24 address */
-	    if ((pj_ntohl(ifs[i].ipv4.sin_addr.s_addr) >> 24)==127)
-		continue;
-
-	    pj_memcpy(&cand_addr, &comp->local_addr, sizeof(pj_sockaddr_in));
-	    cand_addr.sin_addr.s_addr = ifs[i].ipv4.sin_addr.s_addr;
-
-
-	    /* If the IP address is equal to local address, assign it
-	     * as default candidate.
-	     */
-	    if (ifs[i].ipv4.sin_addr.s_addr == comp->local_addr.ipv4.sin_addr.s_addr) {
-		set_default = PJ_TRUE;
-		local_pref = 65535;
-	    } else {
-		set_default = PJ_FALSE;
-		local_pref = 0;
-	    }
-
-	    status = add_cand(ice_st, comp, comp_id, 
-			      PJ_ICE_CAND_TYPE_HOST, 
-			      local_pref, &cand_addr, set_default);
-	    if (status != PJ_SUCCESS)
-		goto on_error;
-	}
-
-
-    } else if ((options & PJ_ICE_ST_OPT_DONT_ADD_CAND)==0) {
-	/* Socket is bound to specific address. 
-	 * In this case only add that address as a single entry in the
-	 * cand_list table.
-	 */
-	status = add_cand(ice_st, comp, comp_id, 
-			  PJ_ICE_CAND_TYPE_HOST, 
-			  65535, &comp->local_addr.ipv4,
-			  PJ_TRUE);
-	if (status != PJ_SUCCESS)
-	    goto on_error;
-
-    } else if (options & PJ_ICE_ST_OPT_DONT_ADD_CAND) {
-	/* If application doesn't want to add candidate, just fix local_addr
-	 * in case its value is zero.
-	 */
-	if (comp->local_addr.ipv4.sin_addr.s_addr == 0) {
-	    status = pj_gethostip(pj_AF_INET(), &comp->local_addr);
-	    if (status != PJ_SUCCESS)
-		return status;
-	}
-    }
-
-
-    /* Done */
-    if (p_comp)
-	*p_comp = comp;
-
-    return PJ_SUCCESS;
-
-on_error:
-    destroy_component(comp);
-    return status;
-}
-
-/* 
- * This is callback called by ioqueue on incoming packet 
- */
-static void on_read_complete(pj_ioqueue_key_t *key, 
-                             pj_ioqueue_op_key_t *op_key, 
-                             pj_ssize_t bytes_read)
-{
-    pj_ice_strans_comp *comp = (pj_ice_strans_comp*) 
-			    pj_ioqueue_get_user_data(key);
-    pj_ice_strans *ice_st = comp->ice_st;
-    pj_ssize_t pkt_size;
-    enum { RETRY = 10 };
-    unsigned retry;
-    pj_status_t status;
-
-    if (bytes_read > 0) {
-	/*
-	 * Okay, we got a packet from the socket for the component. There is
-	 * a bit of situation here, since this packet could be one of these:
-	 *
-	 * 1) this could be the response of STUN binding request sent by
-	 *    this component to a) an initial request to get the STUN mapped
-	 *    address of this component, or b) subsequent request to keep
-	 *    the binding alive.
-	 * 
-	 * 2) this could be a packet (STUN or not STUN) sent from the STUN
-	 *    relay server. In this case, still there are few options to do
-	 *    for this packet: a) process this locally if this packet is
-	 *    related to TURN session management (e.g. Allocate response),
-	 *    b) forward this packet to ICE if this is related to ICE
-	 *    discovery process.
-	 *
-	 * 3) this could be a STUN request or response sent as part of ICE
-	 *    discovery process.
-	 *
-	 * 4) this could be application's packet, e.g. when ICE processing
-	 *    is done and agents start sending RTP/RTCP packets to each
-	 *    other, or when ICE processing is not done and this ICE stream
-	 *    transport decides to allow sending data.
-	 *
-	 * So far we don't have good solution for this.
-	 * The process below is just a workaround.
-	 */
-	status = pj_stun_msg_check(comp->pkt, bytes_read, 
-				   PJ_STUN_IS_DATAGRAM);
-
-	if (status == PJ_SUCCESS) {
-	    if (comp->stun_sess &&
-		PJ_STUN_IS_RESPONSE(((pj_stun_msg_hdr*)comp->pkt)->type) &&
-		pj_memcmp(comp->pkt+8, comp->ka_tsx_id, 12) == 0) 
-	    {
-		status = pj_stun_session_on_rx_pkt(comp->stun_sess, comp->pkt,
-						   bytes_read, 
-						   PJ_STUN_IS_DATAGRAM, NULL,
-						   NULL, &comp->src_addr, 
-						   comp->src_addr_len);
-	    } else if (ice_st->ice) {
-		PJ_TODO(DISTINGUISH_BETWEEN_LOCAL_AND_RELAY);
-
-		TRACE_PKT((comp->ice_st->obj_name, 
-			  "Component %d RX packet from %s:%d",
-			  comp->comp_id,
-			  pj_inet_ntoa(comp->src_addr.ipv4.sin_addr),
-			  (int)pj_ntohs(comp->src_addr.ipv4.sin_port)));
-
-		status = pj_ice_sess_on_rx_pkt(ice_st->ice, comp->comp_id, 
-					       comp->pkt, bytes_read,
-					       &comp->src_addr, 
-					       comp->src_addr_len);
-	    } else {
-		/* This must have been a very late STUN reponse,
-		 * or an early STUN Binding Request when our local
-		 * ICE has not been created yet. */
-	    }
-	} else {
-	    (*ice_st->cb.on_rx_data)(ice_st, comp->comp_id, 
-				     comp->pkt, bytes_read, 
-				     &comp->src_addr, comp->src_addr_len);
-	}
-
-    } else if (bytes_read < 0) {
-	ice_st_perror(comp->ice_st, "ioqueue read callback error", 
-		      -bytes_read);
-    }
-
-    /* Read next packet */
-    for (retry=0; retry<RETRY;) {
-	pkt_size = sizeof(comp->pkt);
-	comp->src_addr_len = sizeof(comp->src_addr);
-	status = pj_ioqueue_recvfrom(key, op_key, comp->pkt, &pkt_size, 
-				     PJ_IOQUEUE_ALWAYS_ASYNC,
-				     &comp->src_addr, &comp->src_addr_len);
-	if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) ||
-	    status == PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) || 
-	    status == PJ_STATUS_FROM_OS(OSERR_ECONNRESET))
-	{
-	    ice_st_perror(comp->ice_st, "ioqueue recvfrom() error", status);
-	    ++retry;
-	    continue;
-	} else if (status != PJ_SUCCESS && status != PJ_EPENDING) {
-	    retry += 2;
-	    ice_st_perror(comp->ice_st, "ioqueue recvfrom() error", status);
-	} else {
-	    break;
-	}
-    }
-}
-
-/* 
- * Destroy a component 
- */
-static void destroy_component(pj_ice_strans_comp *comp)
-{
-    if (comp->stun_sess) {
-	pj_stun_session_destroy(comp->stun_sess);
-	comp->stun_sess = NULL;
-    }
-
-    if (comp->key) {
-	pj_ioqueue_unregister(comp->key);
-	comp->key = NULL;
-	comp->sock = PJ_INVALID_SOCKET;
-    } else if (comp->sock != PJ_INVALID_SOCKET && comp->sock != 0) {
-	pj_sock_close(comp->sock);
-	comp->sock = PJ_INVALID_SOCKET;
-    }
-}
-
-
-/* STUN keep-alive timer callback */
-static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
-{
-    pj_ice_strans *ice_st = (pj_ice_strans*)te->user_data;
-    unsigned i;
-    pj_status_t status;
-
-    PJ_UNUSED_ARG(th);
-
-    ice_st->ka_timer.id = PJ_FALSE;
-
-    for (i=0; i<ice_st->comp_cnt; ++i) {
-	pj_ice_strans_comp *comp = ice_st->comp[i];
-	pj_stun_tx_data *tdata;
-	unsigned j;
-
-	/* Does this component have STUN server reflexive candidate? */
-	for (j=0; j<comp->cand_cnt; ++j) {
-	    if (comp->cand_list[j].type == PJ_ICE_CAND_TYPE_SRFLX)
-		break;
-	}
-	if (j == comp->cand_cnt)
-	    continue;
-
-	/* Create STUN binding request */
-	status = pj_stun_session_create_req(comp->stun_sess,
-					    PJ_STUN_BINDING_REQUEST, 
-					    PJ_STUN_MAGIC,
-					    comp->ka_tsx_id, &tdata);
-	if (status != PJ_SUCCESS)
-	    continue;
-
-	/* tdata->user_data is NULL for keep-alive */
-	//tdata->user_data = NULL;
-
-	++comp->pending_cnt;
-
-
-	/* Send STUN binding request */
-	PJ_LOG(5,(ice_st->obj_name, "Sending STUN keep-alive from %s;%d",
-		  pj_inet_ntoa(comp->local_addr.ipv4.sin_addr),
-		  pj_ntohs(comp->local_addr.ipv4.sin_port)));
-	status = pj_stun_session_send_msg(comp->stun_sess, &comp->cand_list[j],
-					  PJ_FALSE, PJ_TRUE, &ice_st->stun_srv,
-					  sizeof(pj_sockaddr_in), tdata);
-	if (status != PJ_SUCCESS) {
-	    --comp->pending_cnt;
-	}
-    }
-
-    /* Start next timer */
-    start_ka_timer(ice_st);
-}
-
-/* Start STUN keep-alive timer */
-static void start_ka_timer(pj_ice_strans *ice_st)
-{
-    pj_time_val delay;
-
-    /* Skip if timer is already running */
-    if (ice_st->ka_timer.id != PJ_FALSE)
-	return;
-
-    delay.sec = PJ_ICE_ST_KEEP_ALIVE_MIN;
-    delay.msec = pj_rand() % (PJ_ICE_ST_KEEP_ALIVE_MAX_RAND * 1000);
-    pj_time_val_normalize(&delay);
-
-    ice_st->ka_timer.cb = &ka_timer_cb;
-    ice_st->ka_timer.user_data = ice_st;
-    
-    if (pj_timer_heap_schedule(ice_st->stun_cfg.timer_heap, 
-			       &ice_st->ka_timer, &delay)==PJ_SUCCESS)
-    {
-	ice_st->ka_timer.id = PJ_TRUE;
-    }
-}
-
-
-/* Stop STUN keep-alive timer */
-static void stop_ka_timer(pj_ice_strans *ice_st)
-{
-    /* Skip if timer is already stop */
-    if (ice_st->ka_timer.id == PJ_FALSE)
-	return;
-
-    pj_timer_heap_cancel(ice_st->stun_cfg.timer_heap, &ice_st->ka_timer);
-    ice_st->ka_timer.id = PJ_FALSE;
-}
-
-
-/*
- * Add STUN mapping to a component.
- */
-static pj_status_t get_stun_mapped_addr(pj_ice_strans *ice_st,
-					pj_ice_strans_comp *comp)
-{
-    pj_ice_strans_cand *cand;
-    pj_stun_session_cb sess_cb;
-    pj_stun_tx_data *tdata;
-    pj_status_t status;
-
-    PJ_ASSERT_RETURN(ice_st && comp, PJ_EINVAL);
-    
-    /* Bail out if STUN server is still being resolved */
-    if (ice_st->has_rjob)
-	return PJ_EBUSY;
-
-    /* Just return (successfully) if STUN server is not configured */
-    if (ice_st->stun_srv.sin_family == 0)
-	return PJ_SUCCESS;
-
-
-    /* Create STUN session for this component */
-    pj_bzero(&sess_cb, sizeof(sess_cb));
-    sess_cb.on_request_complete = &stun_on_request_complete;
-    sess_cb.on_send_msg = &stun_on_send_msg;
-    status = pj_stun_session_create(&ice_st->stun_cfg, ice_st->obj_name,
-				    &sess_cb, PJ_FALSE, &comp->stun_sess);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    /* Associate component with STUN session */
-    pj_stun_session_set_user_data(comp->stun_sess, (void*)comp);
-
-    /* Create STUN binding request */
-    status = pj_stun_session_create_req(comp->stun_sess, 
-					PJ_STUN_BINDING_REQUEST, 
-					PJ_STUN_MAGIC,
-					comp->ka_tsx_id, 
-					&tdata);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    /* Will be attached to tdata in send_msg() */
-    cand = &comp->cand_list[comp->cand_cnt];
-
-    /* Add pending count first, since stun_on_request_complete()
-     * may be called before this function completes
-     */
-    comp->pending_cnt++;
-
-    /* Add new alias to this component */
-    cand->type = PJ_ICE_CAND_TYPE_SRFLX;
-    cand->status = PJ_EPENDING;
-    cand->ice_cand_id = -1;
-    cand->local_pref = 65535;
-    pj_ice_calc_foundation(ice_st->pool, &cand->foundation, 
-			   PJ_ICE_CAND_TYPE_SRFLX, &comp->local_addr);
-
-    ++comp->cand_cnt;
-
-    /* Send STUN binding request */
-    status = pj_stun_session_send_msg(comp->stun_sess, (void*)cand, PJ_FALSE, 
-				      PJ_TRUE, &ice_st->stun_srv, 
-				      sizeof(pj_sockaddr_in), tdata);
-    if (status != PJ_SUCCESS) {
-	--comp->pending_cnt;
-	--comp->cand_cnt;
-	return status;
-    }
-
-    return PJ_SUCCESS;
-}
-
-
-/*
- * Create the component.
- */
-PJ_DEF(pj_status_t) pj_ice_strans_create_comp(pj_ice_strans *ice_st,
-					      unsigned comp_id,
-					      pj_uint32_t options,
-					      const pj_sockaddr_in *addr)
-{
-    pj_ice_strans_comp *comp = NULL;
-    pj_status_t status;
-
-    /* Verify arguments */
-    PJ_ASSERT_RETURN(ice_st && comp_id, PJ_EINVAL);
-
-    /* Check that component ID present */
-    PJ_ASSERT_RETURN(comp_id <= ice_st->comp_cnt, PJNATH_EICEINCOMPID);
-
-    /* Can't add new component while ICE is running */
-    PJ_ASSERT_RETURN(ice_st->ice == NULL, PJ_EBUSY);
-    
-    /* Can't add new component while resolver is running */
-    PJ_ASSERT_RETURN(ice_st->has_rjob == PJ_FALSE, PJ_EBUSY);
-
-
-    /* Create component */
-    status = create_component(ice_st, comp_id, options, addr, &comp);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    if ((options & PJ_ICE_ST_OPT_DISABLE_STUN) == 0) {
-	status = get_stun_mapped_addr(ice_st, comp);
-	if (status != PJ_SUCCESS) {
-	    destroy_component(comp);
-	    return status;
-	}
-    }
-
-    /* Store this component */
-    ice_st->comp[comp_id-1] = comp;
-
-    return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pj_ice_strans_add_cand( pj_ice_strans *ice_st,
-					    unsigned comp_id,
-					    pj_ice_cand_type type,
-					    pj_uint16_t local_pref,
-					    const pj_sockaddr_in *addr,
-					    pj_bool_t set_default)
-{
-    pj_ice_strans_comp *comp;
-
-
-    PJ_ASSERT_RETURN(ice_st && comp_id && addr, PJ_EINVAL);
-    PJ_ASSERT_RETURN(comp_id <= ice_st->comp_cnt, PJ_EINVAL);
-    PJ_ASSERT_RETURN(ice_st->comp[comp_id-1] != NULL, PJ_EINVALIDOP);
-
-    comp = ice_st->comp[comp_id-1];
-    return add_cand(ice_st, comp, comp_id, type, local_pref, addr, 
-		    set_default);
-}
-
-
-PJ_DEF(pj_status_t) pj_ice_strans_get_comps_status(pj_ice_strans *ice_st)
-{
-    unsigned i;
-    pj_status_t worst = PJ_SUCCESS;
-
-    for (i=0; i<ice_st->comp_cnt; ++i) {
-	pj_ice_strans_comp *comp = ice_st->comp[i];
-
-	if (comp->last_status == PJ_SUCCESS) {
-	    /* okay */
-	} else if (comp->pending_cnt && worst==PJ_SUCCESS) {
-	    worst = PJ_EPENDING;
-	    break;
-	} else if (comp->last_status != PJ_SUCCESS) {
-	    worst = comp->last_status;
-	    break;
-	}
-
-	if (worst != PJ_SUCCESS)
-	    break;
-    }
-
-    return worst;
-}
+	return PJ_TRUE;
+    }
+}
+
+/*
+ * Get user data
+ */
+PJ_DEF(void*) pj_ice_strans_get_user_data(pj_ice_strans *ice_st)
+{
+    PJ_ASSERT_RETURN(ice_st, NULL);
+    return ice_st->user_data;
+}
+
 
 /*
@@ -833,5 +614,5 @@
     unsigned i;
     pj_ice_sess_cb ice_cb;
-    const pj_uint8_t srflx_prio[4] = { 100, 126, 110, 0 };
+    //const pj_uint8_t srflx_prio[4] = { 100, 126, 110, 0 };
 
     /* Check arguments */
@@ -849,5 +630,5 @@
 
     /* Create! */
-    status = pj_ice_sess_create(&ice_st->stun_cfg, ice_st->obj_name, role,
+    status = pj_ice_sess_create(&ice_st->cfg.stun_cfg, ice_st->obj_name, role,
 			        ice_st->comp_cnt, &ice_cb, 
 			        local_ufrag, local_passwd, &ice_st->ice);
@@ -858,4 +639,5 @@
     ice_st->ice->user_data = (void*)ice_st;
 
+#if 0
     /* If default candidate for components are SRFLX one, upload a custom
      * type priority to ICE session so that SRFLX candidates will get
@@ -868,28 +650,42 @@
 	pj_ice_sess_set_prefs(ice_st->ice, srflx_prio);
     }
-
-
-    /* Add candidates */
+#endif
+
+    /* Add components/candidates */
     for (i=0; i<ice_st->comp_cnt; ++i) {
 	unsigned j;
-	pj_ice_strans_comp *comp= ice_st->comp[i];
+	pj_ice_strans_comp *comp = ice_st->comp[i];
+
+	/* Re-enable logging for Send/Data indications */
+	if (comp->turn_sock) {
+	    PJ_LOG(5,(ice_st->obj_name, 
+		      "Disabling STUN Indication logging for "
+		      "component %d", i+1));
+	    pj_turn_sock_set_log(comp->turn_sock, 0xFFFF);
+	}
 
 	for (j=0; j<comp->cand_cnt; ++j) {
-	    pj_ice_strans_cand *cand = &comp->cand_list[j];
+	    pj_ice_sess_cand *cand = &comp->cand_list[j];
+	    unsigned ice_cand_id;
 
 	    /* Skip if candidate is not ready */
 	    if (cand->status != PJ_SUCCESS) {
 		PJ_LOG(5,(ice_st->obj_name, 
-			  "Candidate %d in component %d is not added",
+			  "Candidate %d of comp %d is not added (pending)",
 			  j, i));
 		continue;
 	    }
 
+	    /* Must have address */
+	    pj_assert(pj_sockaddr_has_addr(&cand->addr));
+
+	    /* Add the candidate */
 	    status = pj_ice_sess_add_cand(ice_st->ice, comp->comp_id, 
-					  cand->type, cand->local_pref, 
+					  cand->transport_id, cand->type, 
+					  cand->local_pref, 
 					  &cand->foundation, &cand->addr, 
-					  &comp->local_addr, NULL, 
-					  sizeof(pj_sockaddr_in), 
-					  (unsigned*)&cand->ice_cand_id);
+					  &cand->base_addr,  &cand->rel_addr,
+					  pj_sockaddr_get_len(&cand->addr),
+					  (unsigned*)&ice_cand_id);
 	    if (status != PJ_SUCCESS)
 		goto on_error;
@@ -908,24 +704,67 @@
  */
 PJ_DEF(pj_status_t) pj_ice_strans_enum_cands(pj_ice_strans *ice_st,
-					 unsigned *count,
-					 pj_ice_sess_cand cand[])
+					     unsigned comp_id,
+					     unsigned *count,
+					     pj_ice_sess_cand cand[])
 {
     unsigned i, cnt;
-    pj_ice_sess_cand *pcand;
-
-    PJ_ASSERT_RETURN(ice_st && count && cand, PJ_EINVAL);
-    PJ_ASSERT_RETURN(ice_st->ice, PJ_EINVALIDOP);
-
-    cnt = ice_st->ice->lcand_cnt;
+    pj_ice_strans_comp *comp;
+
+    PJ_ASSERT_RETURN(ice_st && comp_id && comp_id <= ice_st->comp_cnt &&
+		     count && cand, PJ_EINVAL);
+
+    comp = ice_st->comp[comp_id - 1];
+    cnt = comp->cand_cnt;
     cnt = (cnt > *count) ? *count : cnt;
-    *count = 0;
 
     for (i=0; i<cnt; ++i) {
-	pcand = &ice_st->ice->lcand[i];
-	pj_memcpy(&cand[i], pcand, sizeof(pj_ice_sess_cand));
+	pj_memcpy(&cand[i], &comp->cand_list[i], sizeof(pj_ice_sess_cand));
     }
 
     *count = cnt;
     return PJ_SUCCESS;
+}
+
+/*
+ * Get default candidate.
+ */
+PJ_DEF(pj_status_t) pj_ice_strans_get_def_cand( pj_ice_strans *ice_st,
+						unsigned comp_id,
+						pj_ice_sess_cand *cand)
+{
+    const pj_ice_sess_check *valid_pair;
+
+    PJ_ASSERT_RETURN(ice_st && comp_id && comp_id <= ice_st->comp_cnt &&
+		      cand, PJ_EINVAL);
+
+    valid_pair = pj_ice_strans_get_valid_pair(ice_st, comp_id);
+    if (valid_pair) {
+	pj_memcpy(cand, valid_pair->lcand, sizeof(pj_ice_sess_cand));
+    } else {
+	pj_ice_strans_comp *comp = ice_st->comp[comp_id - 1];
+	pj_assert(comp->default_cand>=0 && comp->default_cand<comp->cand_cnt);
+	pj_memcpy(cand, &comp->cand_list[comp->default_cand], 
+		  sizeof(pj_ice_sess_cand));
+    }
+    return PJ_SUCCESS;
+}
+
+/*
+ * Get the current ICE role.
+ */
+PJ_DEF(pj_ice_sess_role) pj_ice_strans_get_role(pj_ice_strans *ice_st)
+{
+    PJ_ASSERT_RETURN(ice_st && ice_st->ice, PJ_ICE_SESS_ROLE_UNKNOWN);
+    return ice_st->ice->role;
+}
+
+/*
+ * Change session role.
+ */
+PJ_DEF(pj_status_t) pj_ice_strans_change_role( pj_ice_strans *ice_st,
+					       pj_ice_sess_role new_role)
+{
+    PJ_ASSERT_RETURN(ice_st && ice_st->ice, PJ_EINVALIDOP);
+    return pj_ice_sess_change_role(ice_st->ice, new_role);
 }
 
@@ -941,4 +780,11 @@
     pj_status_t status;
 
+    PJ_ASSERT_RETURN(ice_st && rem_ufrag && rem_passwd &&
+		     rem_cand_cnt && rem_cand, PJ_EINVAL);
+
+    /* Mark start time */
+    pj_gettimeofday(&ice_st->start_time);
+
+    /* Build check list */
     status = pj_ice_sess_create_check_list(ice_st->ice, rem_ufrag, rem_passwd,
 					   rem_cand_cnt, rem_cand);
@@ -946,4 +792,5 @@
 	return status;
 
+    /* Start ICE negotiation! */
     status = pj_ice_sess_start_check(ice_st->ice);
     if (status != PJ_SUCCESS) {
@@ -955,10 +802,24 @@
 
 /*
+ * Get valid pair.
+ */
+PJ_DEF(const pj_ice_sess_check*) 
+pj_ice_strans_get_valid_pair(const pj_ice_strans *ice_st,
+			     unsigned comp_id)
+{
+    PJ_ASSERT_RETURN(ice_st && comp_id && comp_id <= ice_st->comp_cnt,
+		     NULL);
+    
+    if (ice_st->ice == NULL)
+	return NULL;
+    
+    return ice_st->ice->comp[comp_id-1].valid_check;
+}
+
+/*
  * Stop ICE!
  */
 PJ_DEF(pj_status_t) pj_ice_strans_stop_ice(pj_ice_strans *ice_st)
 {
-    unsigned i;
-
     if (ice_st->ice) {
 	pj_ice_sess_destroy(ice_st->ice);
@@ -966,17 +827,9 @@
     }
 
-    /* Invalidate all candidate Ids */
-    for (i=0; i<ice_st->comp_cnt; ++i) {
-	unsigned j;
-	for (j=0; j<ice_st->comp[i]->cand_cnt; ++j) {
-	    ice_st->comp[i]->cand_list[j].ice_cand_id = -1;
-	}
-    }
-
     return PJ_SUCCESS;
 }
 
 /*
- * Send packet using non-ICE means (e.g. when ICE was not negotiated).
+ * Application wants to send outgoing packet.
  */
 PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st,
@@ -998,16 +851,22 @@
     /* If ICE is available, send data with ICE */
     if (ice_st->ice) {
-	return pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len);
-    }
-
-    /* Otherwise send direcly with the socket. This is for compatibility
-     * with remote that doesn't support ICE.
-     */
-    pkt_size = data_len;
-    status = pj_ioqueue_sendto(comp->key, &comp->write_op, 
-			       data, &pkt_size, 0,
-			       dst_addr, dst_addr_len);
-    
-    return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status;
+	if (comp->turn_sock) {
+	    pj_turn_sock_lock(comp->turn_sock);
+	}
+	status = pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len);
+	if (comp->turn_sock) {
+	    pj_turn_sock_unlock(comp->turn_sock);
+	}
+	return status;
+
+    } else if (comp->stun_sock) {
+
+	pkt_size = data_len;
+	status = pj_stun_sock_sendto(comp->stun_sock, NULL, data, data_len,
+				     0, dst_addr, dst_addr_len);
+	return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status;
+
+    } else
+	return PJ_EINVALIDOP;
 }
 
@@ -1019,7 +878,77 @@
 {
     pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data;
+    pj_time_val t;
+    unsigned msec;
+
+    sess_add_ref(ice_st);
+
+    pj_gettimeofday(&t);
+    PJ_TIME_VAL_SUB(t, ice_st->start_time);
+    msec = PJ_TIME_VAL_MSEC(t);
+
     if (ice_st->cb.on_ice_complete) {
-	(*ice_st->cb.on_ice_complete)(ice_st, status);
-    }
+	if (status != PJ_SUCCESS) {
+	    char errmsg[PJ_ERR_MSG_SIZE];
+	    pj_strerror(status, errmsg, sizeof(errmsg));
+	    PJ_LOG(4,(ice_st->obj_name, 
+		      "ICE negotiation failed after %ds:%03d: %s", 
+		      msec/1000, msec%1000, errmsg));
+	} else {
+	    unsigned i;
+	    enum {
+		msg_disable_ind = 0xFFFF &
+				  ~(PJ_STUN_SESS_LOG_TX_IND|
+				    PJ_STUN_SESS_LOG_RX_IND)
+	    };
+
+	    PJ_LOG(4,(ice_st->obj_name, 
+		      "ICE negotiation success after %ds:%03d",
+		      msec/1000, msec%1000));
+
+	    for (i=0; i<ice_st->comp_cnt; ++i) {
+		const pj_ice_sess_check *check;
+
+		check = pj_ice_strans_get_valid_pair(ice_st, i+1);
+		if (check) {
+		    char lip[PJ_INET6_ADDRSTRLEN+10];
+		    char rip[PJ_INET6_ADDRSTRLEN+10];
+
+		    pj_sockaddr_print(&check->lcand->addr, lip, 
+				      sizeof(lip), 3);
+		    pj_sockaddr_print(&check->rcand->addr, rip, 
+				      sizeof(rip), 3);
+
+		    if (check->lcand->transport_id == TP_TURN) {
+			/* Disable logging for Send/Data indications */
+			PJ_LOG(5,(ice_st->obj_name, 
+				  "Disabling STUN Indication logging for "
+				  "component %d", i+1));
+			pj_turn_sock_set_log(ice_st->comp[i]->turn_sock,
+					     msg_disable_ind);
+		    }
+
+		    PJ_LOG(4,(ice_st->obj_name, " Comp %d: "
+			      "sending from %s candidate %s to "
+			      "%s candidate %s",
+			      i+1, 
+			      pj_ice_get_cand_type_name(check->lcand->type),
+			      lip,
+			      pj_ice_get_cand_type_name(check->rcand->type),
+			      rip));
+			      
+		} else {
+		    PJ_LOG(4,(ice_st->obj_name, 
+			      "Comp %d: disabled", i+1));
+		}
+	    }
+	}
+
+	(*ice_st->cb.on_ice_complete)(ice_st, PJ_ICE_STRANS_OP_NEGOTIATION, 
+				      status);
+
+	
+    }
+
+    sess_dec_ref(ice_st);
 }
 
@@ -1029,4 +958,5 @@
 static pj_status_t ice_tx_pkt(pj_ice_sess *ice, 
 			      unsigned comp_id, 
+			      unsigned transport_id,
 			      const void *pkt, pj_size_t size,
 			      const pj_sockaddr_t *dst_addr,
@@ -1034,23 +964,34 @@
 {
     pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data;
-    pj_ice_strans_comp *comp = NULL;
-    pj_ssize_t pkt_size;
+    pj_ice_strans_comp *comp;
     pj_status_t status;
 
-    PJ_TODO(TX_TO_RELAY);
-
     PJ_ASSERT_RETURN(comp_id && comp_id <= ice_st->comp_cnt, PJ_EINVAL);
+
     comp = ice_st->comp[comp_id-1];
 
     TRACE_PKT((comp->ice_st->obj_name, 
-	      "Component %d TX packet to %s:%d",
-	      comp_id,
+	      "Component %d TX packet to %s:%d with transport %d",
+	      comp_id, 
 	      pj_inet_ntoa(((pj_sockaddr_in*)dst_addr)->sin_addr),
-	      (int)pj_ntohs(((pj_sockaddr_in*)dst_addr)->sin_port)));
-
-    pkt_size = size;
-    status = pj_ioqueue_sendto(comp->key, &comp->write_op, 
-			       pkt, &pkt_size, 0,
-			       dst_addr, dst_addr_len);
+	      (int)pj_ntohs(((pj_sockaddr_in*)dst_addr)->sin_port),
+	      transport_id));
+
+    if (transport_id == TP_TURN) {
+	if (comp->turn_sock) {
+	    status = pj_turn_sock_sendto(comp->turn_sock, 
+					 (const pj_uint8_t*)pkt, size,
+					 dst_addr, dst_addr_len);
+	} else {
+	    status = PJ_EINVALIDOP;
+	}
+    } else if (transport_id == TP_STUN) {
+	status = pj_stun_sock_sendto(comp->stun_sock, NULL, 
+				     pkt, size, 0,
+				     dst_addr, dst_addr_len);
+    } else {
+	pj_assert(!"Invalid transport ID");
+	status = PJ_EINVALIDOP;
+    }
     
     return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status;
@@ -1062,4 +1003,5 @@
 static void ice_rx_data(pj_ice_sess *ice, 
 		        unsigned comp_id, 
+			unsigned transport_id,
 		        void *pkt, pj_size_t size,
 		        const pj_sockaddr_t *src_addr,
@@ -1068,4 +1010,6 @@
     pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data;
 
+    PJ_UNUSED_ARG(transport_id);
+
     if (ice_st->cb.on_rx_data) {
 	(*ice_st->cb.on_rx_data)(ice_st, comp_id, pkt, size, 
@@ -1074,135 +1018,279 @@
 }
 
-/*
- * Callback called by STUN session to send outgoing packet.
- */
-static pj_status_t stun_on_send_msg(pj_stun_session *sess,
-				    void *token,
-				    const void *pkt,
-				    pj_size_t size,
-				    const pj_sockaddr_t *dst_addr,
-				    unsigned dst_addr_len)
+/* Notification when incoming packet has been received from
+ * the STUN socket. 
+ */
+static pj_bool_t stun_on_rx_data(pj_stun_sock *stun_sock,
+				 void *pkt,
+				 unsigned pkt_len,
+				 const pj_sockaddr_t *src_addr,
+				 unsigned addr_len)
 {
     pj_ice_strans_comp *comp;
-    pj_ssize_t pkt_size;
+    pj_ice_strans *ice_st;
     pj_status_t status;
 
-    PJ_UNUSED_ARG(token);
-
-    comp = (pj_ice_strans_comp*) pj_stun_session_get_user_data(sess);
-    pkt_size = size;
-    status = pj_ioqueue_sendto(comp->key, &comp->write_op, 
-			       pkt, &pkt_size, 0,
-			       dst_addr, dst_addr_len);
-    
-    return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status;
-}
-
-/*
- * Callback sent by STUN session when outgoing STUN request has
- * completed.
- */
-static void stun_on_request_complete(pj_stun_session *sess,
-				     pj_status_t status,
-				     void *token,
-				     pj_stun_tx_data *tdata,
-				     const pj_stun_msg *response,
-				     const pj_sockaddr_t *src_addr,
-				     unsigned src_addr_len)
+    comp = (pj_ice_strans_comp*) pj_stun_sock_get_user_data(stun_sock);
+    ice_st = comp->ice_st;
+
+    sess_add_ref(ice_st);
+
+    if (ice_st->ice == NULL) {
+	/* The ICE session is gone, but we're still receiving packets.
+	 * This could also happen if remote doesn't do ICE. So just
+	 * report this to application.
+	 */
+	if (ice_st->cb.on_rx_data) {
+	    (*ice_st->cb.on_rx_data)(ice_st, comp->comp_id, pkt, pkt_len, 
+				     src_addr, addr_len);
+	}
+
+    } else {
+
+	/* Hand over the packet to ICE session */
+	status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, 
+				       TP_STUN, pkt, pkt_len,
+				       src_addr, addr_len);
+
+	if (status != PJ_SUCCESS) {
+	    ice_st_perror(comp->ice_st, "Error processing packet", 
+			  status);
+	}
+    }
+
+    return sess_dec_ref(ice_st);
+}
+
+/* Notifification when asynchronous send operation to the STUN socket
+ * has completed. 
+ */
+static pj_bool_t stun_on_data_sent(pj_stun_sock *stun_sock,
+				   pj_ioqueue_op_key_t *send_key,
+				   pj_ssize_t sent)
+{
+    PJ_UNUSED_ARG(stun_sock);
+    PJ_UNUSED_ARG(send_key);
+    PJ_UNUSED_ARG(sent);
+    return PJ_TRUE;
+}
+
+/* Notification when the status of the STUN transport has changed. */
+static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, 
+				pj_stun_sock_op op,
+				pj_status_t status)
 {
     pj_ice_strans_comp *comp;
-    pj_ice_strans_cand *cand = NULL;
-    pj_stun_xor_mapped_addr_attr *xa;
-    pj_stun_mapped_addr_attr *ma;
-    pj_sockaddr *mapped_addr;
-    char ip[20];
-
-    comp = (pj_ice_strans_comp*) pj_stun_session_get_user_data(sess);
-    cand = (pj_ice_strans_cand*) token;
-
-    PJ_UNUSED_ARG(token);
-    PJ_UNUSED_ARG(tdata);
-    PJ_UNUSED_ARG(src_addr);
-    PJ_UNUSED_ARG(src_addr_len);
-
-    if (cand == NULL) {
-	/* This is keep-alive */
+    pj_ice_strans *ice_st;
+    pj_ice_sess_cand *cand = NULL;
+    unsigned i;
+
+    comp = (pj_ice_strans_comp*) pj_stun_sock_get_user_data(stun_sock);
+    ice_st = comp->ice_st;
+
+    sess_add_ref(ice_st);
+
+    /* Find the srflx cancidate */
+    for (i=0; i<comp->cand_cnt; ++i) {
+	if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_SRFLX) {
+	    cand = &comp->cand_list[i];
+	    break;
+	}
+    }
+
+    pj_assert(status != PJ_EPENDING);
+
+    switch (op) {
+    case PJ_STUN_SOCK_DNS_OP:
 	if (status != PJ_SUCCESS) {
-	    ice_st_perror(comp->ice_st, "STUN keep-alive request failed",
+	    /* May not have cand, e.g. when error during init */
+	    if (cand)
+		cand->status = status;
+	    sess_fail(ice_st, PJ_ICE_STRANS_OP_INIT, "DNS resolution failed", 
+		      status);
+	}
+	break;
+    case PJ_STUN_SOCK_BINDING_OP:
+	if (status == PJ_SUCCESS) {
+	    pj_stun_sock_info info;
+
+	    status = pj_stun_sock_get_info(stun_sock, &info);
+	    if (status == PJ_SUCCESS) {
+		char ipaddr[PJ_INET6_ADDRSTRLEN+10];
+		pj_bool_t dup = PJ_FALSE;
+
+		/* Eliminate the srflx candidate if the address is
+		 * equal to other (host) candidates.
+		 */
+		for (i=0; i<comp->cand_cnt; ++i) {
+		    if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_HOST &&
+			pj_sockaddr_cmp(&comp->cand_list[i].addr,
+					&info.mapped_addr) == 0)
+		    {
+			dup = PJ_TRUE;
+			break;
+		    }
+		}
+
+		if (dup) {
+		    /* Duplicate found, remove the srflx candidate */
+		    pj_array_erase(comp->cand_list, sizeof(comp->cand_list[0]),
+				   comp->cand_cnt, cand - comp->cand_list);
+		    --comp->cand_cnt;
+		} else {
+		    /* Otherwise update the address */
+		    pj_sockaddr_cp(&cand->addr, &info.mapped_addr);
+		    cand->status = PJ_SUCCESS;
+		}
+
+		PJ_LOG(4,(comp->ice_st->obj_name, 
+			  "Comp %d: Binding discovery complete, "
+			  "srflx address is %s",
+			  comp->comp_id, 
+			  pj_sockaddr_print(&info.mapped_addr, ipaddr, 
+					     sizeof(ipaddr), 3)));
+
+		sess_init_update(ice_st);
+	    }
+	}
+
+	if (status != PJ_SUCCESS) {
+	    /* May not have cand, e.g. when error during init */
+	    if (cand)
+		cand->status = status;
+	    sess_fail(ice_st, PJ_ICE_STRANS_OP_INIT, 
+		      "STUN binding request failed", status);
+	}
+	break;
+    case PJ_STUN_SOCK_KEEP_ALIVE_OP:
+	if (status != PJ_SUCCESS) {
+	    pj_assert(cand != NULL);
+	    cand->status = status;
+	    sess_fail(ice_st, PJ_ICE_STRANS_OP_INIT, 
+		      "STUN keep-alive failed", status);
+	}
+	break;
+    }
+
+    return sess_dec_ref(ice_st);
+}
+
+/* Callback when TURN socket has received a packet */
+static void turn_on_rx_data(pj_turn_sock *turn_sock,
+			    void *pkt,
+			    unsigned pkt_len,
+			    const pj_sockaddr_t *peer_addr,
+			    unsigned addr_len)
+{
+    pj_ice_strans_comp *comp;
+    pj_status_t status;
+
+    comp = (pj_ice_strans_comp*) pj_turn_sock_get_user_data(turn_sock);
+    if (comp == NULL) {
+	/* We have disassociated ourselves from the TURN socket */
+	return;
+    }
+
+    sess_add_ref(comp->ice_st);
+
+    if (comp->ice_st->ice == NULL) {
+	/* The ICE session is gone, but we're still receiving packets.
+	 * This could also happen if remote doesn't do ICE and application
+	 * specifies TURN as the default address in SDP.
+	 * So in this case just give the packet to application.
+	 */
+	if (comp->ice_st->cb.on_rx_data) {
+	    (*comp->ice_st->cb.on_rx_data)(comp->ice_st, comp->comp_id, pkt,
+					   pkt_len, peer_addr, addr_len);
+	}
+
+    } else {
+
+	/* Hand over the packet to ICE */
+	status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id,
+				       TP_TURN, pkt, pkt_len,
+				       peer_addr, addr_len);
+
+	if (status != PJ_SUCCESS) {
+	    ice_st_perror(comp->ice_st, 
+			  "Error processing packet from TURN relay", 
 			  status);
 	}
+    }
+
+    sess_dec_ref(comp->ice_st);
+}
+
+
+/* Callback when TURN client state has changed */
+static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
+			  pj_turn_state_t new_state)
+{
+    pj_ice_strans_comp *comp;
+
+    comp = (pj_ice_strans_comp*) pj_turn_sock_get_user_data(turn_sock);
+    if (comp == NULL) {
+	/* Not interested in further state notification once the relay is
+	 * disconnecting.
+	 */
 	return;
     }
 
-    /* Decrement pending count for this component */
-    pj_assert(comp->pending_cnt > 0);
-    comp->pending_cnt--;
-
-    if (status == PJNATH_ESTUNTIMEDOUT) {
+    PJ_LOG(5,(comp->ice_st->obj_name, "TURN client state changed %s --> %s",
+	      pj_turn_state_name(old_state), pj_turn_state_name(new_state)));
+
+    sess_add_ref(comp->ice_st);
+
+    if (new_state == PJ_TURN_STATE_READY) {
+	pj_turn_session_info rel_info;
+	char ipaddr[PJ_INET6_ADDRSTRLEN+8];
+	pj_ice_sess_cand *cand = NULL;
+	unsigned i;
+
+	/* Get allocation info */
+	pj_turn_sock_get_info(turn_sock, &rel_info);
+
+	/* Find relayed candidate in the component */
+	for (i=0; i<comp->cand_cnt; ++i) {
+	    if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_RELAYED) {
+		cand = &comp->cand_list[i];
+		break;
+	    }
+	}
+	pj_assert(cand != NULL);
+
+	/* Update candidate */
+	pj_sockaddr_cp(&cand->addr, &rel_info.relay_addr);
+	pj_sockaddr_cp(&cand->base_addr, &rel_info.relay_addr);
+	pj_sockaddr_cp(&cand->rel_addr, &rel_info.mapped_addr);
+	pj_ice_calc_foundation(comp->ice_st->pool, &cand->foundation, 
+			       PJ_ICE_CAND_TYPE_RELAYED, 
+			       &rel_info.relay_addr);
+	cand->status = PJ_SUCCESS;
 
 	PJ_LOG(4,(comp->ice_st->obj_name, 
-		  "STUN Binding request has timed-out, will retry "
-		  "again alter"));
-
-	/* Restart keep-alive timer */
-	start_ka_timer(comp->ice_st);
-	return;
-
-    } else if (status != PJ_SUCCESS) {
-	comp->last_status = cand->status = status;
-	ice_st_perror(comp->ice_st, "STUN Binding request failed", 
-		      cand->status);
-	return;
-    }
-
-    xa = (pj_stun_xor_mapped_addr_attr*)
-	 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0);
-    ma = (pj_stun_mapped_addr_attr*)
-	 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0);
-
-    if (xa)
-	mapped_addr = &xa->sockaddr;
-    else if (ma)
-	mapped_addr = &ma->sockaddr;
-    else {
-	cand->status = PJNATH_ESTUNNOMAPPEDADDR;
-	ice_st_perror(comp->ice_st, "STUN Binding request failed", 
-		      cand->status);
-	return;
-    }
-
-    /* Save IP address for logging */
-    pj_ansi_strcpy(ip, pj_inet_ntoa(comp->local_addr.ipv4.sin_addr));
-
-    /* Ignore response if it reports the same address */
-    if (comp->local_addr.ipv4.sin_addr.s_addr == mapped_addr->ipv4.sin_addr.s_addr &&
-	comp->local_addr.ipv4.sin_port == mapped_addr->ipv4.sin_port)
-    {
-	PJ_LOG(4,(comp->ice_st->obj_name, 
-		  "Candidate %s:%d is directly connected to Internet, "
-		  "STUN mapped address is ignored",
-		  ip, pj_ntohs(comp->local_addr.ipv4.sin_port)));
-	return;
-    }
-
-    PJ_LOG(5,(comp->ice_st->obj_name, 
-	      "STUN mapped address for %s:%d is %s:%d",
-	      ip, (int)pj_ntohs(comp->local_addr.ipv4.sin_port),
-	      pj_inet_ntoa(mapped_addr->ipv4.sin_addr),
-	      (int)pj_ntohs(mapped_addr->ipv4.sin_port)));
-    pj_memcpy(&cand->addr, mapped_addr, sizeof(pj_sockaddr_in));
-    cand->status = PJ_SUCCESS;
-
-    /* Set this candidate as the default candidate */
-    comp->default_cand = (cand - comp->cand_list);
-    comp->last_status = PJ_SUCCESS;
-
-    /* We have STUN, so we must start the keep-alive timer */
-    start_ka_timer(comp->ice_st);
-
-    /* Notify app that STUN address has changed. */
-    if (comp->ice_st->cb.on_addr_change)
-	(*comp->ice_st->cb.on_addr_change)(comp->ice_st, comp->comp_id, 
-					   (cand - comp->cand_list));
-}
-
+		  "Comp %d: TURN allocation complete, relay address is %s",
+		  comp->comp_id, 
+		  pj_sockaddr_print(&rel_info.relay_addr, ipaddr, 
+				     sizeof(ipaddr), 3)));
+
+	sess_init_update(comp->ice_st);
+
+    } else if (new_state >= PJ_TURN_STATE_DEALLOCATING) {
+	pj_turn_session_info info;
+
+	pj_turn_sock_get_info(turn_sock, &info);
+
+	/* Unregister ourself from the TURN relay */
+	pj_turn_sock_set_user_data(turn_sock, NULL);
+	comp->turn_sock = NULL;
+
+	/* Set session to fail if we're still initializing */
+	if (old_state < PJ_TURN_STATE_READY) {
+	    sess_fail(comp->ice_st, PJ_ICE_STRANS_OP_INIT,
+		      "TURN relay failed", info.last_status);
+	}
+    }
+
+    sess_dec_ref(comp->ice_st);
+}
+
Index: pjproject/trunk/pjnath/src/pjnath/stun_msg.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 1987)
+++ pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 1988)
@@ -1558,5 +1558,5 @@
 PJ_DEF(pj_status_t) pj_stun_msg_add_unknown_attr(pj_pool_t *pool,
 						 pj_stun_msg *msg,
-						 unsigned attr_cnt,
+						 pj_size_t attr_cnt,
 						 const pj_uint16_t attr_types[])
 {
@@ -1647,5 +1647,5 @@
 					       int attr_type,
 					       const pj_uint8_t *data,
-					       unsigned length,
+					       pj_size_t length,
 					       pj_stun_binary_attr **p_attr)
 {
@@ -1674,5 +1674,5 @@
 						int attr_type,
 						const pj_uint8_t *data,
-						unsigned length)
+						pj_size_t length)
 {
     pj_stun_binary_attr *attr = NULL;
@@ -1834,8 +1834,8 @@
  * Check that the PDU is potentially a valid STUN message.
  */
-PJ_DEF(pj_status_t) pj_stun_msg_check(const pj_uint8_t *pdu, unsigned pdu_len,
+PJ_DEF(pj_status_t) pj_stun_msg_check(const pj_uint8_t *pdu, pj_size_t pdu_len,
 				      unsigned options)
 {
-    unsigned msg_len;
+    pj_size_t msg_len;
 
     PJ_ASSERT_RETURN(pdu, PJ_EINVAL);
@@ -1939,8 +1939,8 @@
 PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool,
 				       const pj_uint8_t *pdu,
-				       unsigned pdu_len,
+				       pj_size_t pdu_len,
 				       unsigned options,
 				       pj_stun_msg **p_msg,
-				       unsigned *p_parsed_len,
+				       pj_size_t *p_parsed_len,
 				       pj_stun_msg **p_response)
 {
@@ -2191,7 +2191,7 @@
 PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg,
 				       pj_uint8_t *buf, unsigned buf_size,
-				       unsigned options,
+				       pj_size_t options,
 				       const pj_str_t *key,
-				       unsigned *p_msg_len)
+				       pj_size_t *p_msg_len)
 {
     pj_uint8_t *start = buf;
Index: pjproject/trunk/pjnath/src/pjnath/stun_session.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_session.c	(revision 1987)
+++ pjproject/trunk/pjnath/src/pjnath/stun_session.c	(revision 1988)
@@ -30,7 +30,15 @@
     void		*user_data;
 
+    pj_atomic_t		*busy;
+    pj_bool_t		 destroy_request;
+
     pj_bool_t		 use_fingerprint;
 
+    pj_pool_t		*rx_pool;
+
+#if PJ_LOG_MAX_LEVEL >= 5
     char		 dump_buf[1000];
+#endif
+    unsigned		 log_flag;
 
     pj_stun_auth_type	 auth_type;
@@ -38,4 +46,5 @@
     int			 auth_retry;
     pj_str_t		 next_nonce;
+    pj_str_t		 server_realm;
 
     pj_str_t		 srv_name;
@@ -80,5 +89,5 @@
 			   pj_stun_tx_data *tdata)
 {
-    pj_list_push_back(&sess->pending_request_list, tdata);
+    pj_list_push_front(&sess->pending_request_list, tdata);
     return PJ_SUCCESS;
 }
@@ -139,9 +148,11 @@
 
     tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
+    tsx_erase(tdata->sess, tdata);
+
     pj_stun_client_tsx_destroy(tsx);
     pj_pool_release(tdata->pool);
 }
 
-static void destroy_tdata(pj_stun_tx_data *tdata)
+static void destroy_tdata(pj_stun_tx_data *tdata, pj_bool_t force)
 {
     if (tdata->res_timer.id != PJ_FALSE) {
@@ -152,12 +163,19 @@
     }
 
-    if (tdata->client_tsx) {
-	pj_time_val delay = {2, 0};
-	tsx_erase(tdata->sess, tdata);
-	pj_stun_client_tsx_schedule_destroy(tdata->client_tsx, &delay);
-	tdata->client_tsx = NULL;
+    if (force) {
+	if (tdata->client_tsx) {
+	    tsx_erase(tdata->sess, tdata);
+	    pj_stun_client_tsx_destroy(tdata->client_tsx);
+	}
+	pj_pool_release(tdata->pool);
 
     } else {
-	pj_pool_release(tdata->pool);
+	if (tdata->client_tsx) {
+	    pj_time_val delay = {2, 0};
+	    pj_stun_client_tsx_schedule_destroy(tdata->client_tsx, &delay);
+
+	} else {
+	    pj_pool_release(tdata->pool);
+	}
     }
 }
@@ -170,5 +188,5 @@
 {
     PJ_UNUSED_ARG(sess);
-    destroy_tdata(tdata);
+    destroy_tdata(tdata, PJ_FALSE);
 }
 
@@ -290,4 +308,5 @@
     {
 	const pj_stun_nonce_attr *anonce;
+	const pj_stun_realm_attr *arealm;
 	pj_stun_tx_data *tdata;
 	unsigned i;
@@ -317,4 +336,11 @@
 	pj_strdup(sess->pool, &sess->next_nonce, &anonce->value);
 
+	/* Copy the realm from the response */
+	arealm = (pj_stun_realm_attr*)
+		 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_REALM, 0);
+	if (arealm) {
+	    pj_strdup(sess->pool, &sess->server_realm, &arealm->value);
+	}
+
 	/* Create new request */
 	status = pj_stun_session_create_req(sess, request->msg->hdr.type,
@@ -325,5 +351,6 @@
 
 	/* Duplicate all the attributes in the old request, except
-	 * USERNAME, REALM, M-I, and NONCE 
+	 * USERNAME, REALM, M-I, and NONCE, which will be filled in
+	 * later.
 	 */
 	for (i=0; i<request->msg->attr_count; ++i) {
@@ -374,4 +401,8 @@
     sess = tdata->sess;
 
+    /* Lock the session and prevent user from destroying us in the callback */
+    pj_atomic_inc(sess->busy);
+    pj_lock_acquire(sess->lock);
+
     /* Handle authentication challenge */
     handle_auth_challenge(sess, tdata, response, src_addr,
@@ -388,4 +419,11 @@
     pj_stun_msg_destroy_tdata(sess, tdata);
     tdata = NULL;
+
+    pj_lock_release(sess->lock);
+
+    if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
+	pj_stun_session_destroy(sess);
+	return;
+    }
 }
 
@@ -395,10 +433,25 @@
 {
     pj_stun_tx_data *tdata;
+    pj_stun_session *sess;
+    pj_status_t status;
 
     tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
-
-    return tdata->sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt, 
-				       pkt_size, tdata->dst_addr, 
-				       tdata->addr_len);
+    sess = tdata->sess;
+
+    /* Lock the session and prevent user from destroying us in the callback */
+    pj_atomic_inc(sess->busy);
+    pj_lock_acquire(sess->lock);
+    
+    status = sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt, 
+				  pkt_size, tdata->dst_addr, 
+				  tdata->addr_len);
+    pj_lock_release(sess->lock);
+
+    if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
+	pj_stun_session_destroy(sess);
+	return PJNATH_ESTUNDESTROYED;
+    } else {
+	return status;
+    }
 }
 
@@ -429,4 +482,5 @@
     pj_memcpy(&sess->cb, cb, sizeof(*cb));
     sess->use_fingerprint = fingerprint;
+    sess->log_flag = 0xFFFF;
     
     sess->srv_name.ptr = (char*) pj_pool_alloc(pool, 32);
@@ -434,4 +488,8 @@
 					   "pj_stun-%s", pj_get_version());
 
+    sess->rx_pool = pj_pool_create(sess->cfg->pf, "name", 
+				   PJNATH_POOL_LEN_STUN_TDATA, 
+				   PJNATH_POOL_INC_STUN_TDATA, NULL);
+
     pj_list_init(&sess->pending_request_list);
     pj_list_init(&sess->cached_response_list);
@@ -444,4 +502,11 @@
     sess->delete_lock = PJ_TRUE;
 
+    status = pj_atomic_create(pool, 0, &sess->busy);
+    if (status != PJ_SUCCESS) {
+	pj_lock_destroy(sess->lock);
+	pj_pool_release(pool);
+	return status;
+    }
+
     *p_sess = sess;
 
@@ -454,11 +519,20 @@
 
     pj_lock_acquire(sess->lock);
+
+    /* Can't destroy if we're in a callback */
+    sess->destroy_request = PJ_TRUE;
+    if (pj_atomic_get(sess->busy)) {
+	pj_lock_release(sess->lock);
+	return PJ_EPENDING;
+    }
+
     while (!pj_list_empty(&sess->pending_request_list)) {
 	pj_stun_tx_data *tdata = sess->pending_request_list.next;
-	destroy_tdata(tdata);
-    }
+	destroy_tdata(tdata, PJ_TRUE);
+    }
+
     while (!pj_list_empty(&sess->cached_response_list)) {
 	pj_stun_tx_data *tdata = sess->cached_response_list.next;
-	destroy_tdata(tdata);
+	destroy_tdata(tdata, PJ_TRUE);
     }
     pj_lock_release(sess->lock);
@@ -466,4 +540,9 @@
     if (sess->delete_lock) {
 	pj_lock_destroy(sess->lock);
+    }
+
+    if (sess->rx_pool) {
+	pj_pool_release(sess->rx_pool);
+	sess->rx_pool = NULL;
     }
 
@@ -539,4 +618,10 @@
 }
 
+PJ_DEF(void) pj_stun_session_set_log( pj_stun_session *sess,
+				      unsigned flags)
+{
+    PJ_ASSERT_ON_FAIL(sess, return);
+    sess->log_flag = flags;
+}
 
 static pj_status_t get_auth(pj_stun_session *sess,
@@ -544,5 +629,6 @@
 {
     if (sess->cred.type == PJ_STUN_AUTH_CRED_STATIC) {
-	tdata->auth_info.realm = sess->cred.data.static_cred.realm;
+	//tdata->auth_info.realm = sess->cred.data.static_cred.realm;
+	tdata->auth_info.realm = sess->server_realm;
 	tdata->auth_info.username = sess->cred.data.static_cred.username;
 	tdata->auth_info.nonce = sess->cred.data.static_cred.nonce;
@@ -634,4 +720,5 @@
 	    }
 	    tdata->auth_info.nonce = sess->next_nonce;
+	    tdata->auth_info.realm = sess->server_realm;
 	}
 
@@ -715,6 +802,16 @@
 			unsigned pkt_size, const pj_sockaddr_t *addr)
 {
-    char dst_name[80];
+    char dst_name[PJ_INET6_ADDRSTRLEN+10];
     
+    if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && 
+	 (sess->log_flag & PJ_STUN_SESS_LOG_TX_REQ)==0) ||
+	(PJ_STUN_IS_RESPONSE(msg->hdr.type) &&
+	 (sess->log_flag & PJ_STUN_SESS_LOG_TX_RES)==0) ||
+	(PJ_STUN_IS_INDICATION(msg->hdr.type) &&
+	 (sess->log_flag & PJ_STUN_SESS_LOG_TX_IND)==0))
+    {
+	return;
+    }
+
     pj_sockaddr_print(addr, dst_name, sizeof(dst_name), 3);
 
@@ -750,5 +847,6 @@
     tdata->retransmit = retransmit;
 
-    /* Start locking the session now */
+    /* Lock the session and prevent user from destroying us in the callback */
+    pj_atomic_inc(sess->busy);
     pj_lock_acquire(sess->lock);
 
@@ -758,7 +856,6 @@
     if (status != PJ_SUCCESS) {
 	pj_stun_msg_destroy_tdata(sess, tdata);
-	pj_lock_release(sess->lock);
 	LOG_ERR_(sess, "Error applying options", status);
-	return status;
+	goto on_return;
     }
 
@@ -770,7 +867,6 @@
     if (status != PJ_SUCCESS) {
 	pj_stun_msg_destroy_tdata(sess, tdata);
-	pj_lock_release(sess->lock);
 	LOG_ERR_(sess, "STUN encode() error", status);
-	return status;
+	goto on_return;
     }
 
@@ -798,7 +894,6 @@
 	if (status != PJ_SUCCESS && status != PJ_EPENDING) {
 	    pj_stun_msg_destroy_tdata(sess, tdata);
-	    pj_lock_release(sess->lock);
 	    LOG_ERR_(sess, "Error sending STUN request", status);
-	    return status;
+	    goto on_return;
 	}
 
@@ -825,8 +920,8 @@
 					    &timeout);
 	    if (status != PJ_SUCCESS) {
+		tdata->res_timer.id = PJ_FALSE;
 		pj_stun_msg_destroy_tdata(sess, tdata);
-		pj_lock_release(sess->lock);
 		LOG_ERR_(sess, "Error scheduling response timer", status);
-		return status;
+		goto on_return;
 	    }
 
@@ -839,5 +934,7 @@
 
 	if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	    pj_stun_msg_destroy_tdata(sess, tdata);
 	    LOG_ERR_(sess, "Error sending STUN request", status);
+	    goto on_return;
 	}
 
@@ -848,6 +945,13 @@
     }
 
-
+on_return:
     pj_lock_release(sess->lock);
+
+    /* Check if application has called destroy() in the callback */
+    if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
+	pj_stun_session_destroy(sess);
+	return PJNATH_ESTUNDESTROYED;
+    }
+
     return status;
 }
@@ -893,4 +997,6 @@
     PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL);
 
+    /* Lock the session and prevent user from destroying us in the callback */
+    pj_atomic_inc(sess->busy);
     pj_lock_acquire(sess->lock);
 
@@ -904,4 +1010,10 @@
 
     pj_lock_release(sess->lock);
+
+    if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
+	pj_stun_session_destroy(sess);
+	return PJNATH_ESTUNDESTROYED;
+    }
+
     return PJ_SUCCESS;
 }
@@ -918,4 +1030,6 @@
     PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL);
 
+    /* Lock the session and prevent user from destroying us in the callback */
+    pj_atomic_inc(sess->busy);
     pj_lock_acquire(sess->lock);
 
@@ -923,4 +1037,9 @@
 
     pj_lock_release(sess->lock);
+
+    if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
+	pj_stun_session_destroy(sess);
+	return PJNATH_ESTUNDESTROYED;
+    }
 
     return status;
@@ -1166,4 +1285,34 @@
 
 
+/* Print outgoing message to log */
+static void dump_rx_msg(pj_stun_session *sess, const pj_stun_msg *msg,
+			unsigned pkt_size, const pj_sockaddr_t *addr)
+{
+    char src_info[PJ_INET6_ADDRSTRLEN+10];
+    
+    if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && 
+	 (sess->log_flag & PJ_STUN_SESS_LOG_RX_REQ)==0) ||
+	(PJ_STUN_IS_RESPONSE(msg->hdr.type) &&
+	 (sess->log_flag & PJ_STUN_SESS_LOG_RX_RES)==0) ||
+	(PJ_STUN_IS_INDICATION(msg->hdr.type) &&
+	 (sess->log_flag & PJ_STUN_SESS_LOG_RX_IND)==0))
+    {
+	return;
+    }
+
+    pj_sockaddr_print(addr, src_info, sizeof(src_info), 3);
+
+    PJ_LOG(5,(SNAME(sess),
+	      "RX %d bytes STUN message from %s:\n"
+	      "--- begin STUN message ---\n"
+	      "%s"
+	      "--- end of STUN message ---\n",
+	      pkt_size, src_info,
+	      pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), 
+			       NULL)));
+
+}
+
+/* Incoming packet */
 PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
 					      const void *packet,
@@ -1176,18 +1325,17 @@
 {
     pj_stun_msg *msg, *response;
-    pj_pool_t *tmp_pool;
-    char *dump;
     pj_status_t status;
 
     PJ_ASSERT_RETURN(sess && packet && pkt_size, PJ_EINVAL);
 
-    tmp_pool = pj_pool_create(sess->cfg->pf, "tmpstun", 
-			      PJNATH_POOL_LEN_STUN_TDATA, 
-			      PJNATH_POOL_INC_STUN_TDATA, NULL);
-    if (!tmp_pool)
-	return PJ_ENOMEM;
+    /* Lock the session and prevent user from destroying us in the callback */
+    pj_atomic_inc(sess->busy);
+    pj_lock_acquire(sess->lock);
+
+    /* Reset pool */
+    pj_pool_reset(sess->rx_pool);
 
     /* Try to parse the message */
-    status = pj_stun_msg_decode(tmp_pool, (const pj_uint8_t*)packet,
+    status = pj_stun_msg_decode(sess->rx_pool, (const pj_uint8_t*)packet,
 			        pkt_size, options, 
 				&msg, parsed_len, &response);
@@ -1195,26 +1343,14 @@
 	LOG_ERR_(sess, "STUN msg_decode() error", status);
 	if (response) {
-	    send_response(sess, token, tmp_pool, response, NULL,
+	    send_response(sess, token, sess->rx_pool, response, NULL,
 			  PJ_FALSE, src_addr, src_addr_len);
 	}
-	pj_pool_release(tmp_pool);
-	return status;
-    }
-
-    dump = (char*) pj_pool_alloc(tmp_pool, PJ_STUN_MAX_PKT_LEN);
-
-    PJ_LOG(5,(SNAME(sess),
-	      "RX STUN message from %s:%d:\n"
-	      "--- begin STUN message ---\n"
-	      "%s"
-	      "--- end of STUN message ---\n",
-	      pj_inet_ntoa(((pj_sockaddr_in*)src_addr)->sin_addr),
-	      pj_ntohs(((pj_sockaddr_in*)src_addr)->sin_port),
-	      pj_stun_msg_dump(msg, dump, PJ_STUN_MAX_PKT_LEN, NULL)));
-
-    pj_lock_acquire(sess->lock);
+	goto on_return;
+    }
+
+    dump_rx_msg(sess, msg, pkt_size, src_addr);
 
     /* For requests, check if we have cached response */
-    status = check_cached_response(sess, tmp_pool, msg, 
+    status = check_cached_response(sess, sess->rx_pool, msg, 
 				   src_addr, src_addr_len);
     if (status == PJ_SUCCESS) {
@@ -1232,5 +1368,5 @@
     } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
 
-	status = on_incoming_request(sess, options, token, tmp_pool, 
+	status = on_incoming_request(sess, options, token, sess->rx_pool, 
 				     (const pj_uint8_t*) packet, pkt_size, 
 				     msg, src_addr, src_addr_len);
@@ -1238,5 +1374,5 @@
     } else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) {
 
-	status = on_incoming_indication(sess, token, tmp_pool, 
+	status = on_incoming_indication(sess, token, sess->rx_pool, 
 					(const pj_uint8_t*) packet, pkt_size,
 					msg, src_addr, src_addr_len);
@@ -1250,8 +1386,13 @@
     pj_lock_release(sess->lock);
 
-    pj_pool_release(tmp_pool);
+    /* If we've received destroy request while we're on the callback,
+     * destroy the session now.
+     */
+    if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
+	pj_stun_session_destroy(sess);
+	return PJNATH_ESTUNDESTROYED;
+    }
+
     return status;
 }
 
-
-
Index: pjproject/trunk/pjnath/src/pjnath/stun_sock.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_sock.c	(revision 1988)
+++ pjproject/trunk/pjnath/src/pjnath/stun_sock.c	(revision 1988)
@@ -0,0 +1,829 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjnath/stun_sock.h>
+#include <pjnath/errno.h>
+#include <pjnath/stun_transaction.h>
+#include <pjnath/stun_session.h>
+#include <pjlib-util/srv_resolver.h>
+#include <pj/activesock.h>
+#include <pj/addr_resolv.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/ip_helper.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/rand.h>
+
+
+struct pj_stun_sock
+{
+    char		*obj_name;	/* Log identification	    */
+    pj_pool_t		*pool;		/* Pool			    */
+    void		*user_data;	/* Application user data    */
+
+    int			 af;		/* Address family	    */
+    pj_stun_config	 stun_cfg;	/* STUN config (ioqueue etc)*/
+    pj_stun_sock_cb	 cb;		/* Application callbacks    */
+
+    int			 ka_interval;	/* Keep alive interval	    */
+    pj_timer_entry	 ka_timer;	/* Keep alive timer.	    */
+
+    pj_sockaddr		 srv_addr;	/* Resolved server addr	    */
+    pj_sockaddr		 mapped_addr;	/* Our public address	    */
+
+    pj_dns_async_query	*q;		/* Pending DNS query	    */
+    pj_sock_t		 sock_fd;	/* Socket descriptor	    */
+    pj_activesock_t	*active_sock;	/* Active socket object	    */
+    pj_ioqueue_op_key_t	 send_key;	/* Default send key for app */
+    pj_ioqueue_op_key_t	 int_send_key;	/* Send key for internal    */
+
+    pj_uint16_t		 tsx_id[6];	/* .. to match STUN msg	    */
+    pj_stun_session	*stun_sess;	/* STUN session		    */
+
+};
+
+/* 
+ * Prototypes for static functions 
+ */
+
+/* This callback is called by the STUN session to send packet */
+static pj_status_t sess_on_send_msg(pj_stun_session *sess,
+				    void *token,
+				    const void *pkt,
+				    pj_size_t pkt_size,
+				    const pj_sockaddr_t *dst_addr,
+				    unsigned addr_len);
+
+/* This callback is called by the STUN session when outgoing transaction 
+ * is complete
+ */
+static void sess_on_request_complete(pj_stun_session *sess,
+				     pj_status_t status,
+				     void *token,
+				     pj_stun_tx_data *tdata,
+				     const pj_stun_msg *response,
+				     const pj_sockaddr_t *src_addr,
+				     unsigned src_addr_len);
+/* DNS resolver callback */
+static void dns_srv_resolver_cb(void *user_data,
+				pj_status_t status,
+				const pj_dns_srv_record *rec);
+
+/* Start sending STUN Binding request */
+static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock);
+
+/* Callback from active socket when incoming packet is received */
+static pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
+				  void *data,
+				  pj_size_t size,
+				  const pj_sockaddr_t *src_addr,
+				  int addr_len,
+				  pj_status_t status);
+
+/* Callback from active socket about send status */
+static pj_bool_t on_data_sent(pj_activesock_t *asock,
+			      pj_ioqueue_op_key_t *send_key,
+			      pj_ssize_t sent);
+
+/* Schedule keep-alive timer */
+static void start_ka_timer(pj_stun_sock *stun_sock);
+
+/* Keep-alive timer callback */
+static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te);
+
+#define INTERNAL_MSG_TOKEN  (void*)1
+
+
+/*
+ * Retrieve the name representing the specified operation.
+ */
+PJ_DEF(const char*) pj_stun_sock_op_name(pj_stun_sock_op op)
+{
+    const char *names[] = {
+	"?",
+	"DNS resolution",
+	"STUN Binding request",
+	"Keep-alive"
+    };
+
+    return op <= PJ_STUN_SOCK_KEEP_ALIVE_OP ? names[op] : "?";
+};
+
+
+/*
+ * Initialize the STUN transport setting with its default values.
+ */
+PJ_DEF(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg)
+{
+    pj_bzero(cfg, sizeof(*cfg));
+    cfg->max_pkt_size = PJ_STUN_SOCK_PKT_LEN;
+    cfg->async_cnt = 1;
+    cfg->ka_interval = PJ_STUN_KEEP_ALIVE_SEC;
+}
+
+
+/* Check that configuration setting is valid */
+static pj_bool_t pj_stun_sock_cfg_is_valid(const pj_stun_sock_cfg *cfg)
+{
+    return cfg->max_pkt_size > 1 && cfg->async_cnt >= 1;
+}
+
+/*
+ * Create the STUN transport using the specified configuration.
+ */
+PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
+					 const char *name,
+					 int af,
+					 const pj_stun_sock_cb *cb,
+					 const pj_stun_sock_cfg *cfg,
+					 void *user_data,
+					 pj_stun_sock **p_stun_sock)
+{
+    pj_pool_t *pool;
+    pj_stun_sock *stun_sock;
+    pj_stun_sock_cfg default_cfg;
+    unsigned i;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL);
+    PJ_ASSERT_RETURN(af==pj_AF_INET()||af==pj_AF_INET6(), PJ_EAFNOTSUP);
+    PJ_ASSERT_RETURN(!cfg || pj_stun_sock_cfg_is_valid(cfg), PJ_EINVAL);
+    PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL);
+
+    status = pj_stun_config_check_valid(stun_cfg);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    if (name == NULL)
+	name = "stuntp%p";
+
+    if (cfg == NULL) {
+	pj_stun_sock_cfg_default(&default_cfg);
+	cfg = &default_cfg;
+    }
+
+
+    /* Create structure */
+    pool = pj_pool_create(stun_cfg->pf, name, 256, 512, NULL);
+    stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock);
+    stun_sock->pool = pool;
+    stun_sock->obj_name = pool->obj_name;
+    stun_sock->user_data = user_data;
+    stun_sock->af = af;
+    stun_sock->sock_fd = PJ_INVALID_SOCKET;
+    pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg));
+    pj_memcpy(&stun_sock->cb, cb, sizeof(*cb));
+
+    stun_sock->ka_interval = cfg->ka_interval;
+    if (stun_sock->ka_interval == 0)
+	stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC;
+
+    /* Create socket and bind socket */
+    status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &stun_sock->sock_fd);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    if (pj_sockaddr_has_addr(&cfg->bound_addr)) {
+	status = pj_sock_bind(stun_sock->sock_fd, &cfg->bound_addr,
+			      pj_sockaddr_get_len(&cfg->bound_addr));
+    } else {
+	pj_sockaddr bound_addr;
+
+	pj_sockaddr_init(af, &bound_addr, NULL, 0);
+	status = pj_sock_bind(stun_sock->sock_fd, &bound_addr,
+			      pj_sockaddr_get_len(&bound_addr));
+    }
+
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Create more useful information string about this transport */
+#if 0
+    {
+	pj_sockaddr bound_addr;
+	int addr_len = sizeof(bound_addr);
+
+	status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr, 
+				     &addr_len);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+
+	stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10);
+	pj_sockaddr_print(&bound_addr, stun_sock->info, 
+			  PJ_INET6_ADDRSTRLEN, 3);
+    }
+#endif
+
+    /* Init active socket configuration */
+    {
+	pj_activesock_cfg activesock_cfg;
+	pj_activesock_cb activesock_cb;
+
+	pj_activesock_cfg_default(&activesock_cfg);
+	activesock_cfg.async_cnt = cfg->async_cnt;
+	activesock_cfg.concurrency = 0;
+
+	/* Create the active socket */
+	pj_bzero(&activesock_cb, sizeof(activesock_cb));
+	activesock_cb.on_data_recvfrom = &on_data_recvfrom;
+	activesock_cb.on_data_sent = &on_data_sent;
+	status = pj_activesock_create(pool, stun_sock->sock_fd, 
+				      pj_SOCK_DGRAM(), 
+				      &activesock_cfg, stun_cfg->ioqueue,
+				      &activesock_cb, stun_sock,
+				      &stun_sock->active_sock);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+
+	/* Start asynchronous read operations */
+	status = pj_activesock_start_recvfrom(stun_sock->active_sock, pool,
+					      cfg->max_pkt_size, 0);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+
+	/* Init send keys */
+	pj_ioqueue_op_key_init(&stun_sock->send_key, 
+			       sizeof(stun_sock->send_key));
+	pj_ioqueue_op_key_init(&stun_sock->int_send_key,
+			       sizeof(stun_sock->int_send_key));
+    }
+
+    /* Create STUN session */
+    {
+	pj_stun_session_cb sess_cb;
+
+	pj_bzero(&sess_cb, sizeof(sess_cb));
+	sess_cb.on_request_complete = &sess_on_request_complete;
+	sess_cb.on_send_msg = &sess_on_send_msg;
+	status = pj_stun_session_create(&stun_sock->stun_cfg, 
+					stun_sock->obj_name,
+					&sess_cb, PJ_FALSE, 
+					&stun_sock->stun_sess);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+    }
+
+    /* Associate us with the STUN session */
+    pj_stun_session_set_user_data(stun_sock->stun_sess, stun_sock);
+
+    /* Initialize random numbers to be used as STUN transaction ID for
+     * outgoing Binding request. We use the 80bit number to distinguish
+     * STUN messages we sent with STUN messages that the application sends.
+     * The last 16bit value in the array is a counter.
+     */
+    for (i=0; i<PJ_ARRAY_SIZE(stun_sock->tsx_id); ++i) {
+	stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand();
+    }
+    stun_sock->tsx_id[5] = 0;
+
+
+    /* Init timer entry */
+    stun_sock->ka_timer.cb = &ka_timer_cb;
+    stun_sock->ka_timer.user_data = stun_sock;
+
+    /* Done */
+    *p_stun_sock = stun_sock;
+    return PJ_SUCCESS;
+
+on_error:
+    pj_stun_sock_destroy(stun_sock);
+    return status;
+}
+
+/* Start socket. */
+PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock,
+				        const pj_str_t *domain,
+				        pj_uint16_t default_port,
+				        pj_dns_resolver *resolver)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(stun_sock && domain && default_port, PJ_EINVAL);
+
+    /* Check whether the domain contains IP address */
+    stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)stun_sock->af;
+    status = pj_inet_pton(stun_sock->af, domain, 
+			  pj_sockaddr_get_addr(&stun_sock->srv_addr));
+    if (status != PJ_SUCCESS) {
+	stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)0;
+    }
+
+    /* If resolver is set, try to resolve with DNS SRV first. It
+     * will fallback to DNS A/AAAA when no SRV record is found.
+     */
+    if (status != PJ_SUCCESS && resolver) {
+	const pj_str_t res_name = pj_str("_stun._udp.");
+	unsigned opt;
+
+	pj_assert(stun_sock->q == NULL);
+
+	opt = PJ_DNS_SRV_FALLBACK_A;
+	if (stun_sock->af == pj_AF_INET6()) {
+	    opt |= (PJ_DNS_SRV_RESOLVE_AAAA | PJ_DNS_SRV_FALLBACK_AAAA);
+	}
+
+	status = pj_dns_srv_resolve(domain, &res_name, default_port, 
+				    stun_sock->pool, resolver, opt,
+				    stun_sock, &dns_srv_resolver_cb, 
+				    &stun_sock->q);
+
+	/* Processing will resume when the DNS SRV callback is called */
+	return status;
+
+    } else {
+
+	if (status != PJ_SUCCESS) {
+	    pj_addrinfo ai;
+	    unsigned cnt = 1;
+
+	    status = pj_getaddrinfo(stun_sock->af, domain, &cnt, &ai);
+	    if (status != PJ_SUCCESS)
+		return status;
+
+	    pj_sockaddr_cp(&stun_sock->srv_addr, &ai.ai_addr);
+	}
+
+	pj_sockaddr_set_port(&stun_sock->srv_addr, (pj_uint16_t)default_port);
+
+	/* Start sending Binding request */
+	return get_mapped_addr(stun_sock);
+    }
+}
+
+/* Destroy */
+PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock)
+{
+    if (stun_sock->q) {
+	pj_dns_resolver_cancel_query(stun_sock->q, PJ_FALSE);
+	stun_sock->q = NULL;
+    }
+
+    /* Destroy the active socket first just in case we'll get
+     * stray callback.
+     */
+    if (stun_sock->active_sock != NULL) {
+	pj_activesock_close(stun_sock->active_sock);
+	stun_sock->active_sock = NULL;
+	stun_sock->sock_fd = PJ_INVALID_SOCKET;
+    } else if (stun_sock->sock_fd != PJ_INVALID_SOCKET) {
+	pj_sock_close(stun_sock->sock_fd);
+	stun_sock->sock_fd = PJ_INVALID_SOCKET;
+    }
+
+    if (stun_sock->ka_timer.id != 0) {
+	pj_timer_heap_cancel(stun_sock->stun_cfg.timer_heap, 
+			     &stun_sock->ka_timer);
+	stun_sock->ka_timer.id = 0;
+    }
+
+    if (stun_sock->stun_sess) {
+	pj_stun_session_destroy(stun_sock->stun_sess);
+	stun_sock->stun_sess = NULL;
+    }
+
+    if (stun_sock->pool) {
+	pj_pool_t *pool = stun_sock->pool;
+	stun_sock->pool = NULL;
+	pj_pool_release(pool);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* Associate user data */
+PJ_DEF(pj_status_t) pj_stun_sock_set_user_data( pj_stun_sock *stun_sock,
+					        void *user_data)
+{
+    PJ_ASSERT_RETURN(stun_sock, PJ_EINVAL);
+    stun_sock->user_data = user_data;
+    return PJ_SUCCESS;
+}
+
+
+/* Get user data */
+PJ_DEF(void*) pj_stun_sock_get_user_data(pj_stun_sock *stun_sock)
+{
+    PJ_ASSERT_RETURN(stun_sock, NULL);
+    return stun_sock->user_data;
+}
+
+/* Notify application that session has failed */
+static pj_bool_t sess_fail(pj_stun_sock *stun_sock, 
+			   pj_stun_sock_op op,
+			   pj_status_t status)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+    pj_bool_t ret;
+
+    pj_strerror(status, errmsg, sizeof(errmsg));
+    PJ_LOG(4,(stun_sock->obj_name, "Session failed because %s failed: %s",
+	      pj_stun_sock_op_name(op), errmsg));
+
+    ret = (*stun_sock->cb.on_status)(stun_sock, op, status);
+
+    return ret;
+}
+
+/* DNS resolver callback */
+static void dns_srv_resolver_cb(void *user_data,
+				pj_status_t status,
+				const pj_dns_srv_record *rec)
+{
+    pj_stun_sock *stun_sock = (pj_stun_sock*) user_data;
+
+    /* Clear query */
+    stun_sock->q = NULL;
+
+    /* Handle error */
+    if (status != PJ_SUCCESS) {
+	sess_fail(stun_sock, PJ_STUN_SOCK_DNS_OP, status);
+	return;
+    }
+
+    pj_assert(rec->count);
+    pj_assert(rec->entry[0].server.addr_count);
+
+    PJ_TODO(SUPPORT_IPV6_IN_RESOLVER);
+    pj_assert(stun_sock->af == pj_AF_INET());
+
+    /* Set the address */
+    pj_sockaddr_in_init(&stun_sock->srv_addr.ipv4, NULL,
+			rec->entry[0].port);
+    stun_sock->srv_addr.ipv4.sin_addr = rec->entry[0].server.addr[0];
+
+    /* Start sending Binding request */
+    get_mapped_addr(stun_sock);
+}
+
+
+/* Start sending STUN Binding request */
+static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock)
+{
+    pj_stun_tx_data *tdata;
+    pj_status_t status;
+
+    /* Increment request counter and create STUN Binding request */
+    ++stun_sock->tsx_id[5];
+    status = pj_stun_session_create_req(stun_sock->stun_sess,
+					PJ_STUN_BINDING_REQUEST,
+					PJ_STUN_MAGIC, 
+					(const pj_uint8_t*)stun_sock->tsx_id, 
+					&tdata);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+    
+    /* Send request */
+    status=pj_stun_session_send_msg(stun_sock->stun_sess, INTERNAL_MSG_TOKEN,
+				    PJ_FALSE, PJ_TRUE, &stun_sock->srv_addr,
+				    pj_sockaddr_get_len(&stun_sock->srv_addr),
+				    tdata);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    return PJ_SUCCESS;
+
+on_error:
+    sess_fail(stun_sock, PJ_STUN_SOCK_BINDING_OP, status);
+    return status;
+}
+
+/* Get info */
+PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock,
+					   pj_stun_sock_info *info)
+{
+    int addr_len;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(stun_sock && info, PJ_EINVAL);
+
+    /* Copy STUN server address and mapped address */
+    pj_memcpy(&info->srv_addr, &stun_sock->srv_addr,
+	      sizeof(pj_sockaddr));
+    pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr, 
+	      sizeof(pj_sockaddr));
+
+    /* Retrieve bound address */
+    addr_len = sizeof(info->bound_addr);
+    status = pj_sock_getsockname(stun_sock->sock_fd, &info->bound_addr,
+				 &addr_len);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* If socket is bound to a specific interface, then only put that
+     * interface in the alias list. Otherwise query all the interfaces 
+     * in the host.
+     */
+    if (pj_sockaddr_has_addr(&info->bound_addr)) {
+	info->alias_cnt = 1;
+	pj_sockaddr_cp(&info->aliases[0], &info->bound_addr);
+    } else {
+	unsigned i;
+
+	/* Enum all IP interfaces in the host */
+	info->alias_cnt = PJ_ARRAY_SIZE(info->aliases);
+	status = pj_enum_ip_interface(stun_sock->af, &info->alias_cnt, 
+				      info->aliases);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	/* Set the port number for each address.
+	 */
+	if (stun_sock->af == pj_AF_INET()) {
+	    for (i=0; i<info->alias_cnt; ++i) {
+		pj_sockaddr_set_port(&info->aliases[i],
+				     pj_sockaddr_get_port(&info->bound_addr));
+	    }
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* Send application data */
+PJ_DEF(pj_status_t) pj_stun_sock_sendto( pj_stun_sock *stun_sock,
+					 pj_ioqueue_op_key_t *send_key,
+					 const void *pkt,
+					 unsigned pkt_len,
+					 unsigned flag,
+					 const pj_sockaddr_t *dst_addr,
+					 unsigned addr_len)
+{
+    pj_ssize_t size;
+    PJ_ASSERT_RETURN(stun_sock && pkt && dst_addr && addr_len, PJ_EINVAL);
+    
+    if (send_key==NULL)
+	send_key = &stun_sock->send_key;
+
+    size = pkt_len;
+    return pj_activesock_sendto(stun_sock->active_sock, send_key, 
+				pkt, &size, flag, dst_addr, addr_len);
+}
+
+/* This callback is called by the STUN session to send packet */
+static pj_status_t sess_on_send_msg(pj_stun_session *sess,
+				    void *token,
+				    const void *pkt,
+				    pj_size_t pkt_size,
+				    const pj_sockaddr_t *dst_addr,
+				    unsigned addr_len)
+{
+    pj_stun_sock *stun_sock;
+    pj_ssize_t size;
+
+    stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess);
+
+    pj_assert(token==INTERNAL_MSG_TOKEN);
+    PJ_UNUSED_ARG(token);
+
+    size = pkt_size;
+    return pj_activesock_sendto(stun_sock->active_sock, 
+				&stun_sock->int_send_key,
+				pkt, &size, 0, dst_addr, addr_len);
+}
+
+/* This callback is called by the STUN session when outgoing transaction 
+ * is complete
+ */
+static void sess_on_request_complete(pj_stun_session *sess,
+				     pj_status_t status,
+				     void *token,
+				     pj_stun_tx_data *tdata,
+				     const pj_stun_msg *response,
+				     const pj_sockaddr_t *src_addr,
+				     unsigned src_addr_len)
+{
+    pj_stun_sock *stun_sock;
+    const pj_stun_sockaddr_attr *mapped_attr;
+    pj_stun_sock_op op;
+    pj_bool_t mapped_changed;
+    pj_bool_t resched = PJ_TRUE;
+
+    stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess);
+
+    PJ_UNUSED_ARG(tdata);
+    PJ_UNUSED_ARG(token);
+    PJ_UNUSED_ARG(src_addr);
+    PJ_UNUSED_ARG(src_addr_len);
+
+    /* Check if this is a keep-alive or the first Binding request */
+    if (pj_sockaddr_has_addr(&stun_sock->mapped_addr))
+	op = PJ_STUN_SOCK_KEEP_ALIVE_OP;
+    else
+	op = PJ_STUN_SOCK_BINDING_OP;
+
+    /* Handle failure */
+    if (status != PJ_SUCCESS) {
+	resched = sess_fail(stun_sock, op, status);
+	goto on_return;
+    }
+
+    /* Get XOR-MAPPED-ADDRESS, or MAPPED-ADDRESS when XOR-MAPPED-ADDRESS
+     * doesn't exist.
+     */
+    mapped_attr = (const pj_stun_sockaddr_attr*)
+		  pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR,
+					0);
+    if (mapped_attr==NULL) {
+	mapped_attr = (const pj_stun_sockaddr_attr*)
+		      pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR,
+					0);
+    }
+
+    if (mapped_attr == NULL) {
+	resched = sess_fail(stun_sock, op, PJNATH_ESTUNNOMAPPEDADDR);
+	goto on_return;
+    }
+
+    /* Determine if mapped address has changed, and save the new mapped
+     * address and call callback if so 
+     */
+    mapped_changed = !pj_sockaddr_has_addr(&stun_sock->mapped_addr) ||
+		     pj_sockaddr_cmp(&stun_sock->mapped_addr, 
+				     &mapped_attr->sockaddr) != 0;
+    if (mapped_changed) {
+	/* Print mapped adress */
+	{
+	    char addrinfo[PJ_INET6_ADDRSTRLEN+10];
+	    PJ_LOG(4,(stun_sock->obj_name, 
+		      "STUN mapped address found/changed: %s",
+		      pj_sockaddr_print(&mapped_attr->sockaddr,
+					addrinfo, sizeof(addrinfo), 3)));
+	}
+
+	pj_sockaddr_cp(&stun_sock->mapped_addr, &mapped_attr->sockaddr);
+
+	resched = (*stun_sock->cb.on_status)(stun_sock, op, PJ_SUCCESS);
+
+	goto on_return;
+    }
+
+on_return:
+    /* Start/restart keep-alive timer */
+    if (resched)
+	start_ka_timer(stun_sock);
+}
+
+/* Schedule keep-alive timer */
+static void start_ka_timer(pj_stun_sock *stun_sock)
+{
+    if (stun_sock->ka_timer.id != 0) {
+	pj_timer_heap_cancel(stun_sock->stun_cfg.timer_heap, 
+			     &stun_sock->ka_timer);
+	stun_sock->ka_timer.id = 0;
+    }
+
+    pj_assert(stun_sock->ka_interval != 0);
+    if (stun_sock->ka_interval > 0) {
+	pj_time_val delay;
+
+	delay.sec = stun_sock->ka_interval;
+	delay.msec = 0;
+
+	if (pj_timer_heap_schedule(stun_sock->stun_cfg.timer_heap, 
+				   &stun_sock->ka_timer, 
+				   &delay) == PJ_SUCCESS)
+	{
+	    stun_sock->ka_timer.id = PJ_TRUE;
+	}
+    }
+}
+
+/* Keep-alive timer callback */
+static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
+{
+    pj_stun_sock *stun_sock;
+
+    stun_sock = (pj_stun_sock *) te->user_data;
+
+    PJ_UNUSED_ARG(th);
+
+    /* Time to send STUN Binding request */
+    if (get_mapped_addr(stun_sock) != PJ_SUCCESS)
+	return;
+
+    /* Next keep-alive timer will be scheduled once the request
+     * is complete.
+     */
+}
+
+/* Callback from active socket when incoming packet is received */
+static pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
+				  void *data,
+				  pj_size_t size,
+				  const pj_sockaddr_t *src_addr,
+				  int addr_len,
+				  pj_status_t status)
+{
+    pj_stun_sock *stun_sock;
+    pj_stun_msg_hdr *hdr;
+    pj_uint16_t type;
+
+    stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock);
+
+    /* Log socket error */
+    if (status != PJ_SUCCESS) {
+	char errmsg[PJ_ERR_MSG_SIZE];
+
+	pj_strerror(status, errmsg, sizeof(errmsg));
+	PJ_LOG(4,(stun_sock->obj_name, "recvfrom() error: %s", errmsg));
+	return PJ_TRUE;
+    }
+
+    /* Check that this is STUN message */
+    status = pj_stun_msg_check((const pj_uint8_t*)data, size, 
+    			       PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET);
+    if (status != PJ_SUCCESS) {
+	/* Not STUN -- give it to application */
+	goto process_app_data;
+    }
+
+    /* Treat packet as STUN header and copy the STUN message type.
+     * We don't want to access the type directly from the header
+     * since it may not be properly aligned.
+     */
+    hdr = (pj_stun_msg_hdr*) data;
+    pj_memcpy(&type, &hdr->type, 2);
+    type = pj_ntohs(type);
+
+    /* If the packet is a STUN Binding response and part of the
+     * transaction ID matches our internal ID, then this is
+     * our internal STUN message (Binding request or keep alive).
+     * Give it to our STUN session.
+     */
+    if (!PJ_STUN_IS_RESPONSE(type) ||
+	PJ_STUN_GET_METHOD(type) != PJ_STUN_BINDING_METHOD ||
+	pj_memcmp(hdr->tsx_id, stun_sock->tsx_id, 10) != 0) 
+    {
+	/* Not STUN Binding response, or STUN transaction ID mismatch.
+	 * This is not our message too -- give it to application.
+	 */
+	goto process_app_data;
+    }
+
+    /* This is our STUN Binding response. Give it to the STUN session */
+    status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, data, size,
+				       PJ_STUN_IS_DATAGRAM, NULL, NULL,
+				       src_addr, addr_len);
+    return status!=PJNATH_ESTUNDESTROYED ? PJ_TRUE : PJ_FALSE;
+
+process_app_data:
+    if (stun_sock->cb.on_rx_data) {
+	pj_bool_t ret;
+
+	ret = (*stun_sock->cb.on_rx_data)(stun_sock, data, size,
+					  src_addr, addr_len);
+	return ret;
+    }
+
+    return PJ_TRUE;
+}
+
+/* Callback from active socket about send status */
+static pj_bool_t on_data_sent(pj_activesock_t *asock,
+			      pj_ioqueue_op_key_t *send_key,
+			      pj_ssize_t sent)
+{
+    pj_stun_sock *stun_sock;
+
+    stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock);
+
+    /* Don't report to callback if this is internal message */
+    if (send_key == &stun_sock->int_send_key) {
+	return PJ_TRUE;
+    }
+
+    /* Report to callback */
+    if (stun_sock->cb.on_data_sent) {
+	pj_bool_t ret;
+
+	/* If app gives NULL send_key in sendto() function, then give
+	 * NULL in the callback too 
+	 */
+	if (send_key == &stun_sock->send_key)
+	    send_key = NULL;
+
+	/* Call callback */
+	ret = (*stun_sock->cb.on_data_sent)(stun_sock, send_key, sent);
+
+	return ret;
+    }
+
+    return PJ_TRUE;
+}
+
Index: pjproject/trunk/pjnath/src/pjnath/stun_transaction.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_transaction.c	(revision 1987)
+++ pjproject/trunk/pjnath/src/pjnath/stun_transaction.c	(revision 1988)
@@ -32,5 +32,4 @@
 {
     char		 obj_name[PJ_MAX_OBJ_NAME];
-    pj_stun_config	*cfg;
     pj_stun_tsx_cb	 cb;
     void		*user_data;
@@ -39,7 +38,9 @@
 
     pj_bool_t		 require_retransmit;
+    unsigned		 rto_msec;
     pj_timer_entry	 retransmit_timer;
     unsigned		 transmit_count;
     pj_time_val		 retransmit_time;
+    pj_timer_heap_t	*timer_heap;
 
     pj_timer_entry	 destroy_timer;
@@ -71,5 +72,6 @@
 
     tsx = PJ_POOL_ZALLOC_T(pool, pj_stun_client_tsx);
-    tsx->cfg = cfg;
+    tsx->rto_msec = cfg->rto_msec;
+    tsx->timer_heap = cfg->timer_heap;
     pj_memcpy(&tsx->cb, cb, sizeof(*cb));
 
@@ -100,5 +102,5 @@
     /* Cancel previously registered timer */
     if (tsx->destroy_timer.id != 0) {
-	pj_timer_heap_cancel(tsx->cfg->timer_heap, &tsx->destroy_timer);
+	pj_timer_heap_cancel(tsx->timer_heap, &tsx->destroy_timer);
 	tsx->destroy_timer.id = 0;
     }
@@ -106,9 +108,9 @@
     /* Stop retransmission, just in case */
     if (tsx->retransmit_timer.id != 0) {
-	pj_timer_heap_cancel(tsx->cfg->timer_heap, &tsx->retransmit_timer);
-	tsx->retransmit_timer.id = 0;
-    }
-
-    status = pj_timer_heap_schedule(tsx->cfg->timer_heap,
+	pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
+	tsx->retransmit_timer.id = 0;
+    }
+
+    status = pj_timer_heap_schedule(tsx->timer_heap,
 				    &tsx->destroy_timer, delay);
     if (status != PJ_SUCCESS)
@@ -116,4 +118,5 @@
 
     tsx->destroy_timer.id = TIMER_ACTIVE;
+    tsx->cb.on_complete = NULL;
 
     return PJ_SUCCESS;
@@ -129,9 +132,9 @@
 
     if (tsx->retransmit_timer.id != 0) {
-	pj_timer_heap_cancel(tsx->cfg->timer_heap, &tsx->retransmit_timer);
+	pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
 	tsx->retransmit_timer.id = 0;
     }
     if (tsx->destroy_timer.id != 0) {
-	pj_timer_heap_cancel(tsx->cfg->timer_heap, &tsx->destroy_timer);
+	pj_timer_heap_cancel(tsx->timer_heap, &tsx->destroy_timer);
 	tsx->destroy_timer.id = 0;
     }
@@ -187,5 +190,5 @@
 	if (tsx->transmit_count == 0) {
 	    tsx->retransmit_time.sec = 0;
-	    tsx->retransmit_time.msec = tsx->cfg->rto_msec;
+	    tsx->retransmit_time.msec = tsx->rto_msec;
 
 	} else if (tsx->transmit_count < PJ_STUN_MAX_TRANSMIT_COUNT-1) {
@@ -206,5 +209,5 @@
 	 * cancel transmission).
 	 */;
-	status = pj_timer_heap_schedule(tsx->cfg->timer_heap, 
+	status = pj_timer_heap_schedule(tsx->timer_heap, 
 					&tsx->retransmit_timer,
 					&tsx->retransmit_time);
@@ -224,7 +227,10 @@
     /* Send message */
     status = tsx->cb.on_send_msg(tsx, tsx->last_pkt, tsx->last_pkt_size);
-    if (status != PJ_SUCCESS) {
+
+    if (status == PJNATH_ESTUNDESTROYED) {
+	/* We've been destroyed, don't access the object. */
+    } else if (status != PJ_SUCCESS) {
 	if (tsx->retransmit_timer.id != 0) {
-	    pj_timer_heap_cancel(tsx->cfg->timer_heap, 
+	    pj_timer_heap_cancel(tsx->timer_heap, 
 				 &tsx->retransmit_timer);
 	    tsx->retransmit_timer.id = 0;
@@ -280,4 +286,5 @@
 	    }
 	}
+	/* We might have been destroyed, don't try to access the object */
 	return;
     }
@@ -285,5 +292,7 @@
     tsx->retransmit_timer.id = 0;
     status = tsx_transmit_msg(tsx);
-    if (status != PJ_SUCCESS) {
+    if (status == PJNATH_ESTUNDESTROYED) {
+	/* We've been destroyed, don't try to access the object */
+    } else if (status != PJ_SUCCESS) {
 	tsx->retransmit_timer.id = 0;
 	if (!tsx->complete) {
@@ -293,4 +302,5 @@
 	    }
 	}
+	/* We might have been destroyed, don't try to access the object */
     }
 }
@@ -306,5 +316,5 @@
 
     if (tsx->retransmit_timer.id != 0) {
-	pj_timer_heap_cancel(tsx->cfg->timer_heap, &tsx->retransmit_timer);
+	pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
 	tsx->retransmit_timer.id = 0;
     }
@@ -352,5 +362,5 @@
      */
     if (tsx->retransmit_timer.id) {
-	pj_timer_heap_cancel(tsx->cfg->timer_heap, &tsx->retransmit_timer);
+	pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
 	tsx->retransmit_timer.id = 0;
     }
@@ -385,4 +395,5 @@
 	    tsx->cb.on_complete(tsx, status, msg, src_addr, src_addr_len);
 	}
+	/* We might have been destroyed, don't try to access the object */
     }
 
Index: pjproject/trunk/pjnath/src/pjnath/turn_session.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 1987)
+++ pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 1988)
@@ -30,6 +30,7 @@
 #include <pj/sock.h>
 
-#define MAX_SRV_CNT	    4
-#define REFRESH_SEC_BEFORE  60
+#define PJ_TURN_CHANNEL_MIN	    0x4000
+#define PJ_TURN_CHANNEL_MAX	    0xFFFE  /* inclusive */
+#define PJ_TURN_PEER_HTABLE_SIZE    8
 
 static const char *state_names[] = 
@@ -67,4 +68,5 @@
     pj_turn_session_cb	 cb;
     void		*user_data;
+    pj_stun_config	 stun_cfg;
 
     pj_lock_t		*lock;
@@ -72,4 +74,5 @@
 
     pj_turn_state_t	 state;
+    pj_status_t		 last_status;
     pj_bool_t		 pending_destroy;
     pj_bool_t		 destroy_notified;
@@ -88,5 +91,5 @@
 
     pj_uint16_t		 af;
-    pj_turn_tp_type	 tp_type;
+    pj_turn_tp_type	 conn_type;
     pj_uint16_t		 srv_addr_cnt;
     pj_sockaddr		*srv_addr_list;
@@ -96,4 +99,5 @@
     pj_turn_alloc_param	 alloc_param;
 
+    pj_sockaddr		 mapped_addr;
     pj_sockaddr		 relay_addr;
 
@@ -177,11 +181,11 @@
  * Create TURN client session.
  */
-PJ_DEF(pj_status_t) pj_turn_session_create( pj_stun_config *cfg,
+PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg,
 					    const char *name,
 					    int af,
-					    pj_turn_tp_type tp_type,
+					    pj_turn_tp_type conn_type,
 					    const pj_turn_session_cb *cb,
+					    unsigned options,
 					    void *user_data,
-					    unsigned options,
 					    pj_turn_session **p_sess)
 {
@@ -207,8 +211,11 @@
     sess->timer_heap = cfg->timer_heap;
     sess->af = (pj_uint16_t)af;
-    sess->tp_type = tp_type;
+    sess->conn_type = conn_type;
     sess->ka_interval = PJ_TURN_KEEP_ALIVE_SEC;
     sess->user_data = user_data;
     sess->next_ch = PJ_TURN_CHANNEL_MIN;
+
+    /* Copy STUN session */
+    pj_memcpy(&sess->stun_cfg, cfg, sizeof(pj_stun_config));
 
     /* Copy callback */
@@ -234,6 +241,6 @@
     stun_cb.on_request_complete = &stun_on_request_complete;
     stun_cb.on_rx_indication = &stun_on_rx_indication;
-    status = pj_stun_session_create(cfg, sess->obj_name, &stun_cb, PJ_FALSE,
-				    &sess->stun);
+    status = pj_stun_session_create(&sess->stun_cfg, sess->obj_name, &stun_cb,
+				    PJ_FALSE, &sess->stun);
     if (status != PJ_SUCCESS) {
 	do_destroy(sess);
@@ -334,7 +341,8 @@
 	break;
     case PJ_TURN_STATE_RESOLVING:
-	pj_assert(sess->dns_async != NULL);
-	pj_dns_resolver_cancel_query(sess->dns_async, PJ_FALSE);
-	sess->dns_async = NULL;
+	if (sess->dns_async != NULL) {
+	    pj_dns_resolver_cancel_query(sess->dns_async, PJ_FALSE);
+	    sess->dns_async = NULL;
+	}
 	break;
     case PJ_TURN_STATE_RESOLVED:
@@ -366,4 +374,6 @@
 	pj_time_val delay = {0, 0};
 
+	set_state(sess, PJ_TURN_STATE_DESTROYING);
+
 	if (sess->timer.id != TIMER_NONE) {
 	    pj_timer_heap_cancel(sess->timer_heap, &sess->timer);
@@ -371,6 +381,4 @@
 	}
 
-	set_state(sess, PJ_TURN_STATE_DESTROYING);
-
 	sess->timer.id = TIMER_DESTROY;
 	pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay);
@@ -401,4 +409,6 @@
 PJ_DEF(pj_status_t) pj_turn_session_destroy( pj_turn_session *sess)
 {
+    PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+
     set_state(sess, PJ_TURN_STATE_DEALLOCATED);
     sess_shutdown(sess, PJ_SUCCESS);
@@ -420,6 +430,7 @@
 
     info->state = sess->state;
-    info->tp_type = sess->tp_type;
+    info->conn_type = sess->conn_type;
     info->lifetime = sess->expiry.sec - now.sec;
+    info->last_status = sess->last_status;
 
     if (sess->srv_addr)
@@ -428,5 +439,8 @@
 	pj_bzero(&info->server, sizeof(info->server));
 
-    pj_memcpy(&info->relay_addr, &sess->relay_addr, sizeof(sess->relay_addr));
+    pj_memcpy(&info->mapped_addr, &sess->mapped_addr, 
+	      sizeof(sess->mapped_addr));
+    pj_memcpy(&info->relay_addr, &sess->relay_addr, 
+	      sizeof(sess->relay_addr));
 
     return PJ_SUCCESS;
@@ -451,4 +465,17 @@
 {
     return sess->user_data;
+}
+
+
+/*
+ * Configure message logging. By default all flags are enabled.
+ *
+ * @param sess		The TURN client session.
+ * @param flags		Bitmask combination of #pj_stun_sess_msg_log_flag
+ */
+PJ_DEF(void) pj_turn_session_set_log( pj_turn_session *sess,
+				      unsigned flags)
+{
+    pj_stun_session_set_log(sess->stun, flags);
 }
 
@@ -462,4 +489,6 @@
 						pj_dns_resolver *resolver)
 {
+    pj_sockaddr tmp_addr;
+    pj_bool_t is_ip_addr;
     pj_status_t status;
 
@@ -469,5 +498,11 @@
     pj_lock_acquire(sess->lock);
 
-    if (resolver) {
+    /* See if "domain" contains just IP address */
+    tmp_addr.addr.sa_family = sess->af;
+    status = pj_inet_pton(sess->af, domain, 
+			  pj_sockaddr_get_addr(&tmp_addr));
+    is_ip_addr = (status == PJ_SUCCESS);
+
+    if (!is_ip_addr && resolver) {
 	/* Resolve with DNS SRV resolution, and fallback to DNS A resolution
 	 * if default_port is specified.
@@ -476,5 +511,5 @@
 	pj_str_t res_name;
 
-	switch (sess->tp_type) {
+	switch (sess->conn_type) {
 	case PJ_TURN_TP_UDP:
 	    res_name = pj_str("_turn._udp.");
@@ -502,4 +537,10 @@
 	set_state(sess, PJ_TURN_STATE_RESOLVING);
 
+	/* User may have destroyed us in the callback */
+	if (sess->state != PJ_TURN_STATE_RESOLVING) {
+	    status = PJ_ECANCELLED;
+	    goto on_return;
+	}
+
 	status = pj_dns_srv_resolve(domain, &res_name, default_port, 
 				    sess->pool, resolver, opt, sess, 
@@ -521,5 +562,5 @@
 	sess->default_port = (pj_uint16_t)default_port;
 
-	cnt = MAX_SRV_CNT;
+	cnt = PJ_TURN_MAX_DNS_SRV_CNT;
 	ai = (pj_addrinfo*)
 	     pj_pool_calloc(sess->pool, cnt, sizeof(pj_addrinfo));
@@ -527,4 +568,11 @@
 	PJ_LOG(5,(sess->obj_name, "Resolving %.*s with DNS A",
 		  (int)domain->slen, domain->ptr));
+	set_state(sess, PJ_TURN_STATE_RESOLVING);
+
+	/* User may have destroyed us in the callback */
+	if (sess->state != PJ_TURN_STATE_RESOLVING) {
+	    status = PJ_ECANCELLED;
+	    goto on_return;
+	}
 
 	status = pj_getaddrinfo(sess->af, domain, &cnt, ai);
@@ -637,5 +685,5 @@
     /* Send request */
     set_state(sess, PJ_TURN_STATE_ALLOCATING);
-    retransmit = (sess->tp_type == PJ_TURN_TP_UDP);
+    retransmit = (sess->conn_type == PJ_TURN_TP_UDP);
     status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, 
 				      retransmit, sess->srv_addr,
@@ -682,5 +730,5 @@
 
     status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, 
-				      (sess->tp_type==PJ_TURN_TP_UDP),
+				      (sess->conn_type==PJ_TURN_TP_UDP),
 				      sess->srv_addr,
 				      pj_sockaddr_get_len(sess->srv_addr), 
@@ -834,5 +882,5 @@
      */
     status = pj_stun_session_send_msg(sess->stun, peer, PJ_FALSE, 
-				      (sess->tp_type==PJ_TURN_TP_UDP),
+				      (sess->conn_type==PJ_TURN_TP_UDP),
 				      sess->srv_addr,
 				      pj_sockaddr_get_len(sess->srv_addr),
@@ -850,10 +898,10 @@
  */
 PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
-					      const pj_uint8_t *pkt,
-					      unsigned pkt_len,
-					      pj_bool_t is_datagram)
+					      void *pkt,
+					      unsigned pkt_len)
 {
     pj_bool_t is_stun;
     pj_status_t status;
+    pj_bool_t is_datagram;
 
     /* Packet could be ChannelData or STUN message (response or
@@ -864,6 +912,8 @@
     pj_lock_acquire(sess->lock);
 
+    is_datagram = (sess->conn_type==PJ_TURN_TP_UDP);
+
     /* Quickly check if this is STUN message */
-    is_stun = ((pkt[0] & 0xC0) == 0);
+    is_stun = ((((pj_uint8_t*)pkt)[0] & 0xC0) == 0);
 
     if (is_stun) {
@@ -871,5 +921,5 @@
 	unsigned options;
 
-	options = PJ_STUN_CHECK_PACKET;
+	options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK;
 	if (is_datagram)
 	    options |= PJ_STUN_IS_DATAGRAM;
@@ -906,6 +956,6 @@
 
 	/* Notify application */
-	(*sess->cb.on_rx_data)(sess, pkt+sizeof(cd), cd.length,
-			       &peer->addr,
+	(*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)pkt)+sizeof(cd), 
+			       cd.length, &peer->addr,
 			       pj_sockaddr_get_len(&peer->addr));
 
@@ -954,4 +1004,6 @@
 			     const pj_str_t *reason)
 {
+    sess->last_status = status;
+
     do {
 	pj_str_t reason1;
@@ -1011,4 +1063,5 @@
     const pj_stun_lifetime_attr *lf_attr;
     const pj_stun_relay_addr_attr *raddr_attr;
+    const pj_stun_sockaddr_attr *mapped_attr;
     pj_str_t s;
     pj_time_val timeout;
@@ -1070,4 +1123,10 @@
 				    " address family is not supported "
 				    "for now"));
+	return;
+    }
+    if (raddr_attr && !pj_sockaddr_has_addr(&raddr_attr->sockaddr)) {
+	on_session_fail(sess, method, PJNATH_EINSTUNMSG,
+			pj_cstr(&s, "Error: Invalid IP address in "
+				    "RELAY-ADDRESS attribute"));
 	return;
     }
@@ -1092,4 +1151,12 @@
     }
 
+    /* Get mapped address */
+    mapped_attr = (const pj_stun_sockaddr_attr*)
+		  pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0);
+    if (mapped_attr) {
+	pj_memcpy(&sess->mapped_addr, &mapped_attr->sockaddr,
+		  sizeof(mapped_attr->sockaddr));
+    }
+
     /* Success */
 
@@ -1133,4 +1200,15 @@
 
     if (method == PJ_STUN_ALLOCATE_METHOD) {
+
+	/* Destroy if we have pending destroy request */
+	if (sess->pending_destroy) {
+	    if (status == PJ_SUCCESS)
+		sess->state = PJ_TURN_STATE_READY;
+	    else
+		sess->state = PJ_TURN_STATE_DEALLOCATED;
+	    sess_shutdown(sess, PJ_SUCCESS);
+	    return;
+	}
+
 	/* Handle ALLOCATE response */
 	if (status==PJ_SUCCESS && 
@@ -1299,5 +1377,5 @@
 {
     pj_turn_session *sess = (pj_turn_session*) user_data;
-    unsigned i, cnt;
+    unsigned i, cnt, tot_cnt;
 
     /* Clear async resolver */
@@ -1310,9 +1388,25 @@
     }
 
+    /* Calculate total number of server entries in the response */
+    tot_cnt = 0;
+    for (i=0; i<rec->count; ++i) {
+	tot_cnt += rec->entry[i].server.addr_count;
+    }
+
+    if (tot_cnt > PJ_TURN_MAX_DNS_SRV_CNT)
+	tot_cnt = PJ_TURN_MAX_DNS_SRV_CNT;
+
+    /* Allocate server entries */
+    sess->srv_addr_list = (pj_sockaddr*)
+		           pj_pool_calloc(sess->pool, tot_cnt, 
+					  sizeof(pj_sockaddr));
+
     /* Copy results to server entries */
-    for (i=0, cnt=0; i<rec->count && cnt<MAX_SRV_CNT; ++i) {
+    for (i=0, cnt=0; i<rec->count && cnt<PJ_TURN_MAX_DNS_SRV_CNT; ++i) {
 	unsigned j;
 
-	for (j=0; j<rec->entry[i].server.addr_count && cnt<MAX_SRV_CNT; ++j) {
+	for (j=0; j<rec->entry[i].server.addr_count && 
+		  cnt<PJ_TURN_MAX_DNS_SRV_CNT; ++j) 
+	{
 	    pj_sockaddr_in *addr = &sess->srv_addr_list[cnt].ipv4;
 
Index: pjproject/trunk/pjnath/src/pjnath/turn_sock.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/turn_sock.c	(revision 1987)
+++ pjproject/trunk/pjnath/src/pjnath/turn_sock.c	(revision 1988)
@@ -18,4 +18,5 @@
  */
 #include <pjnath/turn_sock.h>
+#include <pj/activesock.h>
 #include <pj/assert.h>
 #include <pj/errno.h>
@@ -51,9 +52,6 @@
     int			 af;
     pj_turn_tp_type	 conn_type;
-    pj_sock_t		 sock;
-    pj_ioqueue_key_t	*key;
-    pj_ioqueue_op_key_t	 read_key;
+    pj_activesock_t	*active_sock;
     pj_ioqueue_op_key_t	 send_key;
-    pj_uint8_t		 pkt[PJ_TURN_MAX_PKT_LEN];
 };
 
@@ -72,5 +70,5 @@
 				  unsigned ch_num);
 static void turn_on_rx_data(pj_turn_session *sess,
-			    const pj_uint8_t *pkt,
+			    void *pkt,
 			    unsigned pkt_len,
 			    const pj_sockaddr_t *peer_addr,
@@ -79,9 +77,13 @@
 			  pj_turn_state_t old_state,
 			  pj_turn_state_t new_state);
-static void on_read_complete(pj_ioqueue_key_t *key, 
-                             pj_ioqueue_op_key_t *op_key, 
-                             pj_ssize_t bytes_read);
-static void on_connect_complete(pj_ioqueue_key_t *key, 
-                                pj_status_t status);
+
+static pj_bool_t on_data_read(pj_activesock_t *asock,
+			      void *data,
+			      pj_size_t size,
+			      pj_status_t status,
+			      pj_size_t *remainder);
+static pj_bool_t on_connect_complete(pj_activesock_t *asock,
+				     pj_status_t status);
+
 
 
@@ -159,5 +161,5 @@
     sess_cb.on_state = &turn_on_state;
     status = pj_turn_session_create(cfg, pool->obj_name, af, conn_type,
-				    &sess_cb, turn_sock, 0, &turn_sock->sess);
+				    &sess_cb, 0, turn_sock, &turn_sock->sess);
     if (status != PJ_SUCCESS) {
 	destroy(turn_sock);
@@ -188,11 +190,7 @@
     }
 
-    if (turn_sock->key) {
-	pj_ioqueue_unregister(turn_sock->key);
-	turn_sock->key = NULL;
-	turn_sock->sock = 0;
-    } else if (turn_sock->sock) {
-	pj_sock_close(turn_sock->sock);
-	turn_sock->sock = 0;
+    if (turn_sock->active_sock) {
+	pj_activesock_close(turn_sock->active_sock);
+	turn_sock->active_sock = NULL;
     }
 
@@ -272,5 +270,6 @@
 {
     show_err(turn_sock, title, status);
-    pj_turn_session_destroy(turn_sock->sess);
+    if (turn_sock->sess)
+	pj_turn_session_destroy(turn_sock->sess);
 }
 
@@ -281,4 +280,5 @@
 					       void *user_data)
 {
+    PJ_ASSERT_RETURN(turn_sock, PJ_EINVAL);
     turn_sock->user_data = user_data;
     return PJ_SUCCESS;
@@ -290,4 +290,5 @@
 PJ_DEF(void*) pj_turn_sock_get_user_data(pj_turn_sock *turn_sock)
 {
+    PJ_ASSERT_RETURN(turn_sock, NULL);
     return turn_sock->user_data;
 }
@@ -297,5 +298,5 @@
  */
 PJ_DEF(pj_status_t) pj_turn_sock_get_info(pj_turn_sock *turn_sock,
-					 pj_turn_session_info *info)
+					  pj_turn_session_info *info)
 {
     PJ_ASSERT_RETURN(turn_sock && info, PJ_EINVAL);
@@ -310,13 +311,39 @@
 }
 
+/**
+ * Lock the TURN socket. Application may need to call this function to
+ * synchronize access to other objects to avoid deadlock.
+ */
+PJ_DEF(pj_status_t) pj_turn_sock_lock(pj_turn_sock *turn_sock)
+{
+    return pj_lock_acquire(turn_sock->lock);
+}
+
+/**
+ * Unlock the TURN socket.
+ */
+PJ_DEF(pj_status_t) pj_turn_sock_unlock(pj_turn_sock *turn_sock)
+{
+    return pj_lock_release(turn_sock->lock);
+}
+
+/*
+ * Set STUN message logging for this TURN session. 
+ */
+PJ_DEF(void) pj_turn_sock_set_log( pj_turn_sock *turn_sock,
+				   unsigned flags)
+{
+    pj_turn_session_set_log(turn_sock->sess, flags);
+}
+
 /*
  * Initialize.
  */
-PJ_DEF(pj_status_t) pj_turn_sock_init(pj_turn_sock *turn_sock,
-				      const pj_str_t *domain,
-				      int default_port,
-				      pj_dns_resolver *resolver,
-				      const pj_stun_auth_cred *cred,
-				      const pj_turn_alloc_param *param)
+PJ_DEF(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock,
+				       const pj_str_t *domain,
+				       int default_port,
+				       pj_dns_resolver *resolver,
+				       const pj_stun_auth_cred *cred,
+				       const pj_turn_alloc_param *param)
 {
     pj_status_t status;
@@ -393,14 +420,14 @@
  * Notification when outgoing TCP socket has been connected.
  */
-static void on_connect_complete(pj_ioqueue_key_t *key, 
-                                pj_status_t status)
+static pj_bool_t on_connect_complete(pj_activesock_t *asock,
+				     pj_status_t status)
 {
     pj_turn_sock *turn_sock;
 
-    turn_sock = (pj_turn_sock*) pj_ioqueue_get_user_data(key);
+    turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock);
 
     if (status != PJ_SUCCESS) {
 	sess_fail(turn_sock, "TCP connect() error", status);
-	return;
+	return PJ_FALSE;
     }
 
@@ -410,6 +437,6 @@
 
     /* Kick start pending read operation */
-    pj_ioqueue_op_key_init(&turn_sock->read_key, sizeof(turn_sock->read_key));
-    on_read_complete(turn_sock->key, &turn_sock->read_key, INIT);
+    status = pj_activesock_start_read(asock, turn_sock->pool, 
+				      PJ_TURN_MAX_PKT_LEN, 0);
 
     /* Init send_key */
@@ -420,6 +447,8 @@
     if (status != PJ_SUCCESS) {
 	sess_fail(turn_sock, "Error sending ALLOCATE", status);
-	return;
-    }
+	return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
 }
 
@@ -427,47 +456,32 @@
  * Notification from ioqueue when incoming UDP packet is received.
  */
-static void on_read_complete(pj_ioqueue_key_t *key, 
-                             pj_ioqueue_op_key_t *op_key, 
-                             pj_ssize_t bytes_read)
-{
-    enum { MAX_RETRY = 10 };
+static pj_bool_t on_data_read(pj_activesock_t *asock,
+			      void *data,
+			      pj_size_t size,
+			      pj_status_t status,
+			      pj_size_t *remainder)
+{
     pj_turn_sock *turn_sock;
-    int retry = 0;
-    pj_status_t status;
-
-    turn_sock = (pj_turn_sock*) pj_ioqueue_get_user_data(key);
+    pj_bool_t ret = PJ_TRUE;
+
+    turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock);
     pj_lock_acquire(turn_sock->lock);
 
-    do {
-	if (bytes_read == INIT) {
-	    /* Special instruction to initialize pending read() */
-	} else if (bytes_read > 0 && turn_sock->sess) {
-	    /* Report incoming packet to TURN session */
-	    pj_turn_session_on_rx_pkt(turn_sock->sess, turn_sock->pkt, 
-				      bytes_read, 
-				      turn_sock->conn_type == PJ_TURN_TP_UDP);
-	} else if (bytes_read <= 0 && turn_sock->conn_type != PJ_TURN_TP_UDP) {
-	    sess_fail(turn_sock, "TCP connection closed", -bytes_read);
-	    goto on_return;
-	}
-
-	/* Read next packet */
-	bytes_read = sizeof(turn_sock->pkt);
-	status = pj_ioqueue_recv(turn_sock->key, op_key,
-				 turn_sock->pkt, &bytes_read, 0);
-
-	if (status != PJ_EPENDING && status != PJ_SUCCESS) {
-	    char errmsg[PJ_ERR_MSG_SIZE];
-
-	    pj_strerror(status, errmsg, sizeof(errmsg));
-	    sess_fail(turn_sock, "Socket recv() error", status);
-	    goto on_return;
-	}
-
-    } while (status != PJ_EPENDING && status != PJ_ECANCELLED &&
-	     ++retry < MAX_RETRY);
+    if (status == PJ_SUCCESS && turn_sock->sess) {
+	/* Report incoming packet to TURN session */
+	PJ_TODO(REPORT_PARSED_LEN);
+	pj_turn_session_on_rx_pkt(turn_sock->sess, data,  size);
+    } else if (status != PJ_SUCCESS && 
+	       turn_sock->conn_type != PJ_TURN_TP_UDP) 
+    {
+	sess_fail(turn_sock, "TCP connection closed", status);
+	ret = PJ_FALSE;
+	goto on_return;
+    }
 
 on_return:
     pj_lock_release(turn_sock->lock);
+
+    return ret;
 }
 
@@ -483,5 +497,5 @@
 {
     pj_turn_sock *turn_sock = (pj_turn_sock*) 
-			   pj_turn_session_get_user_data(sess);
+			      pj_turn_session_get_user_data(sess);
     pj_ssize_t len = pkt_len;
     pj_status_t status;
@@ -496,6 +510,6 @@
     PJ_UNUSED_ARG(dst_addr_len);
 
-    status = pj_ioqueue_send(turn_sock->key, &turn_sock->send_key, 
-			     pkt, &len, 0);
+    status = pj_activesock_send(turn_sock->active_sock, &turn_sock->send_key,
+				pkt, &len, 0);
     if (status != PJ_SUCCESS && status != PJ_EPENDING) {
 	show_err(turn_sock, "socket send()", status);
@@ -525,5 +539,5 @@
  */
 static void turn_on_rx_data(pj_turn_session *sess,
-			    const pj_uint8_t *pkt,
+			    void *pkt,
 			    unsigned pkt_len,
 			    const pj_sockaddr_t *peer_addr,
@@ -560,5 +574,17 @@
     }
 
-    if (new_state == PJ_TURN_STATE_RESOLVED) {
+    /* Notify app first */
+    if (turn_sock->cb.on_state) {
+	(*turn_sock->cb.on_state)(turn_sock, old_state, new_state);
+    }
+
+    /* Make sure user hasn't destroyed us in the callback */
+    if (turn_sock->sess && new_state == PJ_TURN_STATE_RESOLVED) {
+	pj_turn_session_info info;
+	pj_turn_session_get_info(turn_sock->sess, &info);
+	new_state = info.state;
+    }
+
+    if (turn_sock->sess && new_state == PJ_TURN_STATE_RESOLVED) {
 	/*
 	 * Once server has been resolved, initiate outgoing TCP
@@ -568,5 +594,6 @@
 	char addrtxt[PJ_INET6_ADDRSTRLEN+8];
 	int sock_type;
-	pj_ioqueue_callback ioq_cb;
+	pj_sock_t sock;
+	pj_activesock_cb asock_cb;
 
 	/* Close existing connection, if any. This happens when
@@ -574,11 +601,7 @@
 	 * connection or ALLOCATE request failed.
 	 */
-	if (turn_sock->key) {
-	    pj_ioqueue_unregister(turn_sock->key);
-	    turn_sock->key = NULL;
-	    turn_sock->sock = 0;
-	} else if (turn_sock->sock) {
-	    pj_sock_close(turn_sock->sock);
-	    turn_sock->sock = 0;
+	if (turn_sock->active_sock) {
+	    pj_activesock_close(turn_sock->active_sock);
+	    turn_sock->active_sock = NULL;
 	}
 
@@ -592,6 +615,5 @@
 
 	/* Init socket */
-	status = pj_sock_socket(turn_sock->af, sock_type, 0, 
-				&turn_sock->sock);
+	status = pj_sock_socket(turn_sock->af, sock_type, 0, &sock);
 	if (status != PJ_SUCCESS) {
 	    pj_turn_sock_destroy(turn_sock);
@@ -599,11 +621,13 @@
 	}
 
-	/* Register to ioqeuue */
-	pj_bzero(&ioq_cb, sizeof(ioq_cb));
-	ioq_cb.on_read_complete = &on_read_complete;
-	ioq_cb.on_connect_complete = &on_connect_complete;
-	status = pj_ioqueue_register_sock(turn_sock->pool, turn_sock->cfg.ioqueue, 
-					  turn_sock->sock, turn_sock, 
-					  &ioq_cb, &turn_sock->key);
+	/* Create active socket */
+	pj_bzero(&asock_cb, sizeof(asock_cb));
+	asock_cb.on_data_read = &on_data_read;
+	asock_cb.on_connect_complete = &on_connect_complete;
+	status = pj_activesock_create(turn_sock->pool, sock,
+				      sock_type, NULL,
+				      turn_sock->cfg.ioqueue, &asock_cb, 
+				      turn_sock,
+				      &turn_sock->active_sock);
 	if (status != PJ_SUCCESS) {
 	    pj_turn_sock_destroy(turn_sock);
@@ -617,8 +641,10 @@
 
 	/* Initiate non-blocking connect */
-	status = pj_ioqueue_connect(turn_sock->key, &info.server,
-				    pj_sockaddr_get_len(&info.server));
+	status=pj_activesock_start_connect(turn_sock->active_sock, 
+					   turn_sock->pool,
+					   &info.server, 
+					   pj_sockaddr_get_len(&info.server));
 	if (status == PJ_SUCCESS) {
-	    on_connect_complete(turn_sock->key, PJ_SUCCESS);
+	    on_connect_complete(turn_sock->active_sock, PJ_SUCCESS);
 	} else if (status != PJ_EPENDING) {
 	    pj_turn_sock_destroy(turn_sock);
@@ -631,8 +657,4 @@
     }
 
-    if (turn_sock->cb.on_state) {
-	(*turn_sock->cb.on_state)(turn_sock, old_state, new_state);
-    }
-
     if (new_state >= PJ_TURN_STATE_DESTROYING && turn_sock->sess) {
 	pj_time_val delay = {0, 0};
Index: pjproject/trunk/pjnath/src/pjturn-client/client_main.c
===================================================================
--- pjproject/trunk/pjnath/src/pjturn-client/client_main.c	(revision 1987)
+++ pjproject/trunk/pjnath/src/pjturn-client/client_main.c	(revision 1988)
@@ -69,5 +69,5 @@
 static int worker_thread(void *unused);
 static void turn_on_rx_data(pj_turn_sock *relay,
-			    const pj_uint8_t *pkt,
+			    void *pkt,
 			    unsigned pkt_len,
 			    const pj_sockaddr_t *peer_addr,
@@ -275,5 +275,5 @@
 
     srv = pj_str(o.srv_addr);
-    CHECK( pj_turn_sock_init(g.relay,				 /* the relay */
+    CHECK(pj_turn_sock_alloc(g.relay,				 /* the relay */
 			    &srv,				 /* srv addr */
 			    (o.srv_port?atoi(o.srv_port):PJ_STUN_PORT),/* def port */
@@ -295,5 +295,5 @@
 
 static void turn_on_rx_data(pj_turn_sock *relay,
-			    const pj_uint8_t *pkt,
+			    void *pkt,
 			    unsigned pkt_len,
 			    const pj_sockaddr_t *peer_addr,
Index: pjproject/trunk/pjnath/src/pjturn-srv/auth.c
===================================================================
--- pjproject/trunk/pjnath/src/pjturn-srv/auth.c	(revision 1987)
+++ pjproject/trunk/pjnath/src/pjturn-srv/auth.c	(revision 1988)
@@ -36,6 +36,5 @@
     { "100", "100" },
     { "700", "700" },
-    { "701", "701" },
-    { "702", "702" }
+    { "701", "701" }
 };
 
