Index: /pjproject/branches/iceproject/pjlib-util/build/pjlib_util.dsp
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/build/pjlib_util.dsp	(revision 989)
+++ /pjproject/branches/iceproject/pjlib-util/build/pjlib_util.dsp	(revision 990)
@@ -138,4 +138,8 @@
 # Begin Source File
 
+SOURCE="..\src\pjlib-util\stun_server.c"
+# End Source File
+# Begin Source File
+
 SOURCE="..\src\pjlib-util\stun_simple.c"
 # PROP Exclude_From_Build 1
Index: /pjproject/branches/iceproject/pjlib-util/build/pjlib_util.dsw
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/build/pjlib_util.dsw	(revision 989)
+++ /pjproject/branches/iceproject/pjlib-util/build/pjlib_util.dsw	(revision 990)
@@ -20,8 +20,4 @@
 Package=<5>
 {{{
-    begin source code control
-    "$/pjproject/pjlib/build", UIAAAAAA
-    ..\..\pjlib\build
-    end source code control
 }}}
 
@@ -62,4 +58,22 @@
 ###############################################################################
 
+Project: "pjstun_srv"=".\pjstun_srv.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pjlib
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjlib_util
+    End Project Dependency
+}}}
+
+###############################################################################
+
 Global:
 
Index: /pjproject/branches/iceproject/pjlib-util/build/pjstun_srv.dsp
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/build/pjstun_srv.dsp	(revision 990)
+++ /pjproject/branches/iceproject/pjlib-util/build/pjstun_srv.dsp	(revision 990)
@@ -0,0 +1,106 @@
+# Microsoft Developer Studio Project File - Name="pjstun_srv" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=pjstun_srv - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjstun_srv.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjstun_srv.mak" CFG="pjstun_srv - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjstun_srv - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "pjstun_srv - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjstun_srv - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "./output/pjstun-srv-i386-win32-vc6-release"
+# PROP BASE Intermediate_Dir "./output/pjstun-srv-i386-win32-vc6-release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "./output/pjstun-srv-i386-win32-vc6-release"
+# PROP Intermediate_Dir "./output/pjstun-srv-i386-win32-vc6-release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "../include" /I "../../pjlib/include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../bin/pjstun-srv-i386-win32-vc6-release.exe"
+
+!ELSEIF  "$(CFG)" == "pjstun_srv - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "./output/pjstun-srv-i386-win32-vc6-debug"
+# PROP BASE Intermediate_Dir "./output/pjstun-srv-i386-win32-vc6-debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "./output/pjstun-srv-i386-win32-vc6-debug"
+# PROP Intermediate_Dir "./output/pjstun-srv-i386-win32-vc6-debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 netapi32.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjstun-srv-i386-win32-vc6-debug.exe" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjstun_srv - Win32 Release"
+# Name "pjstun_srv - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE="..\src\pjstun-srv\server_main.c"
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE="..\src\pjstun-srv\server.h"
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
Index: /pjproject/branches/iceproject/pjlib-util/include/pjlib-util.h
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/include/pjlib-util.h	(revision 989)
+++ /pjproject/branches/iceproject/pjlib-util/include/pjlib-util.h	(revision 990)
@@ -32,4 +32,8 @@
 #include <pjlib-util/scanner.h>
 //#include <pjlib-util/stun.h>
+#include <pjlib-util/stun_endpoint.h>
+#include <pjlib-util/stun_msg.h>
+#include <pjlib-util/stun_server.h>
+#include <pjlib-util/stun_transaction.h>
 #include <pjlib-util/xml.h>
 
Index: /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/errno.h
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/errno.h	(revision 989)
+++ /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/errno.h	(revision 990)
@@ -119,4 +119,9 @@
  */
 #define PJLIB_UTIL_ESTUNINVALIDID   (PJLIB_UTIL_ERRNO_START+16)	/* 320016 */
+/**
+ * @hideinitializer
+ * Unable to find handler for the request.
+ */
+#define PJLIB_UTIL_ESTUNNOHANDLER   (PJLIB_UTIL_ERRNO_START+17)	/* 320017 */
 
 
Index: /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_msg.h
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_msg.h	(revision 989)
+++ /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_msg.h	(revision 990)
@@ -179,5 +179,5 @@
  * Determine if the message type is a request.
  */
-#define PJ_STUN_IS_REQUEST(msg_type)	(((msg_type) & 0x0F00) == 0x0000)
+#define PJ_STUN_IS_REQUEST(msg_type)	(((msg_type) & 0x0110) == 0x0000)
 
 
@@ -185,19 +185,34 @@
  * Determine if the message type is a response.
  */
-#define PJ_STUN_IS_RESPONSE(msg_type)	(((msg_type) & 0x0F00) == 0x0100)
-
+#define PJ_STUN_IS_RESPONSE(msg_type)	(((msg_type) & 0x0110) == 0x0100)
+
+
+/**
+ * The response bit in the message type.
+ */
+#define PJ_STUN_RESPONSE_BIT		(0x0100)
 
 /**
  * Determine if the message type is an error response.
  */
-#define PJ_STUN_IS_ERROR_RESPONSE(msg_type) (((msg_type) & 0x0FF0) == 0x0110)
-
-
-#if 0
+#define PJ_STUN_IS_ERROR_RESPONSE(msg_type) (((msg_type) & 0x0110) == 0x0110)
+
+
+/**
+ * The error response bit in the message type.
+ */
+#define PJ_STUN_ERROR_RESPONSE_BIT	(0x0110)
+
+
 /**
  * Determine if the message type is an indication message.
  */
