Major major modifications related to ticket (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: https://svn.pjsip.org/repos/pjproject/trunk@1988 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Benny Prijono 2008-06-06 14:47:10 +00:00
parent 71c8395069
commit ff1df04070
41 changed files with 7973 additions and 2281 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -297,6 +297,10 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\src\pjnath\stun_sock.c"
>
</File>
<File
RelativePath="..\src\pjnath\stun_transaction.c"
>

View File

@ -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

View File

@ -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"\

Binary file not shown.

Binary file not shown.

Before

(image error) Size: 32 KiB

After

(image error) Size: 124 KiB

View File

@ -26,6 +26,7 @@
#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>

View File

@ -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
*/

View File

@ -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

View File

@ -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,

View File

@ -25,6 +25,8 @@
* @brief ICE Stream Transport
*/
#include <pjnath/ice_session.h>
#include <pjnath/stun_sock.h>
#include <pjnath/turn_sock.h>
#include <pjlib-util/resolver.h>
#include <pj/ioqueue.h>
#include <pj/timer.h>
@ -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;
/**
* Local preference value, which typically is 65535.
*/
pj_uint16_t local_pref;
/**
* 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;
/**
* Foundation associated with this candidate, which value normally will be
* calculated by the function.
*/
pj_str_t foundation;
/**
* Type of connection to the TURN server.
*
* Default is PJ_TURN_TP_UDP.
*/
pj_turn_tp_type conn_type;
} pj_ice_strans_cand;
/**
* 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;
/**
* This structure describes an ICE stream transport component. A component
* in ICE stream transport typically corresponds to a single socket created
* for this component, and bound to a specific transport address. This
* component may have multiple alias addresses, for example one alias
* address for each interfaces in multi-homed host, another for server
* reflexive alias, and another for relayed alias. For each transport
* address alias, an ICE stream transport candidate (#pj_ice_strans_cand) will
* be created, and these candidates will eventually registered to the ICE
* session.
*/
typedef struct pj_ice_strans_comp
{
pj_ice_strans *ice_st; /**< ICE stream transport. */
unsigned comp_id; /**< Component ID. */
pj_uint32_t options; /**< Option flags. */
pj_sock_t sock; /**< Socket descriptor. */
pj_stun_session *stun_sess; /**< STUN session. */
pj_uint8_t ka_tsx_id[12]; /**< ID for keep STUN alives */
pj_sockaddr local_addr; /**< Local/base address. */
unsigned pending_cnt; /**< Pending resolution cnt. */
pj_status_t last_status; /**< Last status. */
unsigned cand_cnt; /**< # of candidates/aliaes. */
pj_ice_strans_cand cand_list[PJ_ICE_ST_MAX_CAND]; /**< Cand array */
int default_cand; /**< Default candidate selected */
pj_ioqueue_key_t *key; /**< ioqueue key. */
pj_uint8_t pkt[1500]; /**< Incoming packet buffer. */
pj_ioqueue_op_key_t read_op; /**< ioqueue read operation key */
pj_ioqueue_op_key_t write_op; /**< ioqueue write op. key */
pj_sockaddr src_addr; /**< source packet address buf. */
int src_addr_len; /**< length of src addr. buf. */
} pj_ice_strans_comp;
/**
* This structure represents the ICE stream transport.
*/
struct pj_ice_strans
{
char obj_name[PJ_MAX_OBJ_NAME]; /**< Log ID. */
pj_pool_t *pool; /**< Pool used by this object. */
void *user_data; /**< Application data. */
pj_stun_config stun_cfg; /**< STUN settings. */
pj_ice_strans_cb cb; /**< Application callback. */
pj_ice_sess *ice; /**< ICE session. */
unsigned comp_cnt; /**< Number of components. */
pj_ice_strans_comp **comp; /**< Components array. */
pj_dns_resolver *resolver; /**< The resolver instance. */
pj_bool_t has_rjob; /**< Has pending resolve? */
pj_sockaddr_in stun_srv; /**< STUN server address. */
pj_sockaddr_in turn_srv; /**< TURN server address. */
pj_timer_entry ka_timer; /**< STUN keep-alive timer. */
};
/**
* Create the ICE stream transport containing the specified number of
* components. After the ICE stream transport is created, application
* may initialize the STUN server settings, and after that it has to
* initialize each components by calling #pj_ice_strans_create_comp()
* function.
/**
* Initialize ICE transport configuration with default values.
*
* @param cfg The configuration to be initialized.
*/
PJ_DECL(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg);
/**
* Copy configuration.
*
* @param pool Pool.
* @param dst Destination.
* @param src Source.
*/
PJ_DECL(void) pj_ice_strans_cfg_copy(pj_pool_t *pool,
pj_ice_strans_cfg *dst,
const pj_ice_strans_cfg *src);
/**
* Create and initialize the ICE stream transport with the specified
* parameters.
*
* @param 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.
*
* @param ice_st The ICE stream transport.
* @param stun_srv The STUN server address, or NULL if STUN
* reflexive candidate is not to be used.
* @param turn_srv The TURN server address, or NULL if STUN
* relay candidate is not to be used.
*
* @return PJ_SUCCESS, or the appropriate error code.
*/
PJ_DECL(pj_status_t)
pj_ice_strans_set_stun_srv( pj_ice_strans *ice_st,
const pj_sockaddr_in *stun_srv,
const pj_sockaddr_in *turn_srv);
/**
* Create and initialize the specified component. This function will
* instantiate the socket descriptor for this component, optionally
* bind the socket to the specified address (or bind to any address/port
* if the \a addr parameter is NULL), and start finding all alias
* addresses for this socket. For each alias addresses that if finds,
* it will add an ICE stream transport candidate for this component.
*
* After all components have been initialized, application should poll
* the #pj_ice_strans_get_comps_status() peridically to check if STUN
* server reflexive and relayed candidates have been obtained
* successfully.
*
* @param ice_st The ICE stream transport.
* @param comp_id The component ID, which value must be greater than
* zero and less than or equal to the number of
* components in this ICE stream transport.
* @param options Options, see #pj_ice_strans_option.
* @param addr Local address where socket will be bound to. This
* address will be used as follows:
* - if the value is NULL, then socket will be bound
* to any available port.
* - if the value is not NULL, then if the port number
* is not zero, it will used as the starting port
* where the socket will be bound to. If bind() to
* this port fails, this function will try to bind
* to port+2, repeatedly until it succeeded.
* If application doesn't want this function to
* retry binding the socket to other port, it can
* specify PJ_ICE_ST_OPT_NO_PORT_RETRY option.
* - if the value is not NULL, then if the address
* is not INADDR_ANY, this function will bind the
* socket to this particular interface only, and
* no other host candidates will be added for this
* socket.
*
*
* @return PJ_SUCCESS, or the appropriate error code.
*/
PJ_DECL(pj_status_t) pj_ice_strans_create_comp(pj_ice_strans *ice_st,
unsigned comp_id,
pj_uint32_t options,
const pj_sockaddr_in *addr);
/**
* Manually add a candidate (transport address alias) for the specified
* component. Normally application shouldn't need to use this function,
* as candidates will be added automatically when component is created
* with #pj_ice_strans_create_comp().
*
* @param ice_st ICE stream transport.
* @param comp_id The component ID.
* @param type The candidate type.
* @param local_pref The local preference for this candidate
* (typically the value is 65535).
* @param addr The candidate address.
* @param set_default Set to non-zero to make this candidate the
* default candidate for this component.
*
* @return PJ_SUCCESS, or the appropriate error code.
*/
PJ_DECL(pj_status_t) pj_ice_strans_add_cand(pj_ice_strans *ice_st,
unsigned comp_id,
pj_ice_cand_type type,
pj_uint16_t local_pref,
const pj_sockaddr_in *addr,
pj_bool_t set_default);
/**
* Get the status of components in the ICE stream transports. Since
* some IP address candidates have to be obtained asynchronously (for
* example, the STUN reflexive or relay candidate), application can
* use this function to know whether the address resolution has
* completed.
* Get the user data associated with the ICE stream transport.
*
* @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.
* @return The user data.
*/
PJ_DECL(pj_status_t) pj_ice_strans_get_comps_status(pj_ice_strans *ice_st);
PJ_DECL(void*) pj_ice_strans_get_user_data(pj_ice_strans *ice_st);
/**
* Initialize the ICE session in the ICE stream transport.
* When application is about to send an offer containing ICE capability,
* or when it receives an offer containing ICE capability, it must
* call this function to initialize the internal ICE session. This would
* register all transport address aliases for each component to the ICE
* session as candidates. Then application can enumerate all local
* candidates by calling #pj_ice_strans_enum_cands(), and encode these
* candidates in the SDP to be sent to remote agent.
*
* @param ice_st The ICE stream transport.
* @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[]);
/**
* Start ICE connectivity checks. This function can only be called
* after the ICE session has been created in the ICE stream transport.
* 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.
*
* 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.
* @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
* with #pj_ice_strans_init_ice().
*
* This function must be called once application has received remote
* candidate list (typically from the remote SDP). This function pairs
* local candidates with remote candidates, and starts ICE connectivity
* checks. The ICE session/transport will then notify the application
* via the callback when ICE connectivity checks completes, either
* successfully or with failure.
*
* @param ice_st The ICE stream transport.
* @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[]);
/**
* Stop and destroy the ICE session inside this media transport.
* Retrieve the candidate pair that has been nominated and successfully
* checked for the specified component. If ICE negotiation is still in
* progress or it has failed, this function will return NULL.
*
* @param ice_st The ICE stream transport.
* @param comp_id Component ID.
*
* @return The valid pair as ICE checklist structure if the
* pair exist.
*/
PJ_DECL(const pj_ice_sess_check*)
pj_ice_strans_get_valid_pair(const pj_ice_strans *ice_st,
unsigned comp_id);
/**
* Stop and destroy the ICE session inside this media transport. Application
* needs to call this function once the media session is over (the call has
* been disconnected).
*
* Application MAY reuse this ICE stream transport for subsequent calls.
* In this case, it must call #pj_ice_strans_stop_ice() when the call is
* disconnected, and reinitialize the ICE stream transport for subsequent
* call with #pj_ice_strans_init_ice()/#pj_ice_strans_start_ice(). In this
* case, the ICE stream transport will maintain the internal sockets and
* continue to send STUN keep-alive packets and TURN Refresh request to
* keep the NAT binding/TURN allocation open and to detect change in STUN
* mapped address.
*
* If application does not want to reuse the ICE stream transport for
* subsequent calls, it must call #pj_ice_strans_destroy() to destroy the
* ICE stream transport altogether.
*
* @param ice_st The ICE stream transport.
*
@ -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.

View File

@ -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

View File

@ -25,6 +25,8 @@
*/
#include <pjnath/stun_msg.h>
#include <pj/assert.h>
#include <pj/errno.h>
#include <pj/string.h>
@ -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;
}
/**
* @}
*/

View File

