Index: /pjproject/trunk/pjlib-util/build/Makefile
===================================================================
--- /pjproject/trunk/pjlib-util/build/Makefile	(revision 1001)
+++ /pjproject/trunk/pjlib-util/build/Makefile	(revision 1002)
@@ -27,5 +27,5 @@
 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 \
Index: /pjproject/trunk/pjlib-util/build/pjlib_util.dsp
===================================================================
--- /pjproject/trunk/pjlib-util/build/pjlib_util.dsp	(revision 1001)
+++ /pjproject/trunk/pjlib-util/build/pjlib_util.dsp	(revision 1002)
@@ -88,4 +88,8 @@
 # Begin Source File
 
+SOURCE="..\src\pjlib-util\crc32.c"
+# End Source File
+# Begin Source File
+
 SOURCE="..\src\pjlib-util\dns.c"
 # End Source File
@@ -93,8 +97,4 @@
 
 SOURCE="..\src\pjlib-util\dns_dump.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjlib-util-test\encryption.c"
 # End Source File
 # Begin Source File
@@ -187,4 +187,8 @@
 
 SOURCE="..\include\pjlib-util\config.h"
+# End Source File
+# Begin Source File
+
+SOURCE="..\include\pjlib-util\crc32.h"
 # End Source File
 # Begin Source File
Index: /pjproject/trunk/pjlib-util/build/pjlib_util.vcproj
===================================================================
--- /pjproject/trunk/pjlib-util/build/pjlib_util.vcproj	(revision 1001)
+++ /pjproject/trunk/pjlib-util/build/pjlib_util.vcproj	(revision 1002)
@@ -176,4 +176,8 @@
 			>
 			<File
+				RelativePath="..\src\pjlib-util\crc32.c"
+				>
+			</File>
+			<File
 				RelativePath="..\src\pjlib-util\dns.c"
 				>
@@ -493,4 +497,8 @@
 			</File>
 			<File
+				RelativePath="..\include\pjlib-util\crc32.h"
+				>
+			</File>
+			<File
 				RelativePath="..\include\pjlib-util\dns.h"
 				>
Index: /pjproject/trunk/pjlib-util/build/wince-evc4/pjlib_util_wince.vcp
===================================================================
--- /pjproject/trunk/pjlib-util/build/wince-evc4/pjlib_util_wince.vcp	(revision 1001)
+++ /pjproject/trunk/pjlib-util/build/wince-evc4/pjlib_util_wince.vcp	(revision 1002)
@@ -319,4 +319,241 @@
 # 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"
 
@@ -6627,5 +6864,8 @@
 	"..\..\..\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"\
@@ -10778,4 +11018,8 @@
 # Begin Source File
 
+SOURCE="..\..\include\pjlib-util\crc32.h"
+# End Source File
+# Begin Source File
+
 SOURCE="..\..\include\pjlib-util\dns.h"
 # End Source File
Index: /pjproject/trunk/pjlib-util/include/pjlib-util.h
===================================================================
--- /pjproject/trunk/pjlib-util/include/pjlib-util.h	(revision 1001)
+++ /pjproject/trunk/pjlib-util/include/pjlib-util.h	(revision 1002)
@@ -33,4 +33,5 @@
 
 /* Crypto */
+#include <pjlib-util/crc32.h>
 #include <pjlib-util/hmac_md5.h>
 #include <pjlib-util/hmac_sha1.h>
Index: /pjproject/trunk/pjlib-util/include/pjlib-util/crc32.h
===================================================================
--- /pjproject/trunk/pjlib-util/include/pjlib-util/crc32.h	(revision 1002)
+++ /pjproject/trunk/pjlib-util/include/pjlib-util/crc32.h	(revision 1002)
@@ -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__ */
+
Index: /pjproject/trunk/pjlib-util/include/pjlib-util/errno.h
===================================================================
--- /pjproject/trunk/pjlib-util/include/pjlib-util/errno.h	(revision 1001)
+++ /pjproject/trunk/pjlib-util/include/pjlib-util/errno.h	(revision 1002)
@@ -94,4 +94,14 @@
  */
 #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 */
 
 
@@ -267,10 +277,10 @@
 /**
  * @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 */