-#define PJ_STUN_IS_INDICATION(msg_type)	(((msg_type) & 0x0FF0) == 0x0010)
-#endif
+#define PJ_STUN_IS_INDICATION(msg_type)	(((msg_type) & 0x0110) == 0x0010)
+
+
+/**
+ * The error response bit in the message type.
+ */
+#define PJ_STUN_INDICATION_BIT		(0x0010)
 
 
@@ -325,4 +340,5 @@
     PJ_STUN_ATTR_NONCE		    = 0x0015,/**< NONCE attribute.	    */
     PJ_STUN_ATTR_RELAY_ADDRESS	    = 0x0016,/**< RELAY-ADDRESS attribute.  */
+    PJ_STUN_ATTR_REQUESTED_ADDR_TYPE= 0x0017,/**< REQUESTED-ADDRESS-TYPE    */
     PJ_STUN_ATTR_REQUESTED_PORT_PROPS=0x0018,/**< REQUESTED-PORT-PROPS	    */
     PJ_STUN_ATTR_REQUESTED_TRANSPORT= 0x0019,/**< REQUESTED-TRANSPORT	    */
@@ -330,13 +346,25 @@
     PJ_STUN_ATTR_TIMER_VAL	    = 0x0021,/**< TIMER-VAL attribute.	    */
     PJ_STUN_ATTR_REQUESTED_IP	    = 0x0022,/**< REQUESTED-IP attribute    */
+    PJ_STUN_ATTR_XOR_REFLECTED_FROM = 0x0023,/**< XOR-REFLECTED-FROM	    */
+    PJ_STUN_ATTR_PRIORITY	    = 0x0024,/**< PRIORITY		    */
+    PJ_STUN_ATTR_USE_CANDIDATE	    = 0x0025,/**< USE-CANDIDATE		    */
+    PJ_STUN_ATTR_XOR_INTERNAL_ADDR  = 0x0026,/**< XOR-INTERNAL-ADDRESS	    */
+
+    PJ_STUN_ATTR_END_MANDATORY_ATTR,
+
+    PJ_STUN_ATTR_START_EXTENDED_ATTR= 0x8021,
+
     PJ_STUN_ATTR_FINGERPRINT	    = 0x8021,/**< FINGERPRINT attribute.    */
     PJ_STUN_ATTR_SERVER		    = 0x8022,/**< SERVER attribute.	    */
     PJ_STUN_ATTR_ALTERNATE_SERVER   = 0x8023,/**< ALTERNATE-SERVER.	    */
-    PJ_STUN_ATTR_REFRESH_INTERVAL   = 0x8024 /**< REFRESH-INTERVAL.	    */
+    PJ_STUN_ATTR_REFRESH_INTERVAL   = 0x8024,/**< REFRESH-INTERVAL.	    */
+
+    PJ_STUN_ATTR_END_EXTENDED_ATTR
+
 } pj_stun_attr_type;
 
 
 /**
- * STUN error codes.
+ * STUN error codes, which goes into STUN ERROR-CODE attribute.
  */
 typedef enum pj_stun_status
@@ -491,36 +519,27 @@
 
     /**
-     * The first 8 bits of the attribute are ignored for the purposes
-     * of aligning parameters on natural 32 bit boundaries.
-     */
-    pj_uint8_t		ignored;
-
-    /**
-     * The address family can take on the following values:
-     * 
-     * 0x01:IPv4
-     * 0x02:IPv6
-     */
-    pj_uint8_t		family;
-
-    /**
-     * Port number.
-     */
-    pj_uint16_t		port;
-
-    /**
-     * The address.
+     * The socket address (as a union)
      */
     union {
-
-	/** IPv4 address. */
-	pj_uint32_t	ipv4;
-
-	/** IPv6 address. */
-	char		ipv6[1];
-
+	pj_sockaddr	    addr;   /**< Generic socket address.    */
+	pj_sockaddr_in	    ipv4;   /**< IPv4 socket address.	    */
+	pj_sockaddr_in6	    ipv6;   /**< IPv6 socket address.	    */
     } addr;
 
 } pj_stun_generic_ip_addr_attr;