@ -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);
/**

View File

@ -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,

View File

@ -0,0 +1,403 @@
/* $Id$ */
/*
* Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __PJNATH_STUN_SOCK_H__
#define __PJNATH_STUN_SOCK_H__
/**
* @file stun_sock.h
* @brief STUN aware socket transport
*/
#include <pjnath/stun_config.h>
#include <pjlib-util/resolver.h>
#include <pj/ioqueue.h>
#include <pj/sock.h>
PJ_BEGIN_DECL
/**
* @defgroup PJNATH_STUN_SOCK STUN aware socket transport
* @brief STUN aware socket transport
* @ingroup PJNATH_STUN
* @{
* The STUN transport provides asynchronous UDP like socket transport
* with the additional capability to query the publicly mapped transport
* address (using STUN resolution), to refresh the NAT binding, and to
* demultiplex internal STUN messages from application data (the
* application data may be a STUN message as well).
*/
/**
* Opaque type to represent a STUN transport.
*/
typedef struct pj_stun_sock pj_stun_sock;
/**
* Types of operation being reported in \a on_status() callback of
* pj_stun_sock_cb. Application may retrieve the string representation
* of these constants with pj_stun_sock_op_name().
*/
typedef enum pj_stun_sock_op
{
/**
* Asynchronous DNS resolution.
*/
PJ_STUN_SOCK_DNS_OP = 1,
/**
* Initial STUN Binding request.
*/
PJ_STUN_SOCK_BINDING_OP,
/**
* Subsequent STUN Binding request for keeping the binding
* alive.
*/
PJ_STUN_SOCK_KEEP_ALIVE_OP,
} pj_stun_sock_op;
/**
* This structure contains callbacks that will be called by the STUN
* transport to notify application about various events.
*/
typedef struct pj_stun_sock_cb
{
/**
* Notification when incoming packet has been received.
*
* @param stun_sock The STUN transport.
* @param data The packet.
* @param data_len Length of the packet.
* @param src_addr The source address of the packet.
* @param addr_len The length of the source address.
*
* @return Application should normally return PJ_TRUE to let
* the STUN transport continue its operation. However
* it must return PJ_FALSE if it has destroyed the
* STUN transport in this callback.
*/
pj_bool_t (*on_rx_data)(pj_stun_sock *stun_sock,
void *pkt,
unsigned pkt_len,
const pj_sockaddr_t *src_addr,
unsigned addr_len);
/**
* Notifification when asynchronous send operation has completed.
*
* @param stun_sock The STUN transport.
* @param send_key The send operation key that was given in
* #pj_stun_sock_sendto().
* @param sent If value is positive non-zero it indicates the
* number of data sent. When the value is negative,
* it contains the error code which can be retrieved
* by negating the value (i.e. status=-sent).
*
* @return Application should normally return PJ_TRUE to let
* the STUN transport continue its operation. However
* it must return PJ_FALSE if it has destroyed the
* STUN transport in this callback.
*/
pj_bool_t (*on_data_sent)(pj_stun_sock *stun_sock,
pj_ioqueue_op_key_t *send_key,
pj_ssize_t sent);
/**
* Notification when the status of the STUN transport has changed. This
* callback may be called for the following conditions:
* - the first time the publicly mapped address has been resolved from
* the STUN server, this callback will be called with \a op argument
* set to PJ_STUN_SOCK_BINDING_OP \a status argument set to
* PJ_SUCCESS.
* - anytime when the transport has detected that the publicly mapped
* address has changed, this callback will be called with \a op
* argument set to PJ_STUN_SOCK_KEEP_ALIVE_OP and \a status
* argument set to PJ_SUCCESS. On this case and the case above,
* application will get the resolved public address in the
* #pj_stun_sock_info structure.
* - for any terminal error (such as STUN time-out, DNS resolution
* failure, or keep-alive failure), this callback will be called
* with the \a status argument set to non-PJ_SUCCESS.
*
* @param stun_sock The STUN transport.
* @param op The operation that triggers the callback.
* @param status The status.
*
* @return Must return PJ_FALSE if it has destroyed the
* STUN transport in this callback. Application should
* normally destroy the socket and return PJ_FALSE
* upon encountering terminal error, otherwise it
* should return PJ_TRUE to let the STUN socket operation
* continues.
*/
pj_bool_t (*on_status)(pj_stun_sock *stun_sock,
pj_stun_sock_op op,
pj_status_t status);
} pj_stun_sock_cb;
/**
* This structure contains information about the STUN transport. Application
* may query this information by calling #pj_stun_sock_get_info().
*/
typedef struct pj_stun_sock_info
{
/**
* The bound address of the socket.
*/
pj_sockaddr bound_addr;
/**
* IP address of the STUN server.
*/
pj_sockaddr srv_addr;
/**
* The publicly mapped address. It may contain zero address when the
* mapped address has not been resolved. Application may query whether
* this field contains valid address with pj_sockaddr_has_addr().
*/
pj_sockaddr mapped_addr;
/**
* Number of interface address aliases. The interface address aliases
* are list of all interface addresses in this host.
*/
unsigned alias_cnt;
/**
* Array of interface address aliases.
*/
pj_sockaddr aliases[PJ_ICE_ST_MAX_CAND];
} pj_stun_sock_info;
/**
* This describe the settings to be given to the STUN transport during its
* creation. Application should initialize this structure by calling
* #pj_stun_sock_cfg_default().
*/
typedef struct pj_stun_sock_cfg
{
/**
* Packet buffer size. Default value is PJ_STUN_SOCK_PKT_LEN.
*/
unsigned max_pkt_size;
/**
* Specify the number of simultaneous asynchronous read operations to
* be invoked to the ioqueue. Having more than one read operations will
* increase performance on multiprocessor systems since the application
* will be able to process more than one incoming packets simultaneously.
* Default value is 1.
*/
unsigned async_cnt;
/**
* Specify the interface where the socket should be bound to. If the
* address is zero, socket will be bound to INADDR_ANY. If the address
* is non-zero, socket will be bound to this address only, and the
* transport will have only one address alias (the \a alias_cnt field
* in #pj_stun_sock_info structure.
*/
pj_sockaddr bound_addr;
/**
* Specify the STUN keep-alive duration, in seconds. The STUN transport
* does keep-alive by sending STUN Binding request to the STUN server.
* If this value is zero, the PJ_STUN_KEEP_ALIVE_SEC value will be used.
* If the value is negative, it will disable STUN keep-alive.
*/
int ka_interval;
} pj_stun_sock_cfg;
/**
* Retrieve the name representing the specified operation.
*/
PJ_DECL(const char*) pj_stun_sock_op_name(pj_stun_sock_op op);
/**
* Initialize the STUN transport setting with its default values.
*
* @param cfg The STUN transport config.
*/
PJ_DECL(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg);
/**
* Create the STUN transport using the specified configuration. Once
* the STUN transport has been create, application should call
* #pj_stun_sock_start() to start the transport.
*
* @param stun_cfg The STUN configuration which contains among other
* things the ioqueue and timer heap instance for
* the operation of this transport.
* @param af Address family of socket. Currently pj_AF_INET()
* and pj_AF_INET6() are supported.
* @param name Optional name to be given to this transport to
* assist debugging.
* @param cb Callback to receive events/data from the transport.
* @param cfg Optional transport settings.
* @param user_data Arbitrary application data to be associated with
* this transport.
* @param p_sock Pointer to receive the created transport instance.
*
* @restun PJ_SUCCESS if the operation has been successful,
* or the appropriate error code on failure.
*/
PJ_DECL(pj_status_t) pj_stun_sock_create(pj_stun_config *stun_cfg,
const char *name,
int af,
const pj_stun_sock_cb *cb,
const pj_stun_sock_cfg *cfg,
void *user_data,
pj_stun_sock **p_sock);
/**
* Start the STUN transport. This will start the DNS SRV resolution for
* the STUN server (if desired), and once the server is resolved, STUN
* Binding request will be sent to resolve the publicly mapped address.
* Once the initial STUN Binding response is received, the keep-alive
* timer will be started.
*
* @param stun_sock The STUN transport instance.
* @param domain The domain, hostname, or IP address of the TURN
* server. When this parameter contains domain name,
* the \a resolver parameter must be set to activate
* DNS SRV resolution.
* @param default_port The default STUN port number to use when DNS SRV
* resolution is not used. If DNS SRV resolution is
* used, the server port number will be set from the
* DNS SRV records. The recommended value for this
* parameter is PJ_STUN_PORT.
* @param resolver If this parameter is not NULL, then the \a domain
* parameter will be first resolved with DNS SRV and
* then fallback to using DNS A/AAAA resolution when
* DNS SRV resolution fails. If this parameter is
* NULL, the \a domain parameter will be resolved as
* hostname.
*
* @return PJ_SUCCESS if the operation has been successfully
* queued, or the appropriate error code on failure.
* When this function returns PJ_SUCCESS, the final
* result of the allocation process will be notified
* to application in \a on_state() callback.
*/
PJ_DECL(pj_status_t) pj_stun_sock_start(pj_stun_sock *stun_sock,
const pj_str_t *domain,
pj_uint16_t default_port,
pj_dns_resolver *resolver);
/**
* Destroy the STUN transport.
*
* @param sock The STUN transport socket.
*
* @restun PJ_SUCCESS if the operation has been successful,
* or the appropriate error code on failure.
*/
PJ_DECL(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *sock);
/**
* Associate a user data with this STUN transport. The user data may then
* be retrieved later with #pj_stun_sock_get_user_data().
*
* @param stun_sock The STUN transport instance.
* @param user_data Arbitrary data.
*
* @restun PJ_SUCCESS if the operation has been successful,
* or the appropriate error code on failure.
*/
PJ_DECL(pj_status_t) pj_stun_sock_set_user_data(pj_stun_sock *stun_sock,
void *user_data);
/**
* Retrieve the previously assigned user data associated with this STUN
* transport.
*
* @param stun_sock The STUN transport instance.
*
* @restun The user/application data.
*/
PJ_DECL(void*) pj_stun_sock_get_user_data(pj_stun_sock *stun_sock);
/**
* Get the STUN transport info. The transport info contains, among other
* things, the allocated relay address.
*
* @param stun_sock The STUN transport instance.
* @param info Pointer to be filled with STUN transport info.
*
* @restun PJ_SUCCESS if the operation has been successful,
* or the appropriate error code on failure.
*/
PJ_DECL(pj_status_t) pj_stun_sock_get_info(pj_stun_sock *stun_sock,
pj_stun_sock_info *info);
/**
* Send a data to the specified address. This function may complete
* asynchronously and in this case \a on_data_sent() will be called.
*
* @param stun_sock The STUN transport instance.
* @param op_key Optional send key for sending the packet down to
* the ioqueue. This value will be given back to
* \a on_data_sent() callback
* @param pkt The data/packet to be sent to peer.
* @param pkt_len Length of the data.
* @param flag pj_ioqueue_sendto() flag.
* @param dst_addr The remote address.
* @param addr_len Length of the address.
*
* @return PJ_SUCCESS if data has been sent immediately, or
* PJ_EPENDING if data cannot be sent immediately. In
* this case the \a on_data_sent() callback will be
* called when data is actually sent. Any other return
* value indicates error condition.
*/
PJ_DECL(pj_status_t) pj_stun_sock_sendto(pj_stun_sock *stun_sock,
pj_ioqueue_op_key_t *send_key,
const void *pkt,
unsigned pkt_len,
unsigned flag,
const pj_sockaddr_t *dst_addr,
unsigned addr_len);
/**
* @}
*/
PJ_END_DECL
#endif /* __PJNATH_STUN_SOCK_H__ */

View File

@ -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);

View File

@ -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);
/**
* Set the server or domain name of the server.
* Configure message logging. By default all flags are enabled.
*
* @param sess The TURN client session.
* @param flags Bitmask combination of #pj_stun_sess_msg_log_flag
*/
PJ_DECL(void) pj_turn_session_set_log(pj_turn_session *sess,
unsigned flags);
/**
* Set the server or domain name of the server. Before the application
* can send Allocate request (with pj_turn_session_alloc()), it must first
* resolve the server address(es) using this function. This function will
* resolve the TURN server using DNS SRV resolution if the \a resolver
* is set. The server resolution process will complete asynchronously,
* and application will be notified in \a on_state() callback with the
* session state set to PJ_TURN_STATE_RESOLVED.
*
* Application may call with pj_turn_session_alloc() before the server
* resolution completes. In this case, the operation will be queued by
* the session, and it will be sent once the server resolution completes.
*
* @param sess The TURN client session.
* @param domain The domain, hostname, or IP address of the TURN
* server. When this parameter contains domain name,
* the \a resolver parameter must be set to activate
* DNS SRV resolution.
* @param default_port The default TURN port number to use when DNS SRV
* resolution is not used. If DNS SRV resolution is
* used, the server port number will be set from the
* DNS SRV records.
* @param resolver If this parameter is not NULL, then the \a domain
* parameter will be first resolved with DNS SRV and
* then fallback to using DNS A/AAAA resolution when
* DNS SRV resolution fails. If this parameter is
* NULL, the \a domain parameter will be resolved as
* hostname.
*
* @return PJ_SUCCESS if the operation has been successfully
* queued, or the appropriate error code on failure.
* When this function returns PJ_SUCCESS, the final
* result of the resolution process will be notified
* to application in \a on_state() callback.
*/
PJ_DECL(pj_status_t) pj_turn_session_set_server(pj_turn_session *sess,
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);
/**

View File

@ -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.
*/
PJ_DECL(pj_status_t) pj_turn_sock_set_user_data(pj_turn_sock *turn_sock,
void *user_data);
/**
* Get 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);
/**
* 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);
/**
* Initialize.
* 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_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(pj_status_t) pj_turn_sock_lock(pj_turn_sock *turn_sock);
/**
* Send packet.
* Release the internal mutex previously held with pj_turn_sock_lock().
*
* @param turn_sock The TURN transport instance.
*
* @return PJ_SUCCESS if the operation has been successful,
* or the appropriate error code on failure.
*/
PJ_DECL(pj_status_t) pj_turn_sock_unlock(pj_turn_sock *turn_sock);
/**
* Set STUN message logging for this TURN session.
* See #pj_stun_session_set_log().
*
* @param turn_sock The TURN transport instance.
* @param flags Bitmask combination of #pj_stun_sess_msg_log_flag
*/
PJ_DECL(void) pj_turn_sock_set_log(pj_turn_sock *turn_sock,
unsigned flags);
/**
* Allocate a relay address/resource in the TURN server. This function
* will resolve the TURN server using DNS SRV (if desired) and send TURN
* \a Allocate request using the specified credential to allocate a relay
* address in the server. This function completes asynchronously, and
* application will be notified when the allocation process has been
* successful in the \a on_state() callback when the state is set to
* PJ_TURN_STATE_READY. If the allocation fails, the state will be set
* to PJ_TURN_STATE_DEALLOCATING or greater.
*
* @param turn_sock The TURN transport instance.
* @param domain The domain, hostname, or IP address of the TURN
* server. When this parameter contains domain name,
* the \a resolver parameter must be set to activate
* DNS SRV resolution.
* @param default_port The default TURN port number to use when DNS SRV
* resolution is not used. If DNS SRV resolution is
* used, the server port number will be set from the
* DNS SRV records.
* @param resolver If this parameter is not NULL, then the \a domain
* parameter will be first resolved with DNS SRV and
* then fallback to using DNS A/AAAA resolution when
* DNS SRV resolution fails. If this parameter is
* NULL, the \a domain parameter will be resolved as
* hostname.
* @param cred The STUN credential to be used for the TURN server.
* @param param Optional TURN allocation parameter.
*
* @return PJ_SUCCESS if the operation has been successfully
* queued, or the appropriate error code on failure.
* When this function returns PJ_SUCCESS, the final
* result of the allocation process will be notified
* to application in \a on_state() callback.
*
*/
PJ_DECL(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock,
const pj_str_t *domain,
int default_port,
pj_dns_resolver *resolver,
const pj_stun_auth_cred *cred,
const pj_turn_alloc_param *param);
/**
* Send a data to the specified peer address via the TURN relay. This
* function will encapsulate the data as STUN Send Indication or TURN
* ChannelData packet and send the message to the TURN server. The TURN
* server then will send the data to the peer.
*
* The allocation (pj_turn_sock_alloc()) must have been successfully
* created before application can relay any data.
*
* @param turn_sock The TURN transport instance.
* @param pkt The data/packet to be sent to peer.
* @param pkt_len Length of the data.
* @param peer_addr The remote peer address (the ultimate destination
* of the data, and not the TURN server address).
* @param addr_len Length of the address.
*
* @return PJ_SUCCESS if the operation has been successful,
* or the appropriate error code on failure.
*/
PJ_DECL(pj_status_t) pj_turn_sock_sendto(pj_turn_sock *turn_sock,
const pj_uint8_t *pkt,
unsigned pkt_len,
const pj_sockaddr_t *addr,
const pj_sockaddr_t *peer_addr,
unsigned addr_len);
/**
* Bind a peer address to a channel number.
* Optionally establish channel binding for the specified a peer address.
* This function will assign a unique channel number for the peer address
* and request channel binding to the TURN server for this address. When
* a channel has been bound to a peer, the TURN transport and TURN server
* will exchange data using ChannelData encapsulation format, which has
* lower bandwidth overhead than Send Indication (the default format used
* when peer address is not bound to a channel).
*
* @param turn_sock The TURN transport instance.
* @param peer The remote peer address.
* @param addr_len Length of the address.
*
* @return PJ_SUCCESS if the operation has been successful,
* or the appropriate error code on failure.
*/
PJ_DECL(pj_status_t) pj_turn_sock_bind_channel(pj_turn_sock *turn_sock,
const pj_sockaddr_t *peer,
unsigned addr_len);
const pj_sockaddr_t *peer,
unsigned addr_len);
/**
@ -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__ */

View File