@@ -282,5 +292,5 @@
 /**
  * @hideinitializer
- * Transaction ID mismatch.
+ * STUN transaction ID mismatch.
  */
 #define PJLIB_UTIL_ESTUNINVALIDID   (PJLIB_UTIL_ERRNO_START+115)/* 320115 */
@@ -290,7 +300,21 @@
  */
 #define PJLIB_UTIL_ESTUNNOHANDLER   (PJLIB_UTIL_ERRNO_START+116)/* 320116 */
-
-
-#define PJ_STATUS_FROM_STUN_CODE(code)	-1
+/**
+ * @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)	(PJLIB_UTIL_ERRNO_START+code)
 
 
Index: /pjproject/trunk/pjlib-util/include/pjlib-util/stun_msg.h
===================================================================
--- /pjproject/trunk/pjlib-util/include/pjlib-util/stun_msg.h	(revision 1001)
+++ /pjproject/trunk/pjlib-util/include/pjlib-util/stun_msg.h	(revision 1002)
@@ -602,5 +602,5 @@
      * The raw data.
      */
-    char	       *data;
+    pj_uint8_t	       *data;
 
 } pj_stun_binary_attr;
@@ -1047,4 +1047,21 @@
 
 
+/** 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.
@@ -1088,5 +1105,5 @@
 
 /**
- * Create a blank STUN message.
+ * Create a generic STUN message.
  *
  * @param pool		Pool to create the STUN message.
@@ -1106,4 +1123,26 @@
 					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);
+
 
 /**
@@ -1119,55 +1158,4 @@
 					  pj_stun_attr_hdr *attr);
 
-
-/**
- * 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
- * big chance that the packet is a STUN packet.
- *
- * Note that we cannot be sure that the PDU is a really valid STUN message 
- * until we actually parse the PDU.
- *
- * @param pdu		The packet buffer.
- * @param pdu_len	The length of the packet buffer.
- * @param options	Options.
- *
- * @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,
-				       unsigned options);
-
-
-/**
- * Parse 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 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.
- *
- * @return		PJ_SUCCESS on success or the appropriate error code.
- */
-PJ_DECL(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool,
-				        const pj_uint8_t *pdu,
-				        unsigned pdu_len,
-				        unsigned options,
-				        pj_stun_msg **p_msg,
-					unsigned *p_parsed_len,
-				        unsigned *p_err_code,
-				        unsigned *uattr_cnt,
-				        pj_uint16_t uattr[]);
 
 /**
@@ -1206,4 +1194,92 @@
 				        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
+ * big chance that the packet is a STUN packet.
+ *
+ * Note that we cannot be sure that the PDU is a really valid STUN message 
+ * until we actually parse the PDU.
+ *
+ * @param pdu		The packet buffer.
+ * @param pdu_len	The length of the packet buffer.
+ * @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 pj_uint8_t *pdu, unsigned pdu_len,
+				       unsigned options);
+
+
+/**
+ * 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, 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_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 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,
+				        unsigned pdu_len,
+				        unsigned options,
+				        pj_stun_msg **p_msg,
+					unsigned *p_parsed_len,
+				        pj_stun_msg **p_response);
+
+/**
+ * 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.
+ *
+ * @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 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_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);
+
 
 /**
@@ -1244,6 +1320,7 @@
 
 /**
- * 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.
@@ -1267,4 +1344,27 @@
 
 /**
+ * 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.
  *
@@ -1282,4 +1382,19 @@
 				   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);
 
 /**
@@ -1299,4 +1414,20 @@
 				 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);
+
 
 /**
@@ -1311,5 +1442,4 @@
 pj_stun_msg_integrity_attr_create(pj_pool_t *pool,
 				  pj_stun_msg_integrity_attr **p_attr);
-
 
 /**
@@ -1332,5 +1462,6 @@
 
 /**
- * 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.
@@ -1344,13 +1475,32 @@
 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 a blank binary attribute.
+/**
+ * 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 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.
  *
@@ -1360,6 +1510,29 @@
 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);
+
 
 /**
Index: /pjproject/trunk/pjlib-util/src/pjlib-util-test/encryption.c
===================================================================
--- /pjproject/trunk/pjlib-util/src/pjlib-util-test/encryption.c	(revision 1001)
+++ /pjproject/trunk/pjlib-util/src/pjlib-util-test/encryption.c	(revision 1002)
@@ -384,4 +384,56 @@
 }
 
+/* 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()
@@ -401,4 +453,8 @@
 	return rc;
 
+    rc = crc32_test();
+    if (rc != 0)
+	return rc;
+
     return 0;
 }
Index: /pjproject/trunk/pjlib-util/src/pjlib-util/crc32.c
===================================================================
--- /pjproject/trunk/pjlib-util/src/pjlib-util/crc32.c	(revision 1002)
+++ /pjproject/trunk/pjlib-util/src/pjlib-util/crc32.c	(revision 1002)
@@ -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);
+}
+
Index: /pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg.c
===================================================================
--- /pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg.c	(revision 1001)
+++ /pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg.c	(revision 1002)
@@ -18,5 +18,8 @@
  */
 #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>