+
+
+/**
+ * This structure represents a generic STUN attributes with no payload,
+ * and it is used for example by ICE USE-CANDIDATE attribute.
+ */
+typedef struct pj_stun_empty_attr
+{
+    /**
+     * Standard STUN attribute header.
+     */
+    pj_stun_attr_hdr	hdr;
+
+} pj_stun_empty_attr;
 
 
@@ -900,4 +919,22 @@
 
 /**
+ * This describes the REQUESTED-ADDRESS-TYPE attribute.
+ * The REQUESTED-ADDRESS-TYPE attribute is used by clients to request
+ * the allocation of a specific address type from a server.  The
+ * following is the format of the REQUESTED-ADDRESS-TYPE attribute.
+
+ \verbatim
+
+      0                   1                   2                   3
+      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |        Family                 |           Reserved            |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ \endverbatim
+ */
+typedef struct pj_stun_generic_uint_attr pj_stun_requested_addr_type;
+
+/**
  * This describes the STUN REQUESTED-PORT-PROPS attribute.
  * This attribute allows the client to request certain properties for
@@ -936,4 +973,43 @@
 typedef struct pj_stun_generic_ip_addr_attr pj_stun_requested_ip_attr;
 
+/**
+ * This describes the XOR-REFLECTED-FROM attribute, as described by
+ * draft-macdonald-behave-nat-behavior-discovery-00.
+ * The XOR-REFLECTED-FROM attribute is used in place of the REFLECTED-
+ * FROM attribute.  It provides the same information, but because the
+ * NAT's public address is obfuscated through the XOR function, It can
+ * pass through a NAT that would otherwise attempt to translate it to
+ * the private network address.  XOR-REFLECTED-FROM has identical syntax
+ * to XOR-MAPPED-ADDRESS.
+ */
+typedef struct pj_stun_generic_ip_addr_attr pj_stun_xor_reflected_from_attr;
+
+/**
+ * This describes the PRIORITY attribute from draft-ietf-mmusic-ice-13.
+ * The PRIORITY attribute indicates the priority that is to be
+ * associated with a peer reflexive candidate, should one be discovered
+ * by this check.  It is a 32 bit unsigned integer, and has an attribute
+ * type of 0x0024.
+ */
+typedef struct pj_stun_generic_uint_attr pj_stun_priority_attr;
+
+/**
+ * This describes the USE-CANDIDATE attribute from draft-ietf-mmusic-ice-13.
+ * The USE-CANDIDATE attribute indicates that the candidate pair
+ * resulting from this check should be used for transmission of media.
+ * The attribute has no content (the Length field of the attribute is
+ * zero); it serves as a flag.
+ */
+typedef struct pj_stun_empty_attr pj_stun_use_candidate_attr;
+
+/**
+ * This structure describes STUN XOR-INTERNAL-ADDRESS attribute from
+ * draft-wing-behave-nat-control-stun-usage-00.
+ * This attribute MUST be present in a Binding Response and may be used
+ * in other responses as well.  This attribute is necessary to allow a
+ * STUN client to 'walk backwards' and communicate directly with all of
+ * the STUN-aware NATs along the path.
+ */
+typedef pj_stun_generic_ip_addr_attr pj_stun_xor_internal_addr_attr;
 
 /**
@@ -987,4 +1063,6 @@
  * @param pool		Pool to create the STUN message.
  * @param msg_type	The 14bit message type.
+ * @param magic		Magic value to be put to the mesage; for requests,
+ *			the value should be PJ_STUN_MAGIC.
  * @param tsx_id	Optional transaction ID, or NULL to let the
  *			function generates a random transaction ID.
@@ -995,6 +1073,21 @@
 PJ_DECL(pj_status_t) pj_stun_msg_create(pj_pool_t *pool,
 					unsigned msg_type,
+					pj_uint32_t magic,
 					const pj_uint8_t tsx_id[12],
 					pj_stun_msg **p_msg);
+
+
+/**
+ * Add STUN attribute to STUN message.
+ *
+ * @param msg		The STUN message.
+ * @param attr		The STUN attribute to be added to the message.
+ *
+ * @return		PJ_SUCCESS on success, or PJ_ETOOMANY if there are
+ *			already too many attributes in the message.
+ */
+PJ_DECL(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg,
+					  pj_stun_attr_hdr *attr);
+
 
 /**
@@ -1092,6 +1185,8 @@
  * @param pool		The pool to allocate memory from.
  * @param attr_type	Attribute type.
- * @param ip_addr	IP address, in host byte order.
- * @param port		Port number, in host byte order.
+ * @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.
  * @param p_attr	Pointer to receive the attribute.
  *
@@ -1100,7 +1195,8 @@
 PJ_DECL(pj_status_t) 
 pj_stun_generic_ip_addr_attr_create(pj_pool_t *pool,
-				    int attr_type,
-				    pj_uint32_t ip_addr,
-				    int port,
+				    int attr_type, 
+				    pj_bool_t xor_ed,
+				    unsigned addr_len,
+				    const pj_sockaddr_t *addr,
 				    pj_stun_generic_ip_addr_attr **p_attr);
 
Index: /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_server.h
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_server.h	(revision 989)
+++ /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_server.h	(revision 990)
@@ -51,5 +51,5 @@
      * The STUN message type.
      */
-    int	     msg_type;
+    int		 msg_type;
 
     /** 
@@ -59,5 +59,7 @@
      * @param msg   The STUN message.
      */
-    void    (*handle_msg)(pj_stun_service *svc, const pj_stun_msg *msg);
+    pj_status_t	(*handle_msg)(pj_stun_service *svc, 
+			      void *handle_data,
+			      const pj_stun_msg *msg);
 
 } pj_stun_service_handler;
@@ -68,4 +70,6 @@
  */
 PJ_DECL(pj_status_t) pj_stun_service_create(pj_pool_t *pool,
+					    const char *name,
+					    unsigned options,
 					    unsigned handler_cnt,
 					    pj_stun_service_handler cb[],
@@ -89,4 +93,5 @@
  */
 PJ_DECL(pj_status_t) pj_stun_service_handle_msg(pj_stun_service *svc,
+						void *handle_data,
 						const pj_stun_msg *msg);
 
Index: /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_msg.c
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_msg.c	(revision 989)
+++ /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_msg.c	(revision 990)
@@ -80,4 +80,7 @@
 #define ATTR_HDR_LEN	    4
 
+#define getval16(p, pos)    (pj_uint16_t)(((p)[(pos)] << 8) | \
+					  ((p)[(pos) + 1] << 0))
+
 
 //////////////////////////////////////////////////////////////////////////////
@@ -93,17 +96,30 @@
 pj_stun_generic_ip_addr_attr_create(pj_pool_t *pool,
 				    int attr_type,
-				    pj_uint32_t ip_addr,
-				    int port,
+				    pj_bool_t xor_ed,
+				    unsigned addr_len,
+				    const pj_sockaddr_t *addr,
 				    pj_stun_generic_ip_addr_attr **p_attr)
 {
     pj_stun_generic_ip_addr_attr *attr;
 
-    PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL);
+    PJ_ASSERT_RETURN(pool && addr_len && addr && p_attr, PJ_EINVAL);
+    PJ_ASSERT_RETURN(addr_len == sizeof(pj_sockaddr_in) ||
+		     addr_len == sizeof(pj_sockaddr_in6), PJ_EINVAL);
 
     attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_ip_addr_attr);
     INIT_ATTR(attr, attr_type, STUN_GENERIC_IP_ADDR_LEN);