@ -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
* <B>STUN-bis</B> specification and the <B>TURN</B> usage of STUN,
* as well as other STUN usages. Please see #pj_stun_attr_type for
* list of STUN attributes supported by this library.
*
*
* The following are some design principles that have been utilized
* when implementing the STUN library in PJNATH:
*
* - layered architecture, with \ref PJNATH_STUN_MSG as the lowest
* layer and \ref PJNATH_STUN_SESSION as the highest abstraction
* layer, to accommodate various usage scenario of the library.
*
* - no transport -- the STUN library is pretty much transport
* independent and all sending and receiving functionalities will
* have to be implemented by application or higher level
* abstraction (such as ICE). This helps facilitating an even
* more usage scenarios of the library.
*
* - common functionalities for both STUN client and server
* development. All STUN components can be used to develop both
* STUN client and STUN server application, and in fact, in ICE,
* both STUN client and server functionality exist in a single
* ICE session.
*
* \n
*
* \subsection PJNATH_STUN_ARCH STUN Library Organization
*
* \image html stun-arch.jpg "STUN Library Architecture"
*
* The STUN library is organized as follows:
*
* - for both client and server, the highest abstraction is
* \ref PJNATH_STUN_SESSION, which provides management of incoming
* and outgoing messages and association of STUN credential to
* a STUN session.
*
* - for client, the next layer below is \ref PJNATH_STUN_TRANSACTION,
* which manages retransmissions of STUN request. Server side STUN
* transaction is handled in \ref PJNATH_STUN_SESSION layer above.
*
* - \ref PJNATH_STUN_AUTH provides mechanism to verify STUN
* credential in incoming STUN messages.
*
* - the lowest layer of the library is \ref PJNATH_STUN_MSG. This layer
* provides STUN message representation, validation, parsing,
* encoding MESSAGE-INTEGRITY for outgoing messages, and
* debugging (dump to log) of STUN messages.
*
* All STUN library components are independent of any transports.
* Application gives incoming packet to the STUN components for processing,
* and it must supply the STUN components with callback to send outgoing
* messages.
*
*
* \n
*
* \subsection PJNATH_STUN_CLASSES PJNATH Class Diagram
*
*
* \image html UML-class-diagram.png "Class Diagram"
*
* TBD: write descriptions.
*
* \subsection PJNATH_STUN_USING Using STUN Library
*
* [The developers guide documentation can certainly be improved here]
*
* For a sample STUN and TURN client, please see <tt>pjstun-client</tt>
* project under <tt>pjnath/src</tt> directory.
*
* For a sample STUN and TURN server, please see <tt>pjstun-srv-test</tt>
* project under <tt>pjnath/src</tt> directory.
*
*
* \subsection PJNATH_STUN_REF STUN Reference
*
* References for STUN:
*
* - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-rfc3489bis-15.txt">
* <B>draft-ietf-behave-rfc3489bis-15</b></A>: Session Traversal
* Utilities for (NAT) (STUN),
* - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-turn-07.txt">
* <B>draft-ietf-behave-turn-07</B></A>: Obtaining Relay Addresses
* from Simple Traversal Underneath NAT (STUN)
* - Obsoleted: <A HREF="http://www.ietf.org/rfc/rfc3489.txt">RFC 3489</A>.
*
* \n
*
* \section PJNATH_ICE ICE Implementation
*
* Interactive Connectivity Establishment (ICE) is a standard based
* methodology for traversing Network Address Translator (NAT), and
* is described in
* <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-mmusic-ice-19.txt">
* <B>draft-ietf-mmusic-ice-19.txt</B></A> draft. The PJNATH ICE
* implementation is aimed to provide a usable and generic ICE transports
* for different types of application, including but not limited to
* the usage of ICE in SIP/SDP offer/answer.
*
*
* \subsection PJNATH_ICE_ARCH ICE Library Organization
*
* \image html ice-arch.jpg "ICE Architecture"
*
* The ICE library is organized as follows:
*
* - the highest abstraction is ICE media transport, which maintains
* ICE stream transport and provides SDP translations to be used
* for SIP offer/answer exchanges. ICE media transport is part
* of PJMEDIA library.
*
* - higher in the hierarchy is \ref PJNATH_ICE_STREAM_TRANSPORT,
* which binds ICE with UDP sockets, and provides STUN binding
* and relay/TURN allocation for the sockets. This component can
* be directly used by application, although normally application
* should use the next higher abstraction since it provides
* SDP translations and better integration with other PJ libraries
* such as PJSIP and PJMEDIA.
*
* - the lowest layer is \ref PJNATH_ICE_SESSION, which provides
* ICE management and negotiation in a transport-independent way.
* This layer contains the state machines to perform ICE
* negotiation, and provides the most flexibility to control all
* aspects of ICE session. This layer normally is only usable for
* ICE implementors.
*
* \subsection PJNATH_ICE_USING Using the ICE Library
*
* For ICE implementation that has been integrated with socket transport,
* please see \ref PJNATH_ICE_STREAM_TRANSPORT_USING.
*
* For ICE implementation that has not been integrated with socket
* transport, please see \ref pj_ice_sess_using_sec.
*
* \subsection PJNATH_ICE_REF Reference
*
* References for ICE:
* - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-mmusic-ice-19.txt">
* <B>draft-ietf-mmusic-ice-19.txt</B></A>: Interactive Connectivity
* Establishment (ICE): A Methodology for Network Address Translator
* (NAT) Traversal for Offer/Answer Protocols
*/
@mainpage PJNATH - Open Source ICE, STUN, and TURN Library
\n
This is the documentation of PJNATH, an Open Source library providing
NAT traversal helper functionalities by using standard based protocols
such as STUN, TURN, and ICE.
\n
\n
\section lib_comps Library Components
\subsection comp_stun STUN
Session Traversal Utilities (STUN, or previously known as Simple
Traversal of User Datagram Protocol (UDP) Through Network Address
Translators (NAT)s), is a lightweight protocol that serves as a tool for
application protocols in dealing with NAT traversal. It allows a client
to determine the IP address and port allocated to them by a NAT and to
keep NAT bindings open.
This version of PJNATH implements the following STUN-bis draft:
- <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-rfc3489bis-15.txt">
<B>draft-ietf-behave-rfc3489bis-15</b></A>: Session Traversal
Utilities for (NAT) (STUN),
\subsection comp_turn TURN
Traversal Using Relays around NAT (TURN) allows the host to control the
operation of the relay and to exchange packets with its peers using the relay.
This version of PJNATH implements both TCP and UDP client transport and it
complies with the following TURN draft:
- <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-turn-07.txt">
<B>draft-ietf-behave-turn-07</B></A>: Obtaining Relay Addresses
from Simple Traversal Underneath NAT (STUN)
\subsection comp_ice ICE
Interactive Connectivity Establishment (ICE) is a standard based
methodology for traversing Network Address Translator (NAT). This
implementation is aimed to provide a usable and generic ICE transports
for different types of application, including but not limited to
the usage of ICE in SIP/SDP offer/answer.
This version of PJNATH implements the following ICE draft:
- <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-mmusic-ice-19.txt">
<B>draft-ietf-mmusic-ice-19.txt</B></A> draft. The PJNATH ICE
\subsection comp_natck NAT Classification Utility
The PJNATH library also provides NAT classification utility as
described in <A HREF="http://www.ietf.org/rfc/rfc3489.txt">RFC 3489</A>.
While the practice to detect the NAT type to assist NAT traversal
has been deprecated in favor of ICE, the information may still be
useful for troubleshooting purposes, hence the utility is provided.
\n
\n
\section lib_org Library Organization
The PJNATH library consists of many components with each providing
specific functionality that may or may not be of the interests of
applications (or application developers). This section attempts to
give brief overview on the components provided by PJNATH.
The PJNATH components from the highest layer to the lower layer are
as follows.
\n
\subsection user_comp High-level Transport Objects
PJNATH library provides some high-level objects that may be used
by applications:
\subsubsection stun_sock STUN Transport
The \ref PJNATH_STUN_SOCK provides asynchronous UDP like socket transport
with the additional capability to query the publicly mapped transport
address (using STUN resolution), to refresh the NAT binding, and to
demultiplex internal STUN messages from application data (the
application data may be a STUN message as well).
\subsubsection turn_sock TURN Client Transport
The \ref PJNATH_TURN_SOCK may be used by the application to send and
receive data via TURN server. For more information please see the
documentation of \ref PJNATH_TURN_SOCK.
\subsubsection ice_strans ICE Stream Transport
The \ref PJNATH_ICE_STREAM_TRANSPORT provides transport interface to
send and receive data through connection that is negotiated
with ICE protocol. The \ref PJNATH_ICE_STREAM_TRANSPORT naturally
contains both STUN Transport and \ref PJNATH_TURN_SOCK.
The \ref PJNATH_ICE_STREAM_TRANSPORT interface is suitable for both
SIP or non-SIP use. For SIP use, application may prefer to use the
ICE media transport in PJMEDIA instead where it has been integrated
with the SDP offer and answer mechanism.
\subsubsection natck NAT Classification Utility
PJNATH also provides \a PJNATH_NAT_DETECT to assist troubleshooting
of problems related to NAT traversal.
\n
\subsection sessions Transport Independent Sessions Layer
Right below the high level transports objects are the transport
independent sessions. These sessions don't have access to sockets,
so higher level objects (such as transports) must give incoming
packets to the sessions and provide callback to be called by
sessions to send outgoing packets.
\subsubsection ice_sess ICE Session
The \ref PJNATH_ICE_SESSION is used by the \ref PJNATH_ICE_STREAM_TRANSPORT
and contains the actual logic of the ICE negotiation.
\subsubsection turn_sess TURN Session
The \ref PJNATH_TURN_SESSION is used by the \ref PJNATH_TURN_SOCK
and it contains TURN protocol logic. Implementors may implement
other types of TURN client connection (such as TURN TLS client)
by utilizing this session.
\subsubsection stun_sess STUN Session
The \ref PJNATH_STUN_SESSION manages STUN message exchange between
a client and server (or vice versa). It manages \ref PJNATH_STUN_TRANSACTION
for sending or receiving requests and \ref PJNATH_STUN_AUTH for both
both incoming and outgoing STUN messages.
The \ref PJNATH_STUN_SESSION is naturally used by the \ref PJNATH_TURN_SESSION
and \ref PJNATH_ICE_SESSION
\n
\subsection stun_tsx STUN Transaction Layer
The \ref PJNATH_STUN_TRANSACTION is a thin layer to manage retransmission
of STUN requests.
\n
\subsection stun_msg STUN Messaging Layer
At the very bottom of the PJNATH components is the \ref PJNATH_STUN_MSG
layer. The API contains various representation of STUN messaging components
and it provides API to encode and decode STUN messages.
\n
\n
\section class_dia Class Diagram
The following class diagram shows the interactions between objects in
PJNATH:
\image html UML-class-diagram.png "Class Diagram"
\image latex UML-class-diagram.png "Class Diagram"
\n
\n
\section samples Sample Applications
Some sample applications have been provided with PJNATH, and it's available
under <tt>pjnath/src</tt> directory:
- <b>pjturn-client</b>: this is a stand-alone, console based TURN client
application to be used as a demonstration for PJNATH TURN client
transport API and for simple testing against TURN server implementations.
The client supports both UDP and TCP connection to the TURN server.
- <b>pjturn-srv</b>: this is a simple TURN server to be used for testing
purposes. It supports both UDP and TCP connections to the clients.
*/
/**
* @defgroup PJNATH_STUN STUN Library

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,652 @@
/* $Id$ */
/*
* Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "server.h"
#include "test.h"
#define THIS_FILE "server.c"
#define MAX_STUN_PKT 1500
#define TURN_NONCE "thenonce"
static pj_bool_t stun_on_data_recvfrom(pj_activesock_t *asock,
void *data,
pj_size_t size,
const pj_sockaddr_t *src_addr,
int addr_len,
pj_status_t status);
static pj_bool_t turn_on_data_recvfrom(pj_activesock_t *asock,
void *data,
pj_size_t size,
const pj_sockaddr_t *src_addr,
int addr_len,
pj_status_t status);
static pj_bool_t alloc_on_data_recvfrom(pj_activesock_t *asock,
void *data,
pj_size_t size,
const pj_sockaddr_t *src_addr,
int addr_len,
pj_status_t status);
pj_status_t create_test_server(pj_stun_config *stun_cfg,
pj_uint32_t flags,
const char *domain,
test_server **p_test_srv)
{
pj_pool_t *pool;
test_server *test_srv;
pj_sockaddr hostip;
char strbuf[100];
pj_status_t status;
PJ_ASSERT_RETURN(stun_cfg && domain && p_test_srv, PJ_EINVAL);
status = pj_gethostip(pj_AF_INET(), &hostip);
if (status != PJ_SUCCESS)
return status;
pool = pj_pool_create(mem, THIS_FILE, 512, 512, NULL);
test_srv = (test_server*) PJ_POOL_ZALLOC_T(pool, test_server);
test_srv->pool = pool;
test_srv->flags = flags;
test_srv->stun_cfg = stun_cfg;
pj_strdup2(pool, &test_srv->domain, domain);
test_srv->username = pj_str(TURN_USERNAME);
test_srv->passwd = pj_str(TURN_PASSWD);
pj_ioqueue_op_key_init(&test_srv->send_key, sizeof(test_srv->send_key));
if (flags & CREATE_DNS_SERVER) {
status = pj_dns_server_create(mem, test_srv->stun_cfg->ioqueue,
pj_AF_INET(), DNS_SERVER_PORT,
0, &test_srv->dns_server);
if (status != PJ_SUCCESS) {
destroy_test_server(test_srv);
return status;
}
/* Add DNS A record for the domain, for fallback */
if (flags & CREATE_A_RECORD_FOR_DOMAIN) {
pj_dns_parsed_rr rr;
pj_str_t res_name;
pj_in_addr ip_addr;
pj_strdup2(pool, &res_name, domain);
ip_addr = hostip.ipv4.sin_addr;
pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr);
pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
}
}
if (flags & CREATE_STUN_SERVER) {
pj_activesock_cb stun_sock_cb;
pj_sockaddr bound_addr;
pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb));
stun_sock_cb.on_data_recvfrom = &stun_on_data_recvfrom;
pj_sockaddr_in_init(&bound_addr.ipv4, NULL, STUN_SERVER_PORT);
status = pj_activesock_create_udp(pool, &bound_addr, NULL,
test_srv->stun_cfg->ioqueue,
&stun_sock_cb, test_srv,
&test_srv->stun_sock, NULL);
if (status != PJ_SUCCESS) {
destroy_test_server(test_srv);
return status;
}
status = pj_activesock_start_recvfrom(test_srv->stun_sock, pool,
MAX_STUN_PKT, 0);
if (status != PJ_SUCCESS) {
destroy_test_server(test_srv);
return status;
}
if (test_srv->dns_server && (flags & CREATE_STUN_SERVER_DNS_SRV)) {
pj_str_t res_name, target;
pj_dns_parsed_rr rr;
pj_in_addr ip_addr;
/* Add DNS entries:
* _stun._udp.domain 60 IN SRV 0 0 PORT stun.domain.
* stun.domain IN A 127.0.0.1
*/
pj_ansi_snprintf(strbuf, sizeof(strbuf),
"_stun._udp.%s", domain);
pj_strdup2(pool, &res_name, strbuf);
pj_ansi_snprintf(strbuf, sizeof(strbuf),
"stun.%s", domain);
pj_strdup2(pool, &target, strbuf);
pj_dns_init_srv_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, 0, 0,
STUN_SERVER_PORT, &target);
pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
res_name = target;
ip_addr = hostip.ipv4.sin_addr;
pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr);
pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
}
}
if (flags & CREATE_TURN_SERVER) {
pj_activesock_cb turn_sock_cb;
pj_sockaddr bound_addr;
pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb));
turn_sock_cb.on_data_recvfrom = &turn_on_data_recvfrom;
pj_sockaddr_in_init(&bound_addr.ipv4, NULL, TURN_SERVER_PORT);
status = pj_activesock_create_udp(pool, &bound_addr, NULL,
test_srv->stun_cfg->ioqueue,
&turn_sock_cb, test_srv,
&test_srv->turn_sock, NULL);
if (status != PJ_SUCCESS) {
destroy_test_server(test_srv);
return status;
}
status = pj_activesock_start_recvfrom(test_srv->turn_sock, pool,
MAX_STUN_PKT, 0);
if (status != PJ_SUCCESS) {
destroy_test_server(test_srv);
return status;
}
if (test_srv->dns_server && (flags & CREATE_TURN_SERVER_DNS_SRV)) {
pj_str_t res_name, target;
pj_dns_parsed_rr rr;
pj_in_addr ip_addr;
/* Add DNS entries:
* _turn._udp.domain 60 IN SRV 0 0 PORT turn.domain.
* turn.domain IN A 127.0.0.1
*/
pj_ansi_snprintf(strbuf, sizeof(strbuf),
"_turn._udp.%s", domain);
pj_strdup2(pool, &res_name, strbuf);
pj_ansi_snprintf(strbuf, sizeof(strbuf),
"turn.%s", domain);
pj_strdup2(pool, &target, strbuf);
pj_dns_init_srv_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, 0, 0,
TURN_SERVER_PORT, &target);
pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
res_name = target;
ip_addr = hostip.ipv4.sin_addr;
pj_dns_init_a_rr(&rr, &res_name, PJ_DNS_CLASS_IN, 60, &ip_addr);
pj_dns_server_add_rec(test_srv->dns_server, 1, &rr);
}
}
*p_test_srv = test_srv;
return PJ_SUCCESS;
}
void destroy_test_server(test_server *test_srv)
{
unsigned i;
PJ_ASSERT_ON_FAIL(test_srv, return);
for (i=0; i<test_srv->turn_alloc_cnt; ++i) {
pj_activesock_close(test_srv->turn_alloc[i].sock);
pj_pool_release(test_srv->turn_alloc[i].pool);
}
test_srv->turn_alloc_cnt = 0;
if (test_srv->turn_sock) {
pj_activesock_close(test_srv->turn_sock);
test_srv->turn_sock = NULL;
}
if (test_srv->stun_sock) {
pj_activesock_close(test_srv->stun_sock);
test_srv->stun_sock = NULL;
}
if (test_srv->dns_server) {
pj_dns_server_destroy(test_srv->dns_server);
test_srv->dns_server = NULL;
}
if (test_srv->pool) {
pj_pool_t *pool = test_srv->pool;
test_srv->pool = NULL;
pj_pool_release(pool);
}
}
static pj_bool_t stun_on_data_recvfrom(pj_activesock_t *asock,
void *data,
pj_size_t size,
const pj_sockaddr_t *src_addr,
int addr_len,
pj_status_t status)
{
test_server *test_srv;
pj_stun_msg *req, *resp = NULL;
pj_pool_t *pool;
pj_ssize_t len;
if (status != PJ_SUCCESS)
return PJ_TRUE;
test_srv = (test_server*) pj_activesock_get_user_data(asock);
pool = pj_pool_create(test_srv->stun_cfg->pf, NULL, 512, 512, NULL);
status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size,
PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET,
&req, NULL, NULL);
if (status != PJ_SUCCESS)
goto on_return;
if (req->hdr.type != PJ_STUN_BINDING_REQUEST) {
pj_stun_msg_create_response(pool, req, PJ_STUN_SC_BAD_REQUEST,
NULL, &resp);
goto send_pkt;
}
status = pj_stun_msg_create_response(pool, req, 0, NULL, &resp);
if (status != PJ_SUCCESS)
goto on_return;
pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_XOR_MAPPED_ADDR,
PJ_TRUE, src_addr, addr_len);
send_pkt:
status = pj_stun_msg_encode(resp, (pj_uint8_t*)data, MAX_STUN_PKT,
0, NULL, &size);
if (status != PJ_SUCCESS)
goto on_return;
len = size;
status = pj_activesock_sendto(asock, &test_srv->send_key, data, &len,
0, src_addr, addr_len);
on_return:
pj_pool_release(pool);
return PJ_TRUE;
}
static pj_stun_msg* create_success_response(test_server *test_srv,
turn_allocation *alloc,
pj_stun_msg *req,
pj_pool_t *pool,
unsigned lifetime,
pj_str_t *auth_key)
{
pj_stun_msg *resp;
pj_str_t tmp;
pj_status_t status;
/* Create response */
status = pj_stun_msg_create_response(pool, req, 0, NULL, &resp);
if (status != PJ_SUCCESS) {
return NULL;
}
/* Add TURN_NONCE */
pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, TURN_NONCE));
/* Add LIFETIME */
pj_stun_msg_add_uint_attr(pool, resp, PJ_STUN_ATTR_LIFETIME, lifetime);
if (lifetime != 0) {
/* Add RELAY-ADDRESS */
pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_RELAY_ADDR, PJ_TRUE, &alloc->alloc_addr,
pj_sockaddr_get_len(&alloc->alloc_addr));
/* Add XOR-MAPPED-ADDRESS */
pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &alloc->client_addr,
pj_sockaddr_get_len(&alloc->client_addr));
}
/* Add blank MESSAGE-INTEGRITY */
pj_stun_msg_add_msgint_attr(pool, resp);
/* Set auth key */
pj_stun_create_key(pool, auth_key, &test_srv->domain, &test_srv->username,
PJ_STUN_PASSWD_PLAIN, &test_srv->passwd);
return resp;
}
static pj_bool_t turn_on_data_recvfrom(pj_activesock_t *asock,
void *data,
pj_size_t size,
const pj_sockaddr_t *src_addr,
int addr_len,
pj_status_t status)
{
test_server *test_srv;
pj_pool_t *pool;
turn_allocation *alloc;
pj_stun_msg *req, *resp = NULL;
pj_str_t auth_key = { NULL, 0 };
char client_info[PJ_INET6_ADDRSTRLEN+10];
unsigned i;
pj_ssize_t len;
if (status != PJ_SUCCESS)
return PJ_TRUE;
pj_sockaddr_print(src_addr, client_info, sizeof(client_info), 3);
test_srv = (test_server*) pj_activesock_get_user_data(asock);
pool = pj_pool_create(test_srv->stun_cfg->pf, NULL, 512, 512, NULL);
status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size,
PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET |
PJ_STUN_NO_FINGERPRINT_CHECK,
&req, NULL, NULL);
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(1,("", "STUN message decode error from client %s: %s", client_info, errmsg));
goto on_return;
}
/* Find the client */
for (i=0; i<test_srv->turn_alloc_cnt; i++) {
if (pj_sockaddr_cmp(&test_srv->turn_alloc[i].client_addr, src_addr)==0)
break;
}
if (i==test_srv->turn_alloc_cnt) {
/* New client */
//pj_str_t ip_addr;
pj_stun_username_attr *uname;
pj_activesock_cb alloc_sock_cb;
turn_allocation *alloc;
/* Must be Allocate request */
if (req->hdr.type != PJ_STUN_ALLOCATE_REQUEST) {
PJ_LOG(1,(THIS_FILE, "Invalid %s %s from client %s",
pj_stun_get_method_name(req->hdr.type),
pj_stun_get_class_name(req->hdr.type),
client_info));
if (PJ_STUN_IS_REQUEST(req->hdr.type))
pj_stun_msg_create_response(pool, req, PJ_STUN_SC_BAD_REQUEST, NULL, &resp);
goto send_pkt;
}
test_srv->turn_stat.rx_allocate_cnt++;
/* Skip if we're not responding to Allocate request */
if (!test_srv->turn_respond_allocate)
return PJ_TRUE;
/* Check if we have too many clients */
if (test_srv->turn_alloc_cnt == MAX_TURN_ALLOC) {
pj_stun_msg_create_response(pool, req, PJ_STUN_SC_INSUFFICIENT_CAPACITY, NULL, &resp);
goto send_pkt;
}
/* Get USERNAME attribute */
uname = (pj_stun_username_attr*)
pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0);
/* Reject if it doesn't have MESSAGE-INTEGRITY or USERNAME attributes or
* the user is incorrect
*/
if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0) == NULL ||
uname==NULL || pj_stricmp2(&uname->value, TURN_USERNAME) != 0)
{
pj_str_t tmp;
pj_stun_msg_create_response(pool, req, PJ_STUN_SC_UNAUTHORIZED, NULL, &resp);
pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_REALM, &test_srv->domain);
pj_stun_msg_add_string_attr(pool, resp, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, TURN_NONCE));
goto send_pkt;
}
pj_bzero(&alloc_sock_cb, sizeof(alloc_sock_cb));
alloc_sock_cb.on_data_recvfrom = &alloc_on_data_recvfrom;
/* Create allocation */
alloc = &test_srv->turn_alloc[test_srv->turn_alloc_cnt];
alloc->perm_cnt = 0;
alloc->test_srv = test_srv;
pj_memcpy(&alloc->client_addr, src_addr, addr_len);
pj_ioqueue_op_key_init(&alloc->send_key, sizeof(alloc->send_key));
alloc->pool = pj_pool_create(test_srv->stun_cfg->pf, "alloc", 512, 512, NULL);
/* Create relay socket */
pj_sockaddr_in_init(&alloc->alloc_addr.ipv4, NULL, 0);
pj_gethostip(pj_AF_INET(), &alloc->alloc_addr);
status = pj_activesock_create_udp(alloc->pool, &alloc->alloc_addr, NULL,
test_srv->stun_cfg->ioqueue,
&alloc_sock_cb, alloc,
&alloc->sock, &alloc->alloc_addr);
if (status != PJ_SUCCESS) {
pj_pool_release(alloc->pool);
pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp);
goto send_pkt;
}
//pj_sockaddr_set_str_addr(pj_AF_INET(), &alloc->alloc_addr, &ip_addr);
pj_activesock_set_user_data(alloc->sock, alloc);
status = pj_activesock_start_recvfrom(alloc->sock, alloc->pool, 1500, 0);
if (status != PJ_SUCCESS) {
pj_activesock_close(alloc->sock);
pj_pool_release(alloc->pool);
pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp);
goto send_pkt;
}
/* Create Data indication */
status = pj_stun_msg_create(alloc->pool, PJ_STUN_DATA_INDICATION,
PJ_STUN_MAGIC, NULL, &alloc->data_ind);
if (status != PJ_SUCCESS) {
pj_activesock_close(alloc->sock);
pj_pool_release(alloc->pool);
pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp);
goto send_pkt;
}
pj_stun_msg_add_sockaddr_attr(alloc->pool, alloc->data_ind,
PJ_STUN_ATTR_PEER_ADDR, PJ_TRUE,
&alloc->alloc_addr,
pj_sockaddr_get_len(&alloc->alloc_addr));
pj_stun_msg_add_binary_attr(alloc->pool, alloc->data_ind,
PJ_STUN_ATTR_DATA, (pj_uint8_t*)"", 1);
/* Create response */
resp = create_success_response(test_srv, alloc, req, pool, 600, &auth_key);
if (resp == NULL) {
pj_activesock_close(alloc->sock);
pj_pool_release(alloc->pool);
pj_stun_msg_create_response(pool, req, PJ_STUN_SC_SERVER_ERROR, NULL, &resp);
goto send_pkt;
}
++test_srv->turn_alloc_cnt;
} else {
alloc = &test_srv->turn_alloc[i];
if (req->hdr.type == PJ_STUN_ALLOCATE_REQUEST) {
test_srv->turn_stat.rx_allocate_cnt++;
/* Skip if we're not responding to Allocate request */
if (!test_srv->turn_respond_allocate)
return PJ_TRUE;
resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key);
} else if (req->hdr.type == PJ_STUN_REFRESH_REQUEST) {
pj_stun_lifetime_attr *lf_attr;
test_srv->turn_stat.rx_refresh_cnt++;
/* Skip if we're not responding to Refresh request */
if (!test_srv->turn_respond_refresh)
return PJ_TRUE;
lf_attr = (pj_stun_lifetime_attr*)
pj_stun_msg_find_attr(req, PJ_STUN_ATTR_LIFETIME, 0);
if (lf_attr && lf_attr->value != 0) {
resp = create_success_response(test_srv, alloc, req, pool, 600, &auth_key);
pj_array_erase(test_srv->turn_alloc, sizeof(test_srv->turn_alloc[0]),
test_srv->turn_alloc_cnt, i);
--test_srv->turn_alloc_cnt;
} else
resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key);
} else if (req->hdr.type == PJ_STUN_SEND_INDICATION) {
pj_stun_peer_addr_attr *pa;
pj_stun_data_attr *da;
test_srv->turn_stat.rx_send_ind_cnt++;
pa = (pj_stun_peer_addr_attr*)
pj_stun_msg_find_attr(req, PJ_STUN_ATTR_PEER_ADDR, 0);
da = (pj_stun_data_attr*)
pj_stun_msg_find_attr(req, PJ_STUN_ATTR_DATA, 0);
if (pa && da) {
unsigned j;
char peer_info[PJ_INET6_ADDRSTRLEN];
pj_ssize_t sent;
pj_sockaddr_print(&pa->sockaddr, peer_info, sizeof(peer_info), 3);
for (j=0; j<alloc->perm_cnt; ++j) {
if (pj_sockaddr_cmp(&alloc->perm[j], &pa->sockaddr)==0)
break;
}
if (j==alloc->perm_cnt && alloc->perm_cnt < MAX_TURN_PERM) {
pj_sockaddr_cp(&alloc->perm[alloc->perm_cnt], &pa->sockaddr);
++alloc->perm_cnt;
PJ_LOG(5,("", "Permission %s added to client %s, perm_cnt=%d",
peer_info, client_info, alloc->perm_cnt));
}
PJ_LOG(5,(THIS_FILE, "Relaying %d bytes data from client %s to peer %s, "
"perm_cnt=%d",
da->length, client_info, peer_info, alloc->perm_cnt));
sent = da->length;
pj_activesock_sendto(alloc->sock, &alloc->send_key,
da->data, &sent, 0,
&pa->sockaddr,
pj_sockaddr_get_len(&pa->sockaddr));
} else {
PJ_LOG(1,(THIS_FILE, "Invalid Send Indication from %s", client_info));
}
} else if (PJ_STUN_IS_REQUEST(req->hdr.type)) {
pj_stun_msg_create_response(pool, req, PJ_STUN_SC_BAD_REQUEST, NULL, &resp);
}
}
send_pkt:
if (resp) {
status = pj_stun_msg_encode(resp, (pj_uint8_t*)data, MAX_STUN_PKT,
0, &auth_key, &size);
if (status != PJ_SUCCESS)
goto on_return;
len = size;
status = pj_activesock_sendto(asock, &test_srv->send_key, data, &len,
0, src_addr, addr_len);
}
on_return:
pj_pool_release(pool);
return PJ_TRUE;
}
/* On received data from peer */
static pj_bool_t alloc_on_data_recvfrom(pj_activesock_t *asock,
void *data,
pj_size_t size,
const pj_sockaddr_t *src_addr,
int addr_len,
pj_status_t status)
{
turn_allocation *alloc;
pj_stun_peer_addr_attr *pa;
pj_stun_data_attr *da;
char peer_info[PJ_INET6_ADDRSTRLEN+10];
char client_info[PJ_INET6_ADDRSTRLEN+10];
pj_uint8_t buffer[1500];
pj_ssize_t sent;
unsigned i;
if (status != PJ_SUCCESS)
return PJ_TRUE;
alloc = (turn_allocation*) pj_activesock_get_user_data(asock);
pj_sockaddr_print(&alloc->client_addr, client_info, sizeof(client_info), 3);
pj_sockaddr_print(src_addr, peer_info, sizeof(peer_info), 3);
/* Check that this peer has a permission */
for (i=0; i<alloc->perm_cnt; ++i) {
if (pj_sockaddr_get_len(&alloc->perm[i]) == (unsigned)addr_len &&
pj_memcmp(pj_sockaddr_get_addr(&alloc->perm[i]),
pj_sockaddr_get_addr(src_addr),
addr_len) == 0)
{
break;
}
}
if (i==alloc->perm_cnt) {
PJ_LOG(5,("", "Client %s received %d bytes unauthorized data from peer %s",
client_info, size, peer_info));
if (alloc->perm_cnt == 0)
PJ_LOG(5,("", "Client %s has no permission", client_info));
return PJ_TRUE;
}
/* Format a Data indication */
pa = (pj_stun_peer_addr_attr*)
pj_stun_msg_find_attr(alloc->data_ind, PJ_STUN_ATTR_PEER_ADDR, 0);
da = (pj_stun_data_attr*)
pj_stun_msg_find_attr(alloc->data_ind, PJ_STUN_ATTR_DATA, 0);
pj_assert(pa && da);
pj_sockaddr_cp(&pa->sockaddr, src_addr);
da->data = data;
da->length = size;
/* Encode Data indication */
status = pj_stun_msg_encode(alloc->data_ind, buffer, sizeof(buffer), 0,
NULL, &size);
if (status != PJ_SUCCESS)
return PJ_TRUE;
/* Send */
sent = size;
PJ_LOG(5,("", "Forwarding %d bytes data from peer %s to client %s",
sent, peer_info, client_info));
pj_activesock_sendto(alloc->test_srv->turn_sock, &alloc->send_key, buffer,
&sent, 0, &alloc->client_addr,
pj_sockaddr_get_len(&alloc->client_addr));
return PJ_TRUE;
}