@@ -26,6 +29,6 @@
 #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[] = 
@@ -548,4 +551,26 @@
 
 
+/*
+ * 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, 
@@ -659,4 +684,25 @@
 
 
+/*
+ * 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, 
@@ -828,4 +874,20 @@
 }
 
+/* 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, 
@@ -1088,5 +1150,5 @@
 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)
 {
@@ -1117,4 +1179,21 @@
 
 
+/* 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, 
@@ -1194,4 +1273,6 @@
 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)
 {
@@ -1203,7 +1284,33 @@
     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);
 }
 
@@ -1325,11 +1432,27 @@
 
 
+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);
@@ -1338,10 +1461,6 @@
 	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;
 
@@ -1349,12 +1468,77 @@
      * 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;
 }
@@ -1370,7 +1554,5 @@
 				       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)
 {
     
@@ -1385,7 +1567,15 @@
     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 */
@@ -1399,6 +1589,7 @@
     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 */
@@ -1416,6 +1607,23 @@
 
 	/* 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 */
@@ -1427,9 +1635,4 @@
 	    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? */
@@ -1438,6 +1641,15 @@
 		 * 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;
@@ -1446,4 +1658,6 @@
 	} else {
 	    void *attr;
+	    char err_msg1[PJ_ERR_MSG_SIZE],
+		 err_msg2[PJ_ERR_MSG_SIZE];
 
 	    /* Parse the attribute */
@@ -1451,13 +1665,40 @@
 
 	    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 */
@@ -1470,7 +1711,4 @@
 
     *p_msg = msg;
-
-    if (p_uattr_cnt)
-	*p_uattr_cnt = uattr_cnt;
 
     if (p_parsed_len)
@@ -1495,5 +1733,9 @@
     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);
@@ -1520,6 +1762,4 @@
 	const struct attr_desc *adesc;
 	const pj_stun_attr_hdr *attr_hdr;
-	unsigned printed;
-	pj_status_t status;
 
 	attr_hdr = msg->attr[i];
@@ -1535,7 +1775,12 @@
 	    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;
 	}
 
@@ -1551,14 +1796,126 @@
     }
 
+    /* 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 */
@@ -1588,2 +1945,115 @@
 }
 
+
+/* 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;
+}
+
+
Index: /pjproject/trunk/pjlib-util/src/pjstun-client/stun_session.c
===================================================================
--- /pjproject/trunk/pjlib-util/src/pjstun-client/stun_session.c	(revision 1001)
+++ /pjproject/trunk/pjlib-util/src/pjstun-client/stun_session.c	(revision 1002)
@@ -485,5 +485,5 @@
     /* 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);
@@ -541,5 +541,5 @@
 					      unsigned *parsed_len)
 {
-    pj_stun_msg *msg;
+    pj_stun_msg *msg, *response;
     pj_pool_t *tmp_pool;
     char *dump;
@@ -555,7 +555,10 @@
     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;
Index: /pjproject/trunk/pjlib-util/src/pjstun-srv/server_main.c
===================================================================
--- /pjproject/trunk/pjlib-util/src/pjstun-srv/server_main.c	(revision 1001)
+++ /pjproject/trunk/pjlib-util/src/pjstun-srv/server_main.c	(revision 1002)
@@ -52,29 +52,10 @@
     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) {
+    pj_status_t status;
+
+    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 */
@@ -111,5 +92,5 @@
     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;
@@ -224,8 +205,5 @@
     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;
@@ -236,14 +214,11 @@
     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;