-    attr->family = 1;
-    attr->port = (pj_uint16_t) port;
-    attr->addr.ipv4 = ip_addr;
+
+    if (!xor_ed) {
+	pj_memcpy(&attr->addr, addr, addr_len);
+    } else if (addr_len == sizeof(pj_sockaddr_in)) {
+	const pj_sockaddr_in *addr4 = (const pj_sockaddr_in*) addr;
+
+	pj_sockaddr_in_init(&attr->addr.ipv4, NULL, 0);
+	attr->addr.ipv4.sin_port = (pj_uint16_t)(addr4->sin_port ^ 0x2112);
+	attr->addr.ipv4.sin_addr.s_addr = (addr4->sin_addr.s_addr ^ 
+					   pj_htonl(0x2112A442));
+    } else {
+	return PJ_ENOTSUP;
+    }
 
     *p_attr = attr;
@@ -117,26 +133,14 @@
 					       void **p_attr)
 {
-    enum
-    {
-	ATTR_LEN = STUN_GENERIC_IP_ADDR_LEN + ATTR_HDR_LEN
-    };
     pj_stun_generic_ip_addr_attr *attr;
-
-    /* Check that the struct address is valid */
-    pj_assert(sizeof(pj_stun_generic_ip_addr_attr) == ATTR_LEN);
+    pj_uint32_t val;
 
     /* Create the attribute */
-    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_generic_ip_addr_attr);
-    pj_memcpy(attr, buf, ATTR_LEN);
-
-    /* Check address family is valid (only supports ipv4 for now) */
-    if (attr->family != 1)
-	return PJLIB_UTIL_ESTUNINATTR;
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_ip_addr_attr);
+    pj_memcpy(attr, buf, ATTR_HDR_LEN);
 
     /* Convert to host byte order */
     attr->hdr.type = pj_ntohs(attr->hdr.type);
     attr->hdr.length = pj_ntohs(attr->hdr.length);
-    attr->port = pj_ntohs(attr->port);
-    attr->addr.ipv4 = pj_ntohl(attr->addr.ipv4);
 
     /* Check that the attribute length is valid */
@@ -144,4 +148,16 @@
 	return PJLIB_UTIL_ESTUNINATTRLEN;
 
+    /* Check address family */
+    val = *(pj_uint8_t*)(buf + ATTR_HDR_LEN + 1);
+
+    /* Check address family is valid (only supports ipv4 for now) */
+    if (val != 1)
+	return PJLIB_UTIL_ESTUNINATTR;
+
+    /* Get port and address */
+    pj_sockaddr_in_init(&attr->addr.ipv4, NULL, 0);
+    attr->addr.ipv4.sin_port = getval16(buf, ATTR_HDR_LEN + 2);
+    pj_memcpy(&attr->addr.ipv4.sin_addr, buf+ATTR_HDR_LEN+4, 4);
+
     /* Done */
     *p_attr = attr;
@@ -154,8 +170,10 @@
 					       unsigned len, unsigned *printed)
 {
-    enum
-    {
-	ATTR_LEN = STUN_GENERIC_IP_ADDR_LEN + ATTR_HDR_LEN
+    enum {
+	ATTR_LEN = ATTR_HDR_LEN + STUN_GENERIC_IP_ADDR_LEN
     };
+    pj_uint8_t *start_buf = buf;
+    const pj_stun_generic_ip_addr_attr *ca = 
+	(const pj_stun_generic_ip_addr_attr *)a;
     pj_stun_generic_ip_addr_attr *attr;
 
@@ -163,14 +181,30 @@
 	return PJ_ETOOSMALL;
 
-    /* Copy and convert attribute to network byte order */
-    pj_memcpy(buf, a, ATTR_LEN);
+    /* Copy and convert headers to network byte order */
+    pj_memcpy(buf, a, ATTR_HDR_LEN);
     attr = (pj_stun_generic_ip_addr_attr*) buf;
     attr->hdr.type = pj_htons(attr->hdr.type);
-    attr->hdr.length = pj_htons(attr->hdr.length);
-    attr->port = pj_htons(attr->port);
-    attr->addr.ipv4 = pj_htonl(attr->addr.ipv4);
-
-    /* Done */
-    *printed = ATTR_LEN;
+    attr->hdr.length = pj_htons((pj_uint16_t)ATTR_LEN);
+    buf += ATTR_HDR_LEN;
+    
+    /* Ignored */
+    *buf++ = '\0';
+
+    /* Family (IPv4 only for now) */
+    PJ_ASSERT_RETURN(ca->addr.addr.sa_family == PJ_AF_INET, PJ_EINVAL);
+    *buf++ = 1;
+
+    /* Port */
+    pj_memcpy(buf, &ca->addr.ipv4.sin_port, 2);
+    buf += 2;
+
+    /* Address */
+    pj_memcpy(buf, &ca->addr.ipv4.sin_addr, 4);
+    buf += 4;
+
+    pj_assert(buf - start_buf == ATTR_LEN);
+
+    /* Done */
+    *printed = buf - start_buf;
 
     return PJ_SUCCESS;
@@ -214,5 +248,5 @@
 
     /* Create the attribute */
-    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_generic_string_attr);
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_string_attr);
 
     /* Copy the header */