View File

@ -0,0 +1,108 @@
/* $Id$ */
/*
* Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __PJNATH_TEST_SERVER_H__
#define __PJNATH_TEST_SERVER_H__
#include <pjnath.h>
#include <pjlib-util.h>
#include <pjlib.h>
#define DNS_SERVER_PORT 55533
#define STUN_SERVER_PORT 33478
#define TURN_SERVER_PORT 33479
#define TURN_USERNAME "auser"
#define TURN_PASSWD "apass"
#define MAX_TURN_ALLOC 16
#define MAX_TURN_PERM 16
enum test_server_flags
{
CREATE_DNS_SERVER = (1 << 0),
CREATE_A_RECORD_FOR_DOMAIN = (1 << 1),
CREATE_STUN_SERVER = (1 << 5),
CREATE_STUN_SERVER_DNS_SRV = (1 << 6),
CREATE_TURN_SERVER = (1 << 10),
CREATE_TURN_SERVER_DNS_SRV = (1 << 11),
};
typedef struct test_server test_server;
/* TURN allocation */
typedef struct turn_allocation
{
test_server *test_srv;
pj_pool_t *pool;
pj_activesock_t *sock;
pj_ioqueue_op_key_t send_key;
pj_sockaddr client_addr;
pj_sockaddr alloc_addr;
unsigned perm_cnt;
pj_sockaddr perm[MAX_TURN_PERM];
pj_stun_msg *data_ind;
} turn_allocation;
/*
* Server installation for testing.
* This comprises of DNS server, STUN server, and TURN server.
*/
struct test_server
{
pj_pool_t *pool;
pj_uint32_t flags;
pj_stun_config *stun_cfg;
pj_ioqueue_op_key_t send_key;
pj_dns_server *dns_server;
pj_activesock_t *stun_sock;
pj_activesock_t *turn_sock;
unsigned turn_alloc_cnt;
turn_allocation turn_alloc[MAX_TURN_ALLOC];
pj_bool_t turn_respond_allocate;
pj_bool_t turn_respond_refresh;
struct turn_stat {
unsigned rx_allocate_cnt;
unsigned rx_refresh_cnt;
unsigned rx_send_ind_cnt;
} turn_stat;
pj_str_t domain;
pj_str_t username;
pj_str_t passwd;
};
pj_status_t create_test_server(pj_stun_config *stun_cfg,
pj_uint32_t flags,
const char *domain,
test_server **p_test_srv);
void destroy_test_server(test_server *test_srv);
void test_server_poll_events(test_server *test_srv);
#endif /* __PJNATH_TEST_SERVER_H__ */

View File

@ -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 */

View File

