diff options
Diffstat (limited to 'pjlib-util')
-rw-r--r-- | pjlib-util/build/Makefile | 2 | ||||
-rw-r--r-- | pjlib-util/build/pjlib_util.dsp | 10 | ||||
-rw-r--r-- | pjlib-util/build/pjlib_util.vcproj | 8 | ||||
-rw-r--r-- | pjlib-util/build/wince-evc4/pjlib_util_wince.vcp | 244 | ||||
-rw-r--r-- | pjlib-util/include/pjlib-util.h | 1 | ||||
-rw-r--r-- | pjlib-util/include/pjlib-util/crc32.h | 95 | ||||
-rw-r--r-- | pjlib-util/include/pjlib-util/errno.h | 32 | ||||
-rw-r--r-- | pjlib-util/include/pjlib-util/stun_msg.h | 277 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util-test/encryption.c | 56 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/crc32.c | 192 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_msg.c | 552 | ||||
-rw-r--r-- | pjlib-util/src/pjstun-client/stun_session.c | 9 | ||||
-rw-r--r-- | pjlib-util/src/pjstun-srv/server_main.c | 41 |
13 files changed, 1382 insertions, 137 deletions
diff --git a/pjlib-util/build/Makefile b/pjlib-util/build/Makefile index c7ff0dfa..75089c12 100644 --- a/pjlib-util/build/Makefile +++ b/pjlib-util/build/Makefile @@ -26,7 +26,7 @@ export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJLIB_UTIL_LIB)) \ # export PJLIB_UTIL_SRCDIR = ../src/pjlib-util export PJLIB_UTIL_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ - errno.o dns.o dns_dump.o getopt.o \ + crc32.o errno.o dns.o dns_dump.o getopt.o \ hmac_md5.o hmac_sha1.o md5.o resolver.o \ scanner.o sha1.o string.o stun_simple.o \ stun_simple_client.o xml.o diff --git a/pjlib-util/build/pjlib_util.dsp b/pjlib-util/build/pjlib_util.dsp index 3f5d9f6c..555a6c15 100644 --- a/pjlib-util/build/pjlib_util.dsp +++ b/pjlib-util/build/pjlib_util.dsp @@ -87,15 +87,15 @@ LIB32=link.exe -lib # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
-SOURCE="..\src\pjlib-util\dns.c"
+SOURCE="..\src\pjlib-util\crc32.c"
# End Source File
# Begin Source File
-SOURCE="..\src\pjlib-util\dns_dump.c"
+SOURCE="..\src\pjlib-util\dns.c"
# End Source File
# Begin Source File
-SOURCE="..\src\pjlib-util-test\encryption.c"
+SOURCE="..\src\pjlib-util\dns_dump.c"
# End Source File
# Begin Source File
@@ -189,6 +189,10 @@ SOURCE="..\include\pjlib-util\config.h" # End Source File
# Begin Source File
+SOURCE="..\include\pjlib-util\crc32.h"
+# End Source File
+# Begin Source File
+
SOURCE="..\include\pjlib-util\dns.h"
# End Source File
# Begin Source File
diff --git a/pjlib-util/build/pjlib_util.vcproj b/pjlib-util/build/pjlib_util.vcproj index 204bcdce..d07f073d 100644 --- a/pjlib-util/build/pjlib_util.vcproj +++ b/pjlib-util/build/pjlib_util.vcproj @@ -175,6 +175,10 @@ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
>
<File
+ RelativePath="..\src\pjlib-util\crc32.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjlib-util\dns.c"
>
<FileConfiguration
@@ -492,6 +496,10 @@ >
</File>
<File
+ RelativePath="..\include\pjlib-util\crc32.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjlib-util\dns.h"
>
</File>
diff --git a/pjlib-util/build/wince-evc4/pjlib_util_wince.vcp b/pjlib-util/build/wince-evc4/pjlib_util_wince.vcp index 6ba8eacf..d0a44f04 100644 --- a/pjlib-util/build/wince-evc4/pjlib_util_wince.vcp +++ b/pjlib-util/build/wince-evc4/pjlib_util_wince.vcp @@ -318,6 +318,243 @@ BSC32=bscmake.exe # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE="..\..\src\pjlib-util\crc32.c" + +!IF "$(CFG)" == "pjlib_util_wince - Win32 (WCE emulator) Release" + +DEP_CPP_CRC32=\ + "..\..\..\pjlib\include\pj\compat\cc_gcc.h"\ + "..\..\..\pjlib\include\pj\compat\cc_msvc.h"\ + "..\..\..\pjlib\include\pj\compat\m_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_darwinos.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\ + "..\..\..\pjlib\include\pj\compat\os_palmos.h"\ + "..\..\..\pjlib\include\pj\compat\os_rtems.h"\ + "..\..\..\pjlib\include\pj\compat\os_sunos.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\ + "..\..\..\pjlib\include\pj\compat\size_t.h"\ + "..\..\..\pjlib\include\pj\config.h"\ + "..\..\..\pjlib\include\pj\config_site.h"\ + "..\..\..\pjlib\include\pj\config_site_sample.h"\ + "..\..\..\pjlib\include\pj\types.h"\ + "..\..\include\pjlib-util\crc32.h"\ + + +!ELSEIF "$(CFG)" == "pjlib_util_wince - Win32 (WCE emulator) Debug" + +DEP_CPP_CRC32=\ + "..\..\..\pjlib\include\pj\compat\cc_gcc.h"\ + "..\..\..\pjlib\include\pj\compat\cc_msvc.h"\ + "..\..\..\pjlib\include\pj\compat\m_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_darwinos.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\ + "..\..\..\pjlib\include\pj\compat\os_palmos.h"\ + "..\..\..\pjlib\include\pj\compat\os_rtems.h"\ + "..\..\..\pjlib\include\pj\compat\os_sunos.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\ + "..\..\..\pjlib\include\pj\compat\size_t.h"\ + "..\..\..\pjlib\include\pj\config.h"\ + "..\..\..\pjlib\include\pj\config_site.h"\ + "..\..\..\pjlib\include\pj\config_site_sample.h"\ + "..\..\..\pjlib\include\pj\types.h"\ + "..\..\include\pjlib-util\crc32.h"\ + + +!ELSEIF "$(CFG)" == "pjlib_util_wince - Win32 (WCE ARMV4I) Release" + +DEP_CPP_CRC32=\ + "..\..\..\pjlib\include\pj\compat\cc_gcc.h"\ + "..\..\..\pjlib\include\pj\compat\cc_msvc.h"\ + "..\..\..\pjlib\include\pj\compat\m_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_darwinos.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\ + "..\..\..\pjlib\include\pj\compat\os_palmos.h"\ + "..\..\..\pjlib\include\pj\compat\os_rtems.h"\ + "..\..\..\pjlib\include\pj\compat\os_sunos.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\ + "..\..\..\pjlib\include\pj\compat\size_t.h"\ + "..\..\..\pjlib\include\pj\config.h"\ + "..\..\..\pjlib\include\pj\config_site.h"\ + "..\..\..\pjlib\include\pj\config_site_sample.h"\ + "..\..\..\pjlib\include\pj\types.h"\ + "..\..\include\pjlib-util\crc32.h"\ + + +!ELSEIF "$(CFG)" == "pjlib_util_wince - Win32 (WCE ARMV4I) Debug" + +DEP_CPP_CRC32=\ + "..\..\..\pjlib\include\pj\compat\cc_gcc.h"\ + "..\..\..\pjlib\include\pj\compat\cc_msvc.h"\ + "..\..\..\pjlib\include\pj\compat\m_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_darwinos.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\ + "..\..\..\pjlib\include\pj\compat\os_palmos.h"\ + "..\..\..\pjlib\include\pj\compat\os_rtems.h"\ + "..\..\..\pjlib\include\pj\compat\os_sunos.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\ + "..\..\..\pjlib\include\pj\compat\size_t.h"\ + "..\..\..\pjlib\include\pj\config.h"\ + "..\..\..\pjlib\include\pj\config_site.h"\ + "..\..\..\pjlib\include\pj\config_site_sample.h"\ + "..\..\..\pjlib\include\pj\types.h"\ + "..\..\include\pjlib-util\crc32.h"\ + + +!ELSEIF "$(CFG)" == "pjlib_util_wince - Win32 (WCE ARMV4) Release" + +DEP_CPP_CRC32=\ + "..\..\..\pjlib\include\pj\compat\cc_gcc.h"\ + "..\..\..\pjlib\include\pj\compat\cc_msvc.h"\ + "..\..\..\pjlib\include\pj\compat\m_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_darwinos.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\ + "..\..\..\pjlib\include\pj\compat\os_palmos.h"\ + "..\..\..\pjlib\include\pj\compat\os_rtems.h"\ + "..\..\..\pjlib\include\pj\compat\os_sunos.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\ + "..\..\..\pjlib\include\pj\compat\size_t.h"\ + "..\..\..\pjlib\include\pj\config.h"\ + "..\..\..\pjlib\include\pj\config_site.h"\ + "..\..\..\pjlib\include\pj\config_site_sample.h"\ + "..\..\..\pjlib\include\pj\types.h"\ + "..\..\include\pjlib-util\crc32.h"\ + + +!ELSEIF "$(CFG)" == "pjlib_util_wince - Win32 (WCE ARMV4) Debug" + +DEP_CPP_CRC32=\ + "..\..\..\pjlib\include\pj\compat\cc_gcc.h"\ + "..\..\..\pjlib\include\pj\compat\cc_msvc.h"\ + "..\..\..\pjlib\include\pj\compat\m_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_darwinos.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\ + "..\..\..\pjlib\include\pj\compat\os_palmos.h"\ + "..\..\..\pjlib\include\pj\compat\os_rtems.h"\ + "..\..\..\pjlib\include\pj\compat\os_sunos.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\ + "..\..\..\pjlib\include\pj\compat\size_t.h"\ + "..\..\..\pjlib\include\pj\config.h"\ + "..\..\..\pjlib\include\pj\config_site.h"\ + "..\..\..\pjlib\include\pj\config_site_sample.h"\ + "..\..\..\pjlib\include\pj\types.h"\ + "..\..\include\pjlib-util\crc32.h"\ + + +!ELSEIF "$(CFG)" == "pjlib_util_wince - Win32 (WCE ARMV4T) Release" + +DEP_CPP_CRC32=\ + "..\..\..\pjlib\include\pj\compat\cc_gcc.h"\ + "..\..\..\pjlib\include\pj\compat\cc_msvc.h"\ + "..\..\..\pjlib\include\pj\compat\m_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_darwinos.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\ + "..\..\..\pjlib\include\pj\compat\os_palmos.h"\ + "..\..\..\pjlib\include\pj\compat\os_rtems.h"\ + "..\..\..\pjlib\include\pj\compat\os_sunos.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\ + "..\..\..\pjlib\include\pj\compat\size_t.h"\ + "..\..\..\pjlib\include\pj\config.h"\ + "..\..\..\pjlib\include\pj\config_site.h"\ + "..\..\..\pjlib\include\pj\config_site_sample.h"\ + "..\..\..\pjlib\include\pj\types.h"\ + "..\..\include\pjlib-util\crc32.h"\ + + +!ELSEIF "$(CFG)" == "pjlib_util_wince - Win32 (WCE ARMV4T) Debug" + +DEP_CPP_CRC32=\ + "..\..\..\pjlib\include\pj\compat\cc_gcc.h"\ + "..\..\..\pjlib\include\pj\compat\cc_msvc.h"\ + "..\..\..\pjlib\include\pj\compat\m_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_darwinos.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\ + "..\..\..\pjlib\include\pj\compat\os_palmos.h"\ + "..\..\..\pjlib\include\pj\compat\os_rtems.h"\ + "..\..\..\pjlib\include\pj\compat\os_sunos.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\ + "..\..\..\pjlib\include\pj\compat\size_t.h"\ + "..\..\..\pjlib\include\pj\config.h"\ + "..\..\..\pjlib\include\pj\config_site.h"\ + "..\..\..\pjlib\include\pj\config_site_sample.h"\ + "..\..\..\pjlib\include\pj\types.h"\ + "..\..\include\pjlib-util\crc32.h"\ + + +!ELSEIF "$(CFG)" == "pjlib_util_wince - Win32 (WCE x86) Release" + +DEP_CPP_CRC32=\ + "..\..\..\pjlib\include\pj\compat\cc_gcc.h"\ + "..\..\..\pjlib\include\pj\compat\cc_msvc.h"\ + "..\..\..\pjlib\include\pj\compat\m_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_darwinos.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\ + "..\..\..\pjlib\include\pj\compat\os_palmos.h"\ + "..\..\..\pjlib\include\pj\compat\os_rtems.h"\ + "..\..\..\pjlib\include\pj\compat\os_sunos.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\ + "..\..\..\pjlib\include\pj\compat\size_t.h"\ + "..\..\..\pjlib\include\pj\config.h"\ + "..\..\..\pjlib\include\pj\config_site.h"\ + "..\..\..\pjlib\include\pj\config_site_sample.h"\ + "..\..\..\pjlib\include\pj\types.h"\ + "..\..\include\pjlib-util\crc32.h"\ + + +!ELSEIF "$(CFG)" == "pjlib_util_wince - Win32 (WCE x86) Debug" + +DEP_CPP_CRC32=\ + "..\..\..\pjlib\include\pj\compat\cc_gcc.h"\ + "..\..\..\pjlib\include\pj\compat\cc_msvc.h"\ + "..\..\..\pjlib\include\pj\compat\m_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_auto.h"\ + "..\..\..\pjlib\include\pj\compat\os_darwinos.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux.h"\ + "..\..\..\pjlib\include\pj\compat\os_linux_kernel.h"\ + "..\..\..\pjlib\include\pj\compat\os_palmos.h"\ + "..\..\..\pjlib\include\pj\compat\os_rtems.h"\ + "..\..\..\pjlib\include\pj\compat\os_sunos.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32.h"\ + "..\..\..\pjlib\include\pj\compat\os_win32_wince.h"\ + "..\..\..\pjlib\include\pj\compat\size_t.h"\ + "..\..\..\pjlib\include\pj\config.h"\ + "..\..\..\pjlib\include\pj\config_site.h"\ + "..\..\..\pjlib\include\pj\config_site_sample.h"\ + "..\..\..\pjlib\include\pj\types.h"\ + "..\..\include\pjlib-util\crc32.h"\ + + +!ENDIF + +# End Source File +# Begin Source File + SOURCE="..\..\src\pjlib-util\dns.c" !IF "$(CFG)" == "pjlib_util_wince - Win32 (WCE emulator) Release" @@ -6626,7 +6863,10 @@ DEP_CPP_STUN_M=\ "..\..\..\pjlib\include\pj\unicode.h"\ "..\..\..\pjlib\include\pjlib.h"\ "..\..\include\pjlib-util\config.h"\ + "..\..\include\pjlib-util\crc32.h"\ "..\..\include\pjlib-util\errno.h"\ + "..\..\include\pjlib-util\hmac_sha1.h"\ + "..\..\include\pjlib-util\md5.h"\ "..\..\include\pjlib-util\stun_msg.h"\ "..\..\include\pjlib-util\types.h"\ @@ -10777,6 +11017,10 @@ SOURCE="..\..\include\pjlib-util\config.h" # End Source File # Begin Source File +SOURCE="..\..\include\pjlib-util\crc32.h" +# End Source File +# Begin Source File + SOURCE="..\..\include\pjlib-util\dns.h" # End Source File # Begin Source File diff --git a/pjlib-util/include/pjlib-util.h b/pjlib-util/include/pjlib-util.h index 0cf5a90d..2b3326b4 100644 --- a/pjlib-util/include/pjlib-util.h +++ b/pjlib-util/include/pjlib-util.h @@ -32,6 +32,7 @@ #include <pjlib-util/getopt.h> /* Crypto */ +#include <pjlib-util/crc32.h> #include <pjlib-util/hmac_md5.h> #include <pjlib-util/hmac_sha1.h> #include <pjlib-util/md5.h> diff --git a/pjlib-util/include/pjlib-util/crc32.h b/pjlib-util/include/pjlib-util/crc32.h new file mode 100644 index 00000000..a85a51a6 --- /dev/null +++ b/pjlib-util/include/pjlib-util/crc32.h @@ -0,0 +1,95 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2005 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 __PJLIB_UTIL_CRC32_H__ +#define __PJLIB_UTIL_CRC32_H__ + +/** + * @file crc32.h + * @brief CRC32 implementation + */ + +#include <pj/types.h> + +PJ_BEGIN_DECL + +/** + * @defgroup PJLIB_UTIL_CRC32 CRC32 (Cyclic Redundancy Check) + * @ingroup PJLIB_UTIL_ENCRYPTION + * @{ + * This implements CRC32 algorithm. See ITU-T V.42 for the formal + * specification. + */ + +/** CRC32 context. */ +typedef struct pj_crc32_context +{ + pj_uint32_t crc_state; /**< Current state. */ +} pj_crc32_context; + + +/** + * Initialize CRC32 context. + * + * @param ctx CRC32 context. + */ +PJ_DECL(void) pj_crc32_init(pj_crc32_context *ctx); + +/** + * Feed data incrementally to the CRC32 algorithm. + * + * @param ctx CRC32 context. + * @param data Input data. + * @param nbytes Length of the input data. + * + * @return The current CRC32 value. + */ +PJ_DECL(pj_uint32_t) pj_crc32_update(pj_crc32_context *ctx, + const pj_uint8_t *data, + pj_size_t nbytes); + +/** + * Finalize CRC32 calculation and retrieve the CRC32 value. + * + * @param ctx CRC32 context. + * + * @return The current CRC value. + */ +PJ_DECL(pj_uint32_t) pj_crc32_final(pj_crc32_context *ctx); + +/** + * Perform one-off CRC32 calculation to the specified data. + * + * @param data Input data. + * @param nbytes Length of input data. + * + * @return CRC value of the data. + */ +PJ_DECL(pj_uint32_t) pj_crc32_calc(const pj_uint8_t *data, + pj_size_t nbytes); + + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJLIB_UTIL_CRC32_H__ */ + diff --git a/pjlib-util/include/pjlib-util/errno.h b/pjlib-util/include/pjlib-util/errno.h index 6d8c0b1f..3f004ca3 100644 --- a/pjlib-util/include/pjlib-util/errno.h +++ b/pjlib-util/include/pjlib-util/errno.h @@ -93,6 +93,16 @@ * Symetric NAT detected by STUN */ #define PJLIB_UTIL_ESTUNSYMMETRIC (PJLIB_UTIL_ERRNO_START+11) /* 320011 */ +/** + * @hideinitializer + * Invalid STUN magic value + */ +#define PJLIB_UTIL_ESTUNNOTMAGIC (PJLIB_UTIL_ERRNO_START+12) /* 320012 */ +/** + * @hideinitializer + * Invalid STUN fingerprint value + */ +#define PJLIB_UTIL_ESTUNFINGERPRINT (PJLIB_UTIL_ERRNO_START+13) /* 320013 */ @@ -266,12 +276,12 @@ #define PJLIB_UTIL_ESTUNUNKNOWNATTR (PJLIB_UTIL_ERRNO_START+112)/* 320112 */ /** * @hideinitializer - * Invalid socket address length. + * Invalid STUN socket address length. */ #define PJLIB_UTIL_ESTUNINADDRLEN (PJLIB_UTIL_ERRNO_START+113)/* 320113 */ /** * @hideinitializer - * IPv6 attribute not supported + * STUN IPv6 attribute not supported */ #define PJLIB_UTIL_ESTUNIPV6NOTSUPP (PJLIB_UTIL_ERRNO_START+113)/* 320113 */ /** @@ -281,7 +291,7 @@ #define PJLIB_UTIL_ESTUNNOTRESPONSE (PJLIB_UTIL_ERRNO_START+114)/* 320114 */ /** * @hideinitializer - * Transaction ID mismatch. + * STUN transaction ID mismatch. */ #define PJLIB_UTIL_ESTUNINVALIDID (PJLIB_UTIL_ERRNO_START+115)/* 320115 */ /** @@ -289,9 +299,23 @@ * Unable to find handler for the request. */ #define PJLIB_UTIL_ESTUNNOHANDLER (PJLIB_UTIL_ERRNO_START+116)/* 320116 */ +/** + * @hideinitializer + * Invalid STUN MESSAGE-INTEGRITY attribute position in message. + * STUN MESSAGE-INTEGRITY must be put last in the message, or before + * FINGERPRINT attribute. + */ +#define PJLIB_UTIL_ESTUNMSGINT (PJLIB_UTIL_ERRNO_START+117)/* 320117 */ +/** + * @hideinitializer + * Missing STUN USERNAME attribute. + * When credential is included in the STUN message (MESSAGE-INTEGRITY is + * present), the USERNAME attribute must be present in the message. + */ +#define PJLIB_UTIL_ESTUNNOUSERNAME (PJLIB_UTIL_ERRNO_START+118)/* 320118 */ -#define PJ_STATUS_FROM_STUN_CODE(code) -1 +#define PJ_STATUS_FROM_STUN_CODE(code) (PJLIB_UTIL_ERRNO_START+code) diff --git a/pjlib-util/include/pjlib-util/stun_msg.h b/pjlib-util/include/pjlib-util/stun_msg.h index b15b11f9..652472b5 100644 --- a/pjlib-util/include/pjlib-util/stun_msg.h +++ b/pjlib-util/include/pjlib-util/stun_msg.h @@ -601,7 +601,7 @@ typedef struct pj_stun_binary_attr /** * The raw data. */ - char *data; + pj_uint8_t *data; } pj_stun_binary_attr; @@ -1046,6 +1046,23 @@ typedef struct pj_stun_msg } pj_stun_msg; +/** STUN decoding options */ +enum pj_stun_options +{ + /** + * Tell the decoder that the message was received from datagram + * oriented transport (such as UDP). + */ + PJ_STUN_IS_DATAGRAM = 1, + + /** + * Tell pj_stun_msg_decode() to check the validity of the STUN + * message by calling pj_stun_msg_check() before starting to + * decode the packet. + */ + PJ_STUN_CHECK_PACKET = 2 +}; + /** * Get STUN message method name. * @@ -1087,7 +1104,7 @@ PJ_DECL(pj_str_t) pj_stun_get_err_reason(int err_code); /** - * Create a blank STUN message. + * Create a generic STUN message. * * @param pool Pool to create the STUN message. * @param msg_type The 14bit message type. @@ -1105,6 +1122,28 @@ PJ_DECL(pj_status_t) pj_stun_msg_create(pj_pool_t *pool, const pj_uint8_t tsx_id[12], pj_stun_msg **p_msg); +/** + * Create STUN response message. + * + * @param pool Pool to create the mesage. + * @param req_msg The request message. + * @param err_code STUN error code. If this value is not zero, + * then error response will be created, otherwise + * successful response will be created. + * @param err_msg Optional error message to explain err_code. + * If this value is NULL and err_code is not zero, + * the error string will be taken from the default + * STUN error message. + * @param p_response Pointer to receive the response. + * + * @return PJ_SUCCESS on success, or the appropriate error. + */ +PJ_DECL(pj_status_t) pj_stun_msg_create_response(pj_pool_t *pool, + const pj_stun_msg *req_msg, + unsigned err_code, + const pj_str_t *err_msg, + pj_stun_msg **p_response); + /** * Add STUN attribute to STUN message. @@ -1120,6 +1159,41 @@ PJ_DECL(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg, /** + * Print the STUN message structure to a packet buffer, ready to be + * sent to remote destination. This function will take care about + * calculating the MESSAGE-INTEGRITY digest as well as FINGERPRINT + * value. + * + * If MESSAGE-INTEGRITY attribute is present, the function assumes + * that application wants to include credential (short or long term) + * in the message, and this function will calculate the HMAC digest + * from the message using the supplied password in the parameter. + * If REALM attribute is present, the HMAC digest is calculated as + * long term credential, otherwise as short term credential. + * + * If FINGERPRINT attribute is present, this function will calculate + * the FINGERPRINT CRC attribute for the message. + * + * @param msg The STUN message to be printed. + * @param pkt_buf The buffer to be filled with the packet. + * @param buf_size Size of the buffer. + * @param options Options, which currently must be zero. + * @param password Password to be used when credential is to be + * included. This parameter MUST be specified when + * the message contains MESSAGE-INTEGRITY attribute. + * @param p_msg_len Upon return, it will be filed with the size of + * the packet in bytes, or negative value on error. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg, + pj_uint8_t *pkt_buf, + unsigned buf_size, + unsigned options, + const pj_str_t *password, + unsigned *p_msg_len); + +/** * Check that the PDU is potentially a valid STUN message. This function * is useful when application needs to multiplex STUN packets with other * application traffic. When this function returns PJ_SUCCESS, there is a @@ -1130,35 +1204,38 @@ PJ_DECL(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg, * * @param pdu The packet buffer. * @param pdu_len The length of the packet buffer. - * @param options Options. + * @param options Additional options to be applied in the checking, + * which can be taken from pj_stun_options. One of the + * useful option is PJ_STUN_IS_DATAGRAM which means that + * the pdu represents a whole STUN packet. * * @return PJ_SUCCESS if the PDU is a potentially valid STUN * message. */ -PJ_DECL(pj_status_t) pj_stun_msg_check(const void *pdu, unsigned pdu_len, +PJ_DECL(pj_status_t) pj_stun_msg_check(const pj_uint8_t *pdu, unsigned pdu_len, unsigned options); /** - * Parse incoming packet into STUN message. + * Decode incoming packet into STUN message. * * @param pool Pool to allocate the message. * @param pdu The incoming packet to be parsed. * @param pdu_len The length of the incoming packet. - * @param options Parsing flags. + * @param options Parsing flags, according to pj_stun_options. * @param p_msg Pointer to receive the parsed message. * @param p_parsed_len Optional pointer to receive how many bytes have * been parsed for the STUN message. This is useful * when the packet is received over stream oriented * transport. - * @param p_err_code Optional pointer to receive STUN error code when - * parsing failed. - * @param uattr_cnt Optional pointer to specify the number of elements - * in uattr array. On return, this will be filled with - * the actual number of attributes set in the uattr. - * @param uattr Optional array to receive unknown attribute types. + * @param p_response Optional pointer to receive an instance of response + * message, if one can be created. If the packet being + * decoded is a request message, and it contains error, + * and a response can be created, then the STUN + * response message will be returned on this argument. * - * @return PJ_SUCCESS on success or the appropriate error code. + * @return PJ_SUCCESS if a STUN message has been successfully + * decoded. */ PJ_DECL(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, const pj_uint8_t *pdu, @@ -1166,44 +1243,43 @@ PJ_DECL(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, unsigned options, pj_stun_msg **p_msg, unsigned *p_parsed_len, - unsigned *p_err_code, - unsigned *uattr_cnt, - pj_uint16_t uattr[]); + pj_stun_msg **p_response); /** - * Print the STUN message structure to a packet buffer, ready to be - * sent to remote destination. This function will take care about - * calculating the MESSAGE-INTEGRITY digest as well as FINGERPRINT - * value. - * - * If MESSAGE-INTEGRITY attribute is present, the function assumes - * that application wants to include credential (short or long term) - * in the message, and this function will calculate the HMAC digest - * from the message using the supplied password in the parameter. - * If REALM attribute is present, the HMAC digest is calculated as - * long term credential, otherwise as short term credential. + * Verify credential in the STUN message. Note that before calling this + * function, application must have checked that the message contains + * PJ_STUN_ATTR_MESSAGE_INTEGRITY attribute by calling pj_stun_msg_find_attr() + * function, because this function will reject the message with 401 error + * if it doesn't contain PJ_STUN_ATTR_MESSAGE_INTEGRITY attribute. * - * If FINGERPRINT attribute is present, this function will calculate - * the FINGERPRINT CRC attribute for the message. - * - * @param msg The STUN message to be printed. - * @param pkt_buf The buffer to be filled with the packet. - * @param buf_size Size of the buffer. - * @param options Options, which currently must be zero. - * @param password Password to be used when credential is to be - * included. This parameter MUST be specified when - * the message contains MESSAGE-INTEGRITY attribute. - * @param p_msg_len Upon return, it will be filed with the size of - * the packet in bytes, or negative value on error. + * @param msg The message to be verified. + * @param realm Realm, if long term credential is required. If + * short term credential is required, this argument + * must be set to NULL. + * @param username If this attribute is specified, then the USERNAME + * attribute in the message will be compared against + * this value. If NULL is specified, then this function + * will accept any usernames. + * @param password The password. + * @param options Options, must be zero for now. + * @param pool If response is to be created, then memory will + * be allocated from this pool. + * @param p_response Optional pointer to receive the response message + * then the credential in the request fails to + * authenticate. * - * @return PJ_SUCCESS on success or the appropriate error code. + * @return PJ_SUCCESS if credential is verified successfully. + * If the verification fails and \a p_response is not + * NULL, an appropriate response will be returned in + * \a p_response. */ -PJ_DECL(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg, - pj_uint8_t *pkt_buf, - unsigned buf_size, - unsigned options, - const pj_str_t *password, - unsigned *p_msg_len); +PJ_DECL(pj_status_t) pj_stun_verify_credential(const pj_stun_msg *msg, + const pj_str_t *realm, + const pj_str_t *username, + const pj_str_t *password, + unsigned options, + pj_pool_t *pool, + pj_stun_msg **p_response); /** @@ -1243,8 +1319,9 @@ PJ_DECL(pj_stun_attr_hdr*) pj_stun_msg_find_attr(const pj_stun_msg *msg, /** - * Create a generic STUN IP address attribute for IPv4 address. Note that - * the port and ip_addr parameters are in host byte order. + * Create a generic STUN IP address attribute. The \a addr_len and + * \a addr parameters specify whether the address is IPv4 or IPv4 + * address. * * @param pool The pool to allocate memory from. * @param attr_type Attribute type, from #pj_stun_attr_type. @@ -1266,6 +1343,29 @@ pj_stun_generic_ip_addr_attr_create(pj_pool_t *pool, /** + * Create and add generic STUN IP address attribute to a STUN message. + * The \a addr_len and \a addr parameters specify whether the address is + * IPv4 or IPv4 address. + * + * @param pool The pool to allocate memory from. + * @param msg The STUN message. + * @param attr_type Attribute type, from #pj_stun_attr_type. + * @param xor_ed If non-zero, the port and address will be XOR-ed + * with magic, to make the XOR-MAPPED-ADDRESS attribute. + * @param addr_len Length of \a addr parameter. + * @param addr A pj_sockaddr_in or pj_sockaddr_in6 structure. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) +pj_stun_msg_add_generic_ip_addr_attr(pj_pool_t *pool, + pj_stun_msg *msg, + int attr_type, + pj_bool_t xor_ed, + unsigned addr_len, + const pj_sockaddr_t *addr); + +/** * Create a STUN generic string attribute. * * @param pool The pool to allocate memory from. @@ -1281,6 +1381,21 @@ pj_stun_generic_string_attr_create(pj_pool_t *pool, const pj_str_t *value, pj_stun_generic_string_attr **p_attr); +/** + * Create and add STUN generic string attribute to the message. + * + * @param pool The pool to allocate memory from. + * @param msg The STUN message. + * @param attr_type Attribute type, from #pj_stun_attr_type. + * @param value The string value to be assigned to the attribute. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) +pj_stun_msg_add_generic_string_attr(pj_pool_t *pool, + pj_stun_msg *msg, + int attr_type, + const pj_str_t *value); /** * Create a STUN generic 32bit value attribute. @@ -1298,6 +1413,22 @@ pj_stun_generic_uint_attr_create(pj_pool_t *pool, pj_uint32_t value, pj_stun_generic_uint_attr **p_attr); +/** + * Create and add STUN generic 32bit value attribute to the message. + * + * @param pool The pool to allocate memory from. + * @param msg The STUN message + * @param attr_type Attribute type, from #pj_stun_attr_type. + * @param value The 32bit value to be assigned to the attribute. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) +pj_stun_msg_add_generic_uint_attr(pj_pool_t *pool, + pj_stun_msg *msg, + int attr_type, + pj_uint32_t value); + /** * Create a STUN MESSAGE-INTEGRITY attribute. @@ -1311,7 +1442,6 @@ PJ_DECL(pj_status_t) pj_stun_msg_integrity_attr_create(pj_pool_t *pool, pj_stun_msg_integrity_attr **p_attr); - /** * Create a STUN ERROR-CODE attribute. * @@ -1331,7 +1461,8 @@ pj_stun_error_code_attr_create(pj_pool_t *pool, /** - * Create an empty instance of STUN UNKNOWN-ATTRIBUTES attribute. + * Create instance of STUN UNKNOWN-ATTRIBUTES attribute and copy the + * unknown attribute array to the attribute. * * @param pool The pool to allocate memory from. * @param attr_cnt Number of attributes in the array (can be zero). @@ -1343,15 +1474,34 @@ pj_stun_error_code_attr_create(pj_pool_t *pool, PJ_DECL(pj_status_t) pj_stun_unknown_attr_create(pj_pool_t *pool, unsigned attr_cnt, - pj_uint16_t attr[], + const pj_uint16_t attr[], pj_stun_unknown_attr **p_attr); +/** + * Create and add STUN UNKNOWN-ATTRIBUTES attribute to the message. + * + * @param pool The pool to allocate memory from. + * @param msg The STUN message. + * @param attr_cnt Number of attributes in the array (can be zero). + * @param attr Optional array of attributes. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) +pj_stun_msg_add_unknown_attr(pj_pool_t *pool, + pj_stun_msg *msg, + unsigned attr_cnt, + const pj_uint16_t attr[]); /** - * Create a blank binary attribute. + * Create STUN binary attribute. * * @param pool The pool to allocate memory from. * @param attr_type The attribute type, from #pj_stun_attr_type. + * @param data Data to be coped to the attribute, or NULL + * if no data to be copied now. + * @param length Length of data, or zero if no data is to be + * copied now. * @param p_attr Pointer to receive the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. @@ -1359,8 +1509,31 @@ pj_stun_unknown_attr_create(pj_pool_t *pool, PJ_DECL(pj_status_t) pj_stun_binary_attr_create(pj_pool_t *pool, int attr_type, + const pj_uint8_t *data, + unsigned length, pj_stun_binary_attr **p_attr); +/** + * Create STUN binary attribute and add the attribute to the message. + * + * @param pool The pool to allocate memory from. + * @param msg The STUN message. + * @param attr_type The attribute type, from #pj_stun_attr_type. + * @param data Data to be coped to the attribute, or NULL + * if no data to be copied now. + * @param length Length of data, or zero if no data is to be + * copied now. + * @param p_attr Pointer to receive the attribute. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(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); + /** * @} diff --git a/pjlib-util/src/pjlib-util-test/encryption.c b/pjlib-util/src/pjlib-util-test/encryption.c index 0c5de154..34e5ae59 100644 --- a/pjlib-util/src/pjlib-util-test/encryption.c +++ b/pjlib-util/src/pjlib-util-test/encryption.c @@ -383,6 +383,58 @@ static int rfc2202_test(void) return 0; } +/* CRC32 test data, generated from crc32 test on a Linux box */ +struct +{ + char *input; + pj_uint32_t crc; +} crc32_test_data[] = +{ + { + "", + 0x0 + }, + { + "Hello World", + 0x4a17b156 + }, + { + /* Something read from /dev/random */ + "\x21\x21\x98\x10\x62\x59\xbc\x58\x42\x24\xe5\xf3\x92\x0a\x68\x3c\xa7\x67\x73\xc3", + 0x506693be + }, + { + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0xcab11777 + }, + { + "123456789", + 0xCBF43926 + } +}; + +/* + * CRC32 test + */ +static int crc32_test(void) +{ + unsigned i; + + PJ_LOG(3, (THIS_FILE, " crc32 test..")); + + for (i=0; i<PJ_ARRAY_SIZE(crc32_test_data); ++i) { + pj_uint32_t crc; + + crc = pj_crc32_calc((pj_uint8_t*)crc32_test_data[i].input, + pj_ansi_strlen(crc32_test_data[i].input)); + if (crc != crc32_test_data[i].crc) { + PJ_LOG(3,(THIS_FILE, " error: crc mismatch on test %d", i)); + return -80; + } + } + return 0; +} + int encryption_test() { @@ -400,6 +452,10 @@ int encryption_test() if (rc != 0) return rc; + rc = crc32_test(); + if (rc != 0) + return rc; + return 0; } diff --git a/pjlib-util/src/pjlib-util/crc32.c b/pjlib-util/src/pjlib-util/crc32.c new file mode 100644 index 00000000..fa225ac9 --- /dev/null +++ b/pjlib-util/src/pjlib-util/crc32.c @@ -0,0 +1,192 @@ +/* $Id$ */ +/* + * This is an implementation of CRC32. See ISO 3309 and ITU-T V.42 + * for a formal specification + * + * This file is partly taken from Crypto++ library (http://www.cryptopp.com). + * + * Since the original version of the code is put in public domain, + * this file is put on public domain as well. + */ +#include <pjlib-util/crc32.h> + +// crc.cpp - written and placed in the public domain by Wei Dai + +/* Table of CRC-32's of all single byte values (made by makecrc.c) */ +#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN != 0 + +#define CRC32_INDEX(c) (c & 0xff) +#define CRC32_SHIFTED(c) (c >> 8) + +static const pj_uint32_t crc_tab[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + + +#elif defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN != 0 +#define CRC32_INDEX(c) (c >> 24) +#define CRC32_SHIFTED(c) (c << 8) + +static const pj_uint32_t crc_tab[] = { + 0x00000000L, 0x96300777L, 0x2c610eeeL, 0xba510999L, 0x19c46d07L, + 0x8ff46a70L, 0x35a563e9L, 0xa395649eL, 0x3288db0eL, 0xa4b8dc79L, + 0x1ee9d5e0L, 0x88d9d297L, 0x2b4cb609L, 0xbd7cb17eL, 0x072db8e7L, + 0x911dbf90L, 0x6410b71dL, 0xf220b06aL, 0x4871b9f3L, 0xde41be84L, + 0x7dd4da1aL, 0xebe4dd6dL, 0x51b5d4f4L, 0xc785d383L, 0x56986c13L, + 0xc0a86b64L, 0x7af962fdL, 0xecc9658aL, 0x4f5c0114L, 0xd96c0663L, + 0x633d0ffaL, 0xf50d088dL, 0xc8206e3bL, 0x5e10694cL, 0xe44160d5L, + 0x727167a2L, 0xd1e4033cL, 0x47d4044bL, 0xfd850dd2L, 0x6bb50aa5L, + 0xfaa8b535L, 0x6c98b242L, 0xd6c9bbdbL, 0x40f9bcacL, 0xe36cd832L, + 0x755cdf45L, 0xcf0dd6dcL, 0x593dd1abL, 0xac30d926L, 0x3a00de51L, + 0x8051d7c8L, 0x1661d0bfL, 0xb5f4b421L, 0x23c4b356L, 0x9995bacfL, + 0x0fa5bdb8L, 0x9eb80228L, 0x0888055fL, 0xb2d90cc6L, 0x24e90bb1L, + 0x877c6f2fL, 0x114c6858L, 0xab1d61c1L, 0x3d2d66b6L, 0x9041dc76L, + 0x0671db01L, 0xbc20d298L, 0x2a10d5efL, 0x8985b171L, 0x1fb5b606L, + 0xa5e4bf9fL, 0x33d4b8e8L, 0xa2c90778L, 0x34f9000fL, 0x8ea80996L, + 0x18980ee1L, 0xbb0d6a7fL, 0x2d3d6d08L, 0x976c6491L, 0x015c63e6L, + 0xf4516b6bL, 0x62616c1cL, 0xd8306585L, 0x4e0062f2L, 0xed95066cL, + 0x7ba5011bL, 0xc1f40882L, 0x57c40ff5L, 0xc6d9b065L, 0x50e9b712L, + 0xeab8be8bL, 0x7c88b9fcL, 0xdf1ddd62L, 0x492dda15L, 0xf37cd38cL, + 0x654cd4fbL, 0x5861b24dL, 0xce51b53aL, 0x7400bca3L, 0xe230bbd4L, + 0x41a5df4aL, 0xd795d83dL, 0x6dc4d1a4L, 0xfbf4d6d3L, 0x6ae96943L, + 0xfcd96e34L, 0x468867adL, 0xd0b860daL, 0x732d0444L, 0xe51d0333L, + 0x5f4c0aaaL, 0xc97c0dddL, 0x3c710550L, 0xaa410227L, 0x10100bbeL, + 0x86200cc9L, 0x25b56857L, 0xb3856f20L, 0x09d466b9L, 0x9fe461ceL, + 0x0ef9de5eL, 0x98c9d929L, 0x2298d0b0L, 0xb4a8d7c7L, 0x173db359L, + 0x810db42eL, 0x3b5cbdb7L, 0xad6cbac0L, 0x2083b8edL, 0xb6b3bf9aL, + 0x0ce2b603L, 0x9ad2b174L, 0x3947d5eaL, 0xaf77d29dL, 0x1526db04L, + 0x8316dc73L, 0x120b63e3L, 0x843b6494L, 0x3e6a6d0dL, 0xa85a6a7aL, + 0x0bcf0ee4L, 0x9dff0993L, 0x27ae000aL, 0xb19e077dL, 0x44930ff0L, + 0xd2a30887L, 0x68f2011eL, 0xfec20669L, 0x5d5762f7L, 0xcb676580L, + 0x71366c19L, 0xe7066b6eL, 0x761bd4feL, 0xe02bd389L, 0x5a7ada10L, + 0xcc4add67L, 0x6fdfb9f9L, 0xf9efbe8eL, 0x43beb717L, 0xd58eb060L, + 0xe8a3d6d6L, 0x7e93d1a1L, 0xc4c2d838L, 0x52f2df4fL, 0xf167bbd1L, + 0x6757bca6L, 0xdd06b53fL, 0x4b36b248L, 0xda2b0dd8L, 0x4c1b0aafL, + 0xf64a0336L, 0x607a0441L, 0xc3ef60dfL, 0x55df67a8L, 0xef8e6e31L, + 0x79be6946L, 0x8cb361cbL, 0x1a8366bcL, 0xa0d26f25L, 0x36e26852L, + 0x95770cccL, 0x03470bbbL, 0xb9160222L, 0x2f260555L, 0xbe3bbac5L, + 0x280bbdb2L, 0x925ab42bL, 0x046ab35cL, 0xa7ffd7c2L, 0x31cfd0b5L, + 0x8b9ed92cL, 0x1daede5bL, 0xb0c2649bL, 0x26f263ecL, 0x9ca36a75L, + 0x0a936d02L, 0xa906099cL, 0x3f360eebL, 0x85670772L, 0x13570005L, + 0x824abf95L, 0x147ab8e2L, 0xae2bb17bL, 0x381bb60cL, 0x9b8ed292L, + 0x0dbed5e5L, 0xb7efdc7cL, 0x21dfdb0bL, 0xd4d2d386L, 0x42e2d4f1L, + 0xf8b3dd68L, 0x6e83da1fL, 0xcd16be81L, 0x5b26b9f6L, 0xe177b06fL, + 0x7747b718L, 0xe65a0888L, 0x706a0fffL, 0xca3b0666L, 0x5c0b0111L, + 0xff9e658fL, 0x69ae62f8L, 0xd3ff6b61L, 0x45cf6c16L, 0x78e20aa0L, + 0xeed20dd7L, 0x5483044eL, 0xc2b30339L, 0x612667a7L, 0xf71660d0L, + 0x4d476949L, 0xdb776e3eL, 0x4a6ad1aeL, 0xdc5ad6d9L, 0x660bdf40L, + 0xf03bd837L, 0x53aebca9L, 0xc59ebbdeL, 0x7fcfb247L, 0xe9ffb530L, + 0x1cf2bdbdL, 0x8ac2bacaL, 0x3093b353L, 0xa6a3b424L, 0x0536d0baL, + 0x9306d7cdL, 0x2957de54L, 0xbf67d923L, 0x2e7a66b3L, 0xb84a61c4L, + 0x021b685dL, 0x942b6f2aL, 0x37be0bb4L, 0xa18e0cc3L, 0x1bdf055aL, + 0x8def022dL +}; + +#else +# error "Endianness not defined" +#endif + + +#define CRC32_NEGL 0xffffffffL + + +PJ_DEF(void) pj_crc32_init(pj_crc32_context *ctx) +{ + ctx->crc_state = 0; +} + +PJ_DEF(pj_uint32_t) pj_crc32_update(pj_crc32_context *ctx, + const pj_uint8_t *data, + pj_size_t nbytes) +{ + pj_uint32_t crc = ctx->crc_state ^ CRC32_NEGL; + + for( ; (((pj_uint32_t)data) & 0x03) && nbytes > 0; --nbytes) { + crc = crc_tab[CRC32_INDEX(crc) ^ *data++] ^ CRC32_SHIFTED(crc); + } + + while (nbytes >= 4) { + crc ^= *(const pj_uint32_t *)data; + crc = crc_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); + crc = crc_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); + crc = crc_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); + crc = crc_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); + nbytes -= 4; + data += 4; + } + + while (nbytes--) { + crc = crc_tab[CRC32_INDEX(crc) ^ *data++] ^ CRC32_SHIFTED(crc); + } + + ctx->crc_state = crc ^ CRC32_NEGL; + + return ctx->crc_state; +} + +PJ_DEF(pj_uint32_t) pj_crc32_final(pj_crc32_context *ctx) +{ + return ctx->crc_state; +} + +PJ_DEF(pj_uint32_t) pj_crc32_calc( const pj_uint8_t *data, + pj_size_t nbytes) +{ + pj_crc32_context ctx; + + pj_crc32_init(&ctx); + pj_crc32_update(&ctx, data, nbytes); + return pj_crc32_final(&ctx); +} + diff --git a/pjlib-util/src/pjlib-util/stun_msg.c b/pjlib-util/src/pjlib-util/stun_msg.c index fbe2223c..44c390d8 100644 --- a/pjlib-util/src/pjlib-util/stun_msg.c +++ b/pjlib-util/src/pjlib-util/stun_msg.c @@ -17,7 +17,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <pjlib-util/stun_msg.h> +#include <pjlib-util/crc32.h> #include <pjlib-util/errno.h> +#include <pjlib-util/hmac_sha1.h> +#include <pjlib-util/md5.h> #include <pj/assert.h> #include <pj/log.h> #include <pj/os.h> @@ -25,8 +28,8 @@ #include <pj/rand.h> #include <pj/string.h> -#define THIS_FILE "stun_msg.c" - +#define THIS_FILE "stun_msg.c" +#define STUN_XOR_FINGERPRINT 0x5354554eL static const char *stun_method_names[] = { @@ -547,6 +550,28 @@ pj_stun_generic_ip_addr_attr_create(pj_pool_t *pool, } +/* + * Create and add generic STUN IP address attribute to a STUN message. + */ +PJ_DEF(pj_status_t) +pj_stun_msg_add_generic_ip_addr_attr(pj_pool_t *pool, + pj_stun_msg *msg, + int attr_type, + pj_bool_t xor_ed, + unsigned addr_len, + const pj_sockaddr_t *addr) +{ + pj_stun_generic_ip_addr_attr *attr; + pj_status_t status; + + status = pj_stun_generic_ip_addr_attr_create(pool, attr_type, xor_ed, + addr_len, addr, &attr); + if (status != PJ_SUCCESS) + return status; + + return pj_stun_msg_add_attr(msg, &attr->hdr); +} + static pj_status_t decode_generic_ip_addr_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr) @@ -658,6 +683,27 @@ pj_stun_generic_string_attr_create(pj_pool_t *pool, } +/* + * Create and add STUN generic string attribute to the message. + */ +PJ_DEF(pj_status_t) +pj_stun_msg_add_generic_string_attr(pj_pool_t *pool, + pj_stun_msg *msg, + int attr_type, + const pj_str_t *value) +{ + pj_stun_generic_string_attr *attr; + pj_status_t status; + + status = pj_stun_generic_string_attr_create(pool, attr_type, value, + &attr); + if (status != PJ_SUCCESS) + return status; + + return pj_stun_msg_add_attr(msg, &attr->hdr); +} + + static pj_status_t decode_generic_string_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr) @@ -827,6 +873,22 @@ pj_stun_generic_uint_attr_create(pj_pool_t *pool, return PJ_SUCCESS; } +/* Create and add STUN generic 32bit value attribute to the message. */ +PJ_DEF(pj_status_t) +pj_stun_msg_add_generic_uint_attr(pj_pool_t *pool, + pj_stun_msg *msg, + int attr_type, + pj_uint32_t value) +{ + pj_stun_generic_uint_attr *attr; + pj_status_t status; + + status = pj_stun_generic_uint_attr_create(pool, attr_type, value, &attr); + if (status != PJ_SUCCESS) + return status; + + return pj_stun_msg_add_attr(msg, &attr->hdr); +} static pj_status_t decode_generic_uint_attr(pj_pool_t *pool, const pj_uint8_t *buf, @@ -1087,7 +1149,7 @@ static pj_status_t encode_error_code_attr(const void *a, pj_uint8_t *buf, PJ_DEF(pj_status_t) pj_stun_unknown_attr_create(pj_pool_t *pool, unsigned attr_cnt, - pj_uint16_t attr_array[], + const pj_uint16_t attr_array[], pj_stun_unknown_attr **p_attr) { pj_stun_unknown_attr *attr; @@ -1116,6 +1178,23 @@ 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, + const pj_uint16_t attr_types[]) +{ + pj_stun_unknown_attr *attr; + pj_status_t status; + + status = pj_stun_unknown_attr_create(pool, attr_cnt, attr_types, &attr); + if (status != PJ_SUCCESS) + return status; + + return pj_stun_msg_add_attr(msg, &attr->hdr); +} + static pj_status_t decode_unknown_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr) @@ -1193,6 +1272,8 @@ static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf, 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_stun_binary_attr **p_attr) { pj_stun_binary_attr *attr; @@ -1202,12 +1283,38 @@ pj_stun_binary_attr_create(pj_pool_t *pool, attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_binary_attr); INIT_ATTR(attr, attr_type, sizeof(pj_stun_binary_attr)); + if (data && length) { + attr->length = length; + attr->data = pj_pool_alloc(pool, length); + pj_memcpy(attr->data, data, length); + } + *p_attr = attr; return PJ_SUCCESS; } +/* Create and add binary attr. */ +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_stun_binary_attr *attr; + pj_status_t status; + + status = pj_stun_binary_attr_create(pool, attr_type, + data, length, &attr); + if (status != PJ_SUCCESS) + return status; + + return pj_stun_msg_add_attr(msg, &attr->hdr); +} + + static pj_status_t decode_binary_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr) @@ -1324,42 +1431,119 @@ PJ_DEF(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg, } +PJ_INLINE(pj_uint16_t) GET_VAL16(const pj_uint8_t *pdu, unsigned pos) +{ + pj_uint16_t val = (pj_uint16_t) ((pdu[pos] << 8) + pdu[pos+1]); + return pj_ntohs(val); +} + +PJ_INLINE(pj_uint32_t) GET_VAL32(const pj_uint8_t *pdu, unsigned pos) +{ + pj_uint32_t val = (pdu[pos+0] << 24) + + (pdu[pos+1] << 16) + + (pdu[pos+2] << 8) + + (pdu[pos+3]); + return pj_ntohl(val); +} + + /* * Check that the PDU is potentially a valid STUN message. */ -PJ_DEF(pj_status_t) pj_stun_msg_check(const void *pdu, unsigned pdu_len, +PJ_DEF(pj_status_t) pj_stun_msg_check(const pj_uint8_t *pdu, unsigned pdu_len, unsigned options) { - pj_stun_msg_hdr *hdr; + unsigned msg_len; PJ_ASSERT_RETURN(pdu, PJ_EINVAL); if (pdu_len < sizeof(pj_stun_msg_hdr)) return PJLIB_UTIL_ESTUNINMSGLEN; - PJ_UNUSED_ARG(options); - - hdr = (pj_stun_msg_hdr*) pdu; - /* First byte of STUN message is always 0x00 or 0x01. */ - if ((*(const char*)pdu) != 0x00 && (*(const char*)pdu) != 0x01) + if (*pdu != 0x00 && *pdu != 0x01) return PJLIB_UTIL_ESTUNINMSGTYPE; /* If magic is set, then there is great possibility that this is * a STUN message. */ - if (pj_ntohl(hdr->magic) == PJ_STUN_MAGIC) - return PJ_SUCCESS; + if (GET_VAL32(pdu, 4) != PJ_STUN_MAGIC) + return PJLIB_UTIL_ESTUNNOTMAGIC; /* Check the PDU length */ - if (pj_ntohs(hdr->length) > pdu_len) + msg_len = GET_VAL16(pdu, 2); + if ((msg_len > pdu_len) || + ((options & PJ_STUN_IS_DATAGRAM) && msg_len != pdu_len)) + { return PJLIB_UTIL_ESTUNINMSGLEN; + } + + /* Check if FINGERPRINT attribute is present */ + if (GET_VAL16(pdu, msg_len + 20) == PJ_STUN_ATTR_FINGERPRINT) { + pj_uint16_t attr_len = GET_VAL16(pdu, msg_len + 22); + pj_uint32_t fingerprint = GET_VAL32(pdu, msg_len + 24); + pj_uint32_t crc; + + if (attr_len != 4) + return PJLIB_UTIL_ESTUNINATTRLEN; + + crc = pj_crc32_calc(pdu, msg_len + 20); + crc ^= STUN_XOR_FINGERPRINT; + + if (crc != fingerprint) + return PJLIB_UTIL_ESTUNFINGERPRINT; + } /* Could be a STUN message */ return PJ_SUCCESS; } +/* Create error response */ +PJ_DEF(pj_status_t) pj_stun_msg_create_response(pj_pool_t *pool, + const pj_stun_msg *req_msg, + unsigned err_code, + const pj_str_t *err_msg, + pj_stun_msg **p_response) +{ + unsigned msg_type = req_msg->hdr.type; + pj_stun_msg *response; + pj_stun_error_code_attr *err_attr; + pj_status_t status; + + PJ_ASSERT_RETURN(pool && p_response, PJ_EINVAL); + + PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(msg_type), + PJLIB_UTIL_ESTUNINMSGTYPE); + + /* Create response or error response */ + if (err_code) + msg_type |= PJ_STUN_ERROR_RESPONSE_BIT; + else + msg_type |= PJ_STUN_RESPONSE_BIT; + + status = pj_stun_msg_create(pool, msg_type, req_msg->hdr.magic, + req_msg->hdr.tsx_id, &response); + if (status != PJ_SUCCESS) { + return status; + } + + /* Add error code attribute */ + if (err_code) { + status = pj_stun_error_code_attr_create(pool, err_code, err_msg, + &err_attr); + if (status != PJ_SUCCESS) { + return status; + } + + pj_stun_msg_add_attr(response, &err_attr->hdr); + } + + *p_response = response; + return PJ_SUCCESS; +} + + /* * Parse incoming packet into STUN message. */ @@ -1369,9 +1553,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, unsigned options, pj_stun_msg **p_msg, unsigned *p_parsed_len, - unsigned *p_err_code, - unsigned *p_uattr_cnt, - pj_uint16_t uattr[]) + pj_stun_msg **p_response) { pj_stun_msg *msg; @@ -1384,9 +1566,17 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, PJ_ASSERT_RETURN(pool && pdu && pdu_len && p_msg, PJ_EINVAL); PJ_ASSERT_RETURN(sizeof(pj_stun_msg_hdr) == 20, PJ_EBUG); - /* Application should have checked that this is a valid STUN msg */ - PJ_ASSERT_RETURN((status=pj_stun_msg_check(pdu, pdu_len, options)) - == PJ_SUCCESS, status); + if (p_parsed_len) + *p_parsed_len = 0; + if (p_response) + *p_response = NULL; + + /* Check if this is a STUN message, if necessary */ + if (options & PJ_STUN_CHECK_PACKET) { + status = pj_stun_msg_check(pdu, pdu_len, options); + if (status != PJ_SUCCESS) + return status; + } /* Create the message, copy the header, and convert to host byte order */ msg = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msg); @@ -1398,8 +1588,9 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, pdu += sizeof(pj_stun_msg_hdr); pdu_len -= sizeof(pj_stun_msg_hdr); - if (p_err_code) - *p_err_code = 0; + /* No need to create response if this is not a request */ + if (!PJ_STUN_IS_REQUEST(msg->hdr.type)) + p_response = NULL; /* Parse attributes */ uattr_cnt = 0; @@ -1415,8 +1606,25 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, attr_val_len = (attr_val_len + 3) & (~3); /* Check length */ - if (pdu_len < attr_val_len) + if (pdu_len < attr_val_len) { + pj_str_t err_msg; + char err_msg_buf[80]; + + err_msg.ptr = err_msg_buf; + err_msg.slen = pj_ansi_snprintf(err_msg_buf, sizeof(err_msg_buf), + "Attribute %s has invalid length", + pj_stun_get_attr_name(attr_type)); + + PJ_LOG(4,(THIS_FILE, "Error decoding message: %.*s", + (int)err_msg.slen, err_msg.ptr)); + + if (p_response) { + pj_stun_msg_create_response(pool, msg, + PJ_STUN_STATUS_BAD_REQUEST, + &err_msg, p_response); + } return PJLIB_UTIL_ESTUNINATTRLEN; + } /* Get the attribute descriptor */ adesc = find_attr_desc(attr_type); @@ -1427,38 +1635,71 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, PJ_LOG(4,(THIS_FILE, "Unrecognized attribute type %d", attr_type)); - /* Put to unrecognized attribute array */ - if (p_uattr_cnt && uattr && uattr_cnt < *p_uattr_cnt) { - uattr[uattr_cnt++] = (pj_uint16_t)attr_type; - } - /* Is this a fatal condition? */ if (attr_type <= 0x7FFF) { /* This is a mandatory attribute, we must return error * if we don't understand the attribute. */ - if (p_err_code && *p_err_code == 0) - *p_err_code = PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE; + if (p_response) { + unsigned err_code = PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE; + + status = pj_stun_msg_create_response(pool, msg, + err_code, NULL, + p_response); + if (status==PJ_SUCCESS) { + pj_uint16_t d = (pj_uint16_t)attr_type; + pj_stun_msg_add_unknown_attr(pool, *p_response, 1, &d); + } + } return PJLIB_UTIL_ESTUNUNKNOWNATTR; } } else { void *attr; + char err_msg1[PJ_ERR_MSG_SIZE], + err_msg2[PJ_ERR_MSG_SIZE]; /* Parse the attribute */ status = (adesc->decode_attr)(pool, pdu, &attr); if (status != PJ_SUCCESS) { + pj_strerror(status, err_msg1, sizeof(err_msg1)); + + if (p_response) { + pj_str_t e; + + e.ptr = err_msg2; + e.slen= pj_ansi_snprintf(err_msg2, sizeof(err_msg2), + "%s in %s", + err_msg1, + pj_stun_get_attr_name(attr_type)); + + pj_stun_msg_create_response(pool, msg, + PJ_STUN_STATUS_BAD_REQUEST, + &e, p_response); + } + PJ_LOG(4,(THIS_FILE, - "Error parsing STUN attribute type %d: status=%d", - attr_type, status)); + "Error parsing STUN attribute %s: %s", + pj_stun_get_attr_name(attr_type), + err_msg1)); + return status; } /* Make sure we have rooms for the new attribute */ - if (msg->attr_count >= PJ_STUN_MAX_ATTR) + if (msg->attr_count >= PJ_STUN_MAX_ATTR) { + if (p_response) { + pj_str_t e; + + e = pj_str("Too many attributes"); + pj_stun_msg_create_response(pool, msg, + PJ_STUN_STATUS_BAD_REQUEST, + &e, p_response); + } return PJLIB_UTIL_ESTUNTOOMANYATTR; + } /* Add the attribute */ msg->attr[msg->attr_count++] = (pj_stun_attr_hdr*)attr; @@ -1470,9 +1711,6 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, *p_msg = msg; - if (p_uattr_cnt) - *p_uattr_cnt = uattr_cnt; - if (p_parsed_len) *p_parsed_len = (pdu - start_pdu); @@ -1494,7 +1732,11 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg, pj_stun_realm_attr *arealm = NULL; pj_stun_username_attr *auname = NULL; pj_stun_msg_integrity_attr *amsg_integrity = NULL; - unsigned i; + pj_stun_fingerprint_attr *afingerprint = NULL; + unsigned printed; + pj_status_t status; + unsigned i, length; + PJ_ASSERT_RETURN(msg && buf && buf_size, PJ_EINVAL); @@ -1519,8 +1761,6 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg, for (i=0; i<msg->attr_count; ++i) { const struct attr_desc *adesc; const pj_stun_attr_hdr *attr_hdr; - unsigned printed; - pj_status_t status; attr_hdr = msg->attr[i]; @@ -1534,9 +1774,14 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg, } else if (attr_hdr->type == PJ_STUN_ATTR_USERNAME) { pj_assert(auname == NULL); auname = (pj_stun_username_attr*) attr_hdr; + } else if (attr_hdr->type == PJ_STUN_ATTR_REALM) { pj_assert(arealm == NULL); arealm = (pj_stun_realm_attr*) attr_hdr; + + } else if (attr_hdr->type == PJ_STUN_ATTR_FINGERPRINT) { + afingerprint = (pj_stun_fingerprint_attr*) attr_hdr; + break; } adesc = find_attr_desc(attr_hdr->type); @@ -1550,16 +1795,128 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg, buf_size -= printed; } + /* Calculate message integrity, if present */ if (amsg_integrity != NULL) { - PJ_TODO(IMPLEMENT_MSG_INTEGRITY); + + pj_uint8_t md5_key_buf[16]; + pj_str_t key; + + /* MESSAGE-INTEGRITY must be the last attribute in the message, or + * the last attribute before FINGERPRINT. + */ + if (i < msg->attr_count-2) { + /* Should not happen for message generated by us */ + pj_assert(PJ_FALSE); + return PJLIB_UTIL_ESTUNMSGINT; + + } else if (i == msg->attr_count-2) { + if (msg->attr[i+1]->type != PJ_STUN_ATTR_FINGERPRINT) { + /* Should not happen for message generated by us */ + pj_assert(PJ_FALSE); + return PJLIB_UTIL_ESTUNMSGINT; + } else { + afingerprint = (pj_stun_fingerprint_attr*) msg->attr[i+1]; + } + } + + /* Must have USERNAME attribute */ + if (auname == NULL) { + /* Should not happen for message generated by us */ + pj_assert(PJ_FALSE); + return PJLIB_UTIL_ESTUNNOUSERNAME; + } + + /* Password must be specified */ + PJ_ASSERT_RETURN(password, PJ_EINVAL); + + /* Get the key to sign the message */ + if (arealm == NULL ) { + /* For short term credential, the key is the password */ + key = *password; + + } else { + /* The 16-byte key for MESSAGE-INTEGRITY HMAC is formed by taking + * the MD5 hash of the result of concatenating the following five + * fields: (1) The username, with any quotes and trailing nulls + * removed, (2) A single colon, (3) The realm, with any quotes and + * trailing nulls removed, (4) A single colon, and (5) The + * password, with any trailing nulls removed. + */ + pj_md5_context ctx; + pj_str_t s; + + pj_md5_init(&ctx); + +#define REMOVE_QUOTE(s) if (s.slen && *s.ptr=='"') \ + s.ptr++, s.slen--; \ + if (s.slen && s.ptr[s.slen-1]=='"') \ + s.slen--; + + /* Add username */ + s = auname->value; + REMOVE_QUOTE(s); + pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen); + + /* Add single colon */ + pj_md5_update(&ctx, (pj_uint8_t*)":", 1); + + /* Add realm */ + s = arealm->value; + REMOVE_QUOTE(s); + pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen); + +#undef REMOVE_QUOTE + + /* Another colon */ + pj_md5_update(&ctx, (pj_uint8_t*)":", 1); + + /* Add password */ + pj_md5_update(&ctx, (pj_uint8_t*)password->ptr, password->slen); + + /* Done */ + pj_md5_final(&ctx, md5_key_buf); + key.ptr = (char*) md5_key_buf; + key.slen = 16; + } + + /* Calculate HMAC-SHA1 digest */ + pj_hmac_sha1((pj_uint8_t*)buf, buf-start, + (pj_uint8_t*)key.ptr, key.slen, + amsg_integrity->hmac); + + /* Put this attribute in the message */ + status = encode_msg_integrity_attr(amsg_integrity, buf, buf_size, + &printed); + if (status != PJ_SUCCESS) + return status; + + buf += printed; + buf_size -= printed; } + /* Calculate FINGERPRINT if present */ + if (afingerprint != NULL) { + afingerprint->value = pj_crc32_calc(start, buf-start); + afingerprint->value ^= STUN_XOR_FINGERPRINT; + + /* Put this attribute in the message */ + status = encode_generic_uint_attr(afingerprint, buf, buf_size, + &printed); + if (status != PJ_SUCCESS) + return status; + + buf += printed; + buf_size -= printed; + } /* Update the message length in the header. * Note that length is not including the 20 bytes header. */ - hdr->length = (pj_uint16_t)((buf - start) - 20); - hdr->length = pj_htons(hdr->length); + length = (pj_uint16_t)((buf - start) - 20); + /* hdr->length = pj_htons(length); */ + *(buf+2) = (pj_uint8_t)((length >> 8) & 0x00FF); + *(buf+3) = (pj_uint8_t)(length & 0x00FF); + /* Done */ if (p_msg_len) @@ -1587,3 +1944,116 @@ PJ_DEF(pj_stun_attr_hdr*) pj_stun_msg_find_attr( const pj_stun_msg *msg, return NULL; } + +/* Verify credential */ +PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_stun_msg *msg, + const pj_str_t *realm, + const pj_str_t *username, + const pj_str_t *password, + unsigned options, + pj_pool_t *pool, + pj_stun_msg **p_response) +{ + const pj_stun_msg_integrity_attr *amsgi; + const pj_stun_username_attr *auser; + const pj_stun_realm_attr *arealm; + + PJ_ASSERT_RETURN(msg && password, PJ_EINVAL); + PJ_ASSERT_RETURN(options==0, PJ_EINVAL); + PJ_UNUSED_ARG(options); + + if (p_response) + *p_response = NULL; + + /* First check that MESSAGE-INTEGRITY is present */ + amsgi = (const pj_stun_msg_integrity_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0); + if (amsgi == NULL) { + if (pool && p_response) { + pj_status_t rc; + + rc = pj_stun_msg_create_response(pool, msg, + PJ_STUN_STATUS_UNAUTHORIZED, + NULL, p_response); + if (rc==PJ_SUCCESS && realm) { + pj_stun_msg_add_generic_string_attr(pool, *p_response, + PJ_STUN_ATTR_REALM, + realm); + } + } + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_UNAUTHORIZED); + } + + /* Next check that USERNAME is present */ + auser = (const pj_stun_username_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); + if (auser == NULL) { + if (pool && p_response) { + pj_status_t rc; + + rc = pj_stun_msg_create_response(pool, msg, + PJ_STUN_STATUS_MISSING_USERNAME, + NULL, p_response); + if (rc==PJ_SUCCESS && realm) { + pj_stun_msg_add_generic_string_attr(pool, *p_response, + PJ_STUN_ATTR_REALM, + realm); + } + } + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_USERNAME); + } + + /* Check if username match */ + if (username && pj_stricmp(&auser->value, username) != 0) { + /* Username mismatch */ + if (pool && p_response) { + pj_status_t rc; + + rc = pj_stun_msg_create_response(pool, msg, + PJ_STUN_STATUS_WRONG_USERNAME, + NULL, p_response); + if (rc==PJ_SUCCESS && realm) { + pj_stun_msg_add_generic_string_attr(pool, *p_response, + PJ_STUN_ATTR_REALM, + realm); + } + } + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_WRONG_USERNAME); + } + + /* Next check that REALM is present */ + arealm = (const pj_stun_realm_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0); + if (realm != NULL && arealm == NULL) { + /* Long term credential is required */ + if (pool && p_response) { + pj_status_t rc; + + rc = pj_stun_msg_create_response(pool, msg, + PJ_STUN_STATUS_MISSING_REALM, + NULL, p_response); + if (rc==PJ_SUCCESS) { + pj_stun_msg_add_generic_string_attr(pool, *p_response, + PJ_STUN_ATTR_REALM, + realm); + } + } + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_REALM); + + } else if (realm != NULL && arealm != NULL) { + + + } else if (realm == NULL && arealm != NULL) { + /* We want to use short term credential, but client uses long + * term credential. The draft doesn't mention anything about + * switching between long term and short term. + */ + PJ_TODO(SWITCHING_BETWEEN_SHORT_TERM_AND_LONG_TERM); + } + + PJ_TODO(CONTINUE_IMPLEMENTATION); + + return PJ_SUCCESS; +} + + diff --git a/pjlib-util/src/pjstun-client/stun_session.c b/pjlib-util/src/pjstun-client/stun_session.c index 101bb0b4..03dc6f19 100644 --- a/pjlib-util/src/pjstun-client/stun_session.c +++ b/pjlib-util/src/pjstun-client/stun_session.c @@ -484,7 +484,7 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, /* Encode message */ status = pj_stun_msg_encode(tdata->msg, tdata->pkt, tdata->max_len, - 0, &tdata->pkt_size); + 0, NULL, &tdata->pkt_size); if (status != PJ_SUCCESS) { LOG_ERR_(sess, "STUN encode() error", status); destroy_tdata(tdata); @@ -540,7 +540,7 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, pj_size_t pkt_size, unsigned *parsed_len) { - pj_stun_msg *msg; + pj_stun_msg *msg, *response; pj_pool_t *tmp_pool; char *dump; pj_status_t status; @@ -554,9 +554,12 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, /* Try to parse the message */ status = pj_stun_msg_decode(tmp_pool, (const pj_uint8_t*)packet, pkt_size, 0, &msg, parsed_len, - NULL, NULL, NULL); + &response); if (status != PJ_SUCCESS) { LOG_ERR_(sess, "STUN msg_decode() error", status); + if (response) { + PJ_TODO(SEND_RESPONSE); + } pj_pool_release(tmp_pool); return status; } diff --git a/pjlib-util/src/pjstun-srv/server_main.c b/pjlib-util/src/pjstun-srv/server_main.c index 5fd58ef9..46dc2752 100644 --- a/pjlib-util/src/pjstun-srv/server_main.c +++ b/pjlib-util/src/pjstun-srv/server_main.c @@ -51,31 +51,12 @@ static pj_status_t create_response(pj_pool_t *pool, { pj_uint32_t msg_type = req_msg->hdr.type; pj_stun_msg *response; - pj_stun_error_code_attr *err_attr; pj_status_t status; - /* Create response or error response */ - if (err_code) - msg_type |= PJ_STUN_ERROR_RESPONSE_BIT; - else - msg_type |= PJ_STUN_RESPONSE_BIT; - - status = pj_stun_msg_create(pool, msg_type, req_msg->hdr.magic, - req_msg->hdr.tsx_id, &response); - if (status != PJ_SUCCESS) { + status = pj_stun_msg_create_response(pool, req_msg, err_code, NULL, + &response); + if (status != PJ_SUCCESS) return status; - } - - /* Add error code attribute */ - if (err_code) { - status = pj_stun_error_code_attr_create(pool, err_code, NULL, - &err_attr); - if (status != PJ_SUCCESS) { - return status; - } - - pj_stun_msg_add_attr(response, &err_attr->hdr); - } /* Add unknown_attribute attributes if err_code is 420 */ if (err_code == PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE) { @@ -110,7 +91,7 @@ static pj_status_t send_msg(struct service *svc, const pj_stun_msg *msg) /* Encode packet */ tx_pkt_len = sizeof(svc->tx_pkt); status = pj_stun_msg_encode(msg, svc->tx_pkt, tx_pkt_len, 0, - &tx_pkt_len); + NULL, &tx_pkt_len); if (status != PJ_SUCCESS) return status; @@ -223,10 +204,7 @@ static void on_read_complete(pj_ioqueue_key_t *key, { struct service *svc = (struct service *) pj_ioqueue_get_user_data(key); pj_pool_t *pool = NULL; - pj_stun_msg *rx_msg; - unsigned err_code; - unsigned uattr_cnt; - pj_uint16_t uattr_types[16]; + pj_stun_msg *rx_msg, *response; char dump[512]; pj_status_t status; @@ -235,16 +213,13 @@ static void on_read_complete(pj_ioqueue_key_t *key, pool = pj_pool_create(&server.cp.factory, "service", 4000, 4000, NULL); - err_code = 0; - uattr_cnt = PJ_ARRAY_SIZE(uattr_types); rx_msg = NULL; status = pj_stun_msg_decode(pool, svc->rx_pkt, bytes_read, 0, &rx_msg, - NULL, &err_code, &uattr_cnt, uattr_types); + NULL, &response); if (status != PJ_SUCCESS) { server_perror(THIS_FILE, "STUN msg_decode() error", status); - if (err_code != 0 && rx_msg && PJ_STUN_IS_REQUEST(rx_msg->hdr.type)) { - err_respond(svc, pool, rx_msg, err_code, - uattr_cnt, uattr_types); + if (response) { + send_msg(svc, response); } goto next_read; } |