@@ -247,12 +281,14 @@
     /* Calculated total attr_len (add padding if necessary) */
     *printed = (ca->value.slen + ATTR_HDR_LEN + 3) & (~3);
-    if (len < *printed)
+    if (len < *printed) {
+	*printed = 0;
 	return PJ_ETOOSMALL;
+    }
 
     /* Copy header */
     pj_memcpy(buf, a, ATTR_HDR_LEN);
+    attr = (pj_stun_attr_hdr*)buf;
 
     /* Set the correct length */
-    attr = (pj_stun_attr_hdr*)buf;
     attr->length = (pj_uint16_t) ca->value.slen;
 
@@ -265,4 +301,79 @@
 
     /* Done */
+    return PJ_SUCCESS;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * STUN empty attribute (used by USE-CANDIDATE).
+ */
+
+/*
+ * Create a STUN empty attribute.
+ */
+PJ_DEF(pj_status_t) 
+pj_stun_empty_attr_create(pj_pool_t *pool,
+			  int attr_type,
+			  pj_stun_empty_attr **p_attr)
+{
+    pj_stun_empty_attr *attr;
+
+    PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL);
+
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_empty_attr);
+    INIT_ATTR(attr, attr_type, sizeof(pj_stun_empty_attr));
+
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t decode_empty_attr(pj_pool_t *pool, 
+				     const pj_uint8_t *buf, 
+				     void **p_attr)
+{
+    pj_stun_empty_attr *attr;
+
+    /* Check that the struct address is valid */
+    pj_assert(sizeof(pj_stun_empty_attr) == ATTR_HDR_LEN);
+
+    /* Create the attribute */
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_empty_attr);
+    pj_memcpy(attr, buf, ATTR_HDR_LEN);
+
+    /* Convert to host byte order */
+    attr->hdr.type = pj_ntohs(attr->hdr.type);
+    attr->hdr.length = pj_ntohs(attr->hdr.length);
+
+    /* Check that the attribute length is valid */
+    if (attr->hdr.length != ATTR_HDR_LEN)
+	return PJLIB_UTIL_ESTUNINATTRLEN;
+
+    /* Done */
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t encode_empty_attr(const void *a, pj_uint8_t *buf, 
+				     unsigned len, unsigned *printed)
+{
+    pj_stun_empty_attr *attr;
+
+    if (len < ATTR_HDR_LEN) 
+	return PJ_ETOOSMALL;
+
+    /* Copy and convert attribute to network byte order */
+    pj_memcpy(buf, a, ATTR_HDR_LEN);
+    attr = (pj_stun_empty_attr*) buf;
+    attr->hdr.type = pj_htons(attr->hdr.type);
+    attr->hdr.length = pj_htons(attr->hdr.length);
+
+    /* Done */
+    *printed = ATTR_HDR_LEN;
+
     return PJ_SUCCESS;
 }
@@ -312,5 +423,5 @@
 
     /* Create the attribute */
-    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_generic_uint_attr);
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_uint_attr);
     pj_memcpy(attr, buf, ATTR_LEN);
 
@@ -397,5 +508,5 @@
 
     /* Create attribute */
-    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_msg_integrity_attr);
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msg_integrity_attr);
     pj_memcpy(attr, buf, sizeof(pj_stun_msg_integrity_attr));
     attr->hdr.type = pj_ntohs(attr->hdr.type);
@@ -486,5 +597,5 @@
 
     /* Create the attribute */
-    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_error_code_attr);
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_error_code_attr);
 
     /* Copy the header */
@@ -683,5 +794,5 @@
 
     /* Create the attribute */
-    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_binary_attr);
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_binary_attr);
 
     /* Copy the header */
@@ -740,7 +851,4 @@
 struct attr_desc
 {
-    unsigned  type;
-    const char *attr_name;
-
     pj_status_t	(*decode_attr)(pj_pool_t *pool, const pj_uint8_t *buf, 
 			       void **p_attr);
@@ -748,182 +856,261 @@
 			       unsigned len, unsigned *printed);
 