@ -0,0 +1,844 @@
/* $Id$ */
/*
* Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "test.h"
#define THIS_FILE "stun_sock_test.c"
enum {
RESPOND_STUN = 1,
WITH_MAPPED = 2,
WITH_XOR_MAPPED = 4,
ECHO = 8
};
/*
* Simple STUN server
*/
struct stun_srv
{
pj_activesock_t *asock;
unsigned flag;
pj_sockaddr addr;
unsigned rx_cnt;
pj_ioqueue_op_key_t send_key;
pj_str_t ip_to_send;
pj_uint16_t port_to_send;
};
static pj_bool_t srv_on_data_recvfrom(pj_activesock_t *asock,
void *data,
pj_size_t size,
const pj_sockaddr_t *src_addr,
int addr_len,
pj_status_t status)
{
struct stun_srv *srv;
pj_ssize_t sent;
srv = pj_activesock_get_user_data(asock);
/* Ignore error */
if (status != PJ_SUCCESS)
return PJ_TRUE;
++srv->rx_cnt;
/* Ignore if we're not responding */
if (srv->flag & RESPOND_STUN) {
pj_pool_t *pool;
pj_stun_msg *req_msg, *res_msg;
pool = pj_pool_create(mem, "stunsrv", 512, 512, NULL);
/* Parse request */
status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size,
PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET,
&req_msg, NULL, NULL);
if (status != PJ_SUCCESS) {
app_perror(" pj_stun_msg_decode()", status);
pj_pool_release(pool);
return PJ_TRUE;
}
/* Create response */
status = pj_stun_msg_create(pool, PJ_STUN_BINDING_RESPONSE, PJ_STUN_MAGIC,
req_msg->hdr.tsx_id, &res_msg);
if (status != PJ_SUCCESS) {
app_perror(" pj_stun_msg_create()", status);
pj_pool_release(pool);
return PJ_TRUE;
}
/* Add MAPPED-ADDRESS or XOR-MAPPED-ADDRESS (or don't add) */
if (srv->flag & WITH_MAPPED) {
pj_sockaddr_in addr;
pj_sockaddr_in_init(&addr, &srv->ip_to_send, srv->port_to_send);
pj_stun_msg_add_sockaddr_attr(pool, res_msg, PJ_STUN_ATTR_MAPPED_ADDR,
PJ_FALSE, &addr, sizeof(addr));
} else if (srv->flag & WITH_XOR_MAPPED) {
pj_sockaddr_in addr;
pj_sockaddr_in_init(&addr, &srv->ip_to_send, srv->port_to_send);
pj_stun_msg_add_sockaddr_attr(pool, res_msg,
PJ_STUN_ATTR_XOR_MAPPED_ADDR,
PJ_TRUE, &addr, sizeof(addr));
}
/* Encode */
status = pj_stun_msg_encode(res_msg, (pj_uint8_t*)data, 100, 0,
NULL, &size);
if (status != PJ_SUCCESS) {
app_perror(" pj_stun_msg_encode()", status);
pj_pool_release(pool);
return PJ_TRUE;
}
/* Send back */
sent = size;
pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0,
src_addr, addr_len);
pj_pool_release(pool);
} else if (srv->flag & ECHO) {
/* Send back */
sent = size;
pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0,
src_addr, addr_len);
}
return PJ_TRUE;
}
static pj_status_t create_server(pj_pool_t *pool,
pj_ioqueue_t *ioqueue,
unsigned flag,
struct stun_srv **p_srv)
{
struct stun_srv *srv;
pj_activesock_cb activesock_cb;
pj_status_t status;
srv = PJ_POOL_ZALLOC_T(pool, struct stun_srv);
srv->flag = flag;
srv->ip_to_send = pj_str("1.1.1.1");
srv->port_to_send = 1000;
status = pj_sockaddr_in_init(&srv->addr.ipv4, NULL, 0);
if (status != PJ_SUCCESS)
return status;
pj_bzero(&activesock_cb, sizeof(activesock_cb));
activesock_cb.on_data_recvfrom = &srv_on_data_recvfrom;
status = pj_activesock_create_udp(pool, &srv->addr, NULL, ioqueue,
&activesock_cb, srv, &srv->asock,
&srv->addr);
if (status != PJ_SUCCESS)
return status;
pj_ioqueue_op_key_init(&srv->send_key, sizeof(srv->send_key));
status = pj_activesock_start_recvfrom(srv->asock, pool, 512, 0);
if (status != PJ_SUCCESS) {
pj_activesock_close(srv->asock);
return status;
}
*p_srv = srv;
return PJ_SUCCESS;
}
static void destroy_server(struct stun_srv *srv)
{
pj_activesock_close(srv->asock);
}
struct stun_client
{
pj_pool_t *pool;
pj_stun_sock *sock;
pj_ioqueue_op_key_t send_key;
pj_bool_t destroy_on_err;
unsigned on_status_cnt;
pj_stun_sock_op last_op;
pj_status_t last_status;
unsigned on_rx_data_cnt;
};
static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock,
pj_stun_sock_op op,
pj_status_t status)
{
struct stun_client *client;
client = pj_stun_sock_get_user_data(stun_sock);
client->on_status_cnt++;
client->last_op = op;
client->last_status = status;
if (status != PJ_SUCCESS && client->destroy_on_err) {
pj_stun_sock_destroy(client->sock);
client->sock = NULL;
return PJ_FALSE;
}
return PJ_TRUE;
}
static pj_bool_t stun_sock_on_rx_data(pj_stun_sock *stun_sock,
void *pkt,
unsigned pkt_len,
const pj_sockaddr_t *src_addr,
unsigned addr_len)
{
struct stun_client *client;
PJ_UNUSED_ARG(pkt);
PJ_UNUSED_ARG(pkt_len);
PJ_UNUSED_ARG(src_addr);
PJ_UNUSED_ARG(addr_len);
client = pj_stun_sock_get_user_data(stun_sock);
client->on_rx_data_cnt++;
return PJ_TRUE;
}
static pj_status_t create_client(pj_stun_config *cfg,
struct stun_client **p_client,
pj_bool_t destroy_on_err)
{
pj_pool_t *pool;
struct stun_client *client;
pj_stun_sock_cfg sock_cfg;
pj_stun_sock_cb cb;
pj_status_t status;
pool = pj_pool_create(mem, "test", 512, 512, NULL);
client = PJ_POOL_ZALLOC_T(pool, struct stun_client);
client->pool = pool;
pj_stun_sock_cfg_default(&sock_cfg);
pj_bzero(&cb, sizeof(cb));
cb.on_status = &stun_sock_on_status;
cb.on_rx_data = &stun_sock_on_rx_data;
status = pj_stun_sock_create(cfg, NULL, pj_AF_INET(), &cb,
&sock_cfg, client, &client->sock);
if (status != PJ_SUCCESS) {
app_perror(" pj_stun_sock_create()", status);
pj_pool_release(pool);
return status;
}
pj_stun_sock_set_user_data(client->sock, client);
pj_ioqueue_op_key_init(&client->send_key, sizeof(client->send_key));
client->destroy_on_err = destroy_on_err;
*p_client = client;
return PJ_SUCCESS;
}
static void destroy_client(struct stun_client *client)
{
if (client->sock) {
pj_stun_sock_destroy(client->sock);
client->sock = NULL;
}
pj_pool_release(client->pool);
}
static void handle_events(pj_stun_config *cfg, unsigned msec_delay)
{
pj_time_val delay;
pj_timer_heap_poll(cfg->timer_heap, NULL);
delay.sec = 0;
delay.msec = msec_delay;
pj_time_val_normalize(&delay);
pj_ioqueue_poll(cfg->ioqueue, &delay);
}
/*
* Timeout test: scenario when no response is received from server
*/
static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
{
struct stun_srv *srv;
struct stun_client *client;
pj_str_t srv_addr;
pj_time_val timeout, t;
int ret = 0;
pj_status_t status;
PJ_LOG(3,(THIS_FILE, " timeout test [%d]", destroy_on_err));
status = create_client(cfg, &client, destroy_on_err);
if (status != PJ_SUCCESS)
return -10;
status = create_server(client->pool, cfg->ioqueue, 0, &srv);
if (status != PJ_SUCCESS) {
destroy_client(client);
return -20;
}
srv_addr = pj_str("127.0.0.1");
status = pj_stun_sock_start(client->sock, &srv_addr,
pj_ntohs(srv->addr.ipv4.sin_port), NULL);
if (status != PJ_SUCCESS) {
destroy_server(srv);
destroy_client(client);
return -30;
}
/* Wait until on_status() callback is called with the failure */
pj_gettimeofday(&timeout);
timeout.sec += 60;
do {
handle_events(cfg, 100);
pj_gettimeofday(&t);
} while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
/* Check that callback with correct operation is called */
if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
PJ_LOG(3,(THIS_FILE, " error: expecting Binding operation status"));
ret = -40;
goto on_return;
}
/* .. and with the correct status */
if (client->last_status != PJNATH_ESTUNTIMEDOUT) {
PJ_LOG(3,(THIS_FILE, " error: expecting PJNATH_ESTUNTIMEDOUT"));
ret = -50;
goto on_return;
}
/* Check that server received correct retransmissions */
if (srv->rx_cnt != PJ_STUN_MAX_TRANSMIT_COUNT) {
PJ_LOG(3,(THIS_FILE, " error: expecting %d retransmissions, got %d",
PJ_STUN_MAX_TRANSMIT_COUNT, srv->rx_cnt));
ret = -60;
goto on_return;
}
/* Check that client doesn't receive anything */
if (client->on_rx_data_cnt != 0) {
PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
ret = -70;
goto on_return;
}
on_return:
destroy_server(srv);
destroy_client(client);
return ret;
}
/*
* Invalid response scenario: when server returns no MAPPED-ADDRESS or
* XOR-MAPPED-ADDRESS attribute.
*/
static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
{
struct stun_srv *srv;
struct stun_client *client;
pj_str_t srv_addr;
pj_time_val timeout, t;
int ret = 0;
pj_status_t status;
PJ_LOG(3,(THIS_FILE, " missing attribute test [%d]", destroy_on_err));
status = create_client(cfg, &client, destroy_on_err);
if (status != PJ_SUCCESS)
return -110;
status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN, &srv);
if (status != PJ_SUCCESS) {
destroy_client(client);
return -120;
}
srv_addr = pj_str("127.0.0.1");
status = pj_stun_sock_start(client->sock, &srv_addr,
pj_ntohs(srv->addr.ipv4.sin_port), NULL);
if (status != PJ_SUCCESS) {
destroy_server(srv);
destroy_client(client);
return -130;
}
/* Wait until on_status() callback is called with the failure */
pj_gettimeofday(&timeout);
timeout.sec += 60;
do {
handle_events(cfg, 100);
pj_gettimeofday(&t);
} while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
/* Check that callback with correct operation is called */
if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
PJ_LOG(3,(THIS_FILE, " error: expecting Binding operation status"));
ret = -140;
goto on_return;
}
if (client->last_status != PJNATH_ESTUNNOMAPPEDADDR) {
PJ_LOG(3,(THIS_FILE, " error: expecting PJNATH_ESTUNNOMAPPEDADDR"));
ret = -150;
goto on_return;
}
/* Check that client doesn't receive anything */
if (client->on_rx_data_cnt != 0) {
PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
ret = -170;
goto on_return;
}
on_return:
destroy_server(srv);
destroy_client(client);
return ret;
}
/*
* Keep-alive test.
*/
static int keep_alive_test(pj_stun_config *cfg)
{
struct stun_srv *srv;
struct stun_client *client;
pj_sockaddr_in mapped_addr;
pj_stun_sock_info info;
pj_str_t srv_addr;
pj_time_val timeout, t;
int ret = 0;
pj_status_t status;
PJ_LOG(3,(THIS_FILE, " normal operation"));
status = create_client(cfg, &client, PJ_TRUE);
if (status != PJ_SUCCESS)
return -310;
status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN|WITH_XOR_MAPPED, &srv);
if (status != PJ_SUCCESS) {
destroy_client(client);
return -320;
}
/*
* Part 1: initial Binding resolution.
*/
PJ_LOG(3,(THIS_FILE, " initial Binding request"));
srv_addr = pj_str("127.0.0.1");
status = pj_stun_sock_start(client->sock, &srv_addr,
pj_ntohs(srv->addr.ipv4.sin_port), NULL);
if (status != PJ_SUCCESS) {
destroy_server(srv);
destroy_client(client);
return -330;
}
/* Wait until on_status() callback is called with success status */
pj_gettimeofday(&timeout);
timeout.sec += 60;
do {
handle_events(cfg, 100);
pj_gettimeofday(&t);
} while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
/* Check that callback with correct operation is called */
if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
PJ_LOG(3,(THIS_FILE, " error: expecting Binding operation status"));
ret = -340;
goto on_return;
}
if (client->last_status != PJ_SUCCESS) {
PJ_LOG(3,(THIS_FILE, " error: expecting PJ_SUCCESS status"));
ret = -350;
goto on_return;
}
/* Check that client doesn't receive anything */
if (client->on_rx_data_cnt != 0) {
PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
ret = -370;
goto on_return;
}
/* Get info */
pj_bzero(&info, sizeof(info));
pj_stun_sock_get_info(client->sock, &info);
/* Check that we have server address */
if (!pj_sockaddr_has_addr(&info.srv_addr)) {
PJ_LOG(3,(THIS_FILE, " error: missing server address"));
ret = -380;
goto on_return;
}
/* .. and bound address port must not be zero */
if (pj_sockaddr_get_port(&info.bound_addr)==0) {
PJ_LOG(3,(THIS_FILE, " error: bound address is zero"));
ret = -381;
goto on_return;
}
/* .. and mapped address */
if (!pj_sockaddr_has_addr(&info.mapped_addr)) {
PJ_LOG(3,(THIS_FILE, " error: missing mapped address"));
ret = -382;
goto on_return;
}
/* verify the mapped address */
pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send);
if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) {
PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched"));
ret = -383;
goto on_return;
}
/* .. and at least one alias */
if (info.alias_cnt == 0) {
PJ_LOG(3,(THIS_FILE, " error: must have at least one alias"));
ret = -384;
goto on_return;
}
if (!pj_sockaddr_has_addr(&info.aliases[0])) {
PJ_LOG(3,(THIS_FILE, " error: missing alias"));
ret = -386;
goto on_return;
}
/*
* Part 2: sending and receiving data
*/
PJ_LOG(3,(THIS_FILE, " sending/receiving data"));
/* Change server operation mode to echo back data */
srv->flag = ECHO;
/* Reset server */
srv->rx_cnt = 0;
/* Client sending data to echo server */
{
char txt[100];
PJ_LOG(3,(THIS_FILE, " sending to %s", pj_sockaddr_print(&info.srv_addr, txt, sizeof(txt), 3)));
}
status = pj_stun_sock_sendto(client->sock, NULL, &ret, sizeof(ret),
0, &info.srv_addr,
pj_sockaddr_get_len(&info.srv_addr));
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
app_perror(" error: server sending data", status);
ret = -390;
goto on_return;
}
/* Wait for a short period until client receives data. We can't wait for
* too long otherwise the keep-alive will kick in.
*/
pj_gettimeofday(&timeout);
timeout.sec += 1;
do {
handle_events(cfg, 100);
pj_gettimeofday(&t);
} while (client->on_rx_data_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
/* Check that data is received in server */
if (srv->rx_cnt == 0) {
PJ_LOG(3,(THIS_FILE, " error: server didn't receive data"));
ret = -395;
goto on_return;
}
/* Check that status is still OK */
if (client->last_status != PJ_SUCCESS) {
app_perror(" error: client has failed", client->last_status);
ret = -400;
goto on_return;
}
/* Check that data has been received */
if (client->on_rx_data_cnt == 0) {
PJ_LOG(3,(THIS_FILE, " error: client doesn't receive data"));
ret = -410;
goto on_return;
}
/*
* Part 3: Successful keep-alive,
*/
PJ_LOG(3,(THIS_FILE, " successful keep-alive scenario"));
/* Change server operation mode to normal mode */
srv->flag = RESPOND_STUN | WITH_XOR_MAPPED;
/* Reset server */
srv->rx_cnt = 0;
/* Reset client */
client->on_status_cnt = 0;
client->last_status = PJ_SUCCESS;
client->on_rx_data_cnt = 0;
/* Wait for keep-alive duration to see if client actually sends the
* keep-alive.
*/
pj_gettimeofday(&timeout);
timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1);
do {
handle_events(cfg, 100);
pj_gettimeofday(&t);
} while (PJ_TIME_VAL_LT(t, timeout));
/* Check that server receives some packets */
if (srv->rx_cnt == 0) {
PJ_LOG(3, (THIS_FILE, " error: no keep-alive was received"));
ret = -420;
goto on_return;
}
/* Check that client status is still okay and on_status() callback is NOT
* called
*/
if (client->on_status_cnt != 0) {
PJ_LOG(3, (THIS_FILE, " error: on_status() must not be called on successful"
"keep-alive when mapped-address does not change"));
ret = -430;
goto on_return;
}
/* Check that client doesn't receive anything */
if (client->on_rx_data_cnt != 0) {
PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
ret = -440;
goto on_return;
}
/*
* Part 4: Successful keep-alive with IP address change
*/
PJ_LOG(3,(THIS_FILE, " mapped IP address change"));
/* Change server operation mode to normal mode */
srv->flag = RESPOND_STUN | WITH_XOR_MAPPED;
/* Change mapped address in the response */
srv->ip_to_send = pj_str("2.2.2.2");
srv->port_to_send++;
/* Reset server */
srv->rx_cnt = 0;
/* Reset client */
client->on_status_cnt = 0;
client->last_status = PJ_SUCCESS;
client->on_rx_data_cnt = 0;
/* Wait for keep-alive duration to see if client actually sends the
* keep-alive.
*/
pj_gettimeofday(&timeout);
timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1);
do {
handle_events(cfg, 100);
pj_gettimeofday(&t);
} while (PJ_TIME_VAL_LT(t, timeout));
/* Check that server receives some packets */
if (srv->rx_cnt == 0) {
PJ_LOG(3, (THIS_FILE, " error: no keep-alive was received"));
ret = -450;
goto on_return;
}
/* Check that on_status() callback is called (because mapped address
* has changed)
*/
if (client->on_status_cnt != 1) {
PJ_LOG(3, (THIS_FILE, " error: on_status() was not called"));
ret = -460;
goto on_return;
}
/* Check that callback was called with correct operation */
if (client->last_op != PJ_STUN_SOCK_KEEP_ALIVE_OP) {
PJ_LOG(3,(THIS_FILE, " error: expecting keep-alive operation status"));
ret = -470;
goto on_return;
}
/* Check that last status is still success */
if (client->last_status != PJ_SUCCESS) {
PJ_LOG(3, (THIS_FILE, " error: expecting successful status"));
ret = -480;
goto on_return;
}
/* Check that client doesn't receive anything */
if (client->on_rx_data_cnt != 0) {
PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
ret = -490;
goto on_return;
}
/* Get info */
pj_bzero(&info, sizeof(info));
pj_stun_sock_get_info(client->sock, &info);
/* Check that we have server address */
if (!pj_sockaddr_has_addr(&info.srv_addr)) {
PJ_LOG(3,(THIS_FILE, " error: missing server address"));
ret = -500;
goto on_return;
}
/* .. and mapped address */
if (!pj_sockaddr_has_addr(&info.mapped_addr)) {
PJ_LOG(3,(THIS_FILE, " error: missing mapped address"));
ret = -510;
goto on_return;
}
/* verify the mapped address */
pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send);
if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) {
PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched"));
ret = -520;
goto on_return;
}
/* .. and at least one alias */
if (info.alias_cnt == 0) {
PJ_LOG(3,(THIS_FILE, " error: must have at least one alias"));
ret = -530;
goto on_return;
}
if (!pj_sockaddr_has_addr(&info.aliases[0])) {
PJ_LOG(3,(THIS_FILE, " error: missing alias"));
ret = -540;
goto on_return;
}
/*
* Part 5: Failed keep-alive
*/
PJ_LOG(3,(THIS_FILE, " failed keep-alive scenario"));
/* Change server operation mode to respond without attribute */
srv->flag = RESPOND_STUN;
/* Reset server */
srv->rx_cnt = 0;
/* Reset client */
client->on_status_cnt = 0;
client->last_status = PJ_SUCCESS;
client->on_rx_data_cnt = 0;
/* Wait until on_status() is called with failure. */
pj_gettimeofday(&timeout);
timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + PJ_STUN_TIMEOUT_VALUE + 5);
do {
handle_events(cfg, 100);
pj_gettimeofday(&t);
} while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
/* Check that callback with correct operation is called */
if (client->last_op != PJ_STUN_SOCK_KEEP_ALIVE_OP) {
PJ_LOG(3,(THIS_FILE, " error: expecting keep-alive operation status"));
ret = -600;
goto on_return;
}
if (client->last_status == PJ_SUCCESS) {
PJ_LOG(3,(THIS_FILE, " error: expecting failed keep-alive"));
ret = -610;
goto on_return;
}
/* Check that client doesn't receive anything */
if (client->on_rx_data_cnt != 0) {
PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
ret = -620;
goto on_return;
}
on_return:
destroy_server(srv);
destroy_client(client);
return ret;
}
#define DO_TEST(expr) \
capture_pjlib_state(&stun_cfg, &pjlib_state); \
ret = expr; \
if (ret != 0) goto on_return; \
ret = check_pjlib_state(&stun_cfg, &pjlib_state); \
if (ret != 0) goto on_return;
int stun_sock_test(void)
{
struct pjlib_state pjlib_state;
pj_stun_config stun_cfg;
pj_ioqueue_t *ioqueue = NULL;
pj_timer_heap_t *timer_heap = NULL;
pj_pool_t *pool = NULL;
pj_status_t status;
int ret = 0;
pool = pj_pool_create(mem, NULL, 512, 512, NULL);
status = pj_ioqueue_create(pool, 12, &ioqueue);
if (status != PJ_SUCCESS) {
app_perror(" pj_ioqueue_create()", status);
ret = -4;
goto on_return;
}
status = pj_timer_heap_create(pool, 100, &timer_heap);
if (status != PJ_SUCCESS) {
app_perror(" pj_timer_heap_create()", status);
ret = -8;
goto on_return;
}
pj_stun_config_init(&stun_cfg, mem, 0, ioqueue, timer_heap);
DO_TEST(timeout_test(&stun_cfg, PJ_FALSE));
DO_TEST(timeout_test(&stun_cfg, PJ_TRUE));
DO_TEST(missing_attr_test(&stun_cfg, PJ_FALSE));
DO_TEST(missing_attr_test(&stun_cfg, PJ_TRUE));
DO_TEST(keep_alive_test(&stun_cfg));
on_return:
if (timer_heap) pj_timer_heap_destroy(timer_heap);
if (ioqueue) pj_ioqueue_destroy(ioqueue);
if (pool) pj_pool_release(pool);
return ret;
}

