From 8ec5bd6b3d5bafb1d3ab11236a3adc45ac5f04d8 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Fri, 6 Jun 2008 14:47:10 +0000 Subject: Major major modifications related to ticket #485 (support for TURN-07): - Added STUN socket transport pj_stun_sock - Integration of TURN-07 to ICE - Major refactoring in ICE stream transport to make it simpler - Major modification (i.e. API change) in almost everywhere else - Much more elaborate STUN, TURN, and ICE tests in pjnath-test git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1988 74dad513-b988-da41-8d7b-12977e46ad98 --- build.symbian/pjnath.mmp | 1 + pjnath/build/Makefile | 7 +- pjnath/build/pjnath.dsp | 8 + pjnath/build/pjnath.vcproj | 4 + pjnath/build/pjnath_test.dsp | 23 + pjnath/build/wince-evc4/pjnath_wince.vcp | 842 +++++++++++++++ pjnath/docs/UML-class-diagram.dia | Bin 4576 -> 5001 bytes pjnath/docs/UML-class-diagram.png | Bin 32852 -> 126452 bytes pjnath/include/pjnath.h | 1 + pjnath/include/pjnath/config.h | 78 ++ pjnath/include/pjnath/errno.h | 9 + pjnath/include/pjnath/ice_session.h | 77 +- pjnath/include/pjnath/ice_strans.h | 695 +++++------- pjnath/include/pjnath/nat_detect.h | 1 - pjnath/include/pjnath/stun_config.h | 13 + pjnath/include/pjnath/stun_msg.h | 21 +- pjnath/include/pjnath/stun_session.h | 50 +- pjnath/include/pjnath/stun_sock.h | 403 +++++++ pjnath/include/pjnath/stun_transaction.h | 17 +- pjnath/include/pjnath/turn_session.h | 346 +++++- pjnath/include/pjnath/turn_sock.h | 230 +++- pjnath/include/pjnath/types.h | 378 ++++--- pjnath/src/pjnath-test/ice_test.c | 1096 ++++++++++++------- pjnath/src/pjnath-test/server.c | 652 ++++++++++++ pjnath/src/pjnath-test/server.h | 108 ++ pjnath/src/pjnath-test/sess_auth.c | 6 + pjnath/src/pjnath-test/stun_sock_test.c | 844 +++++++++++++++ pjnath/src/pjnath-test/test.c | 112 ++ pjnath/src/pjnath-test/test.h | 29 + pjnath/src/pjnath-test/turn_sock_test.c | 515 +++++++++ pjnath/src/pjnath/errno.c | 2 + pjnath/src/pjnath/ice_session.c | 176 +-- pjnath/src/pjnath/ice_strans.c | 1708 ++++++++++++++++-------------- pjnath/src/pjnath/stun_msg.c | 18 +- pjnath/src/pjnath/stun_session.c | 253 ++++- pjnath/src/pjnath/stun_sock.c | 829 +++++++++++++++ pjnath/src/pjnath/stun_transaction.c | 39 +- pjnath/src/pjnath/turn_session.c | 158 ++- pjnath/src/pjnath/turn_sock.c | 216 ++-- pjnath/src/pjturn-client/client_main.c | 6 +- pjnath/src/pjturn-srv/auth.c | 3 +- 41 files changed, 7833 insertions(+), 2141 deletions(-) create mode 100644 pjnath/include/pjnath/stun_sock.h create mode 100644 pjnath/src/pjnath-test/server.c create mode 100644 pjnath/src/pjnath-test/server.h create mode 100644 pjnath/src/pjnath-test/stun_sock_test.c create mode 100644 pjnath/src/pjnath-test/turn_sock_test.c create mode 100644 pjnath/src/pjnath/stun_sock.c diff --git a/build.symbian/pjnath.mmp b/build.symbian/pjnath.mmp index ecdc991e..3060c4c7 100644 --- a/build.symbian/pjnath.mmp +++ b/build.symbian/pjnath.mmp @@ -37,6 +37,7 @@ SOURCE stun_auth.c SOURCE stun_msg.c SOURCE stun_msg_dump.c SOURCE stun_session.c +SOURCE stun_sock.c SOURCE stun_transaction.c SOURCE turn_session.c SOURCE turn_sock.c diff --git a/pjnath/build/Makefile b/pjnath/build/Makefile index a80fcdad..9292ba59 100644 --- a/pjnath/build/Makefile +++ b/pjnath/build/Makefile @@ -31,15 +31,16 @@ export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJNATH_LIB)) \ export PJNATH_SRCDIR = ../src/pjnath 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) ############################################################################### # Defines for building test application # 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) export PJNATH_TEST_EXE:=../bin/pjnath-test-$(TARGET_NAME)$(HOST_EXE) diff --git a/pjnath/build/pjnath.dsp b/pjnath/build/pjnath.dsp index 615a8d81..51f001e2 100644 --- a/pjnath/build/pjnath.dsp +++ b/pjnath/build/pjnath.dsp @@ -121,6 +121,10 @@ SOURCE=..\src\pjnath\stun_session.c # End Source File # Begin Source File +SOURCE=..\src\pjnath\stun_sock.c +# End Source File +# Begin Source File + SOURCE=..\src\pjnath\stun_transaction.c # End Source File # Begin Source File @@ -177,6 +181,10 @@ SOURCE=..\include\pjnath\stun_session.h # End Source File # Begin Source File +SOURCE=..\include\pjnath\stun_sock.h +# End Source File +# Begin Source File + SOURCE=..\include\pjnath\stun_transaction.h # End Source File # Begin Source File diff --git a/pjnath/build/pjnath.vcproj b/pjnath/build/pjnath.vcproj index 9be22ce5..1fc1dbea 100644 --- a/pjnath/build/pjnath.vcproj +++ b/pjnath/build/pjnath.vcproj @@ -297,6 +297,10 @@ /> + + diff --git a/pjnath/build/pjnath_test.dsp b/pjnath/build/pjnath_test.dsp index 0c6b862f..32789682 100644 --- a/pjnath/build/pjnath_test.dsp +++ b/pjnath/build/pjnath_test.dsp @@ -88,6 +88,13 @@ LINK32=link.exe # Begin Source File 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 @@ -95,6 +102,10 @@ SOURCE="..\src\pjnath-test\main.c" # End Source File # Begin Source File +SOURCE="..\src\pjnath-test\server.c" +# End Source File +# Begin Source File + SOURCE="..\src\pjnath-test\sess_auth.c" # End Source File # Begin Source File @@ -103,14 +114,26 @@ SOURCE="..\src\pjnath-test\stun.c" # End Source File # 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 # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File +SOURCE="..\src\pjnath-test\server.h" +# End Source File +# Begin Source File + SOURCE="..\src\pjnath-test\test.h" # End Source File # End Group diff --git a/pjnath/build/wince-evc4/pjnath_wince.vcp b/pjnath/build/wince-evc4/pjnath_wince.vcp index 4088a14d..98cc9ac1 100644 --- a/pjnath/build/wince-evc4/pjnath_wince.vcp +++ b/pjnath/build/wince-evc4/pjnath_wince.vcp @@ -581,6 +581,7 @@ DEP_CPP_ERRNO=\ !ELSEIF "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4) Release" DEP_CPP_ERRNO=\ + "..\..\..\pjlib\include\pj\activesock.h"\ "..\..\..\pjlib\include\pj\addr_resolv.h"\ "..\..\..\pjlib\include\pj\array.h"\ "..\..\..\pjlib\include\pj\assert.h"\ @@ -626,6 +627,7 @@ DEP_CPP_ERRNO=\ "..\..\..\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"\ @@ -1256,6 +1258,7 @@ DEP_CPP_ICE_S=\ !ELSEIF "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4) Release" DEP_CPP_ICE_S=\ + "..\..\..\pjlib\include\pj\activesock.h"\ "..\..\..\pjlib\include\pj\addr_resolv.h"\ "..\..\..\pjlib\include\pj\array.h"\ "..\..\..\pjlib\include\pj\assert.h"\ @@ -1301,6 +1304,7 @@ DEP_CPP_ICE_S=\ "..\..\..\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"\ @@ -1985,6 +1989,7 @@ DEP_CPP_ICE_ST=\ "..\..\..\pjlib-util\include\pjlib-util\dns.h"\ "..\..\..\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"\ "..\..\..\pjlib\include\pj\assert.h"\ @@ -2031,6 +2036,7 @@ DEP_CPP_ICE_ST=\ "..\..\..\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"\ @@ -2054,7 +2060,10 @@ DEP_CPP_ICE_ST=\ "..\..\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\turn_session.h"\ + "..\..\include\pjnath\turn_sock.h"\ "..\..\include\pjnath\types.h"\ @@ -2739,6 +2748,7 @@ DEP_CPP_NAT_D=\ !ELSEIF "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4) Release" DEP_CPP_NAT_D=\ + "..\..\..\pjlib\include\pj\activesock.h"\ "..\..\..\pjlib\include\pj\addr_resolv.h"\ "..\..\..\pjlib\include\pj\array.h"\ "..\..\..\pjlib\include\pj\assert.h"\ @@ -2785,6 +2795,7 @@ DEP_CPP_NAT_D=\ "..\..\..\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"\ @@ -3469,6 +3480,7 @@ DEP_CPP_STUN_=\ "..\..\..\pjlib-util\include\pjlib-util\hmac_sha1.h"\ "..\..\..\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"\ "..\..\..\pjlib\include\pj\assert.h"\ @@ -3514,6 +3526,7 @@ DEP_CPP_STUN_=\ "..\..\..\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"\ @@ -4166,6 +4179,7 @@ DEP_CPP_STUN_M=\ "..\..\..\pjlib-util\include\pjlib-util\hmac_sha1.h"\ "..\..\..\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"\ "..\..\..\pjlib\include\pj\assert.h"\ @@ -4211,6 +4225,7 @@ DEP_CPP_STUN_M=\ "..\..\..\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"\ @@ -4847,6 +4862,7 @@ DEP_CPP_STUN_MS=\ !ELSEIF "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4) Release" DEP_CPP_STUN_MS=\ + "..\..\..\pjlib\include\pj\activesock.h"\ "..\..\..\pjlib\include\pj\addr_resolv.h"\ "..\..\..\pjlib\include\pj\array.h"\ "..\..\..\pjlib\include\pj\assert.h"\ @@ -4892,6 +4908,7 @@ DEP_CPP_STUN_MS=\ "..\..\..\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"\ @@ -5514,6 +5531,7 @@ DEP_CPP_STUN_S=\ !ELSEIF "$(CFG)" == "pjnath_wince - Win32 (WCE ARMV4) Release" DEP_CPP_STUN_S=\ + "..\..\..\pjlib\include\pj\activesock.h"\ "..\..\..\pjlib\include\pj\addr_resolv.h"\ "..\..\..\pjlib\include\pj\array.h"\ "..\..\..\pjlib\include\pj\assert.h"\ @@ -5559,6 +5577,7 @@ DEP_CPP_STUN_S=\ "..\..\..\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"\ @@ -5920,6 +5939,823 @@ DEP_CPP_STUN_S=\ "..\..\include\pjnath\types.h"\ +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\src\pjnath\stun_sock.c + +!IF "$(CFG)" == "pjnath_wince - Win32 (WCE emulator) 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 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"\ + "..\..\..\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) 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 @@ -6196,6 +7032,7 @@ DEP_CPP_STUN_T=\ !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"\ @@ -6241,6 +7078,7 @@ DEP_CPP_STUN_T=\ "..\..\..\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"\ @@ -6926,6 +7764,7 @@ DEP_CPP_TURN_=\ "..\..\..\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"\ @@ -6971,6 +7810,7 @@ DEP_CPP_TURN_=\ "..\..\..\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"\ @@ -7718,6 +8558,7 @@ DEP_CPP_TURN_S=\ "..\..\..\pjlib-util\include\pjlib-util\dns.h"\ "..\..\..\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"\ "..\..\..\pjlib\include\pj\assert.h"\ @@ -7763,6 +8604,7 @@ DEP_CPP_TURN_S=\ "..\..\..\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"\ diff --git a/pjnath/docs/UML-class-diagram.dia b/pjnath/docs/UML-class-diagram.dia index a378d7f0..ac9ffd3e 100644 Binary files a/pjnath/docs/UML-class-diagram.dia and b/pjnath/docs/UML-class-diagram.dia differ diff --git a/pjnath/docs/UML-class-diagram.png b/pjnath/docs/UML-class-diagram.png index 9c70c750..49c68519 100644 Binary files a/pjnath/docs/UML-class-diagram.png and b/pjnath/docs/UML-class-diagram.png differ diff --git a/pjnath/include/pjnath.h b/pjnath/include/pjnath.h index 962aeba0..84096beb 100644 --- a/pjnath/include/pjnath.h +++ b/pjnath/include/pjnath.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h index c84fe18c..e002a105 100644 --- a/pjnath/include/pjnath/config.h +++ b/pjnath/include/pjnath/config.h @@ -141,6 +141,84 @@ #endif +/* ************************************************************************** + * 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 */ diff --git a/pjnath/include/pjnath/errno.h b/pjnath/include/pjnath/errno.h index 56763916..3cd3320a 100644 --- a/pjnath/include/pjnath/errno.h +++ b/pjnath/include/pjnath/errno.h @@ -121,6 +121,15 @@ #define PJNATH_ESTUNINSERVER (PJNATH_ERRNO_START+42) /* 370042 */ +/************************************************************ + * STUN SESSION/TRANSPORT ERROR CODES + ***********************************************************/ +/** + * @hideinitializer + * STUN object has been destoyed. + */ +#define PJNATH_ESTUNDESTROYED (PJNATH_ERRNO_START+60) /* 370060 */ + /************************************************************ * ICE ERROR CODES diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h index 2af223f3..87b723af 100644 --- a/pjnath/include/pjnath/ice_session.h +++ b/pjnath/include/pjnath/ice_session.h @@ -128,6 +128,11 @@ PJ_BEGIN_DECL * application via \a on_rx_data callback. */ +/** + * Forward declaration for checklist. + */ +typedef struct pj_ice_sess_checklist pj_ice_sess_checklist; + /** * This enumeration describes the type of an ICE candidate. */ @@ -193,6 +198,24 @@ typedef struct pj_ice_sess_comp } 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; + + /** * This structure describes an ICE candidate. * ICE candidate is a transport address that is to be tested by ICE @@ -203,17 +226,35 @@ typedef struct pj_ice_sess_comp */ typedef struct pj_ice_sess_cand { + /** + * 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; + pj_uint8_t comp_id; /** - * The candidate type, as described in #pj_ice_cand_type enumeration. + * Transport ID to be used to send packets for this candidate. */ - pj_ice_cand_type type; + pj_uint8_t transport_id; + + /** + * Local preference value, which typically is 65535. + */ + pj_uint16_t local_pref; /** * The foundation string, which is an identifier which value will be @@ -383,7 +424,7 @@ typedef enum pj_ice_sess_checklist_state * This structure represents ICE check list, that is an ordered set of * candidate pairs that an agent will use to generate checks. */ -typedef struct pj_ice_sess_checklist +struct pj_ice_sess_checklist { /** * The checklist state. @@ -405,7 +446,7 @@ typedef struct pj_ice_sess_checklist */ pj_timer_entry timer; -} pj_ice_sess_checklist; +}; /** @@ -430,12 +471,14 @@ typedef struct pj_ice_sess_cb * * @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. * @param dst_addr Packet destination address. * @param dst_addr_len Length of destination address. */ 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, unsigned dst_addr_len); @@ -446,6 +489,7 @@ typedef struct pj_ice_sess_cb * * @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. * @param src_addr Source address where this packet was received @@ -453,6 +497,7 @@ typedef struct pj_ice_sess_cb * @param src_addr_len The length of source address. */ 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, unsigned src_addr_len); @@ -496,6 +541,7 @@ typedef struct pj_ice_rx_check PJ_DECL_LIST_MEMBER(struct pj_ice_rx_check); unsigned comp_id; /**< Component ID. */ + unsigned transport_id; /**< Transport ID. */ pj_sockaddr src_addr; /**< Source address of request */ unsigned src_addr_len; /**< Length of src address. */ @@ -553,6 +599,9 @@ struct pj_ice_sess 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 */ pj_ice_rx_check early_check; /**< Early checks. */ @@ -581,6 +630,17 @@ struct pj_ice_sess 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); + + /** * This is a utility function to calculate the foundation identification * for a candidate. @@ -685,6 +745,8 @@ PJ_DECL(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice, * * @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 * normally should be set to 65535. @@ -699,6 +761,7 @@ PJ_DECL(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice, */ 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, const pj_str_t *foundation, @@ -797,6 +860,9 @@ PJ_DECL(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice, * * @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. * @param src_addr Source address of the packet. @@ -806,6 +872,7 @@ PJ_DECL(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice, */ 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, const pj_sockaddr_t *src_addr, diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h index 09fedbad..8097b382 100644 --- a/pjnath/include/pjnath/ice_strans.h +++ b/pjnath/include/pjnath/ice_strans.h @@ -25,6 +25,8 @@ * @brief ICE Stream Transport */ #include +#include +#include #include #include #include @@ -44,137 +46,28 @@ PJ_BEGIN_DECL * library. * * 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 * \ref PJNATH_ICE_SESSION for performing connectivity checks among the. * 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; + /** * This structure contains callbacks that will be called by the * ICE stream transport. @@ -200,195 +93,200 @@ typedef struct pj_ice_strans_cb unsigned src_addr_len); /** - * 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. - * - * @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. + * This structure describes ICE stream transport configuration. Application + * should initialize the structure by calling #pj_ice_strans_cfg_default() + * before changing the settings. */ -enum pj_ice_strans_option +typedef struct pj_ice_strans_cfg { /** - * 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. + * Address family, IPv4 or IPv6. Currently only pj_AF_INET() (IPv4) + * is supported, and this is the default value. */ - PJ_ICE_ST_OPT_DONT_ADD_CAND = 1, + int af; /** - * 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. + * STUN configuration which contains the timer heap and + * ioqueue instance to be used, and STUN retransmission + * settings. This setting is mandatory. * - * 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. + * The default value is all zero. Application must initialize + * this setting with #pj_stun_config_init(). */ - PJ_ICE_ST_OPT_NO_PORT_RETRY = 8, -}; + pj_stun_config stun_cfg; - -/** - * 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. + * DNS resolver to be used to resolve servers. If DNS SRV + * resolution is required, the resolver must be set. * - * 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. + * The default value is NULL. */ - pj_status_t status; + pj_dns_resolver *resolver; /** - * The candidate transport address. + * STUN and local transport settings. This specifies the + * settings for local UDP socket, which will be resolved + * to get the STUN mapped address. */ - pj_sockaddr addr; + 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; /** - * 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. + * TURN specific settings. */ - int ice_cand_id; + 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; - /** - * 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. +/** + * Initialize ICE transport configuration with default values. + * + * @param cfg The configuration to be initialized. */ -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; +PJ_DECL(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg); /** - * This structure represents the ICE stream transport. + * Copy configuration. + * + * @param pool Pool. + * @param dst Destination. + * @param src Source. */ -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. */ -}; +PJ_DECL(void) pj_ice_strans_cfg_copy(pj_pool_t *pool, + pj_ice_strans_cfg *dst, + const pj_ice_strans_cfg *src); /** - * 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. + * Create and initialize the ICE stream transport with the specified + * parameters. * - * @param stun_cfg The STUN settings. * @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 * ICE stream transport. @@ -399,8 +297,8 @@ struct pj_ice_strans * @return PJ_SUCCESS if ICE stream transport is created * 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, const pj_ice_strans_cb *cb, @@ -419,133 +317,24 @@ PJ_DECL(pj_status_t) pj_ice_strans_destroy(pj_ice_strans *ice_st); /** - * 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. + * Get the user data associated with the ICE stream transport. * * @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. + * @return The user data. */ -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); +PJ_DECL(void*) pj_ice_strans_get_user_data(pj_ice_strans *ice_st); -/** - * 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); /** * 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. * @param role ICE role. @@ -560,10 +349,10 @@ PJ_DECL(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, const pj_str_t *local_passwd); /** - * Enumerate the local candidates. This function can only be called - * after the ICE session has been created in 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 * the number of candidates copied to the @@ -573,38 +362,113 @@ PJ_DECL(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, * @return PJ_SUCCESS, or the appropriate error code. */ 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. + * after the ICE session has been created in the ICE stream transport + * with #pj_ice_strans_init_ice(). * - * 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. + * 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. * @param rem_ufrag Remote ufrag, as seen in the SDP received from * the remote agent. * @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[]); +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. + * 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. * @@ -614,11 +478,16 @@ PJ_DECL(pj_status_t) pj_ice_strans_stop_ice(pj_ice_strans *ice_st); /** - * 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. * @param comp_id Component ID. diff --git a/pjnath/include/pjnath/nat_detect.h b/pjnath/include/pjnath/nat_detect.h index a4bc5fec..53fcceec 100644 --- a/pjnath/include/pjnath/nat_detect.h +++ b/pjnath/include/pjnath/nat_detect.h @@ -32,7 +32,6 @@ PJ_BEGIN_DECL /** * @defgroup PJNATH_NAT_DETECT NAT Classification/Detection Tool * @brief NAT Classification/Detection Tool - * @ingroup PJNATH_ICE * @{ * * This module provides one function to perform NAT classification and diff --git a/pjnath/include/pjnath/stun_config.h b/pjnath/include/pjnath/stun_config.h index d724ed9c..eba1fce0 100644 --- a/pjnath/include/pjnath/stun_config.h +++ b/pjnath/include/pjnath/stun_config.h @@ -25,6 +25,8 @@ */ #include +#include +#include #include @@ -102,6 +104,17 @@ PJ_INLINE(void) pj_stun_config_init(pj_stun_config *cfg, } +/** + * 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; +} + + /** * @} */ diff --git a/pjnath/include/pjnath/stun_msg.h b/pjnath/include/pjnath/stun_msg.h index 7c3805ea..e293f6fe 100644 --- a/pjnath/include/pjnath/stun_msg.h +++ b/pjnath/include/pjnath/stun_msg.h @@ -34,8 +34,8 @@ PJ_BEGIN_DECL /* **************************************************************************/ /** * @defgroup PJNATH_STUN_MSG STUN Message Representation and Parsing - * @brief Low-level representation and parsing of STUN messages. * @ingroup PJNATH_STUN + * @brief Low-level representation and parsing of STUN messages. * @{ */ @@ -955,9 +955,8 @@ typedef struct pj_stun_uint_attr pj_stun_req_addr_type; /** * 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 * the relayed transport address that is allocated by the server. The @@ -1164,7 +1163,7 @@ enum pj_stun_decode_options * Disable FINGERPRINT verification. This option can be used when calling * #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 }; @@ -1292,7 +1291,7 @@ PJ_DECL(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg, * value, if these attributes are present in the message. * * 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 * the parameter. The key should be set to the password if short term @@ -1320,10 +1319,10 @@ PJ_DECL(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg, */ 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); /** * Check that the PDU is potentially a valid STUN message. This function @@ -1345,7 +1344,7 @@ PJ_DECL(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, * message. */ 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); /** @@ -1371,10 +1370,10 @@ PJ_DECL(pj_status_t) pj_stun_msg_check(const pj_uint8_t *pdu, */ 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); /** diff --git a/pjnath/include/pjnath/stun_session.h b/pjnath/include/pjnath/stun_session.h index 821e765b..896a2b58 100644 --- a/pjnath/include/pjnath/stun_session.h +++ b/pjnath/include/pjnath/stun_session.h @@ -213,6 +213,7 @@ struct pj_stun_rx_data */ struct pj_stun_tx_data { + /** PJLIB list interface */ PJ_DECL_LIST_MEMBER(struct pj_stun_tx_data); pj_pool_t *pool; /**< Pool. */ @@ -239,6 +240,21 @@ struct pj_stun_tx_data }; +/** + * 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; + + /** * Create a STUN session. * @@ -258,11 +274,16 @@ PJ_DECL(pj_status_t) pj_stun_session_create(pj_stun_config *cfg, pj_stun_session **p_sess); /** - * Destroy the STUN session. + * 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); @@ -333,6 +354,14 @@ PJ_DECL(pj_status_t) pj_stun_session_set_server_name(pj_stun_session *sess, PJ_DECL(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess, 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); /** * Create a STUN request message. After the message has been successfully @@ -381,7 +410,7 @@ PJ_DECL(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess, * call. * * @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. * This argument MUST be zero if successful response is @@ -432,6 +461,9 @@ PJ_DECL(pj_status_t) pj_stun_session_create_res(pj_stun_session *sess, * be sent. * * @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, void *token, @@ -449,7 +481,7 @@ PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess, * * @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 * to be created. @@ -472,6 +504,9 @@ PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess, * @param addr_len Address length. * * @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, const pj_stun_rx_data *rdata, @@ -496,6 +531,9 @@ PJ_DECL(pj_status_t) pj_stun_session_respond(pj_stun_session *sess, * callback. This error status MUST NOT be PJ_SUCCESS. * * @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, pj_stun_tx_data *tdata, @@ -511,6 +549,9 @@ PJ_DECL(pj_status_t) pj_stun_session_cancel_req(pj_stun_session *sess, * @param tdata The request message previously sent. * * @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, pj_stun_tx_data *tdata); @@ -548,6 +589,9 @@ PJ_DECL(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess, * @param src_addr_len Length of the source address. * * @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, const void *packet, diff --git a/pjnath/include/pjnath/stun_sock.h b/pjnath/include/pjnath/stun_sock.h new file mode 100644 index 00000000..a5ac7f69 --- /dev/null +++ b/pjnath/include/pjnath/stun_sock.h @@ -0,0 +1,403 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2007 Benny Prijono + * + * 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 +#include +#include +#include + + +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__ */ + diff --git a/pjnath/include/pjnath/stun_transaction.h b/pjnath/include/pjnath/stun_transaction.h index c17bc359..842c8145 100644 --- a/pjnath/include/pjnath/stun_transaction.h +++ b/pjnath/include/pjnath/stun_transaction.h @@ -89,7 +89,9 @@ typedef struct pj_stun_tsx_cb * @param pkt_size Size of the STUN packet. * * @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, const void *stun_pkt, @@ -161,7 +163,8 @@ pj_stun_client_tsx_schedule_destroy(pj_stun_client_tsx *tsx, * * @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); @@ -214,7 +217,10 @@ PJ_DECL(void*) pj_stun_client_tsx_get_data(pj_stun_client_tsx *tsx); * @param pkt The STUN packet to send. * @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, pj_bool_t retransmit, @@ -228,7 +234,10 @@ PJ_DECL(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx, * * @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); diff --git a/pjnath/include/pjnath/turn_session.h b/pjnath/include/pjnath/turn_session.h index 2c10387b..73be679c 100644 --- a/pjnath/include/pjnath/turn_session.h +++ b/pjnath/include/pjnath/turn_session.h @@ -29,12 +29,25 @@ 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. */ /** @@ -43,18 +56,6 @@ PJ_BEGIN_DECL 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 - - /** * TURN transport types, which will be used both to specify the connection * type for reaching TURN server and the type of allocation transport to be @@ -133,14 +134,21 @@ typedef enum pj_turn_state_t } pj_turn_state_t; -/* 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() + /** * Callback to receive events from TURN session. @@ -151,18 +159,33 @@ typedef struct pj_turn_session_cb * This callback will be called by the TURN session whenever it * 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, const pj_uint8_t *pkt, unsigned pkt_len, const pj_sockaddr_t *dst_addr, - unsigned dst_addr_len); + unsigned addr_len); /** * Notification when peer address has been bound successfully to * 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, const pj_sockaddr_t *peer_addr, @@ -173,10 +196,16 @@ typedef struct pj_turn_session_cb * Notification when incoming data has been received, either through * 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, unsigned addr_len); @@ -185,26 +214,50 @@ typedef struct pj_turn_session_cb * Notification when TURN session state has changed. Application should * implement this callback at least to know that the TURN session is * going to be destroyed. + * + * @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, + void (*on_state)(pj_turn_session *sess, + pj_turn_state_t old_state, pj_turn_state_t new_state); } pj_turn_session_cb; /** - * 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 { @@ -214,20 +267,30 @@ typedef struct pj_turn_session_info pj_turn_state_t state; /** - * Type of connection to the TURN server. + * Last error (if session was terminated because of error) */ - pj_turn_tp_type tp_type; + pj_status_t last_status; /** - * The relay address + * Type of connection to the TURN server. */ - pj_sockaddr relay_addr; + 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; + + /** + * The relay address + */ + pj_sockaddr relay_addr; + /** * Current seconds before allocation expires. */ @@ -237,67 +300,173 @@ typedef struct pj_turn_session_info /** - * 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, pj_turn_alloc_param *dst, const pj_turn_alloc_param *src); /** - * 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); /** - * Create TURN client session. + * 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(pj_stun_config *cfg, +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, - void *user_data, unsigned options, + void *user_data, 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); /** - * 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); /** - * 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, pj_turn_session_info *info); /** - * 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, void *user_data); /** - * 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); + +/** + * 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. + * 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, const pj_str_t *domain, @@ -306,43 +475,120 @@ PJ_DECL(pj_status_t) pj_turn_session_set_server(pj_turn_session *sess, /** - * 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, const pj_stun_auth_cred *cred); /** - * 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, const pj_turn_alloc_param *param); /** - * 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, const pj_sockaddr_t *peer, unsigned addr_len); /** - * 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); /** diff --git a/pjnath/include/pjnath/turn_sock.h b/pjnath/include/pjnath/turn_sock.h index 91bf9e20..875f6313 100644 --- a/pjnath/include/pjnath/turn_sock.h +++ b/pjnath/include/pjnath/turn_sock.h @@ -16,8 +16,8 @@ * 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_turn_sock_H__ -#define __PJNATH_turn_sock_H__ +#ifndef __PJNATH_TURN_SOCK_H__ +#define __PJNATH_TURN_SOCK_H__ /** * @file turn_sock.h @@ -31,46 +31,87 @@ PJ_BEGIN_DECL /* **************************************************************************/ /** - * @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, unsigned addr_len); /** * 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); } pj_turn_sock_cb; /** - * 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, int af, @@ -81,53 +122,174 @@ PJ_DECL(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg, pj_turn_sock **p_turn_sock); /** - * 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); + void *user_data); /** - * Get 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); /** - * 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); + 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); + /** - * Initialize. + * 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(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_DECL(void) pj_turn_sock_set_log(pj_turn_sock *turn_sock, + unsigned flags); /** - * Send packet. + * 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); /** @@ -138,5 +300,5 @@ PJ_DECL(pj_status_t) pj_turn_sock_bind_channel(pj_turn_sock *turn_sock, PJ_END_DECL -#endif /* __PJNATH_turn_sock_H__ */ +#endif /* __PJNATH_TURN_SOCK_H__ */ diff --git a/pjnath/include/pjnath/types.h b/pjnath/include/pjnath/types.h index 88872dcb..66ab4cdd 100644 --- a/pjnath/include/pjnath/types.h +++ b/pjnath/include/pjnath/types.h @@ -34,6 +34,13 @@ PJ_BEGIN_DECL +/** + * This constant describes a number to be used to identify an invalid TURN + * channel number. + */ +#define PJ_TURN_INVALID_CHANNEL 0xFFFF + + /** * Initialize pjnath library. * @@ -43,7 +50,7 @@ PJ_DECL(pj_status_t) pjnath_init(void); /** - * Display error to the log. + * Display error to the log. * * @param sender The sender name. * @param title Title message. @@ -67,167 +74,216 @@ PJ_END_DECL /* Doxygen documentation below: */ /** - * @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 - * STUN-bis specification and the TURN 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 pjstun-client - * project under pjnath/src directory. - * - * For a sample STUN and TURN server, please see pjstun-srv-test - * project under pjnath/src directory. - * - * - * \subsection PJNATH_STUN_REF STUN Reference - * - * References for STUN: - * - * - - * draft-ietf-behave-rfc3489bis-15: Session Traversal - * Utilities for (NAT) (STUN), - * - - * draft-ietf-behave-turn-07: Obtaining Relay Addresses - * from Simple Traversal Underneath NAT (STUN) - * - Obsoleted: RFC 3489. - * - * \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 - * - * draft-ietf-mmusic-ice-19.txt 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: - * - - * draft-ietf-mmusic-ice-19.txt: 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: +- + draft-ietf-behave-rfc3489bis-15: 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: + - + draft-ietf-behave-turn-07: 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: + - + draft-ietf-mmusic-ice-19.txt draft. The PJNATH ICE + + +\subsection comp_natck NAT Classification Utility + +The PJNATH library also provides NAT classification utility as +described in RFC 3489. +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 pjnath/src directory: + + - pjturn-client: 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. + + - pjturn-srv: this is a simple TURN server to be used for testing + purposes. It supports both UDP and TCP connections to the clients. + + +*/ /** * @defgroup PJNATH_STUN STUN Library diff --git a/pjnath/src/pjnath-test/ice_test.c b/pjnath/src/pjnath-test/ice_test.c index 38bb6d02..4f644c1d 100644 --- a/pjnath/src/pjnath-test/ice_test.c +++ b/pjnath/src/pjnath-test/ice_test.c @@ -17,506 +17,858 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "test.h" +#include "server.h" -#define THIS_FILE "ice_test.c" +enum +{ + NO = 0, + YES = 1, + SRV = 3, +}; + +#define NODELAY 0xFFFFFFFF +#define SRV_DOMAIN "pjsip.lab.domain" +#define INDENT " " -struct ice_data +/* Client flags */ +enum { - 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]; + WRONG_TURN = 1, + DEL_ON_ERR = 2, }; -static pj_stun_config stun_cfg; -static void on_ice_complete(pj_ice_strans *icest, - pj_status_t status) +/* Test results */ +struct test_result { - 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"))); -} + pj_status_t init_status; /* init successful? */ + pj_status_t nego_status; /* negotiation successful? */ + unsigned rx_cnt[4]; /* Number of data received */ +}; -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) +/* Test session configuration */ +struct test_cfg { - 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"); - } + 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 */ +}; - PJ_UNUSED_ARG(src_addr); - PJ_UNUSED_ARG(src_addr_len); -} +/* 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 */ +}; -static void handle_events(unsigned msec_timeout) +/* The test session */ +struct test_sess { - pj_time_val delay; + pj_pool_t *pool; + pj_stun_config *stun_cfg; + pj_dns_resolver *resolver; - pj_timer_heap_poll(stun_cfg.timer_heap, NULL); + test_server *server; - delay.sec = 0; - delay.msec = msec_timeout; - pj_time_val_normalize(&delay); - - pj_ioqueue_poll(stun_cfg.ioqueue, &delay); -} + unsigned server_flag; + struct ice_ept caller; + struct ice_ept callee; +}; -/* Basic create and destroy test */ -static int ice_basic_create_destroy_test() +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 *im; - pj_ice_strans_cb icest_cb; + 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; - PJ_LOG(3,(THIS_FILE, "...basic create/destroy")); + 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; + } - pj_bzero(&icest_cb, sizeof(icest_cb)); - icest_cb.on_ice_complete = &on_ice_complete; - icest_cb.on_rx_data = &on_rx_data; + 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; + } - status = pj_ice_strans_create(&stun_cfg, "icetest", 2, NULL, &icest_cb, &im); - if (status != PJ_SUCCESS) - return -10; - pj_ice_strans_destroy(im); + 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); + } - return 0; -} + /* 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); -static pj_status_t start_ice(pj_ice_strans *ist, pj_ice_strans *remote) + /* Looks alright */ + *p_ice = ice; + return PJ_SUCCESS; +} + +/* 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) { - unsigned count; - pj_ice_sess_cand cand[PJ_ICE_MAX_CAND]; + pj_pool_t *pool; + struct test_sess *sess; + pj_str_t ns_ip; + pj_uint16_t ns_port; + unsigned flags; 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); -} + /* 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; -struct dummy_cand -{ - unsigned comp_id; - pj_ice_cand_type type; - const char *addr; - unsigned port; -}; + pj_memcpy(&sess->callee.cfg, callee_cfg, sizeof(*callee_cfg)); + sess->callee.result.init_status = sess->callee.result.nego_status = PJ_EPENDING; -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 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 components */ - for (i=0; icomp_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; + /* 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; } - /* Add dummy candidates */ - for (i=0; iresolver, 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; + } - 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; + /* 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; } - /* Add the real candidate */ - if (add_valid_comp) { - for (i=0; icomp_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; - } + /* 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; } - -/* 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) +/* Destroy test session */ +static void destroy_sess(struct test_sess *sess, unsigned wait_msec) { - 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; + if (sess->caller.ice) { + pj_ice_strans_destroy(sess->caller.ice); + sess->caller.ice = NULL; } - /* Get default remote candidate of agent 1 */ - rcand = agent1->ice->comp[comp_id-1].valid_check->rcand; + if (sess->callee.ice) { + pj_ice_strans_destroy(sess->callee.ice); + sess->callee.ice = NULL; + } - /* 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; + poll_events(sess->stun_cfg, wait_msec, PJ_FALSE); - lcand = &agent2->ice->lcand[lcand_id]; + if (sess->resolver) { + pj_dns_resolver_destroy(sess->resolver, PJ_FALSE); + sess->resolver = NULL; + } - 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; + if (sess->server) { + destroy_test_server(sess->server); + sess->server = NULL; } - return 0; + if (sess->pool) { + pj_pool_t *pool = sess->pool; + sess->pool = NULL; + pj_pool_release(pool); + } } - -/* 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[]) +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) { - pj_ice_strans *im1, *im2; - pj_ice_strans_cb icest_cb; - struct ice_data *id1, *id2; - pj_timestamp t_start, t_end; - unsigned i; - pj_str_t data_from_offerer, data_from_answerer; - pj_status_t status; + struct ice_ept *ept; -#define CHECK_COMPLETE() if (id1->complete && id2->complete) { \ - if (t_end.u32.lo==0) pj_get_timestamp(&t_end); \ - } else {} + PJ_UNUSED_ARG(pkt); + PJ_UNUSED_ARG(size); + PJ_UNUSED_ARG(src_addr); + PJ_UNUSED_ARG(src_addr_len); - PJ_LOG(3,(THIS_FILE, "...%s", title)); + ept = (struct ice_ept*) pj_ice_strans_get_user_data(ice_st); + ept->result.rx_cnt[comp_id]++; +} - 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; +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"); + } +} + - /* Create first ICE */ - status = pj_ice_strans_create(&stun_cfg, "offerer", comp_cnt, NULL, &icest_cb, &im1); - if (status != PJ_SUCCESS) - return -20; +/* 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; - id1 = PJ_POOL_ZALLOC_T(im1->pool, struct ice_data); - id1->obj_name = "offerer"; - im1->user_data = id1; + /* Enum remote candidates */ + for (i=0; icfg.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; + } - /* Init components */ - status = init_ice_st(im1, add_valid_comp, ocand_cnt, ocand); - if (status != 0) + 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; + } - /* Create second ICE */ - status = pj_ice_strans_create(&stun_cfg, "answerer", comp_cnt, NULL, &icest_cb, &im2); - if (status != PJ_SUCCESS) - return -25; + return PJ_SUCCESS; +} - 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; +/* 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; iice, 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; + } - /* Init ICE on im2 */ - status = pj_ice_strans_init_ice(im2, PJ_ICE_SESS_ROLE_CONTROLLED, NULL, NULL); - if (status != PJ_SUCCESS) - return -29; + 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; + } - /* Start ICE on im2 */ - status = start_ice(im2, im1); - if (status != PJ_SUCCESS) { - app_perror(" error starting ICE", status); - return -30; + 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; + } } - /* Start ICE on im1 */ - status = start_ice(im1, im2); - if (status != PJ_SUCCESS) - return -35; + /* Extra components must not have valid pair */ + for (; icfg.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; + } + } - /* Apply delay to let other checks commence */ - pj_thread_sleep(40); + return 0; +} - /* 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; +#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; - handle_events(1); + PJ_LOG(3,("", INDENT "%s", title)); - CHECK_COMPLETE(); + capture_pjlib_state(stun_cfg, &pjlib_state); - pj_get_timestamp(&t_now); - if (pj_elapsed_msec(&t_start, &t_now) >= wait_before_send) - break; - } + rc = create_sess(stun_cfg, server_flag, caller_cfg, callee_cfg, &sess); + if (rc != 0) + return rc; - /* 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; +#define ALL_READY (sess->caller.result.init_status!=PJ_EPENDING && \ + sess->callee.result.init_status!=PJ_EPENDING) - 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; - } + /* Wait until both ICE transports are initialized */ + WAIT_UNTIL(30, ALL_READY, rc); - /* 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; - } + if (!ALL_READY) { + PJ_LOG(3,("", INDENT "err: init timed-out")); + destroy_sess(sess, 500); + return -100; } - /* Just wait until both completes, or timed out */ - while (!id1->complete || !id2->complete) { - pj_timestamp t_now; + 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; + } - handle_events(1); + /* Failure condition */ + if (sess->caller.result.init_status != PJ_SUCCESS || + sess->callee.result.init_status != PJ_SUCCESS) + { + rc = 0; + goto on_return; + } - 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; - } + /* 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; } - /* Mark end-time */ - CHECK_COMPLETE(); + /* 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; + } - /* 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; + /* Start ICE on callee */ + rc = start_ice(&sess->callee, &sess->caller); + if (rc != PJ_SUCCESS) { + destroy_sess(sess, 500); + return -120; } - /* Check status */ - if (id1->err_code != PJ_SUCCESS) - return -53; - if (id2->err_code != PJ_SUCCESS) - return -56; + /* Wait for callee's answer_delay */ + poll_events(stun_cfg, sess->callee.cfg.answer_delay, PJ_FALSE); - /* Verify that offerer gets answerer's transport address */ - for (i=0; icaller, &sess->callee); + if (rc != PJ_SUCCESS) { + destroy_sess(sess, 500); + return -130; } - /* And the other way around */ - for (i=0; icaller.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; } - /* 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 (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 (pj_strcmp2(&data_from_answerer, id1->last_rx_rtp_data) != 0) { - PJ_LOG(3,(THIS_FILE, "....error: data mismatch in offerer")); - return -82; + + 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; } - /* And the same in answerer */ - if (id2->rx_rtp_cnt != 1) { - PJ_LOG(3,(THIS_FILE, "....error: data not received in answerer")); - return -84; + /* 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; } - if (pj_strcmp2(&data_from_offerer, id2->last_rx_rtp_data) != 0) { - PJ_LOG(3,(THIS_FILE, "....error: data mismatch in answerer")); - return -82; + rc = check_pair(&sess->callee, &sess->caller, -180); + if (rc != 0) { + destroy_sess(sess, 500); + return rc; } + /* Looks like everything is okay */ -on_return: + /* 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; + } - /* Done */ - PJ_LOG(3,(THIS_FILE, "....success: ICE completed in %d msec, waiting..", - pj_elapsed_msec(&t_start, &t_end))); +on_return: + /* Wait.. */ + poll_events(stun_cfg, 500, PJ_FALSE); - /* Wait for some more time */ - for (;;) { - pj_timestamp t_now; + /* Now destroy everything */ + destroy_sess(sess, 500); - pj_get_timestamp(&t_now); - if (pj_elapsed_msec(&t_start, &t_now) > max_total_time) - break; + /* Flush events */ + poll_events(stun_cfg, 100, PJ_FALSE); - handle_events(1); + rc = check_pjlib_state(stun_cfg, &pjlib_state); + if (rc != 0) { + return rc; } - - pj_ice_strans_destroy(im1); - pj_ice_strans_destroy(im2); - handle_events(100); return 0; } +#define ROLE1 PJ_ICE_SESS_ROLE_CONTROLLED +#define ROLE2 PJ_ICE_SESS_ROLE_CONTROLLING 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[] = + pj_stun_config stun_cfg; + unsigned i; + int rc; + struct sess_cfg_t { + const char *title; + unsigned server_flag; + struct test_cfg ua1; + struct test_cfg ua2; + } sess_cfg[] = { - {1, PJ_ICE_CAND_TYPE_SRFLX, "127.2.2.2", 65534 }, - {2, PJ_ICE_CAND_TYPE_SRFLX, "127.2.2.2", 65535 }, + /* 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, 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; + 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; + } - /* 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; + /* 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; + } - /* 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; + /* 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; + } - 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; + } - /* Direct communication with mismatch number of components */ + /* 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; + } - /* 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; + /* 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; ititle)); + + /* For each test item, test with various answer delay */ + for (d=0; dua1.answer_delay = delay[d]; + cfg->ua2.answer_delay = delay[d]; + + /* For each test item, test with role conflict scenarios */ + for (j=0; jua1.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: - pj_log_set_level(3); - pj_ioqueue_destroy(stun_cfg.ioqueue); + destroy_stun_config(&stun_cfg); pj_pool_release(pool); return rc; } diff --git a/pjnath/src/pjnath-test/server.c b/pjnath/src/pjnath-test/server.c new file mode 100644 index 00000000..6d4f2e8b --- /dev/null +++ b/pjnath/src/pjnath-test/server.c @@ -0,0 +1,652 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2007 Benny Prijono + * + * 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; iturn_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; iturn_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; jperm_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; iperm_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; +} + diff --git a/pjnath/src/pjnath-test/server.h b/pjnath/src/pjnath-test/server.h new file mode 100644 index 00000000..f7955275 --- /dev/null +++ b/pjnath/src/pjnath-test/server.h @@ -0,0 +1,108 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2007 Benny Prijono + * + * 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 +#include +#include + +#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__ */ + diff --git a/pjnath/src/pjnath-test/sess_auth.c b/pjnath/src/pjnath-test/sess_auth.c index 0505fbe1..7aee423b 100644 --- a/pjnath/src/pjnath-test/sess_auth.c +++ b/pjnath/src/pjnath-test/sess_auth.c @@ -1098,6 +1098,11 @@ int sess_auth_test(void) /* 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 PJ_STUN_AUTH_LONG_TERM, // server auth @@ -1116,6 +1121,7 @@ int sess_auth_test(void) if (rc != 0) { goto done; } +#endif /* Invalid HMAC */ diff --git a/pjnath/src/pjnath-test/stun_sock_test.c b/pjnath/src/pjnath-test/stun_sock_test.c new file mode 100644 index 00000000..2c4462d7 --- /dev/null +++ b/pjnath/src/pjnath-test/stun_sock_test.c @@ -0,0 +1,844 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2007 Benny Prijono + * + * 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; +} + + diff --git a/pjnath/src/pjnath-test/test.c b/pjnath/src/pjnath-test/test.c index 312c1ea2..2abc1696 100644 --- a/pjnath/src/pjnath-test/test.c +++ b/pjnath/src/pjnath-test/test.c @@ -29,6 +29,109 @@ void app_perror(const char *msg, pj_status_t rc) PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf)); } +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, ¤t_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)); \ rc = test; \ @@ -64,6 +167,7 @@ static int test_inner(void) pj_dump_config(); pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 ); + pjlib_util_init(); pjnath_init(); #if INCLUDE_STUN_TEST @@ -75,6 +179,14 @@ static int test_inner(void) 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 + on_return: return rc; } diff --git a/pjnath/src/pjnath-test/test.h b/pjnath/src/pjnath-test/test.h index 713f3da8..a0187310 100644 --- a/pjnath/src/pjnath-test/test.h +++ b/pjnath/src/pjnath-test/test.h @@ -22,12 +22,41 @@ #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); extern void app_perror(const char *title, pj_status_t rc); 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 + diff --git a/pjnath/src/pjnath-test/turn_sock_test.c b/pjnath/src/pjnath-test/turn_sock_test.c new file mode 100644 index 00000000..52d2242f --- /dev/null +++ b/pjnath/src/pjnath-test/turn_sock_test.c @@ -0,0 +1,515 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2007 Benny Prijono + * + * 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< 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<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; +} + diff --git a/pjnath/src/pjnath/errno.c b/pjnath/src/pjnath/errno.c index a075a0c0..e71fa634 100644 --- a/pjnath/src/pjnath/errno.c +++ b/pjnath/src/pjnath/errno.c @@ -52,6 +52,8 @@ static const struct PJ_BUILD_ERR( PJNATH_ESTUNIPV6NOTSUPP, "STUN IPv6 attribute not supported"), 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"), PJ_BUILD_ERR( PJNATH_EICEINPROGRESS, "ICE check is in progress"), diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c index 8b0538d8..ccf545c5 100644 --- a/pjnath/src/pjnath/ice_session.c +++ b/pjnath/src/pjnath/ice_session.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -101,6 +102,11 @@ typedef struct timer_data } timer_data; +/* This is the data that will be attached as token to outgoing + * STUN messages. + */ + + /* Forward declarations */ static void destroy_ice(pj_ice_sess *ice, pj_status_t reason); @@ -169,6 +175,21 @@ PJ_DEF(const char*) pj_ice_get_cand_type_name(pj_ice_cand_type type) } +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) { @@ -183,17 +204,28 @@ static int get_type_prefix(pj_ice_cand_type type) } } -/* 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, pj_ice_cand_type type, const pj_sockaddr *base_addr) { 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); } @@ -263,7 +295,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, PJ_ASSERT_RETURN(stun_cfg && cb && p_ice, PJ_EINVAL); if (name == NULL) - name = "ice%p"; + name = "icess%p"; pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_SESS, PJNATH_POOL_INC_ICE_SESS, NULL); @@ -300,6 +332,12 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, } } + /* Initialize transport datas */ + for (i=0; itp_data); ++i) { + ice->tp_data[i].transport_id = i; + ice->tp_data[i].has_req_data = PJ_FALSE; + } + if (local_ufrag == NULL) { ice->rx_ufrag.ptr = (char*) pj_pool_alloc(ice->pool, PJ_ICE_UFRAG_LEN); pj_create_random_string(ice->rx_ufrag.ptr, PJ_ICE_UFRAG_LEN); @@ -551,6 +589,7 @@ static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice, */ 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, const pj_str_t *foundation, @@ -576,17 +615,14 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, } 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); lcand->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id); 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)); LOG4((ice->obj_name, @@ -1322,9 +1358,13 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list( } /* 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; icomp_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 * because timer is not running. @@ -1345,26 +1385,13 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list( return PJ_SUCCESS; } - -/* 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, pj_ice_sess_checklist *clist, unsigned check_id) { 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; const pj_ice_sess_cand *rcand; @@ -1392,10 +1419,12 @@ static pj_status_t perform_check(pj_ice_sess *ice, /* Attach data to be retrieved later when STUN request transaction * 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 */ prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535, @@ -1427,7 +1456,7 @@ static pj_status_t perform_check(pj_ice_sess *ice, */ /* 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); if (status != PJ_SUCCESS) { @@ -1655,12 +1684,10 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess, { 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); } @@ -1673,7 +1700,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, const pj_sockaddr_t *src_addr, 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; pj_ice_sess_cand *lcand; @@ -1684,9 +1711,12 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, PJ_UNUSED_ARG(stun_sess); 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 */ pj_assert(tdata == check->tdata); @@ -1739,7 +1769,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, /* Resend request */ LOG4((ice->obj_name, "Resending check because of role conflict")); check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_WAITING, 0); - perform_check(ice, clist, rd->ckid); + perform_check(ice, clist, msg_data->data.req.ckid); pj_mutex_unlock(ice->mutex); return; } @@ -1846,6 +1876,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, /* 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, &xaddr->sockaddr, @@ -1919,6 +1950,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, { 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; pj_stun_use_candidate_attr *uc_attr; @@ -1929,12 +1961,11 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, 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; } @@ -2001,7 +2032,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, } else { /* 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); return PJ_SUCCESS; @@ -2013,7 +2044,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) { /* 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); return PJ_SUCCESS; @@ -2034,11 +2065,18 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, return status; } + /* 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); @@ -2058,6 +2096,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, /* 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); rcheck->use_candidate = (uc_attr != NULL); @@ -2090,7 +2129,6 @@ static void handle_incoming_check(pj_ice_sess *ice, pj_ice_sess_cand *lcand = NULL; pj_ice_sess_cand *rcand; unsigned i; - pj_bool_t is_relayed; comp = find_comp(ice, rcheck->comp_id); @@ -2109,7 +2147,7 @@ static void handle_incoming_check(pj_ice_sess *ice, */ 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; pj_memcpy(&rcand->addr, &rcheck->src_addr, rcheck->src_addr_len); @@ -2147,12 +2185,14 @@ static void handle_incoming_check(pj_ice_sess *ice, } } #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; iclist.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; } @@ -2170,11 +2210,6 @@ static void handle_incoming_check(pj_ice_sess *ice, /* * 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; /* * 7.2.1.4. Triggered Checks @@ -2309,6 +2344,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice, { pj_status_t status = PJ_SUCCESS; pj_ice_sess_comp *comp; + pj_ice_sess_cand *cand; PJ_ASSERT_RETURN(ice && comp_id, PJ_EINVAL); @@ -2332,7 +2368,9 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice, goto on_return; } - 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)); @@ -2344,6 +2382,7 @@ on_return: 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, const pj_sockaddr_t *src_addr, @@ -2351,6 +2390,8 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, { 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; PJ_ASSERT_RETURN(ice, PJ_EINVAL); @@ -2363,11 +2404,24 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, goto on_return; } + /* Find transport */ + for (i=0; itp_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) { pj_strerror(status, ice->tmp.errmsg, sizeof(ice->tmp.errmsg)); @@ -2375,7 +2429,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, ice->tmp.errmsg)); } } 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); } diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c index 7266e079..34b74f0d 100644 --- a/pjnath/src/pjnath/ice_strans.c +++ b/pjnath/src/pjnath/ice_strans.c @@ -19,9 +19,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -35,792 +37,571 @@ #endif +/* 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 */ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status); 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) - -/* - * Create ICE stream transport +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. */ -PJ_DEF(pj_status_t) pj_ice_strans_create( pj_stun_config *stun_cfg, - const char *name, - unsigned comp_cnt, - void *user_data, - const pj_ice_strans_cb *cb, - pj_ice_strans **p_ice_st) +typedef struct pj_ice_strans_comp { - pj_pool_t *pool; - pj_ice_strans *ice_st; + pj_ice_strans *ice_st; /**< ICE stream transport. */ + unsigned comp_id; /**< Component ID. */ - 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); + pj_stun_sock *stun_sock; /**< STUN transport. */ + pj_turn_sock *turn_sock; /**< TURN relay transport. */ - if (name == NULL) - name = "icstr%p"; + unsigned cand_cnt; /**< # of candidates/aliaes. */ + pj_ice_sess_cand cand_list[PJ_ICE_ST_MAX_CAND]; /**< Cand array */ - pool = pj_pool_create(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->user_data = user_data; - - ice_st->comp_cnt = comp_cnt; - ice_st->comp = (pj_ice_strans_comp**) pj_pool_calloc(pool, comp_cnt, - sizeof(void*)); + unsigned default_cand; /**< Default candidate. */ - pj_memcpy(&ice_st->cb, cb, sizeof(*cb)); - pj_memcpy(&ice_st->stun_cfg, stun_cfg, sizeof(*stun_cfg)); +} pj_ice_strans_comp; - PJ_LOG(4,(ice_st->obj_name, "ICE stream transport created")); +/** + * 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. */ - *p_ice_st = ice_st; - return PJ_SUCCESS; -} + pj_ice_sess *ice; /**< ICE session. */ + pj_time_val start_time;/**< Time when ICE was started */ -/* Destroy ICE */ -static void destroy_ice_st(pj_ice_strans *ice_st, pj_status_t reason) -{ - unsigned i; - char obj_name[PJ_MAX_OBJ_NAME]; + unsigned comp_cnt; /**< Number of components. */ + pj_ice_strans_comp **comp; /**< Components array. */ - 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")); - } + pj_timer_entry ka_timer; /**< STUN keep-alive timer. */ - /* Kill keep-alive timer, if any */ - stop_ka_timer(ice_st); + 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?*/ +}; - /* Destroy ICE if we have ICE */ - if (ice_st->ice) { - pj_ice_sess_destroy(ice_st->ice); - ice_st->ice = NULL; - } - /* Destroy all components */ - for (i=0; icomp_cnt; ++i) { - if (ice_st->comp[i]) { - destroy_component(ice_st->comp[i]); - ice_st->comp[i] = NULL; - } - } - ice_st->comp_cnt = 0; +/* Validate configuration */ +static pj_status_t pj_ice_strans_cfg_check_valid(const pj_ice_strans_cfg *cfg) +{ + pj_status_t status; - /* Done */ - pj_pool_release(ice_st->pool); + status = pj_stun_config_check_valid(&cfg->stun_cfg); + if (!status) + return status; - if (reason == PJ_SUCCESS) { - PJ_LOG(4,(obj_name, "ICE stream transport destroyed")); - } + return PJ_SUCCESS; } + /* - * Destroy ICE stream transport. + * Initialize ICE transport configuration with default values. */ -PJ_DEF(pj_status_t) pj_ice_strans_destroy(pj_ice_strans *ice_st) +PJ_DEF(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg) { - destroy_ice_st(ice_st, PJ_SUCCESS); - return PJ_SUCCESS; + 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; } + /* - * Resolve STUN server + * Copy configuration. */ -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) +PJ_DEF(void) pj_ice_strans_cfg_copy( pj_pool_t *pool, + pj_ice_strans_cfg *dst, + const pj_ice_strans_cfg *src) { - /* Yeah, TODO */ - PJ_UNUSED_ARG(ice_st); - PJ_UNUSED_ARG(resolver); - PJ_UNUSED_ARG(domain); - return -1; + 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); } /* - * Set STUN server address. + * Create the component. */ -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) +static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id) { - 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)); - } 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; icand_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; - } - } + pj_ice_strans_comp *comp = NULL; + pj_status_t status; - 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; -} + /* Verify arguments */ + PJ_ASSERT_RETURN(ice_st && comp_id, PJ_EINVAL); -/* 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; + /* 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; - 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; retrysock, &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; + ice_st->comp[comp_id-1] = comp; - /* Set default IP interface as the base address */ - status = pj_gethostip(pj_AF_INET(), &comp->local_addr); + /* 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) - goto on_error; - - /* Add candidate entry for each interface */ - for (i=0; i> 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; + 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; + } - /* 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; + /* 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; } - 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; + /* 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; } - - } 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. + /* Add local addresses to host candidates, unless no_host_cands + * flag is set. */ - 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; + if (ice_st->cfg.stun.no_host_cands == PJ_FALSE) { + pj_stun_sock_info stun_sock_info; + unsigned i; - } 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); + /* Enumerate addresses */ + status = pj_stun_sock_get_info(comp->stun_sock, &stun_sock_info); if (status != PJ_SUCCESS) return status; + + for (i=0; icand_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; - /* Done */ - if (p_comp) - *p_comp = comp; - - return PJ_SUCCESS; - -on_error: - destroy_component(comp); - return status; -} + /* 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; -/* - * 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; + 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; + } - 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); + /* Add pending job */ + ///sess_add_ref(ice_st); - 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); + /* 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; } - } else if (bytes_read < 0) { - ice_st_perror(comp->ice_st, "ioqueue read callback error", - -bytes_read); - } + /* 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; - /* Read next packet */ - for (retry=0; retrypkt); - 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; - } - } -} + PJ_LOG(4,(ice_st->obj_name, + "Comp %d: TURN relay candidate waiting for allocation", + comp_id)); -/* - * 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; + /* Set default candidate to relay */ + comp->default_cand = cand - comp->cand_list; } - 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; - } + return PJ_SUCCESS; } -/* STUN keep-alive timer callback */ -static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te) +/* + * Create ICE stream transport + */ +PJ_DEF(pj_status_t) pj_ice_strans_create( const char *name, + const pj_ice_strans_cfg *cfg, + unsigned comp_cnt, + void *user_data, + const pj_ice_strans_cb *cb, + pj_ice_strans **p_ice_st) { - pj_ice_strans *ice_st = (pj_ice_strans*)te->user_data; + pj_pool_t *pool; + pj_ice_strans *ice_st; unsigned i; pj_status_t status; - PJ_UNUSED_ARG(th); + status = pj_ice_strans_cfg_check_valid(cfg); + if (status != PJ_SUCCESS) + return status; - ice_st->ka_timer.id = PJ_FALSE; + PJ_ASSERT_RETURN(comp_cnt && cb && p_ice_st, PJ_EINVAL); - for (i=0; icomp_cnt; ++i) { - pj_ice_strans_comp *comp = ice_st->comp[i]; - pj_stun_tx_data *tdata; - unsigned j; + if (name == NULL) + name = "ice%p"; - /* Does this component have STUN server reflexive candidate? */ - for (j=0; jcand_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; + 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; + ice_st->obj_name = pool->obj_name; + ice_st->user_data = user_data; - /* tdata->user_data is NULL for keep-alive */ - //tdata->user_data = NULL; + PJ_LOG(4,(ice_st->obj_name, + "Creating ICE stream transport with %d component(s)", + comp_cnt)); - ++comp->pending_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(pj_ice_strans_comp*)); - /* 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); + for (i=0; ipending_cnt; + destroy_ice_st(ice_st); + return status; } } - /* Start next timer */ - start_ka_timer(ice_st); -} + /* Check if all candidates are ready (this may call callback) */ + sess_init_update(ice_st); -/* Start STUN keep-alive timer */ -static void start_ka_timer(pj_ice_strans *ice_st) -{ - pj_time_val delay; + PJ_LOG(4,(ice_st->obj_name, "ICE stream transport created")); - /* Skip if timer is already running */ - if (ice_st->ka_timer.id != PJ_FALSE) - return; + *p_ice_st = ice_st; + return PJ_SUCCESS; +} - 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); +/* Destroy ICE */ +static void destroy_ice_st(pj_ice_strans *ice_st) +{ + unsigned i; - 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; + /* Destroy ICE if we have ICE */ + if (ice_st->ice) { + pj_ice_sess_destroy(ice_st->ice); + ice_st->ice = NULL; } -} + /* Destroy all components */ + for (i=0; icomp_cnt; ++i) { + if (ice_st->comp[i]) { + 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; -/* 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; + /* 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; + } - pj_timer_heap_cancel(ice_st->stun_cfg.timer_heap, &ice_st->ka_timer); - ice_st->ka_timer.id = PJ_FALSE; + /* Done */ + pj_pool_release(ice_st->pool); } - -/* - * Add STUN mapping to a component. - */ -static pj_status_t get_stun_mapped_addr(pj_ice_strans *ice_st, - pj_ice_strans_comp *comp) +/* Notification about failure */ +static void sess_fail(pj_ice_strans *ice_st, pj_ice_strans_op op, + const char *title, pj_status_t status) { - pj_ice_strans_cand *cand; - pj_stun_session_cb sess_cb; - pj_stun_tx_data *tdata; - pj_status_t status; + char errmsg[PJ_ERR_MSG_SIZE]; - 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; + 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; - /* Just return (successfully) if STUN server is not configured */ - if (ice_st->stun_srv.sin_family == 0) - return PJ_SUCCESS; + ice_st->cb_called = PJ_TRUE; + if (ice_st->cb.on_ice_complete) + (*ice_st->cb.on_ice_complete)(ice_st, op, status); +} - /* 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; +/* Update initialization status */ +static void sess_init_update(pj_ice_strans *ice_st) +{ + unsigned i; - /* Associate component with STUN session */ - pj_stun_session_set_user_data(comp->stun_sess, (void*)comp); + /* Ignore if init callback has been called */ + if (ice_st->cb_called) + return; - /* 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; + /* Notify application when all candidates have been gathered */ + for (i=0; icomp_cnt; ++i) { + unsigned j; + pj_ice_strans_comp *comp = ice_st->comp[i]; - /* Will be attached to tdata in send_msg() */ - cand = &comp->cand_list[comp->cand_cnt]; + for (j=0; jcand_cnt; ++j) { + pj_ice_sess_cand *cand = &comp->cand_list[j]; - /* 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; + if (cand->status == PJ_EPENDING) + return; + } } - return PJ_SUCCESS; + /* 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); } - /* - * Create the component. + * Destroy ICE stream transport. */ -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_DEF(pj_status_t) pj_ice_strans_destroy(pj_ice_strans *ice_st) { - 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); - + char obj_name[PJ_MAX_OBJ_NAME]; - /* Create component */ - status = create_component(ice_st, comp_id, options, addr, &comp); - if (status != PJ_SUCCESS) - return status; + PJ_ASSERT_RETURN(ice_st, PJ_EINVAL); - 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; - } + 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); - /* Store this component */ - ice_st->comp[comp_id-1] = comp; - + PJ_LOG(4,(obj_name, "ICE stream transport destroyed")); 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) +/* + * Increment busy counter. + */ +static void sess_add_ref(pj_ice_strans *ice_st) { - 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_atomic_inc(ice_st->busy_cnt); } - -PJ_DEF(pj_status_t) pj_ice_strans_get_comps_status(pj_ice_strans *ice_st) +/* + * 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) { - unsigned i; - pj_status_t worst = PJ_SUCCESS; - - for (i=0; icomp_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; + 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 { + return PJ_TRUE; } +} - return worst; +/* + * 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; } + /* * Create ICE! */ @@ -832,7 +613,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, pj_status_t status; 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 */ PJ_ASSERT_RETURN(ice_st, PJ_EINVAL); @@ -848,7 +629,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, ice_cb.on_tx_pkt = &ice_tx_pkt; /* 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); if (status != PJ_SUCCESS) @@ -857,6 +638,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, /* Associate user data */ ice_st->ice->user_data = (void*)ice_st; +#if 0 /* If default candidate for components are SRFLX one, upload a custom * type priority to ICE session so that SRFLX candidates will get * checked first. @@ -867,30 +649,44 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, { pj_ice_sess_set_prefs(ice_st->ice, srflx_prio); } +#endif - - /* Add candidates */ + /* Add components/candidates */ for (i=0; icomp_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; jcand_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; } @@ -907,28 +703,71 @@ on_error: * Enum candidates */ 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_ice_strans_comp *comp; - PJ_ASSERT_RETURN(ice_st && count && cand, PJ_EINVAL); - PJ_ASSERT_RETURN(ice_st->ice, PJ_EINVALIDOP); + PJ_ASSERT_RETURN(ice_st && comp_id && comp_id <= ice_st->comp_cnt && + count && cand, PJ_EINVAL); - cnt = ice_st->ice->lcand_cnt; + comp = ice_st->comp[comp_id - 1]; + cnt = comp->cand_cnt; cnt = (cnt > *count) ? *count : cnt; - *count = 0; for (i=0; iice->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_candcand_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); +} + /* * Start ICE processing ! */ @@ -940,11 +779,19 @@ PJ_DEF(pj_status_t) pj_ice_strans_start_ice( pj_ice_strans *ice_st, { 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); if (status != PJ_SUCCESS) return status; + /* Start ICE negotiation! */ status = pj_ice_sess_start_check(ice_st->ice); if (status != PJ_SUCCESS) { pj_ice_strans_stop_ice(ice_st); @@ -953,31 +800,37 @@ PJ_DEF(pj_status_t) pj_ice_strans_start_ice( pj_ice_strans *ice_st, return status; } +/* + * 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); ice_st->ice = NULL; } - /* Invalidate all candidate Ids */ - for (i=0; icomp_cnt; ++i) { - unsigned j; - for (j=0; jcomp[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, unsigned comp_id, @@ -997,18 +850,24 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st, /* 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); - } + 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; - /* 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; + } 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; } /* @@ -1018,9 +877,79 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st, static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) { 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; icomp_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); } /* @@ -1028,30 +957,42 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) */ 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) { 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; } @@ -1061,148 +1002,295 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, */ 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, unsigned src_addr_len) { 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, src_addr, src_addr_len); } } -/* - * Callback called by STUN session to send outgoing packet. +/* Notification when incoming packet has been received from + * the STUN socket. */ -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) +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_sock_get_user_data(stun_sock); + ice_st = comp->ice_st; - 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; + 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); } -/* - * Callback sent by STUN session when outgoing STUN request has - * completed. +/* Notifification when asynchronous send operation to the STUN socket + * 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) +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; icand_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", - status); + /* 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; icand_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; } - /* Decrement pending count for this component */ - pj_assert(comp->pending_cnt > 0); - comp->pending_cnt--; + sess_add_ref(comp->ice_st); - if (status == PJNATH_ESTUNTIMEDOUT) { + 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); + } - PJ_LOG(4,(comp->ice_st->obj_name, - "STUN Binding request has timed-out, will retry " - "again alter")); + } else { - /* Restart keep-alive timer */ - start_ka_timer(comp->ice_st); - return; + /* 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); - } else if (status != PJ_SUCCESS) { - comp->last_status = cand->status = status; - ice_st_perror(comp->ice_st, "STUN Binding request failed", - cand->status); - return; + if (status != PJ_SUCCESS) { + ice_st_perror(comp->ice_st, + "Error processing packet from TURN relay", + status); + } } - 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); + 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; } - /* Save IP address for logging */ - pj_ansi_strcpy(ip, pj_inet_ntoa(comp->local_addr.ipv4.sin_addr)); + 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; icand_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; - /* 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; + "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); + } } - 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)); + sess_dec_ref(comp->ice_st); } diff --git a/pjnath/src/pjnath/stun_msg.c b/pjnath/src/pjnath/stun_msg.c index 21315f3b..97aaeb27 100644 --- a/pjnath/src/pjnath/stun_msg.c +++ b/pjnath/src/pjnath/stun_msg.c @@ -1557,7 +1557,7 @@ PJ_DEF(pj_status_t) pj_stun_unknown_attr_create(pj_pool_t *pool, /* Create and add STUN UNKNOWN-ATTRIBUTES attribute to the message. */ 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[]) { pj_stun_unknown_attr *attr = NULL; @@ -1646,7 +1646,7 @@ static void* clone_unknown_attr(pj_pool_t *pool, const void *src) PJ_DEF(pj_status_t) pj_stun_binary_attr_create(pj_pool_t *pool, int attr_type, const pj_uint8_t *data, - unsigned length, + pj_size_t length, pj_stun_binary_attr **p_attr) { pj_stun_binary_attr *attr; @@ -1673,7 +1673,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_add_binary_attr(pj_pool_t *pool, pj_stun_msg *msg, int attr_type, const pj_uint8_t *data, - unsigned length) + pj_size_t length) { pj_stun_binary_attr *attr = NULL; pj_status_t status; @@ -1833,10 +1833,10 @@ PJ_DEF(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg, /* * 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); @@ -1938,10 +1938,10 @@ PJ_DEF(pj_status_t) pj_stun_msg_create_response(pj_pool_t *pool, */ 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) { @@ -2190,9 +2190,9 @@ static char *print_binary(const pj_uint8_t *data, unsigned data_len) */ 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; pj_stun_msgint_attr *amsgint = NULL; diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c index 6545c750..6e7d4ef3 100644 --- a/pjnath/src/pjnath/stun_session.c +++ b/pjnath/src/pjnath/stun_session.c @@ -29,14 +29,23 @@ struct pj_stun_session pj_stun_session_cb cb; 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; pj_stun_auth_cred cred; int auth_retry; pj_str_t next_nonce; + pj_str_t server_realm; pj_str_t srv_name; @@ -79,7 +88,7 @@ static pj_stun_tsx_cb tsx_cb = static pj_status_t tsx_add(pj_stun_session *sess, 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; } @@ -138,11 +147,13 @@ static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx) pj_stun_tx_data *tdata; 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) { pj_timer_heap_cancel(tdata->sess->cfg->timer_heap, @@ -151,14 +162,21 @@ static void destroy_tdata(pj_stun_tx_data *tdata) pj_list_erase(tdata); } - 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); + } } } @@ -169,7 +187,7 @@ PJ_DEF(void) pj_stun_msg_destroy_tdata( pj_stun_session *sess, pj_stun_tx_data *tdata) { PJ_UNUSED_ARG(sess); - destroy_tdata(tdata); + destroy_tdata(tdata, PJ_FALSE); } @@ -289,6 +307,7 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, ea->err_code == PJ_STUN_SC_STALE_NONCE) { const pj_stun_nonce_attr *anonce; + const pj_stun_realm_attr *arealm; pj_stun_tx_data *tdata; unsigned i; pj_status_t status; @@ -316,6 +335,13 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, /* Save next_nonce */ 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, request->msg->hdr.magic, @@ -324,7 +350,8 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, return status; /* 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; imsg->attr_count; ++i) { const pj_stun_attr_hdr *asrc = request->msg->attr[i]; @@ -373,6 +400,10 @@ static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx); 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, src_addr_len, ¬ify_user); @@ -387,6 +418,13 @@ static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, */ 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; + } } static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx, @@ -394,12 +432,27 @@ static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx, pj_size_t pkt_size) { 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); + 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); - return tdata->sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt, - pkt_size, tdata->dst_addr, - tdata->addr_len); + if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) { + pj_stun_session_destroy(sess); + return PJNATH_ESTUNDESTROYED; + } else { + return status; + } } /* **************************************************************************/ @@ -428,11 +481,16 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, sess->pool = pool; 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); sess->srv_name.slen = pj_ansi_snprintf(sess->srv_name.ptr, 32, "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); @@ -443,6 +501,13 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, } 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; return PJ_SUCCESS; @@ -453,13 +518,22 @@ PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess) PJ_ASSERT_RETURN(sess, PJ_EINVAL); 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); @@ -467,6 +541,11 @@ PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess) pj_lock_destroy(sess->lock); } + if (sess->rx_pool) { + pj_pool_release(sess->rx_pool); + sess->rx_pool = NULL; + } + pj_pool_release(sess->pool); return PJ_SUCCESS; @@ -538,12 +617,19 @@ PJ_DEF(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess, return PJ_SUCCESS; } +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, pj_stun_tx_data *tdata) { 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; @@ -633,6 +719,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, return status; } tdata->auth_info.nonce = sess->next_nonce; + tdata->auth_info.realm = sess->server_realm; } } else { @@ -714,8 +801,18 @@ PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess, static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg, 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); PJ_LOG(5,(SNAME(sess), @@ -749,7 +846,8 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, tdata->token = token; 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); /* Apply options */ @@ -757,9 +855,8 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, tdata->msg); 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; } /* Encode message */ @@ -769,9 +866,8 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, &tdata->pkt_size); 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; } /* Dump packet */ @@ -797,9 +893,8 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, tdata->pkt, tdata->pkt_size); 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; } /* Add to pending request list */ @@ -824,10 +919,10 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, &tdata->res_timer, &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; } pj_list_push_back(&sess->cached_response_list, tdata); @@ -838,7 +933,9 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, tdata->pkt_size, server, addr_len); 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; } /* Destroy only when response is not cached*/ @@ -847,8 +944,15 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, } } - +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; } @@ -892,6 +996,8 @@ PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess, PJ_ASSERT_RETURN(!notify || notify_status!=PJ_SUCCESS, PJ_EINVAL); 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); if (notify) { @@ -903,6 +1009,12 @@ PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess, pj_stun_msg_destroy_tdata(sess, tdata); 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; } @@ -917,12 +1029,19 @@ PJ_DEF(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess, PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL); 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); status = pj_stun_client_tsx_retransmit(tdata->client_tsx); 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; } @@ -1165,6 +1284,36 @@ static pj_status_t on_incoming_indication(pj_stun_session *sess, } +/* 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, pj_size_t pkt_size, @@ -1175,47 +1324,34 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, unsigned src_addr_len) { 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); if (status != PJ_SUCCESS) { 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; + goto on_return; } - 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); + 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) { goto on_return; @@ -1231,13 +1367,13 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, } 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); } 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); @@ -1249,9 +1385,14 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, on_return: 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; } - - diff --git a/pjnath/src/pjnath/stun_sock.c b/pjnath/src/pjnath/stun_sock.c new file mode 100644 index 00000000..8ff5a1d6 --- /dev/null +++ b/pjnath/src/pjnath/stun_sock.c @@ -0,0 +1,829 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2007 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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; itsx_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; ialias_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; +} + diff --git a/pjnath/src/pjnath/stun_transaction.c b/pjnath/src/pjnath/stun_transaction.c index ebee152a..1242dfe7 100644 --- a/pjnath/src/pjnath/stun_transaction.c +++ b/pjnath/src/pjnath/stun_transaction.c @@ -31,16 +31,17 @@ struct pj_stun_client_tsx { char obj_name[PJ_MAX_OBJ_NAME]; - pj_stun_config *cfg; pj_stun_tsx_cb cb; void *user_data; pj_bool_t complete; 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; @@ -70,7 +71,8 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg, PJ_ASSERT_RETURN(cb->on_send_msg, PJ_EINVAL); 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)); tsx->retransmit_timer.cb = &retransmit_timer_callback; @@ -99,22 +101,23 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_schedule_destroy( /* 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; } /* Stop retransmission, just in case */ 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; } - status = pj_timer_heap_schedule(tsx->cfg->timer_heap, + status = pj_timer_heap_schedule(tsx->timer_heap, &tsx->destroy_timer, delay); if (status != PJ_SUCCESS) return status; tsx->destroy_timer.id = TIMER_ACTIVE; + tsx->cb.on_complete = NULL; return PJ_SUCCESS; } @@ -128,11 +131,11 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx) PJ_ASSERT_RETURN(tsx, PJ_EINVAL); 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; } @@ -186,7 +189,7 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx) /* Calculate retransmit/timeout delay */ 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) { unsigned msec; @@ -205,7 +208,7 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx) * cancel it (as opposed to when schedule_timer() failed we cannot * 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); if (status != PJ_SUCCESS) { @@ -223,9 +226,12 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx) /* 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; } @@ -279,12 +285,15 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, tsx->cb.on_complete(tsx, PJNATH_ESTUNTIMEDOUT, NULL, NULL, 0); } } + /* We might have been destroyed, don't try to access the object */ return; } 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) { tsx->complete = PJ_TRUE; @@ -292,6 +301,7 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, tsx->cb.on_complete(tsx, status, NULL, NULL, 0); } } + /* We might have been destroyed, don't try to access the object */ } } @@ -305,7 +315,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx) } 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; } @@ -351,7 +361,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, * We can cancel retransmit timer now. */ 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; } @@ -384,6 +394,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, if (tsx->cb.on_complete) { tsx->cb.on_complete(tsx, status, msg, src_addr, src_addr_len); } + /* We might have been destroyed, don't try to access the object */ } return PJ_SUCCESS; diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c index 6c662d66..0176e139 100644 --- a/pjnath/src/pjnath/turn_session.c +++ b/pjnath/src/pjnath/turn_session.c @@ -29,8 +29,9 @@ #include #include -#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[] = { @@ -66,11 +67,13 @@ struct pj_turn_session const char *obj_name; pj_turn_session_cb cb; void *user_data; + pj_stun_config stun_cfg; pj_lock_t *lock; int busy; pj_turn_state_t state; + pj_status_t last_status; pj_bool_t pending_destroy; pj_bool_t destroy_notified; @@ -87,7 +90,7 @@ struct pj_turn_session pj_uint16_t default_port; 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; pj_sockaddr *srv_addr; @@ -95,6 +98,7 @@ struct pj_turn_session pj_bool_t pending_alloc; pj_turn_alloc_param alloc_param; + pj_sockaddr mapped_addr; pj_sockaddr relay_addr; pj_hash_table_t *peer_table; @@ -176,13 +180,13 @@ PJ_DEF(const char*) pj_turn_state_name(pj_turn_state_t state) /* * 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, - void *user_data, unsigned options, + void *user_data, pj_turn_session **p_sess) { pj_pool_t *pool; @@ -206,11 +210,14 @@ PJ_DEF(pj_status_t) pj_turn_session_create( pj_stun_config *cfg, sess->obj_name = pool->obj_name; 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 */ pj_memcpy(&sess->cb, cb, sizeof(*cb)); @@ -233,8 +240,8 @@ PJ_DEF(pj_status_t) pj_turn_session_create( pj_stun_config *cfg, stun_cb.on_send_msg = &stun_on_send_msg; 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); return status; @@ -333,9 +340,10 @@ static void sess_shutdown(pj_turn_session *sess, case PJ_TURN_STATE_NULL: 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: break; @@ -365,13 +373,13 @@ static void sess_shutdown(pj_turn_session *sess, /* Schedule destroy */ 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); sess->timer.id = TIMER_NONE; } - set_state(sess, PJ_TURN_STATE_DESTROYING); - sess->timer.id = TIMER_DESTROY; pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay); } @@ -400,6 +408,8 @@ PJ_DEF(pj_status_t) pj_turn_session_shutdown(pj_turn_session *sess) */ 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); return PJ_SUCCESS; @@ -419,15 +429,19 @@ PJ_DEF(pj_status_t) pj_turn_session_get_info( pj_turn_session *sess, pj_gettimeofday(&now); 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) pj_memcpy(&info->server, sess->srv_addr, sizeof(info->server)); else 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; } @@ -453,6 +467,19 @@ PJ_DEF(void*) pj_turn_session_get_user_data(pj_turn_session *sess) } +/* + * 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); +} + + /** * Set the server or domain name of the server. */ @@ -461,6 +488,8 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, int default_port, pj_dns_resolver *resolver) { + pj_sockaddr tmp_addr; + pj_bool_t is_ip_addr; pj_status_t status; PJ_ASSERT_RETURN(sess && domain, PJ_EINVAL); @@ -468,14 +497,20 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, 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. */ unsigned opt = 0; pj_str_t res_name; - switch (sess->tp_type) { + switch (sess->conn_type) { case PJ_TURN_TP_UDP: res_name = pj_str("_turn._udp."); break; @@ -501,6 +536,12 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, (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_dns_srv_resolve(domain, &res_name, default_port, sess->pool, resolver, opt, sess, &dns_srv_resolver_cb, &sess->dns_async); @@ -520,12 +561,19 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess, PJ_ASSERT_RETURN(default_port>0 && default_port<65536, PJ_EINVAL); 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)); 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); if (status != PJ_SUCCESS) @@ -636,7 +684,7 @@ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess, /* 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, pj_sockaddr_get_len(sess->srv_addr), @@ -681,7 +729,7 @@ static void send_refresh(pj_turn_session *sess, int lifetime) } 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), tdata); @@ -833,7 +881,7 @@ PJ_DEF(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess, * for future reference when we receive the ChannelBind response. */ 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), tdata); @@ -849,12 +897,12 @@ on_return: * The packet maybe a STUN packet or ChannelData packet. */ 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 * indication). @@ -863,14 +911,16 @@ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess, /* Start locking the session */ 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) { /* This looks like STUN, give it to the STUN session */ 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; status=pj_stun_session_on_rx_pkt(sess->stun, pkt, pkt_len, @@ -905,8 +955,8 @@ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess, } /* 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)); status = PJ_SUCCESS; @@ -953,6 +1003,8 @@ static void on_session_fail( pj_turn_session *sess, pj_status_t status, const pj_str_t *reason) { + sess->last_status = status; + do { pj_str_t reason1; char err_msg[PJ_ERR_MSG_SIZE]; @@ -1010,6 +1062,7 @@ static void on_allocate_success(pj_turn_session *sess, { 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; @@ -1071,6 +1124,12 @@ static void on_allocate_success(pj_turn_session *sess, "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; + } /* Save relayed address */ if (raddr_attr) { @@ -1091,6 +1150,14 @@ static void on_allocate_success(pj_turn_session *sess, } } + /* 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 */ /* Cancel existing keep-alive timer, if any */ @@ -1132,6 +1199,17 @@ static void stun_on_request_complete(pj_stun_session *stun, sess = (pj_turn_session*)pj_stun_session_get_user_data(stun); 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 && PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) @@ -1298,7 +1376,7 @@ static void dns_srv_resolver_cb(void *user_data, const pj_dns_srv_record *rec) { pj_turn_session *sess = (pj_turn_session*) user_data; - unsigned i, cnt; + unsigned i, cnt, tot_cnt; /* Clear async resolver */ sess->dns_async = NULL; @@ -1309,11 +1387,27 @@ static void dns_srv_resolver_cb(void *user_data, return; } + /* Calculate total number of server entries in the response */ + tot_cnt = 0; + for (i=0; icount; ++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; icount && cntcount && cntentry[i].server.addr_count && cntentry[i].server.addr_count && + cntsrv_addr_list[cnt].ipv4; addr->sin_family = sess->af; diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c index cd2e08d4..e595d271 100644 --- a/pjnath/src/pjnath/turn_sock.c +++ b/pjnath/src/pjnath/turn_sock.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include #include #include @@ -50,11 +51,8 @@ struct pj_turn_sock 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]; }; @@ -71,18 +69,22 @@ static void turn_on_channel_bound(pj_turn_session *sess, unsigned addr_len, 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, unsigned addr_len); static void turn_on_state(pj_turn_session *sess, 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); + static void destroy(pj_turn_sock *turn_sock); @@ -158,7 +160,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg, sess_cb.on_rx_data = &turn_on_rx_data; 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); return status; @@ -187,13 +189,9 @@ static void destroy(pj_turn_sock *turn_sock) turn_sock->sess = NULL; } - 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; } if (turn_sock->lock) { @@ -271,7 +269,8 @@ static void sess_fail(pj_turn_sock *turn_sock, const char *title, pj_status_t status) { show_err(turn_sock, title, status); - pj_turn_session_destroy(turn_sock->sess); + if (turn_sock->sess) + pj_turn_session_destroy(turn_sock->sess); } /* @@ -280,6 +279,7 @@ static void sess_fail(pj_turn_sock *turn_sock, const char *title, PJ_DEF(pj_status_t) pj_turn_sock_set_user_data( pj_turn_sock *turn_sock, void *user_data) { + PJ_ASSERT_RETURN(turn_sock, PJ_EINVAL); turn_sock->user_data = user_data; return PJ_SUCCESS; } @@ -289,6 +289,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_set_user_data( pj_turn_sock *turn_sock, */ 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; } @@ -296,7 +297,7 @@ PJ_DEF(void*) pj_turn_sock_get_user_data(pj_turn_sock *turn_sock) * Get info. */ 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); @@ -309,15 +310,41 @@ PJ_DEF(pj_status_t) pj_turn_sock_get_info(pj_turn_sock *turn_sock, } } +/** + * 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; @@ -392,16 +419,16 @@ PJ_DEF(pj_status_t) pj_turn_sock_bind_channel( pj_turn_sock *turn_sock, /* * 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; } if (turn_sock->conn_type != PJ_TURN_TP_UDP) { @@ -409,8 +436,8 @@ static void on_connect_complete(pj_ioqueue_key_t *key, } /* 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 */ pj_ioqueue_op_key_init(&turn_sock->send_key, sizeof(turn_sock->send_key)); @@ -419,56 +446,43 @@ static void on_connect_complete(pj_ioqueue_key_t *key, status = pj_turn_session_alloc(turn_sock->sess, &turn_sock->alloc_param); if (status != PJ_SUCCESS) { sess_fail(turn_sock, "Error sending ALLOCATE", status); - return; + return PJ_FALSE; } + + return PJ_TRUE; } /* * 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) +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) { - enum { MAX_RETRY = 10 }; pj_turn_sock *turn_sock; - int retry = 0; - pj_status_t status; + pj_bool_t ret = PJ_TRUE; - turn_sock = (pj_turn_sock*) pj_ioqueue_get_user_data(key); + 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; } @@ -482,7 +496,7 @@ static pj_status_t turn_on_send_pkt(pj_turn_session *sess, unsigned dst_addr_len) { 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; @@ -495,8 +509,8 @@ static pj_status_t turn_on_send_pkt(pj_turn_session *sess, PJ_UNUSED_ARG(dst_addr); 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); } @@ -524,7 +538,7 @@ static void turn_on_channel_bound(pj_turn_session *sess, * Callback from TURN session upon incoming data. */ 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, unsigned addr_len) @@ -559,7 +573,19 @@ static void turn_on_state(pj_turn_session *sess, return; } - 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 * connection to the server. @@ -567,19 +593,16 @@ static void turn_on_state(pj_turn_session *sess, pj_turn_session_info info; 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 * we're switching to alternate TURN server when either TCP * 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; } /* Get server address from session info */ @@ -591,20 +614,21 @@ static void turn_on_state(pj_turn_session *sess, sock_type = pj_SOCK_STREAM(); /* 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); return; } - /* 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); return; @@ -616,10 +640,12 @@ static void turn_on_state(pj_turn_session *sess, sizeof(addrtxt), 3))); /* 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); return; @@ -630,10 +656,6 @@ static void turn_on_state(pj_turn_session *sess, */ } - 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}; diff --git a/pjnath/src/pjturn-client/client_main.c b/pjnath/src/pjturn-client/client_main.c index ea6ffc39..823cf497 100644 --- a/pjnath/src/pjturn-client/client_main.c +++ b/pjnath/src/pjturn-client/client_main.c @@ -68,7 +68,7 @@ static struct options 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, unsigned addr_len); @@ -274,7 +274,7 @@ static pj_status_t create_relay(void) } 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 */ NULL, /* resolver */ @@ -294,7 +294,7 @@ static void destroy_relay(void) 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, unsigned addr_len) diff --git a/pjnath/src/pjturn-srv/auth.c b/pjnath/src/pjturn-srv/auth.c index d2527dfa..4e0e10a4 100644 --- a/pjnath/src/pjturn-srv/auth.c +++ b/pjnath/src/pjturn-srv/auth.c @@ -35,8 +35,7 @@ static struct cred_t { { "100", "100" }, { "700", "700" }, - { "701", "701" }, - { "702", "702" } + { "701", "701" } }; #define THE_NONCE "pjnath" -- cgit v1.2.3