-} attr_desc[] = 
-{
-    {
-	PJ_STUN_ATTR_MAPPED_ADDR,
-	"MAPPED-ADDRESS",
+};
+
+struct attr_desc mandatory_attr_desc[] = 
+{
+    {
+	/* type zero */
+	NULL,
+	NULL
+    },
+    {
+	/* PJ_STUN_ATTR_MAPPED_ADDR, */
 	&decode_generic_ip_addr_attr,
 	&encode_generic_ip_addr_attr
     },
     {
-	PJ_STUN_ATTR_RESPONSE_ADDR,
-	"RESPONSE-ADDRESS",
+	/* PJ_STUN_ATTR_RESPONSE_ADDR, */
 	&decode_generic_ip_addr_attr,
 	&encode_generic_ip_addr_attr
     },
     {
-	PJ_STUN_ATTR_CHANGE_REQUEST,
-	"CHANGE-REQUEST",
+	/* PJ_STUN_ATTR_CHANGE_REQUEST, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* PJ_STUN_ATTR_SOURCE_ADDR, */
 	&decode_generic_ip_addr_attr,
 	&encode_generic_ip_addr_attr
     },
     {
-	PJ_STUN_ATTR_SOURCE_ADDR,
-	"SOURCE-ADDRESS",
+	/* PJ_STUN_ATTR_CHANGED_ADDR, */
 	&decode_generic_ip_addr_attr,
 	&encode_generic_ip_addr_attr
     },
     {
-	PJ_STUN_ATTR_CHANGED_ADDR,
-	"CHANGED-ADDRESS",
+	/* PJ_STUN_ATTR_USERNAME, */
+	&decode_generic_string_attr,
+	&encode_generic_string_attr
+    },
+    {
+	/* PJ_STUN_ATTR_PASSWORD, */
+	&decode_generic_string_attr,
+	&encode_generic_string_attr
+    },
+    {
+	/* PJ_STUN_ATTR_MESSAGE_INTEGRITY, */
+	&decode_msg_integrity_attr,
+	&encode_msg_integrity_attr
+    },
+    {
+	/* PJ_STUN_ATTR_ERROR_CODE, */
+	&decode_error_code_attr,
+	&encode_error_code_attr
+    },
+    {
+	/* PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES, */
+	&decode_unknown_attr,
+	&encode_unknown_attr
+    },
+    {
+	/* PJ_STUN_ATTR_REFLECTED_FROM, */
 	&decode_generic_ip_addr_attr,
 	&encode_generic_ip_addr_attr
     },
     {
-	PJ_STUN_ATTR_USERNAME,
-	"USERNAME",
+	/* ID 0x000C is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* PJ_STUN_ATTR_LIFETIME, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* ID 0x000E is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* ID 0x000F is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* PJ_STUN_ATTR_BANDWIDTH, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* ID 0x0011 is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* PJ_STUN_ATTR_REMOTE_ADDRESS, */
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	/* PJ_STUN_ATTR_DATA, */
+	&decode_binary_attr,
+	&encode_binary_attr
+    },
+    {
+	/* PJ_STUN_ATTR_REALM, */
 	&decode_generic_string_attr,
 	&encode_generic_string_attr
     },
     {
-	PJ_STUN_ATTR_PASSWORD,
-	"PASSWORD",
+	/* PJ_STUN_ATTR_NONCE, */
 	&decode_generic_string_attr,
 	&encode_generic_string_attr
     },
     {
-	PJ_STUN_ATTR_MESSAGE_INTEGRITY,
-	"MESSAGE-INTEGRITY",
-	&decode_msg_integrity_attr,
-	&encode_msg_integrity_attr
-    },
-    {
-	PJ_STUN_ATTR_ERROR_CODE,
-	"ERROR-CODE",
-	&decode_error_code_attr,
-	&encode_error_code_attr
-    },
-    {
-	PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES,
-	"UNKNOWN-ATTRIBUTES",
-	&decode_unknown_attr,
-	&encode_unknown_attr
-    },
-    {
-	PJ_STUN_ATTR_REFLECTED_FROM,
-	"REFLECTED-FROM",
+	/* PJ_STUN_ATTR_RELAY_ADDRESS, */
 	&decode_generic_ip_addr_attr,
 	&encode_generic_ip_addr_attr
     },
     {
-	PJ_STUN_ATTR_REALM,
-	"REALM",
+	/* PJ_STUN_ATTR_REQUESTED_ADDR_TYPE, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* PJ_STUN_ATTR_REQUESTED_PORT_PROPS, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* PJ_STUN_ATTR_REQUESTED_TRANSPORT, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* ID 0x001A is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* ID 0x001B is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* ID 0x001C is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* ID 0x001D is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* ID 0x001E is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* ID 0x001F is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* PJ_STUN_ATTR_XOR_MAPPED_ADDRESS, */
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	/* PJ_STUN_ATTR_TIMER_VAL, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* PJ_STUN_ATTR_REQUESTED_IP, */
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	/* PJ_STUN_ATTR_XOR_REFLECTED_FROM, */
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	/* PJ_STUN_ATTR_PRIORITY, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* PJ_STUN_ATTR_USE_CANDIDATE, */
+	&decode_empty_attr,
+	&encode_empty_attr
+    },
+    {
+	/* PJ_STUN_ATTR_XOR_INTERNAL_ADDR, */
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+
+    /* Sentinel */
+    {
+	/* PJ_STUN_ATTR_END_MANDATORY_ATTR */
+	NULL,
+	NULL
+    }
+};
+
+static struct attr_desc extended_attr_desc[] =
+{
+    {
+	/* PJ_STUN_ATTR_FINGERPRINT, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* PJ_STUN_ATTR_SERVER, */
 	&decode_generic_string_attr,
 	&encode_generic_string_attr
     },
     {
-	PJ_STUN_ATTR_NONCE,
-	"NONCE",
-	&decode_generic_string_attr,
-	&encode_generic_string_attr
-    },
-    {
-	PJ_STUN_ATTR_XOR_MAPPED_ADDRESS,
-	"XOR-MAPPED-ADDRESS",
+	/* PJ_STUN_ATTR_ALTERNATE_SERVER, */
 	&decode_generic_ip_addr_attr,
 	&encode_generic_ip_addr_attr
     },
     {
-	PJ_STUN_ATTR_FINGERPRINT,
-	"FINGERPRINT",
+	/* PJ_STUN_ATTR_REFRESH_INTERVAL, */
 	&decode_generic_uint_attr,
 	&encode_generic_uint_attr
     },
-    {
-	PJ_STUN_ATTR_SERVER,
-	"SERVER",
-	&decode_generic_string_attr,
-	&encode_generic_string_attr
-    },
-    {
-	PJ_STUN_ATTR_ALTERNATE_SERVER,
-	"ALTERNATE-SERVER",
-	&decode_generic_ip_addr_attr,
-	&encode_generic_ip_addr_attr
-    },
-    {
-	PJ_STUN_ATTR_REFRESH_INTERVAL,
-	"REFRESH-INTERVAL",
-	&decode_generic_uint_attr,
-	&encode_generic_uint_attr
-    },
-    {
-	PJ_STUN_ATTR_LIFETIME,
-	"LIFETIME",
-	&decode_generic_uint_attr,
-	&encode_generic_uint_attr
-    },
-    {
-	PJ_STUN_ATTR_BANDWIDTH,
-	"BANDWIDTH",
-	&decode_generic_uint_attr,
-	&encode_generic_uint_attr
-    },
-    {
-	PJ_STUN_ATTR_REMOTE_ADDRESS,
-	"REMOTE-ADDRESS",
-	&decode_generic_ip_addr_attr,
-	&encode_generic_ip_addr_attr
-    },
-    {
-	PJ_STUN_ATTR_DATA,
-	"DATA",
-	&decode_binary_attr,
-	&encode_binary_attr
-    },
-    {
-	PJ_STUN_ATTR_RELAY_ADDRESS,
-	"RELAY-ADDRESS",
-	&decode_generic_ip_addr_attr,
-	&encode_generic_ip_addr_attr
-    },
-    {
-	PJ_STUN_ATTR_REQUESTED_PORT_PROPS,
-	"REQUESTED-PORT-PROPS",
-	&decode_generic_uint_attr,
-	&encode_generic_uint_attr
-    },
-    {
-	PJ_STUN_ATTR_REQUESTED_TRANSPORT,
-	"REQUESTED-TRANSPORT",
-	&decode_generic_uint_attr,
-	&encode_generic_uint_attr
-    },
-    {
-	PJ_STUN_ATTR_REQUESTED_IP,
-	"STUN REQUESTED-IP",
-	&decode_generic_ip_addr_attr,
-	&encode_generic_ip_addr_attr
-    },
-    {
-	PJ_STUN_ATTR_TIMER_VAL,
-	"TIMER-VAL",
-	&decode_generic_uint_attr,
-	&encode_generic_uint_attr
-    }
 };
 
-static struct attr_desc *find_attr_desc(unsigned attr_type)
-{
-    unsigned i;
-
-    for (i=0; i<PJ_ARRAY_SIZE(attr_desc); ++i) {
-	if (attr_desc[i].type == attr_type)
-	    return &attr_desc[i];
-    }
-
-    return NULL;
-}
-
+
+static const struct attr_desc *find_attr_desc(unsigned attr_type)
+{
+    struct attr_desc *desc;
+
+    /* Check that attr_desc array is valid */
+    pj_assert(PJ_ARRAY_SIZE(mandatory_attr_desc)==
+	      PJ_STUN_ATTR_END_MANDATORY_ATTR+1);
+    pj_assert(mandatory_attr_desc[PJ_STUN_ATTR_END_MANDATORY_ATTR].decode_attr
+	      == NULL);
+    pj_assert(mandatory_attr_desc[PJ_STUN_ATTR_USE_CANDIDATE].decode_attr 
+	      == &decode_empty_attr);
+    pj_assert(PJ_ARRAY_SIZE(extended_attr_desc) ==
+	      PJ_STUN_ATTR_END_EXTENDED_ATTR-PJ_STUN_ATTR_START_EXTENDED_ATTR);
+
+    if (attr_type < PJ_STUN_ATTR_START_EXTENDED_ATTR)
+	desc = &mandatory_attr_desc[attr_type];
+    else if (attr_type >= PJ_STUN_ATTR_START_EXTENDED_ATTR &&
+	     attr_type < PJ_STUN_ATTR_END_EXTENDED_ATTR)
+	desc = &extended_attr_desc[attr_type-PJ_STUN_ATTR_START_EXTENDED_ATTR];
+    else
+	return NULL;
+
+    return desc->decode_attr == NULL ? NULL : desc;
+}
 
 
@@ -933,4 +1120,5 @@
 PJ_DEF(pj_status_t) pj_stun_msg_create( pj_pool_t *pool,
 					unsigned msg_type,
+					pj_uint32_t magic,
 					const pj_uint8_t tsx_id[12],
 					pj_stun_msg **p_msg)
@@ -942,5 +1130,5 @@
     msg = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msg);
     msg->hdr.type = (pj_uint16_t) msg_type;
-    msg->hdr.magic = (pj_uint32_t) PJ_STUN_MAGIC;
+    msg->hdr.magic = magic;
 
     if (tsx_id) {
@@ -969,4 +1157,18 @@
 
 /*
+ * Add STUN attribute to STUN message.
+ */
+PJ_DEF(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg,
+					 pj_stun_attr_hdr *attr)
+{
+    PJ_ASSERT_RETURN(msg && attr, PJ_EINVAL);
+    PJ_ASSERT_RETURN(msg->attr_count < PJ_STUN_MAX_ATTR, PJ_ETOOMANY);
+
+    msg->attr[msg->attr_count++] = attr;
+    return PJ_SUCCESS;
+}
+
+
+/*
  * Check that the PDU is potentially a valid STUN message.
  */
@@ -985,5 +1187,5 @@
     /* First byte of STUN message is always 0x00 or 0x01. */
     if ((*(const char*)pdu) != 0x00 && (*(const char*)pdu) != 0x01)
-	return PJ_FALSE;
+	return PJLIB_UTIL_ESTUNINMSGTYPE;
 
     /* If magic is set, then there is great possibility that this is
@@ -991,12 +1193,12 @@
      */
     if (pj_ntohl(hdr->magic) == PJ_STUN_MAGIC)
-	return PJ_TRUE;
+	return PJ_SUCCESS;
 
     /* Check the PDU length */
     if (pj_ntohs(hdr->length) > pdu_len)
-	return PJ_FALSE;
+	return PJLIB_UTIL_ESTUNINMSGLEN;
 
     /* Could be a STUN message */
-    return PJ_TRUE;
+    return PJ_SUCCESS;
 }
 
@@ -1031,5 +1233,5 @@
 
     /* Create the message, copy the header, and convert to host byte order */
-    msg = PJ_POOL_ALLOC_TYPE(pool, pj_stun_msg);
+    msg = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msg);
     pj_memcpy(&msg->hdr, pdu, sizeof(pj_stun_msg_hdr));
     msg->hdr.type = pj_ntohs(msg->hdr.type);