View File

@ -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, &current_state);
if (current_state.timer_cnt > initial_st->timer_cnt) {
PJ_LOG(3,("", " error: possibly leaking timer"));
rc |= ERR_TIMER_LEAK;
}
if (current_state.pool_used_cnt > initial_st->pool_used_cnt) {
PJ_LOG(3,("", " error: possibly leaking memory"));
PJ_LOG(3,("", " dumping memory pool:"));
pj_pool_factory_dump(mem, PJ_TRUE);
rc |= ERR_MEMORY_LEAK;
}
return rc;
}
#define DO_TEST(test) do { \
PJ_LOG(3, ("test", "Running %s...", #test)); \
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;
}

View File

@ -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

View File

@ -0,0 +1,515 @@
/* $Id$ */
/*
* Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "test.h"
#include "server.h"
#define SRV_DOMAIN "pjsip.lab.domain"
#define KA_INTERVAL 50
struct test_result
{
unsigned state_called;
unsigned rx_data_cnt;
};
struct test_session
{
pj_pool_t *pool;
pj_stun_config *stun_cfg;
pj_turn_sock *turn_sock;
pj_dns_resolver *resolver;
test_server *test_srv;
pj_bool_t destroy_called;
int destroy_on_state;
struct test_result result;
};
struct test_session_cfg
{
struct {
pj_bool_t enable_dns_srv;
int destroy_on_state;
} client;
struct {
pj_uint32_t flags;
pj_bool_t respond_allocate;
pj_bool_t respond_refresh;
} srv;
};
static void turn_on_rx_data(pj_turn_sock *turn_sock,
void *pkt,
unsigned pkt_len,
const pj_sockaddr_t *peer_addr,
unsigned addr_len);
static void turn_on_state(pj_turn_sock *turn_sock,
pj_turn_state_t old_state,
pj_turn_state_t new_state);
static void destroy_session(struct test_session *sess)
{
if (sess->resolver) {
pj_dns_resolver_destroy(sess->resolver, PJ_TRUE);
sess->resolver = NULL;
}
if (sess->turn_sock) {
if (!sess->destroy_called) {
sess->destroy_called = PJ_TRUE;
pj_turn_sock_destroy(sess->turn_sock);
}
sess->turn_sock = NULL;
}
if (sess->test_srv) {
destroy_test_server(sess->test_srv);
sess->test_srv = NULL;
}
if (sess->pool) {
pj_pool_release(sess->pool);
}
}
static int create_test_session(pj_stun_config *stun_cfg,
const struct test_session_cfg *cfg,
struct test_session **p_sess)
{
struct test_session *sess;
pj_pool_t *pool;
pj_turn_sock_cb turn_sock_cb;
pj_turn_alloc_param alloc_param;
pj_stun_auth_cred cred;
pj_status_t status;
/* Create client */
pool = pj_pool_create(mem, "turnclient", 512, 512, NULL);
sess = PJ_POOL_ZALLOC_T(pool, struct test_session);
sess->pool = pool;
sess->stun_cfg = stun_cfg;
sess->destroy_on_state = cfg->client.destroy_on_state;
pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb));
turn_sock_cb.on_rx_data = &turn_on_rx_data;
turn_sock_cb.on_state = &turn_on_state;
status = pj_turn_sock_create(sess->stun_cfg, pj_AF_INET(), PJ_TURN_TP_UDP,
&turn_sock_cb, 0, sess, &sess->turn_sock);
if (status != PJ_SUCCESS) {
destroy_session(sess);
return -20;
}
/* Create test server */
status = create_test_server(sess->stun_cfg, cfg->srv.flags,
SRV_DOMAIN, &sess->test_srv);
if (status != PJ_SUCCESS) {
destroy_session(sess);
return -30;
}
sess->test_srv->turn_respond_allocate = cfg->srv.respond_allocate;
sess->test_srv->turn_respond_refresh = cfg->srv.respond_refresh;
/* Create client resolver */
status = pj_dns_resolver_create(mem, "resolver", 0, sess->stun_cfg->timer_heap,
sess->stun_cfg->ioqueue, &sess->resolver);
if (status != PJ_SUCCESS) {
destroy_session(sess);
return -40;
} else {
pj_str_t dns_srv = pj_str("127.0.0.1");
pj_uint16_t dns_srv_port = (pj_uint16_t) DNS_SERVER_PORT;
status = pj_dns_resolver_set_ns(sess->resolver, 1, &dns_srv, &dns_srv_port);
if (status != PJ_SUCCESS) {
destroy_session(sess);
return -50;
}
}
/* Init TURN credential */
pj_bzero(&cred, sizeof(cred));
cred.type = PJ_STUN_AUTH_CRED_STATIC;
cred.data.static_cred.realm = pj_str(SRV_DOMAIN);
cred.data.static_cred.username = pj_str(TURN_USERNAME);
cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
cred.data.static_cred.data = pj_str(TURN_PASSWD);
/* Init TURN allocate parameter */
pj_turn_alloc_param_default(&alloc_param);
alloc_param.ka_interval = KA_INTERVAL;
/* Start the client */
if (cfg->client.enable_dns_srv) {
/* Use DNS SRV to resolve server, may fallback to DNS A */
pj_str_t domain = pj_str(SRV_DOMAIN);
status = pj_turn_sock_alloc(sess->turn_sock, &domain, TURN_SERVER_PORT,
sess->resolver, &cred, &alloc_param);
} else {
/* Explicitly specify server address */
pj_str_t host = pj_str("127.0.0.1");
status = pj_turn_sock_alloc(sess->turn_sock, &host, TURN_SERVER_PORT,
NULL, &cred, &alloc_param);
}
if (status != PJ_SUCCESS) {
if (cfg->client.destroy_on_state >= PJ_TURN_STATE_READY) {
destroy_session(sess);
return -70;
}
}
*p_sess = sess;
return 0;
}
static void turn_on_rx_data(pj_turn_sock *turn_sock,
void *pkt,
unsigned pkt_len,
const pj_sockaddr_t *peer_addr,
unsigned addr_len)
{
struct test_session *sess;
PJ_UNUSED_ARG(pkt);
PJ_UNUSED_ARG(pkt_len);
PJ_UNUSED_ARG(peer_addr);
PJ_UNUSED_ARG(addr_len);
sess = (struct test_session*) pj_turn_sock_get_user_data(turn_sock);
if (sess == NULL)
return;
sess->result.rx_data_cnt++;
}
static void turn_on_state(pj_turn_sock *turn_sock,
pj_turn_state_t old_state,
pj_turn_state_t new_state)
{
struct test_session *sess;
unsigned i, mask;
PJ_UNUSED_ARG(old_state);
sess = (struct test_session*) pj_turn_sock_get_user_data(turn_sock);
if (sess == NULL)
return;
/* This state must not be called before */
pj_assert((sess->result.state_called & (1<<new_state)) == 0);
/* new_state must be greater than old_state */
pj_assert(new_state > old_state);
/* must not call any greater state before */
mask = 0;
for (i=new_state+1; i<31; ++i) mask |= (1 << i);
pj_assert((sess->result.state_called & mask) == 0);
sess->result.state_called |= (1 << new_state);
if (new_state >= sess->destroy_on_state && !sess->destroy_called) {
sess->destroy_called = PJ_TRUE;
pj_turn_sock_destroy(turn_sock);
}
if (new_state >= PJ_TURN_STATE_DESTROYING) {
pj_turn_sock_set_user_data(sess->turn_sock, NULL);
sess->turn_sock = NULL;
}
}
/////////////////////////////////////////////////////////////////////
static int state_progression_test(pj_stun_config *stun_cfg)
{
struct test_session_cfg test_cfg =
{
{ /* Client cfg */
/* DNS SRV */ /* Destroy on state */
PJ_TRUE, 0xFFFF
},
{ /* Server cfg */
0xFFFFFFFF, /* flags */
PJ_TRUE, /* respond to allocate */
PJ_TRUE /* respond to refresh */
}
};
struct test_session *sess;
unsigned i;
int rc;
PJ_LOG(3,("", " state progression tests"));
for (i=0; i<=1; ++i) {
enum { TIMEOUT = 60 };
pjlib_state pjlib_state;
pj_turn_session_info info;
struct test_result result;
pj_time_val tstart;
PJ_LOG(3,("", " %s DNS SRV resolution",
(i==0? "without" : "with")));
capture_pjlib_state(stun_cfg, &pjlib_state);
test_cfg.client.enable_dns_srv = i;
rc = create_test_session(stun_cfg, &test_cfg, &sess);
if (rc != 0)
return rc;
pj_bzero(&info, sizeof(info));
/* Wait until state is READY */
pj_gettimeofday(&tstart);
while (sess->turn_sock) {
pj_time_val now;
poll_events(stun_cfg, 10, PJ_FALSE);
rc = pj_turn_sock_get_info(sess->turn_sock, &info);
if (rc!=PJ_SUCCESS)
break;
if (info.state >= PJ_TURN_STATE_READY)
break;
pj_gettimeofday(&now);
if (now.sec - tstart.sec > TIMEOUT) {
PJ_LOG(3,("", " timed-out"));
break;
}
}
if (info.state != PJ_TURN_STATE_READY) {
PJ_LOG(3,("", " error: state is not READY"));
destroy_session(sess);
return -130;
}
/* Deallocate */
pj_turn_sock_destroy(sess->turn_sock);
/* Wait for couple of seconds.
* We can't poll the session info since the session may have
* been destroyed
*/
poll_events(stun_cfg, 2000, PJ_FALSE);
sess->turn_sock = NULL;
pj_memcpy(&result, &sess->result, sizeof(result));
destroy_session(sess);
/* Check the result */
if ((result.state_called & (1<<PJ_TURN_STATE_RESOLVING)) == 0) {
PJ_LOG(3,("", " error: PJ_TURN_STATE_RESOLVING is not called"));
return -140;
}
if ((result.state_called & (1<<PJ_TURN_STATE_RESOLVED)) == 0) {
PJ_LOG(3,("", " error: PJ_TURN_STATE_RESOLVED is not called"));
return -150;
}
if ((result.state_called & (1<<PJ_TURN_STATE_ALLOCATING)) == 0) {
PJ_LOG(3,("", " error: PJ_TURN_STATE_ALLOCATING is not called"));
return -155;
}
if ((result.state_called & (1<<PJ_TURN_STATE_READY)) == 0) {
PJ_LOG(3,("", " error: PJ_TURN_STATE_READY is not called"));
return -160;
}
if ((result.state_called & (1<<PJ_TURN_STATE_DEALLOCATING)) == 0) {
PJ_LOG(3,("", " error: PJ_TURN_STATE_DEALLOCATING is not called"));
return -170;
}
if ((result.state_called & (1<<PJ_TURN_STATE_DEALLOCATED)) == 0) {
PJ_LOG(3,("", " error: PJ_TURN_STATE_DEALLOCATED is not called"));
return -180;
}
if ((result.state_called & (1<<PJ_TURN_STATE_DESTROYING)) == 0) {
PJ_LOG(3,("", " error: PJ_TURN_STATE_DESTROYING is not called"));
return -190;
}
poll_events(stun_cfg, 500, PJ_FALSE);
rc = check_pjlib_state(stun_cfg, &pjlib_state);
if (rc != 0) {
PJ_LOG(3,("", " error: memory/timer-heap leak detected"));
return rc;
}
}
return 0;
}
/////////////////////////////////////////////////////////////////////
static int destroy_test(pj_stun_config *stun_cfg,
pj_bool_t with_dns_srv,
pj_bool_t in_callback)
{
struct test_session_cfg test_cfg =
{
{ /* Client cfg */
/* DNS SRV */ /* Destroy on state */
PJ_TRUE, 0xFFFF
},
{ /* Server cfg */
0xFFFFFFFF, /* flags */
PJ_TRUE, /* respond to allocate */
PJ_TRUE /* respond to refresh */
}
};
struct test_session *sess;
int target_state;
int rc;
PJ_LOG(3,("", " destroy test %s %s",
(in_callback? "in callback" : ""),
(with_dns_srv? "with DNS srv" : "")
));
test_cfg.client.enable_dns_srv = with_dns_srv;
for (target_state=PJ_TURN_STATE_RESOLVING; target_state<=PJ_TURN_STATE_READY; ++target_state) {
enum { TIMEOUT = 60 };
pjlib_state pjlib_state;
pj_turn_session_info info;
pj_time_val tstart;
capture_pjlib_state(stun_cfg, &pjlib_state);
PJ_LOG(3,("", " %s", pj_turn_state_name((pj_turn_state_t)target_state)));
if (in_callback)
test_cfg.client.destroy_on_state = target_state;
rc = create_test_session(stun_cfg, &test_cfg, &sess);
if (rc != 0)
return rc;
if (in_callback) {
pj_gettimeofday(&tstart);
rc = 0;
while (sess->turn_sock) {
pj_time_val now;
poll_events(stun_cfg, 100, PJ_FALSE);
pj_gettimeofday(&now);
if (now.sec - tstart.sec > TIMEOUT) {
rc = -7;
break;
}
}
} else {
pj_gettimeofday(&tstart);
rc = 0;
while (sess->turn_sock) {
pj_time_val now;
poll_events(stun_cfg, 1, PJ_FALSE);
pj_turn_sock_get_info(sess->turn_sock, &info);
if (info.state >= target_state) {
pj_turn_sock_destroy(sess->turn_sock);
break;
}
pj_gettimeofday(&now);
if (now.sec - tstart.sec > TIMEOUT) {
rc = -8;
break;
}
}
}
if (rc != 0) {
PJ_LOG(3,("", " error: timeout"));
return rc;
}
poll_events(stun_cfg, 1000, PJ_FALSE);
destroy_session(sess);
rc = check_pjlib_state(stun_cfg, &pjlib_state);
if (rc != 0) {
PJ_LOG(3,("", " error: memory/timer-heap leak detected"));
return rc;
}
}
return 0;
}
/////////////////////////////////////////////////////////////////////
int turn_sock_test(void)
{
pj_pool_t *pool;
pj_stun_config stun_cfg;
int i, rc = 0;
pool = pj_pool_create(mem, "turntest", 512, 512, NULL);
rc = create_stun_config(pool, &stun_cfg);
if (rc != PJ_SUCCESS) {
pj_pool_release(pool);
return -2;
}
rc = state_progression_test(&stun_cfg);
if (rc != 0)
goto on_return;
for (i=0; i<=1; ++i) {
int j;
for (j=0; j<=1; ++j) {
rc = destroy_test(&stun_cfg, i, j);
if (rc != 0)
goto on_return;
}
}
on_return:
destroy_stun_config(&stun_cfg);
pj_pool_release(pool);
return rc;
}