@@ -1040,9 +1242,12 @@
     pdu_len -= sizeof(pj_stun_msg_hdr);
 
+    if (p_err_code)
+	*p_err_code = 0;
+
     /* Parse attributes */
     uattr_cnt = 0;
     while (pdu_len > 0) {
 	unsigned attr_type, attr_val_len;
-	struct attr_desc *adesc;
+	const struct attr_desc *adesc;
 
 	/* Get attribute type and length. If length is not aligned
@@ -1053,10 +1258,10 @@
 	attr_val_len = (attr_val_len + 3) & (~3);
 
-	/* Get the attribute descriptor */
-	adesc = find_attr_desc(attr_type);
-
 	/* Check length */
 	if (pdu_len < attr_val_len)
 	    return PJLIB_UTIL_ESTUNINATTRLEN;
+
+	/* Get the attribute descriptor */
+	adesc = find_attr_desc(attr_type);
 
 	if (adesc == NULL) {
@@ -1076,5 +1281,5 @@
 		 * if we don't understand the attribute.
 		 */
-		if (p_err_code)
+		if (p_err_code && *p_err_code == 0)
 		    *p_err_code = PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE;
 
@@ -1090,6 +1295,6 @@
 	    if (status != PJ_SUCCESS) {
 		PJ_LOG(4,(THIS_FILE, 
-			  "Error parsing STUN %s attribute: status=%d",
-			  adesc->attr_name, status));
+			  "Error parsing STUN attribute type %d: status=%d",
+			  attr_type, status));
 		return status;
 	    }