View File

@ -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"),

View File

@ -21,6 +21,7 @@
#include <pj/array.h>
#include <pj/assert.h>
#include <pj/guid.h>
#include <pj/hash.h>
#include <pj/log.h>
#include <pj/os.h>
#include <pj/pool.h>
@ -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; i<PJ_ARRAY_SIZE(ice->tp_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; i<ice->comp_cnt; ++i) {
if (ice->comp[i].stun_sess) {
pj_stun_session_destroy(ice->comp[i].stun_sess);
pj_bzero(&ice->comp[i], sizeof(ice->comp[i]));
}
}
ice->comp_cnt = highest_comp;
/* Init timer entry in the checklist. Initially the timer ID is FALSE
* 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; i<ice->clist.count; ++i) {
pj_ice_sess_check *c = &ice->clist.checks[i];
if (c->lcand->comp_id == rcheck->comp_id) {
if (c->lcand->comp_id == rcheck->comp_id &&
c->lcand->transport_id == rcheck->transport_id)
{
lcand = c->lcand;
break;
}
@ -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; i<PJ_ARRAY_SIZE(ice->tp_data); ++i) {
if (ice->tp_data[i].transport_id == transport_id) {
msg_data = &ice->tp_data[i];
break;
}
}
if (msg_data == NULL) {
pj_assert(!"Invalid transport ID");
status = PJ_EINVAL;
goto on_return;
}
stun_status = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_size,
PJ_STUN_IS_DATAGRAM);
if (stun_status == PJ_SUCCESS) {
status = pj_stun_session_on_rx_pkt(comp->stun_sess, pkt, pkt_size,
PJ_STUN_IS_DATAGRAM, NULL,
PJ_STUN_IS_DATAGRAM, msg_data,
NULL, src_addr, src_addr_len);
if (status != PJ_SUCCESS) {
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);
}

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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; i<request->msg->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, &notify_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;
return tdata->sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt,
pkt_size, tdata->dst_addr,
tdata->addr_len);
/* Lock the session and prevent user from destroying us in the callback */
pj_atomic_inc(sess->busy);
pj_lock_acquire(sess->lock);
status = sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt,
pkt_size, tdata->dst_addr,
tdata->addr_len);
pj_lock_release(sess->lock);
if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
pj_stun_session_destroy(sess);
return PJNATH_ESTUNDESTROYED;
} else {
return status;
}
}
/* **************************************************************************/
@ -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;
}

View File