@@ -1151,5 +1356,5 @@
     /* Print each attribute */
     for (i=0; i<msg->attr_count; ++i) {
-	struct attr_desc *adesc;
+	const struct attr_desc *adesc;
 	const pj_stun_attr_hdr *attr_hdr;
 	unsigned printed;
@@ -1169,5 +1374,7 @@
     }
 
-    /* Update the message length in the header */
+    /* Update the message length in the header. 
+     * Note that length is not including the 20 bytes header.
+     */
     hdr->length = pj_htons((pj_uint16_t)(buf - start));
 
Index: /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_server.c
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_server.c	(revision 990)
+++ /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_server.c	(revision 990)
@@ -0,0 +1,129 @@
+/* $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 
+ */
+#include <pjlib-util/stun_server.h>
+#include <pjlib-util/errno.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+struct pj_stun_service
+{
+    pj_str_t		     name;
+    unsigned		     options;
+    void		    *user_data;
+    unsigned		     cb_cnt;
+    pj_stun_service_handler *cb;
+};
+
+
+/*
+ * Create STUN service.
+ */
+PJ_DEF(pj_status_t) pj_stun_service_create( pj_pool_t *pool,
+					    const char *name,
+					    unsigned options,
+					    unsigned handler_cnt,
+					    pj_stun_service_handler cb[],
+					    void *user_data,
+					    pj_stun_service **p_svc)
+{
+    pj_stun_service *svc;
+
+    PJ_ASSERT_RETURN(pool && handler_cnt && cb && p_svc, PJ_EINVAL);
+
+    svc = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_service);
+    svc->options = options;
+    svc->user_data = user_data;
+
+    if (!name) name = "pj_stun_service";
+
+    pj_strdup2_with_null(pool, &svc->name, name);
+    
+    svc->cb_cnt = handler_cnt;
+    svc->cb = pj_pool_calloc(pool, handler_cnt,	
+			     sizeof(pj_stun_service_handler));
+    pj_memcpy(svc->cb, cb, handler_cnt * sizeof(pj_stun_service_handler));
+
+    *p_svc = svc;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Destroy STUN service
+ */
+PJ_DEF(pj_status_t) pj_stun_service_destroy(pj_stun_service *svc)
+{
+    PJ_ASSERT_RETURN(svc, PJ_EINVAL);
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get user data associated with the STUN service.
+ */
+PJ_DEF(void*) pj_stun_service_get_user_data(pj_stun_service *svc)
+{
+    PJ_ASSERT_RETURN(svc, NULL);
+    return svc->user_data;
+}
+
+
+/*
+ * Find handler.
+ */
+static pj_stun_service_handler *find_handler(pj_stun_service *svc,
+					     int msg_type)
+{
+    unsigned i;
+
+    for (i=0; i<svc->cb_cnt; ++i) {
+	if (svc->cb[i].msg_type == msg_type)
+	    return &svc->cb[i];
+    }
+
+    return NULL;
+}
+
+
+/*
+ * Instruct the STUN service to handle incoming STUN message.
+ */
+PJ_DEF(pj_status_t) pj_stun_service_handle_msg( pj_stun_service *svc,
+					        void *handle_data,
+						const pj_stun_msg *msg)
+{
+    pj_stun_service_handler *handler;
+
+    PJ_ASSERT_RETURN(svc && msg, PJ_EINVAL);
+
+    handler = find_handler(svc, msg->hdr.type);
+    if (handler == NULL)
+	return PJLIB_UTIL_ESTUNNOHANDLER;
+
+    return (*handler->handle_msg)(svc, handle_data, msg);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+
+