@ -0,0 +1,829 @@
/* $Id$ */
/*
* Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjnath/stun_sock.h>
#include <pjnath/errno.h>
#include <pjnath/stun_transaction.h>
#include <pjnath/stun_session.h>
#include <pjlib-util/srv_resolver.h>
#include <pj/activesock.h>
#include <pj/addr_resolv.h>
#include <pj/array.h>
#include <pj/assert.h>
#include <pj/ip_helper.h>
#include <pj/log.h>
#include <pj/pool.h>
#include <pj/rand.h>
struct pj_stun_sock
{
char *obj_name; /* Log identification */
pj_pool_t *pool; /* Pool */
void *user_data; /* Application user data */
int af; /* Address family */
pj_stun_config stun_cfg; /* STUN config (ioqueue etc)*/
pj_stun_sock_cb cb; /* Application callbacks */
int ka_interval; /* Keep alive interval */
pj_timer_entry ka_timer; /* Keep alive timer. */
pj_sockaddr srv_addr; /* Resolved server addr */
pj_sockaddr mapped_addr; /* Our public address */
pj_dns_async_query *q; /* Pending DNS query */
pj_sock_t sock_fd; /* Socket descriptor */
pj_activesock_t *active_sock; /* Active socket object */
pj_ioqueue_op_key_t send_key; /* Default send key for app */
pj_ioqueue_op_key_t int_send_key; /* Send key for internal */
pj_uint16_t tsx_id[6]; /* .. to match STUN msg */
pj_stun_session *stun_sess; /* STUN session */
};
/*
* Prototypes for static functions
*/
/* This callback is called by the STUN session to send packet */
static pj_status_t sess_on_send_msg(pj_stun_session *sess,
void *token,
const void *pkt,
pj_size_t pkt_size,
const pj_sockaddr_t *dst_addr,
unsigned addr_len);
/* This callback is called by the STUN session when outgoing transaction
* is complete
*/
static void sess_on_request_complete(pj_stun_session *sess,
pj_status_t status,
void *token,
pj_stun_tx_data *tdata,
const pj_stun_msg *response,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len);
/* DNS resolver callback */
static void dns_srv_resolver_cb(void *user_data,
pj_status_t status,
const pj_dns_srv_record *rec);
/* Start sending STUN Binding request */
static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock);
/* Callback from active socket when incoming packet is received */
static pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
void *data,
pj_size_t size,
const pj_sockaddr_t *src_addr,
int addr_len,
pj_status_t status);
/* Callback from active socket about send status */
static pj_bool_t on_data_sent(pj_activesock_t *asock,
pj_ioqueue_op_key_t *send_key,
pj_ssize_t sent);
/* Schedule keep-alive timer */
static void start_ka_timer(pj_stun_sock *stun_sock);
/* Keep-alive timer callback */
static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te);
#define INTERNAL_MSG_TOKEN (void*)1
/*
* Retrieve the name representing the specified operation.
*/
PJ_DEF(const char*) pj_stun_sock_op_name(pj_stun_sock_op op)
{
const char *names[] = {
"?",
"DNS resolution",
"STUN Binding request",
"Keep-alive"
};
return op <= PJ_STUN_SOCK_KEEP_ALIVE_OP ? names[op] : "?";
};
/*
* Initialize the STUN transport setting with its default values.
*/
PJ_DEF(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg)
{
pj_bzero(cfg, sizeof(*cfg));
cfg->max_pkt_size = PJ_STUN_SOCK_PKT_LEN;
cfg->async_cnt = 1;
cfg->ka_interval = PJ_STUN_KEEP_ALIVE_SEC;
}
/* Check that configuration setting is valid */
static pj_bool_t pj_stun_sock_cfg_is_valid(const pj_stun_sock_cfg *cfg)
{
return cfg->max_pkt_size > 1 && cfg->async_cnt >= 1;
}
/*
* Create the STUN transport using the specified configuration.
*/
PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
const char *name,
int af,
const pj_stun_sock_cb *cb,
const pj_stun_sock_cfg *cfg,
void *user_data,
pj_stun_sock **p_stun_sock)
{
pj_pool_t *pool;
pj_stun_sock *stun_sock;
pj_stun_sock_cfg default_cfg;
unsigned i;
pj_status_t status;
PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL);
PJ_ASSERT_RETURN(af==pj_AF_INET()||af==pj_AF_INET6(), PJ_EAFNOTSUP);
PJ_ASSERT_RETURN(!cfg || pj_stun_sock_cfg_is_valid(cfg), PJ_EINVAL);
PJ_ASSERT_RETURN(cb->on_status, PJ_EINVAL);
status = pj_stun_config_check_valid(stun_cfg);
if (status != PJ_SUCCESS)
return status;
if (name == NULL)
name = "stuntp%p";
if (cfg == NULL) {
pj_stun_sock_cfg_default(&default_cfg);
cfg = &default_cfg;
}
/* Create structure */
pool = pj_pool_create(stun_cfg->pf, name, 256, 512, NULL);
stun_sock = PJ_POOL_ZALLOC_T(pool, pj_stun_sock);
stun_sock->pool = pool;
stun_sock->obj_name = pool->obj_name;
stun_sock->user_data = user_data;
stun_sock->af = af;
stun_sock->sock_fd = PJ_INVALID_SOCKET;
pj_memcpy(&stun_sock->stun_cfg, stun_cfg, sizeof(*stun_cfg));
pj_memcpy(&stun_sock->cb, cb, sizeof(*cb));
stun_sock->ka_interval = cfg->ka_interval;
if (stun_sock->ka_interval == 0)
stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC;
/* Create socket and bind socket */
status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &stun_sock->sock_fd);
if (status != PJ_SUCCESS)
goto on_error;
if (pj_sockaddr_has_addr(&cfg->bound_addr)) {
status = pj_sock_bind(stun_sock->sock_fd, &cfg->bound_addr,
pj_sockaddr_get_len(&cfg->bound_addr));
} else {
pj_sockaddr bound_addr;
pj_sockaddr_init(af, &bound_addr, NULL, 0);
status = pj_sock_bind(stun_sock->sock_fd, &bound_addr,
pj_sockaddr_get_len(&bound_addr));
}
if (status != PJ_SUCCESS)
goto on_error;
/* Create more useful information string about this transport */
#if 0
{
pj_sockaddr bound_addr;
int addr_len = sizeof(bound_addr);
status = pj_sock_getsockname(stun_sock->sock_fd, &bound_addr,
&addr_len);
if (status != PJ_SUCCESS)
goto on_error;
stun_sock->info = pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+10);
pj_sockaddr_print(&bound_addr, stun_sock->info,
PJ_INET6_ADDRSTRLEN, 3);
}
#endif
/* Init active socket configuration */
{
pj_activesock_cfg activesock_cfg;
pj_activesock_cb activesock_cb;
pj_activesock_cfg_default(&activesock_cfg);
activesock_cfg.async_cnt = cfg->async_cnt;
activesock_cfg.concurrency = 0;
/* Create the active socket */
pj_bzero(&activesock_cb, sizeof(activesock_cb));
activesock_cb.on_data_recvfrom = &on_data_recvfrom;
activesock_cb.on_data_sent = &on_data_sent;
status = pj_activesock_create(pool, stun_sock->sock_fd,
pj_SOCK_DGRAM(),
&activesock_cfg, stun_cfg->ioqueue,
&activesock_cb, stun_sock,
&stun_sock->active_sock);
if (status != PJ_SUCCESS)
goto on_error;
/* Start asynchronous read operations */
status = pj_activesock_start_recvfrom(stun_sock->active_sock, pool,
cfg->max_pkt_size, 0);
if (status != PJ_SUCCESS)
goto on_error;
/* Init send keys */
pj_ioqueue_op_key_init(&stun_sock->send_key,
sizeof(stun_sock->send_key));
pj_ioqueue_op_key_init(&stun_sock->int_send_key,
sizeof(stun_sock->int_send_key));
}
/* Create STUN session */
{
pj_stun_session_cb sess_cb;
pj_bzero(&sess_cb, sizeof(sess_cb));
sess_cb.on_request_complete = &sess_on_request_complete;
sess_cb.on_send_msg = &sess_on_send_msg;
status = pj_stun_session_create(&stun_sock->stun_cfg,
stun_sock->obj_name,
&sess_cb, PJ_FALSE,
&stun_sock->stun_sess);
if (status != PJ_SUCCESS)
goto on_error;
}
/* Associate us with the STUN session */
pj_stun_session_set_user_data(stun_sock->stun_sess, stun_sock);
/* Initialize random numbers to be used as STUN transaction ID for
* outgoing Binding request. We use the 80bit number to distinguish
* STUN messages we sent with STUN messages that the application sends.
* The last 16bit value in the array is a counter.
*/
for (i=0; i<PJ_ARRAY_SIZE(stun_sock->tsx_id); ++i) {
stun_sock->tsx_id[i] = (pj_uint16_t) pj_rand();
}
stun_sock->tsx_id[5] = 0;
/* Init timer entry */
stun_sock->ka_timer.cb = &ka_timer_cb;
stun_sock->ka_timer.user_data = stun_sock;
/* Done */
*p_stun_sock = stun_sock;
return PJ_SUCCESS;
on_error:
pj_stun_sock_destroy(stun_sock);
return status;
}
/* Start socket. */
PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock,
const pj_str_t *domain,
pj_uint16_t default_port,
pj_dns_resolver *resolver)
{
pj_status_t status;
PJ_ASSERT_RETURN(stun_sock && domain && default_port, PJ_EINVAL);
/* Check whether the domain contains IP address */
stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)stun_sock->af;
status = pj_inet_pton(stun_sock->af, domain,
pj_sockaddr_get_addr(&stun_sock->srv_addr));
if (status != PJ_SUCCESS) {
stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)0;
}
/* If resolver is set, try to resolve with DNS SRV first. It
* will fallback to DNS A/AAAA when no SRV record is found.
*/
if (status != PJ_SUCCESS && resolver) {
const pj_str_t res_name = pj_str("_stun._udp.");
unsigned opt;
pj_assert(stun_sock->q == NULL);
opt = PJ_DNS_SRV_FALLBACK_A;
if (stun_sock->af == pj_AF_INET6()) {
opt |= (PJ_DNS_SRV_RESOLVE_AAAA | PJ_DNS_SRV_FALLBACK_AAAA);
}
status = pj_dns_srv_resolve(domain, &res_name, default_port,
stun_sock->pool, resolver, opt,
stun_sock, &dns_srv_resolver_cb,
&stun_sock->q);
/* Processing will resume when the DNS SRV callback is called */
return status;
} else {
if (status != PJ_SUCCESS) {
pj_addrinfo ai;
unsigned cnt = 1;
status = pj_getaddrinfo(stun_sock->af, domain, &cnt, &ai);
if (status != PJ_SUCCESS)
return status;
pj_sockaddr_cp(&stun_sock->srv_addr, &ai.ai_addr);
}
pj_sockaddr_set_port(&stun_sock->srv_addr, (pj_uint16_t)default_port);
/* Start sending Binding request */
return get_mapped_addr(stun_sock);
}
}
/* Destroy */
PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock)
{
if (stun_sock->q) {
pj_dns_resolver_cancel_query(stun_sock->q, PJ_FALSE);
stun_sock->q = NULL;
}
/* Destroy the active socket first just in case we'll get
* stray callback.
*/
if (stun_sock->active_sock != NULL) {
pj_activesock_close(stun_sock->active_sock);
stun_sock->active_sock = NULL;
stun_sock->sock_fd = PJ_INVALID_SOCKET;
} else if (stun_sock->sock_fd != PJ_INVALID_SOCKET) {
pj_sock_close(stun_sock->sock_fd);
stun_sock->sock_fd = PJ_INVALID_SOCKET;
}
if (stun_sock->ka_timer.id != 0) {
pj_timer_heap_cancel(stun_sock->stun_cfg.timer_heap,
&stun_sock->ka_timer);
stun_sock->ka_timer.id = 0;
}
if (stun_sock->stun_sess) {
pj_stun_session_destroy(stun_sock->stun_sess);
stun_sock->stun_sess = NULL;
}
if (stun_sock->pool) {
pj_pool_t *pool = stun_sock->pool;
stun_sock->pool = NULL;
pj_pool_release(pool);
}
return PJ_SUCCESS;
}
/* Associate user data */
PJ_DEF(pj_status_t) pj_stun_sock_set_user_data( pj_stun_sock *stun_sock,
void *user_data)
{
PJ_ASSERT_RETURN(stun_sock, PJ_EINVAL);
stun_sock->user_data = user_data;
return PJ_SUCCESS;
}
/* Get user data */
PJ_DEF(void*) pj_stun_sock_get_user_data(pj_stun_sock *stun_sock)
{
PJ_ASSERT_RETURN(stun_sock, NULL);
return stun_sock->user_data;
}
/* Notify application that session has failed */
static pj_bool_t sess_fail(pj_stun_sock *stun_sock,
pj_stun_sock_op op,
pj_status_t status)
{
char errmsg[PJ_ERR_MSG_SIZE];
pj_bool_t ret;
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(4,(stun_sock->obj_name, "Session failed because %s failed: %s",
pj_stun_sock_op_name(op), errmsg));
ret = (*stun_sock->cb.on_status)(stun_sock, op, status);
return ret;
}
/* DNS resolver callback */
static void dns_srv_resolver_cb(void *user_data,
pj_status_t status,
const pj_dns_srv_record *rec)
{
pj_stun_sock *stun_sock = (pj_stun_sock*) user_data;
/* Clear query */
stun_sock->q = NULL;
/* Handle error */
if (status != PJ_SUCCESS) {
sess_fail(stun_sock, PJ_STUN_SOCK_DNS_OP, status);
return;
}
pj_assert(rec->count);
pj_assert(rec->entry[0].server.addr_count);
PJ_TODO(SUPPORT_IPV6_IN_RESOLVER);
pj_assert(stun_sock->af == pj_AF_INET());
/* Set the address */
pj_sockaddr_in_init(&stun_sock->srv_addr.ipv4, NULL,
rec->entry[0].port);
stun_sock->srv_addr.ipv4.sin_addr = rec->entry[0].server.addr[0];
/* Start sending Binding request */
get_mapped_addr(stun_sock);
}
/* Start sending STUN Binding request */
static pj_status_t get_mapped_addr(pj_stun_sock *stun_sock)
{
pj_stun_tx_data *tdata;
pj_status_t status;
/* Increment request counter and create STUN Binding request */
++stun_sock->tsx_id[5];
status = pj_stun_session_create_req(stun_sock->stun_sess,
PJ_STUN_BINDING_REQUEST,
PJ_STUN_MAGIC,
(const pj_uint8_t*)stun_sock->tsx_id,
&tdata);
if (status != PJ_SUCCESS)
goto on_error;
/* Send request */
status=pj_stun_session_send_msg(stun_sock->stun_sess, INTERNAL_MSG_TOKEN,
PJ_FALSE, PJ_TRUE, &stun_sock->srv_addr,
pj_sockaddr_get_len(&stun_sock->srv_addr),
tdata);
if (status != PJ_SUCCESS)
goto on_error;
return PJ_SUCCESS;
on_error:
sess_fail(stun_sock, PJ_STUN_SOCK_BINDING_OP, status);
return status;
}
/* Get info */
PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock,
pj_stun_sock_info *info)
{
int addr_len;
pj_status_t status;
PJ_ASSERT_RETURN(stun_sock && info, PJ_EINVAL);
/* Copy STUN server address and mapped address */
pj_memcpy(&info->srv_addr, &stun_sock->srv_addr,
sizeof(pj_sockaddr));
pj_memcpy(&info->mapped_addr, &stun_sock->mapped_addr,
sizeof(pj_sockaddr));
/* Retrieve bound address */
addr_len = sizeof(info->bound_addr);
status = pj_sock_getsockname(stun_sock->sock_fd, &info->bound_addr,
&addr_len);
if (status != PJ_SUCCESS)
return status;
/* If socket is bound to a specific interface, then only put that
* interface in the alias list. Otherwise query all the interfaces
* in the host.
*/
if (pj_sockaddr_has_addr(&info->bound_addr)) {
info->alias_cnt = 1;
pj_sockaddr_cp(&info->aliases[0], &info->bound_addr);
} else {
unsigned i;
/* Enum all IP interfaces in the host */
info->alias_cnt = PJ_ARRAY_SIZE(info->aliases);
status = pj_enum_ip_interface(stun_sock->af, &info->alias_cnt,
info->aliases);
if (status != PJ_SUCCESS)
return status;
/* Set the port number for each address.
*/
if (stun_sock->af == pj_AF_INET()) {
for (i=0; i<info->alias_cnt; ++i) {
pj_sockaddr_set_port(&info->aliases[i],
pj_sockaddr_get_port(&info->bound_addr));
}
}
}
return PJ_SUCCESS;
}
/* Send application data */
PJ_DEF(pj_status_t) pj_stun_sock_sendto( pj_stun_sock *stun_sock,
pj_ioqueue_op_key_t *send_key,
const void *pkt,
unsigned pkt_len,
unsigned flag,
const pj_sockaddr_t *dst_addr,
unsigned addr_len)
{
pj_ssize_t size;
PJ_ASSERT_RETURN(stun_sock && pkt && dst_addr && addr_len, PJ_EINVAL);
if (send_key==NULL)
send_key = &stun_sock->send_key;
size = pkt_len;
return pj_activesock_sendto(stun_sock->active_sock, send_key,
pkt, &size, flag, dst_addr, addr_len);
}
/* This callback is called by the STUN session to send packet */
static pj_status_t sess_on_send_msg(pj_stun_session *sess,
void *token,
const void *pkt,
pj_size_t pkt_size,
const pj_sockaddr_t *dst_addr,
unsigned addr_len)
{
pj_stun_sock *stun_sock;
pj_ssize_t size;
stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess);
pj_assert(token==INTERNAL_MSG_TOKEN);
PJ_UNUSED_ARG(token);
size = pkt_size;
return pj_activesock_sendto(stun_sock->active_sock,
&stun_sock->int_send_key,
pkt, &size, 0, dst_addr, addr_len);
}
/* This callback is called by the STUN session when outgoing transaction
* is complete
*/
static void sess_on_request_complete(pj_stun_session *sess,
pj_status_t status,
void *token,
pj_stun_tx_data *tdata,
const pj_stun_msg *response,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
pj_stun_sock *stun_sock;
const pj_stun_sockaddr_attr *mapped_attr;
pj_stun_sock_op op;
pj_bool_t mapped_changed;
pj_bool_t resched = PJ_TRUE;
stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess);
PJ_UNUSED_ARG(tdata);
PJ_UNUSED_ARG(token);
PJ_UNUSED_ARG(src_addr);
PJ_UNUSED_ARG(src_addr_len);
/* Check if this is a keep-alive or the first Binding request */
if (pj_sockaddr_has_addr(&stun_sock->mapped_addr))
op = PJ_STUN_SOCK_KEEP_ALIVE_OP;
else
op = PJ_STUN_SOCK_BINDING_OP;
/* Handle failure */
if (status != PJ_SUCCESS) {
resched = sess_fail(stun_sock, op, status);
goto on_return;
}
/* Get XOR-MAPPED-ADDRESS, or MAPPED-ADDRESS when XOR-MAPPED-ADDRESS
* doesn't exist.
*/
mapped_attr = (const pj_stun_sockaddr_attr*)
pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR,
0);
if (mapped_attr==NULL) {
mapped_attr = (const pj_stun_sockaddr_attr*)
pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR,
0);
}
if (mapped_attr == NULL) {
resched = sess_fail(stun_sock, op, PJNATH_ESTUNNOMAPPEDADDR);
goto on_return;
}
/* Determine if mapped address has changed, and save the new mapped
* address and call callback if so
*/
mapped_changed = !pj_sockaddr_has_addr(&stun_sock->mapped_addr) ||
pj_sockaddr_cmp(&stun_sock->mapped_addr,
&mapped_attr->sockaddr) != 0;
if (mapped_changed) {
/* Print mapped adress */
{
char addrinfo[PJ_INET6_ADDRSTRLEN+10];
PJ_LOG(4,(stun_sock->obj_name,
"STUN mapped address found/changed: %s",
pj_sockaddr_print(&mapped_attr->sockaddr,
addrinfo, sizeof(addrinfo), 3)));
}
pj_sockaddr_cp(&stun_sock->mapped_addr, &mapped_attr->sockaddr);
resched = (*stun_sock->cb.on_status)(stun_sock, op, PJ_SUCCESS);
goto on_return;
}
on_return:
/* Start/restart keep-alive timer */
if (resched)
start_ka_timer(stun_sock);
}
/* Schedule keep-alive timer */
static void start_ka_timer(pj_stun_sock *stun_sock)
{
if (stun_sock->ka_timer.id != 0) {
pj_timer_heap_cancel(stun_sock->stun_cfg.timer_heap,
&stun_sock->ka_timer);
stun_sock->ka_timer.id = 0;
}
pj_assert(stun_sock->ka_interval != 0);
if (stun_sock->ka_interval > 0) {
pj_time_val delay;
delay.sec = stun_sock->ka_interval;
delay.msec = 0;
if (pj_timer_heap_schedule(stun_sock->stun_cfg.timer_heap,
&stun_sock->ka_timer,
&delay) == PJ_SUCCESS)
{
stun_sock->ka_timer.id = PJ_TRUE;
}
}
}
/* Keep-alive timer callback */
static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
{
pj_stun_sock *stun_sock;
stun_sock = (pj_stun_sock *) te->user_data;
PJ_UNUSED_ARG(th);
/* Time to send STUN Binding request */
if (get_mapped_addr(stun_sock) != PJ_SUCCESS)
return;
/* Next keep-alive timer will be scheduled once the request
* is complete.
*/
}
/* Callback from active socket when incoming packet is received */
static pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
void *data,
pj_size_t size,
const pj_sockaddr_t *src_addr,
int addr_len,
pj_status_t status)
{
pj_stun_sock *stun_sock;
pj_stun_msg_hdr *hdr;
pj_uint16_t type;
stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock);
/* Log socket error */
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(4,(stun_sock->obj_name, "recvfrom() error: %s", errmsg));
return PJ_TRUE;
}
/* Check that this is STUN message */
status = pj_stun_msg_check((const pj_uint8_t*)data, size,
PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET);
if (status != PJ_SUCCESS) {
/* Not STUN -- give it to application */
goto process_app_data;
}
/* Treat packet as STUN header and copy the STUN message type.
* We don't want to access the type directly from the header
* since it may not be properly aligned.
*/
hdr = (pj_stun_msg_hdr*) data;
pj_memcpy(&type, &hdr->type, 2);
type = pj_ntohs(type);
/* If the packet is a STUN Binding response and part of the
* transaction ID matches our internal ID, then this is
* our internal STUN message (Binding request or keep alive).
* Give it to our STUN session.
*/
if (!PJ_STUN_IS_RESPONSE(type) ||
PJ_STUN_GET_METHOD(type) != PJ_STUN_BINDING_METHOD ||
pj_memcmp(hdr->tsx_id, stun_sock->tsx_id, 10) != 0)
{
/* Not STUN Binding response, or STUN transaction ID mismatch.
* This is not our message too -- give it to application.
*/
goto process_app_data;
}
/* This is our STUN Binding response. Give it to the STUN session */
status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, data, size,
PJ_STUN_IS_DATAGRAM, NULL, NULL,
src_addr, addr_len);
return status!=PJNATH_ESTUNDESTROYED ? PJ_TRUE : PJ_FALSE;
process_app_data:
if (stun_sock->cb.on_rx_data) {
pj_bool_t ret;
ret = (*stun_sock->cb.on_rx_data)(stun_sock, data, size,
src_addr, addr_len);
return ret;
}
return PJ_TRUE;
}
/* Callback from active socket about send status */
static pj_bool_t on_data_sent(pj_activesock_t *asock,
pj_ioqueue_op_key_t *send_key,
pj_ssize_t sent)
{
pj_stun_sock *stun_sock;
stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock);
/* Don't report to callback if this is internal message */
if (send_key == &stun_sock->int_send_key) {
return PJ_TRUE;
}
/* Report to callback */
if (stun_sock->cb.on_data_sent) {
pj_bool_t ret;
/* If app gives NULL send_key in sendto() function, then give
* NULL in the callback too
*/
if (send_key == &stun_sock->send_key)
send_key = NULL;
/* Call callback */
ret = (*stun_sock->cb.on_data_sent)(stun_sock, send_key, sent);
return ret;
}
return PJ_TRUE;
}

View File

@ -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;

View File

@ -29,8 +29,9 @@
#include <pj/pool.h>
#include <pj/sock.h>
#define MAX_SRV_CNT 4
#define REFRESH_SEC_BEFORE 60
#define PJ_TURN_CHANNEL_MIN 0x4000
#define PJ_TURN_CHANNEL_MAX 0xFFFE /* inclusive */
#define PJ_TURN_PEER_HTABLE_SIZE 8
static const char *state_names[] =
{
@ -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; i<rec->count; ++i) {
tot_cnt += rec->entry[i].server.addr_count;
}
if (tot_cnt > PJ_TURN_MAX_DNS_SRV_CNT)
tot_cnt = PJ_TURN_MAX_DNS_SRV_CNT;
/* Allocate server entries */
sess->srv_addr_list = (pj_sockaddr*)
pj_pool_calloc(sess->pool, tot_cnt,
sizeof(pj_sockaddr));
/* Copy results to server entries */
for (i=0, cnt=0; i<rec->count && cnt<MAX_SRV_CNT; ++i) {
for (i=0, cnt=0; i<rec->count && cnt<PJ_TURN_MAX_DNS_SRV_CNT; ++i) {
unsigned j;
for (j=0; j<rec->entry[i].server.addr_count && cnt<MAX_SRV_CNT; ++j) {
for (j=0; j<rec->entry[i].server.addr_count &&
cnt<PJ_TURN_MAX_DNS_SRV_CNT; ++j)
{
pj_sockaddr_in *addr = &sess->srv_addr_list[cnt].ipv4;
addr->sin_family = sess->af;

View File

@ -17,6 +17,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjnath/turn_sock.h>
#include <pj/activesock.h>
#include <pj/assert.h>
#include <pj/errno.h>
#include <pj/lock.h>
@ -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};

View File

@ -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)

View File

@ -35,8 +35,7 @@ static struct cred_t
{
{ "100", "100" },
{ "700", "700" },
{ "701", "701" },
{ "702", "702" }
{ "701", "701" }
};
#define THE_NONCE "pjnath"