Index: /pjproject/branches/iceproject/pjlib-util/build/pjlib_util.dsp
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/build/pjlib_util.dsp	(revision 912)
+++ /pjproject/branches/iceproject/pjlib-util/build/pjlib_util.dsp	(revision 913)
@@ -130,9 +130,23 @@
 # Begin Source File
 
-SOURCE="..\src\pjlib-util\stun.c"
-# End Source File
-# Begin Source File
-
-SOURCE="..\src\pjlib-util\stun_client.c"
+SOURCE="..\src\pjlib-util\stun_endpoint.c"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pjlib-util\stun_msg.c"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pjlib-util\stun_simple.c"
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pjlib-util\stun_simple_client.c"
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pjlib-util\stun_transaction.c"
 # End Source File
 # Begin Source File
@@ -194,5 +208,17 @@
 # Begin Source File
 
-SOURCE="..\include\pjlib-util\stun.h"
+SOURCE="..\include\pjlib-util\stun_endpoint.h"
+# End Source File
+# Begin Source File
+
+SOURCE="..\include\pjlib-util\stun_msg.h"
+# End Source File
+# Begin Source File
+
+SOURCE="..\include\pjlib-util\stun_simple.h"
+# End Source File
+# Begin Source File
+
+SOURCE="..\include\pjlib-util\stun_transaction.h"
 # End Source File
 # Begin Source File
Index: /pjproject/branches/iceproject/pjlib-util/include/pjlib-util.h
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/include/pjlib-util.h	(revision 912)
+++ /pjproject/branches/iceproject/pjlib-util/include/pjlib-util.h	(revision 913)
@@ -31,5 +31,5 @@
 #include <pjlib-util/resolver.h>
 #include <pjlib-util/scanner.h>
-#include <pjlib-util/stun.h>
+//#include <pjlib-util/stun.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 912)
+++ /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/errno.h	(revision 913)
@@ -94,5 +94,32 @@
  */
 #define PJLIB_UTIL_ESTUNSYMMETRIC   (PJLIB_UTIL_ERRNO_START+11)	/* 320011 */
-
+/**
+ * @hideinitializer
+ * Invalid STUN attribute
+ */
+#define PJLIB_UTIL_ESTUNINATTR	    (PJLIB_UTIL_ERRNO_START+12)	/* 320012 */
+/**
+ * @hideinitializer
+ * Too many STUN attributes.
+ */
+#define PJLIB_UTIL_ESTUNTOOMANYATTR (PJLIB_UTIL_ERRNO_START+13)	/* 320013 */
+/**
+ * @hideinitializer
+ * Unknown STUN attribute.
+ */
+#define PJLIB_UTIL_ESTUNUNKNOWNATTR (PJLIB_UTIL_ERRNO_START+14)	/* 320014 */
+/**
+ * @hideinitializer
+ * Expecting STUN response message.
+ */
+#define PJLIB_UTIL_ESTUNNOTRESPONSE (PJLIB_UTIL_ERRNO_START+15)	/* 320015 */
+/**
+ * @hideinitializer
+ * Transaction ID mismatch.
+ */
+#define PJLIB_UTIL_ESTUNINVALIDID   (PJLIB_UTIL_ERRNO_START+16)	/* 320016 */
+
+
+#define PJ_STATUS_FROM_STUN_CODE(code)	-1
 
 
Index: project/branches/iceproject/pjlib-util/include/pjlib-util/stun.h
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun.h	(revision 912)
+++ 	(revision )
@@ -1,201 +1,0 @@
-/* $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 __PJ_STUN_H__
-#define __PJ_STUN_H__
-
-/**
- * @file stun.h
- * @brief STUN client.
- */
-
-#include <pjlib-util/types.h>
-#include <pj/sock.h>
-
-/**
- * @defgroup PJLIB_UTIL_STUN_CLIENT Mini/Tiny STUN Client
- * @ingroup PJLIB_UTIL
- * @{
- */
-
-PJ_BEGIN_DECL
-
-/**
- * This enumeration describes STUN message types.
- */
-typedef enum pj_stun_msg_type
-{
-    PJ_STUN_BINDING_REQUEST		    = 0x0001,
-    PJ_STUN_BINDING_RESPONSE		    = 0x0101,
-    PJ_STUN_BINDING_ERROR_RESPONSE	    = 0x0111,
-    PJ_STUN_SHARED_SECRET_REQUEST	    = 0x0002,
-    PJ_STUN_SHARED_SECRET_RESPONSE	    = 0x0102,
-    PJ_STUN_SHARED_SECRET_ERROR_RESPONSE    = 0x0112
-} pj_stun_msg_type;
-
-
-/**
- * This enumeration describes STUN attribute types.
- */
-typedef enum pj_stun_attr_type
-{
-    PJ_STUN_ATTR_MAPPED_ADDR = 1,
-    PJ_STUN_ATTR_RESPONSE_ADDR,
-    PJ_STUN_ATTR_CHANGE_REQUEST,
-    PJ_STUN_ATTR_SOURCE_ADDR,
-    PJ_STUN_ATTR_CHANGED_ADDR,
-    PJ_STUN_ATTR_USERNAME,
-    PJ_STUN_ATTR_PASSWORD,
-    PJ_STUN_ATTR_MESSAGE_INTEGRITY,
-    PJ_STUN_ATTR_ERROR_CODE,
-    PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES,
-    PJ_STUN_ATTR_REFLECTED_FORM
-} pj_stun_attr_type;
-
-
-/**
- * This structre describes STUN message header.
- */
-typedef struct pj_stun_msg_hdr
-{
-    pj_uint16_t		type;
-    pj_uint16_t		length;
-    pj_uint32_t		tsx[4];
-} pj_stun_msg_hdr;
-
-
-/**
- * This structre describes STUN attribute header.
- */
-typedef struct pj_stun_attr_hdr
-{
-    pj_uint16_t		type;
-    pj_uint16_t		length;
-} pj_stun_attr_hdr;
-
-
-/**
- * This structre describes STUN MAPPED-ADDR attribute.
- */
-typedef struct pj_stun_mapped_addr_attr
-{
-    pj_stun_attr_hdr	hdr;
-    pj_uint8_t		ignored;
-    pj_uint8_t		family;
-    pj_uint16_t		port;
-    pj_uint32_t		addr;
-} pj_stun_mapped_addr_attr;
-
-typedef pj_stun_mapped_addr_attr pj_stun_response_addr_attr;
-typedef pj_stun_mapped_addr_attr pj_stun_changed_addr_attr;
-typedef pj_stun_mapped_addr_attr pj_stun_src_addr_attr;
-typedef pj_stun_mapped_addr_attr pj_stun_reflected_form_attr;
-
-typedef struct pj_stun_change_request_attr
-{
-    pj_stun_attr_hdr	hdr;
-    pj_uint32_t		value;
-} pj_stun_change_request_attr;
-
-typedef struct pj_stun_username_attr
-{
-    pj_stun_attr_hdr	hdr;
-    pj_uint32_t		value[1];
-} pj_stun_username_attr;
-
-typedef pj_stun_username_attr pj_stun_password_attr;
-
-typedef struct pj_stun_error_code_attr
-{
-    pj_stun_attr_hdr	hdr;
-    pj_uint16_t		ignored;
-    pj_uint8_t		err_class;
-    pj_uint8_t		number;
-    char		reason[4];
-} pj_stun_error_code_attr;
-
-typedef struct pj_stun_msg
-{
-    pj_stun_msg_hdr    *hdr;
-    int			attr_count;
-    pj_stun_attr_hdr   *attr[PJ_STUN_MAX_ATTR];
-} pj_stun_msg;
-
-/* STUN message API (stun.c). */
-
-PJ_DECL(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool, 
-					      void **msg, pj_size_t *len,
-					      pj_uint32_t id_hi,
-					      pj_uint32_t id_lo);
-PJ_DECL(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len,
-				        pj_stun_msg *msg);
-PJ_DECL(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t);
-
-
-/**
- * This is the main function to request the mapped address of local sockets
- * to multiple STUN servers. This function is able to find the mapped 
- * addresses of multiple sockets simultaneously, and for each socket, two
- * requests will be sent to two different STUN servers to see if both servers
- * get the same public address for the same socket. (Note that application can
- * specify the same address for the two servers, but still two requests will
- * be sent for each server).
- *
- * This function will perform necessary retransmissions of the requests if
- * response is not received within a predetermined period. When all responses
- * have been received, the function will compare the mapped addresses returned
- * by the servers, and when both are equal, the address will be returned in
- * \a mapped_addr argument.
- *
- * @param pf		The pool factory where memory will be allocated from.
- * @param sock_cnt	Number of sockets in the socket array.
- * @param sock		Array of local UDP sockets which public addresses are
- *			to be queried from the STUN servers.
- * @param srv1		Host name or IP address string of the first STUN
- *			server.
- * @param port1		The port number of the first STUN server. 
- * @param srv2		Host name or IP address string of the second STUN
- *			server.
- * @param port2		The port number of the second STUN server. 
- * @param mapped_addr	Array to receive the mapped public address of the local
- *			UDP sockets, when the function returns PJ_SUCCESS.
- *
- * @return		This functions returns PJ_SUCCESS if responses are
- *			received from all servers AND all servers returned the
- *			same mapped public address. Otherwise this function may
- *			return one of the following error codes:
- *			- PJLIB_UTIL_ESTUNNOTRESPOND: no respons from servers.
- *			- PJLIB_UTIL_ESTUNSYMMETRIC: different mapped addresses
- *			  are returned by servers.
- *			- etc.
- *
- */
-PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf,
-					      int sock_cnt, pj_sock_t sock[],
-					      const pj_str_t *srv1, int port1,
-					      const pj_str_t *srv2, int port2,
-					      pj_sockaddr_in mapped_addr[]);
-
-PJ_END_DECL
-
-/**
- * @}
- */
-
-#endif	/* __PJ_STUN_H__ */
-
Index: /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_endpoint.h
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_endpoint.h	(revision 913)
+++ /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_endpoint.h	(revision 913)
@@ -0,0 +1,85 @@
+/* $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 __PJ_STUN_ENDPOINT_H__
+#define __PJ_STUN_ENDPOINT_H__
+
+/**
+ * @file stun_msg.h
+ * @brief STUN message components.
+ */
+
+#include <pjlib-util/stun_msg.h>
+
+
+PJ_BEGIN_DECL
+
+
+/* **************************************************************************/
+/**
+ * @defgroup PJLIB_UTIL_STUN_ENDPOINT STUN Endpoint
+ * @brief Management of incoming and outgoing STUN transactions.
+ * @ingroup PJLIB_UTIL_STUN
+ * @{
+ */
+
+/**
+ * Opaque declaration for STUN endpoint. STUN endpoint manages client and
+ * server STUN transactions, and it needs to be initialized before application
+ * can send or receive STUN messages.
+ */
+typedef struct pj_stun_endpoint
+{
+    pj_pool_factory	*pf;
+    pj_ioqueue_t	*ioqueue;
+    pj_timer_heap_t	*timer_heap;
+    unsigned		 options;
+
+    unsigned		 rto_msec;
+
+    pj_pool_t		*pool;
+
+} pj_stun_endpoint;
+
+
+
+/**
+ * Create a STUN endpoint instance.
+ */
+PJ_DECL(pj_status_t) pj_stun_endpt_create(pj_pool_factory *factory,
+					  unsigned options,
+					  pj_ioqueue_t *ioqueue,
+					  pj_timer_heap_t *timer_heap,
+					  pj_stun_endpoint **p_endpt);
+
+/**
+ * Destroy STUN endpoint instance.
+ */
+PJ_DECL(pj_status_t) pj_stun_endpt_destroy(pj_stun_endpoint *endpt);
+
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+
+#endif	/* __PJ_STUN_ENDPOINT_H__ */
+
Index: /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_msg.h
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_msg.h	(revision 913)
+++ /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_msg.h	(revision 913)
@@ -0,0 +1,1217 @@
+/* $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 __PJ_STUN_MSG_H__
+#define __PJ_STUN_MSG_H__
+
+/**
+ * @file stun_msg.h
+ * @brief STUN message components.
+ */
+
+#include <pjlib-util/types.h>
+#include <pj/sock.h>
+
+/**
+ * @defgroup PJLIB_UTIL_STUN Simple Traversal Underneath NAT (STUN)
+ * @ingroup PJLIB_UTIL
+ */
+
+PJ_BEGIN_DECL
+
+
+/* **************************************************************************/
+/**
+ * @defgroup PJLIB_UTIL_STUN_MSG STUN Message Representation and Parsing
+ * @brief Low-level representation and parsing of STUN messages.
+ * @ingroup PJLIB_UTIL_STUN
+ * @{
+ */
+
+
+/**
+ * The default initial STUN round-trip time estimation (the RTO value
+ * in RFC 3489-bis), in miliseconds. 
+ * This value is used to control the STUN request 
+ * retransmit time. The initial value of retransmission interval 
+ * would be set to this value, and will be doubled after each
+ * retransmission.
+ */
+#ifndef PJ_STUN_RTO_VALUE
+#   define PJ_STUN_RTO_VALUE		    100
+#endif
+
+
+/**
+ * The STUN transaction timeout value, in miliseconds.
+ * After the last retransmission is sent and if no response is received 
+ * after this time, the STUN transaction will be considered to have failed.
+ *
+ * The default value is 1600 miliseconds (as per RFC 3489-bis).
+ */
+#ifndef PJ_STUN_TIMEOUT_VALUE
+#   define PJ_STUN_TIMEOUT_VALUE	    1600
+#endif
+
+
+/**
+ * Maximum number of STUN retransmission count.
+ *
+ * Default: 7 (as per RFC 3489-bis)
+ */
+#ifndef PJ_STUN_MAX_RETRANSMIT_COUNT
+#   define PJ_STUN_MAX_RETRANSMIT_COUNT	    7
+#endif
+
+
+/**
+ * Maximum size of STUN message.
+ */
+#ifndef PJ_STUN_MAX_PKT_LEN
+#   define PJ_STUN_MAX_PKT_LEN		    512
+#endif
+
+
+/**
+ * Default STUN port as defined by RFC 3489.
+ */
+#define PJ_STUN_PORT			    3478
+
+/**
+ * STUN magic cookie.
+ */
+#define PJ_STUN_MAGIC			    0x2112A442
+
+
+/**
+ * STUN method constants.
+ */
+enum pj_stun_method_e
+{
+    /**
+     * STUN Binding method as defined by RFC 3489-bis.
+     */
+    PJ_STUN_BINDING_METHOD		    = 1,
+
+    /**
+     * STUN Shared Secret method as defined by RFC 3489-bis.
+     */
+    PJ_STUN_SHARED_SECRET_METHOD	    = 2,
+
+    /**
+     * STUN/TURN Allocate method as defined by draft-ietf-behave-turn
+     */
+    PJ_STUN_ALLOCATE_METHOD		    = 3,
+
+    /**
+     * STUN/TURN Send Indication as defined by draft-ietf-behave-turn
+     */
+    PJ_STUN_SEND_INDICATION_METHOD	    = 4,
+
+    /**
+     * STUN/TURN Data Indication as defined by draft-ietf-behave-turn
+     */
+    PJ_STUN_DATA_INDICATION_METHOD	    = 5,
+
+    /**
+     * STUN/TURN Set Active Destination as defined by draft-ietf-behave-turn
+     */
+    PJ_STUN_SET_ACTIVE_DESTINATION_METHOD   = 6,
+
+    /**
+     * STUN/TURN Connect method as defined by draft-ietf-behave-turn
+     */
+    PJ_STUN_CONNECT_METHOD		    = 7,
+
+    /**
+     * STUN/TURN Connect Status indication method.
+     */
+    PJ_STUN_CONNECT_STATUS_METHOD	    = 8
+};
+
+
+/**
+ * Retrieve the STUN method from the message-type field of the STUN
+ * message.
+ */
+#define PJ_STUN_GET_METHOD(msg_type)	((msg_type) & 0x000F)
+
+
+/**
+ * STUN message classes constants.
+ */
+enum pj_stun_msg_class_e
+{
+    /**
+     * This specifies that the message type is a STUN request message.
+     */
+    PJ_STUN_REQUEST_CLASS	    = 0,
+
+    /**
+     * This specifies that the message type is a STUN indication message.
+     */
+    PJ_STUN_INDICATION_CLASS	    = 1,
+
+    /**
+     * This specifies that the message type is a STUN successful response.
+     */
+    PJ_STUN_SUCCESS_CLASS	    = 2,
+
+    /**
+     * This specifies that the message type is a STUN error response.
+     */
+    PJ_STUN_ERROR_CLASS		    = 3
+};
+
+
+/**
+ * Determine if the message type is a request.
+ */
+#define PJ_STUN_IS_REQUEST(msg_type)	(((msg_type) & 0x0F00) == 0x0000)
+
+
+/**
+ * Determine if the message type is a response.
+ */
+#define PJ_STUN_IS_RESPONSE(msg_type)	(((msg_type) & 0x0F00) == 0x0100)
+
+
+/**
+ * Determine if the message type is an error response.
+ */
+#define PJ_STUN_IS_ERROR_RESPONSE(msg_type) (((msg_type) & 0x0FF0) == 0x0110)
+
+
+#if 0
+/**
+ * Determine if the message type is an indication message.
+ */
+#define PJ_STUN_IS_INDICATION(msg_type)	(((msg_type) & 0x0FF0) == 0x0010)
+#endif
+
+
+/**
+ * This enumeration describes STUN message types.
+ */
+typedef enum pj_stun_msg_type
+{
+    /**
+     * STUN BINDING request.
+     */
+    PJ_STUN_BINDING_REQUEST		    = 0x0001,
+
+    /**
+     * Successful response to STUN BINDING-REQUEST.
+     */
+    PJ_STUN_BINDING_RESPONSE		    = 0x0101,
+
+    /**
+     * Error response to STUN BINDING-REQUEST.
+     */
+    PJ_STUN_BINDING_ERROR_RESPONSE	    = 0x0111,
+
+    /**
+     * STUN SHARED-SECRET reqeust.
+     */
+    PJ_STUN_SHARED_SECRET_REQUEST	    = 0x0002,
+
+    /**
+     * Successful response to STUN SHARED-SECRET reqeust.
+     */
+    PJ_STUN_SHARED_SECRET_RESPONSE	    = 0x0102,
+
+    /**
+     * Error response to STUN SHARED-SECRET reqeust.
+     */
+    PJ_STUN_SHARED_SECRET_ERROR_RESPONSE    = 0x0112,
+
+    /**
+     * STUN/TURN Allocate Request
+     */
+    PJ_STUN_ALLOCATE_REQUEST		    = 0x0003,
+
+    /**
+     * Successful response to STUN/TURN Allocate Request
+     */
+    PJ_STUN_ALLOCATE_RESPONSE		    = 0x0103,
+
+    /**
+     * Failure response to STUN/TURN Allocate Request
+     */
+    PJ_STUN_ALLOCATE_ERROR_RESPONSE	    = 0x0113,
+
+    /**
+     * STUN/TURN Send Indication
+     */
+    PJ_STUN_SEND_INDICATION		    = 0x0004,
+
+    /**
+     * STUN/TURN Data Indication
+     */
+    PJ_STUN_DATA_INDICATION		    = 0x0115,
+
+    /**
+     * STUN/TURN Set Active Destination Request
+     */
+    PJ_STUN_SET_ACTIVE_DESTINATION_REQUEST  = 0x0006,
+
+    /** 
+     * STUN/TURN Set Active Destination Response
+     */
+    PJ_STUN_SET_ACTIVE_DESTINATION_RESPONSE = 0x0106,
+
+    /**
+     * STUN/TURN Set Active Destination Error Response
+     */
+    PJ_STUN_SET_ACTIVE_DESTINATION_ERROR_RESPONSE = 0x0116,
+
+    /**
+     * STUN/TURN Connect Request
+     */
+    PJ_STUN_CONNECT_REQUEST		    = 0x0007,
+
+    /**
+     * STUN/TURN Connect Response
+     */
+    PJ_STUN_CONNECT_RESPONSE		    = 0x0107,
+
+    /**
+     * STUN/TURN Connect Error Response
+     */
+    PJ_STUN_CONNECT_ERROR_RESPONSE	    = 0x0117,
+
+    /**
+     * STUN/TURN Connect Status Indication
+     */
+    PJ_STUN_CONNECT_STATUS_INDICATION	    = 0x0118
+
+
+} pj_stun_msg_type;
+
+
+
+/**
+ * This enumeration describes STUN attribute types.
+ */
+typedef enum pj_stun_attr_type
+{
+    PJ_STUN_ATTR_MAPPED_ADDR	    = 0x0001,/**< MAPPED-ADDRESS.	    */
+    PJ_STUN_ATTR_RESPONSE_ADDR	    = 0x0002,/**< RESPONSE-ADDRESS (deprcatd)*/
+    PJ_STUN_ATTR_CHANGE_REQUEST	    = 0x0003,/**< CHANGE-REQUEST (deprecated)*/
+    PJ_STUN_ATTR_SOURCE_ADDR	    = 0x0004,/**< SOURCE-ADDRESS (deprecated)*/
+    PJ_STUN_ATTR_CHANGED_ADDR	    = 0x0005,/**< CHANGED-ADDRESS (deprecatd)*/
+    PJ_STUN_ATTR_USERNAME	    = 0x0006,/**< USERNAME attribute.	    */
+    PJ_STUN_ATTR_PASSWORD	    = 0x0007,/**< PASSWORD attribute.	    */
+    PJ_STUN_ATTR_MESSAGE_INTEGRITY  = 0x0008,/**< MESSAGE-INTEGRITY.	    */
+    PJ_STUN_ATTR_ERROR_CODE	    = 0x0009,/**< ERROR-CODE.		    */
+    PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES = 0x000A,/**< UNKNOWN-ATTRIBUTES.	    */
+    PJ_STUN_ATTR_REFLECTED_FROM	    = 0x000B,/**< REFLECTED-FROM (deprecatd)*/
+    PJ_STUN_ATTR_LIFETIME	    = 0x000D,/**< LIFETIME attribute.	    */
+    PJ_STUN_ATTR_BANDWIDTH	    = 0x0010,/**< BANDWIDTH attribute	    */
+    PJ_STUN_ATTR_REMOTE_ADDRESS	    = 0x0012,/**< REMOTE-ADDRESS attribute  */
+    PJ_STUN_ATTR_DATA		    = 0x0013,/**< DATA attribute.	    */
+    PJ_STUN_ATTR_REALM		    = 0x0014,/**< REALM attribute.	    */
+    PJ_STUN_ATTR_NONCE		    = 0x0015,/**< NONCE attribute.	    */
+    PJ_STUN_ATTR_RELAY_ADDRESS	    = 0x0016,/**< RELAY-ADDRESS attribute.  */
+    PJ_STUN_ATTR_REQUESTED_PORT_PROPS=0x0018,/**< REQUESTED-PORT-PROPS	    */
+    PJ_STUN_ATTR_REQUESTED_TRANSPORT= 0x0019,/**< REQUESTED-TRANSPORT	    */
+    PJ_STUN_ATTR_XOR_MAPPED_ADDRESS = 0x0020,/**< XOR-MAPPED-ADDRESS	    */
+    PJ_STUN_ATTR_TIMER_VAL	    = 0x0021,/**< TIMER-VAL attribute.	    */
+    PJ_STUN_ATTR_REQUESTED_IP	    = 0x0022,/**< REQUESTED-IP attribute    */
+    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_type;
+
+
+/**
+ * STUN error codes.
+ */
+typedef enum pj_stun_status
+{
+    PJ_STUN_STATUS_TRY_ALTERNATE	    = 300,  /**< Try Alternate	    */
+    PJ_STUN_STATUS_BAD_REQUEST		    = 400,  /**< Bad Request	    */
+    PJ_STUN_STATUS_UNAUTHORIZED		    = 401,  /**< Unauthorized	    */
+    PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE	    = 420,  /**< Unknown Attribute  */
+    PJ_STUN_STATUS_STALE_CREDENTIALS	    = 430,  /**< Stale Credentials  */
+    PJ_STUN_STATUS_INTEGRITY_CHECK_FAILURE  = 431,  /**< Integrity Chk Fail */
+    PJ_STUN_STATUS_MISSING_USERNAME	    = 432,  /**< Missing Username   */
+    PJ_STUN_STATUS_USE_TLS		    = 433,  /**< Use TLS	    */
+    PJ_STUN_STATUS_MISSING_REALM	    = 434,  /**< Missing Realm	    */
+    PJ_STUN_STATUS_MISSING_NONCE	    = 435,  /**< Missing Nonce	    */
+    PJ_STUN_STATUS_UNKNOWN_USERNAME	    = 436,  /**< Unknown Username   */
+    PJ_STUN_STATUS_NO_BINDING		    = 437,  /**< No Binding.	    */
+    PJ_STUN_STATUS_STALE_NONCE		    = 438,  /**< Stale Nonce	    */
+    PJ_STUN_STATUS_TRANSITIONING	    = 439,  /**< Transitioning.	    */
+    PJ_STUN_STATUS_WRONG_USERNAME	    = 441,  /**< Wrong Username.    */
+    PJ_STUN_STATUS_UNSUPP_TRANSPORT_PROTO   = 442,  /**< Unsupported Transport Protocol */
+    PJ_STUN_STATUS_INVALID_IP_ADDR	    = 443,  /**< Invalid IP Address */
+    PJ_STUN_STATUS_INVALID_PORT		    = 444,  /**< Invalid Port	    */
+    PJ_STUN_STATUS_OPER_TCP_ONLY	    = 445,  /**< Operation for TCP Only */
+    PJ_STUN_STATUS_CONNECTION_FAILURE	    = 446,  /**< Connection Failure */
+    PJ_STUN_STATUS_CONNECTION_TIMEOUT	    = 447,  /**< Connection Timeout */
+    PJ_STUN_STATUS_SERVER_ERROR		    = 500,  /**< Server Error	    */
+    PJ_STUN_STATUS_GLOBAL_FAILURE	    = 600   /**< Global Failure	    */
+} pj_stun_status;
+
+
+/**
+ * This structure describes STUN message header. A STUN message has the 
+ * following format:
+ *
+ * \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
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |0 0|     STUN Message Type     |         Message Length        |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                         Magic Cookie                          |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                                Transaction ID
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                                                                       |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   \endverbatim
+ */
+typedef struct pj_stun_msg_hdr
+{
+    /**
+     * STUN message type, which the first two bits must be zeroes.
+     */
+    pj_uint16_t		type;
+
+    /**
+     * The message length is the size, in bytes, of the message not
+     * including the 20 byte STUN header.
+     */
+    pj_uint16_t		length;
+
+    /**
+     * The magic cookie is a fixed value, 0x2112A442 (PJ_STUN_MAGIC constant).
+     * In the previous version of this specification [15] this field was part 
+     * of the transaction ID.
+     */
+    pj_uint32_t		magic;
+
+    /**
+     * The transaction ID is a 96 bit identifier.  STUN transactions are
+     * identified by their unique 96-bit transaction ID.  For request/
+     * response transactions, the transaction ID is chosen by the STUN
+     * client and MUST be unique for each new STUN transaction generated by
+     * that STUN client.  The transaction ID MUST be uniformly and randomly
+     * distributed between 0 and 2**96 - 1. 
+     */
+    pj_uint8_t		tsx_id[12];
+
+} pj_stun_msg_hdr;
+
+
+/**
+ * This structre describes STUN attribute header. Each attribute is
+ * TLV encoded, with a 16 bit type, 16 bit length, and variable value.
+ * Each STUN attribute ends on a 32 bit boundary:
+ *
+ * \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
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |         Type                  |            Length             |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                             Value                 ....        |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   \endverbatim
+ */
+typedef struct pj_stun_attr_hdr
+{
+    /**
+     * STUN attribute type.
+     */
+    pj_uint16_t		type;
+
+    /**
+     * The Length refers to the length of the actual useful content of the
+     * Value portion of the attribute, measured in bytes. The value
+     * in the Length field refers to the length of the Value part of the
+     * attribute prior to padding - i.e., the useful content.
+     */
+    pj_uint16_t		length;
+
+} pj_stun_attr_hdr;
+
+
+/**
+ * This structure describes STUN generic IP address attribute, used for
+ * example to represent STUN MAPPED-ADDRESS attribute.
+ *
+ * The generic IP address attribute indicates the transport address.
+ * It consists of an eight bit address family, and a sixteen bit port,
+ * followed by a fixed length value representing the IP address.  If the
+ * address family is IPv4, the address is 32 bits, in network byte
+ * order.  If the address family is IPv6, the address is 128 bits in
+ * network byte order.
+ *
+ * The format of the generic IP address attribute is:
+ *
+ * \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
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |x x x x x x x x|    Family     |           Port                |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                   Address  (variable)
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+   \endverbatim
+ */
+typedef struct pj_stun_generic_ip_addr_attr
+{
+    /**
+     * Standard STUN attribute header.
+     */
+    pj_stun_attr_hdr	hdr;
+
+    /**
+     * 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.
+     */
+    union {
+
+	/** IPv4 address. */
+	pj_uint32_t	ipv4;
+
+	/** IPv6 address. */
+	char		ipv6[1];
+
+    } addr;
+
+} pj_stun_generic_ip_addr_attr;
+
+
+/**
+ * This structure represents generic STUN string attributes, such as STUN
+ * USERNAME, PASSWORD, SERVER, REALM, and NONCE attributes. Note that for REALM and
+ * NONCE attributes, the text MUST be quoted with.
+ */
+typedef struct pj_stun_generic_string_attr
+{
+    /**
+     * Standard STUN attribute header.
+     */
+    pj_stun_attr_hdr	hdr;
+
+    /**
+     * The string value.
+     */
+    pj_str_t		value;
+
+} pj_stun_generic_string_attr;
+
+
+/**
+ * This structure represents a generic STUN attributes with 32bit (unsigned)
+ * integer value, such as STUN FINGERPRINT and REFRESH-INTERVAL attributes.
+ */
+typedef struct pj_stun_generic_uint_attr
+{
+    /**
+     * Standard STUN attribute header.
+     */
+    pj_stun_attr_hdr	hdr;
+    
+    /**
+     * The 32bit value.
+     */
+    pj_uint32_t		value;
+
+} pj_stun_generic_uint_attr;
+
+
+/**
+ * This structure represents generic STUN attributes to hold a raw binary
+ * data.
+ */
+typedef struct pj_stun_binary_attr
+{
+    /**
+     * Standard STUN attribute header.
+     */
+    pj_stun_attr_hdr	hdr;
+    
+    /**
+     * Length of the data.
+     */
+    unsigned		length;
+
+    /**
+     * The raw data.
+     */
+    char	       *data;
+
+} pj_stun_binary_attr;
+
+
+/**
+ * This structure describes STUN MESSAGE-INTEGRITY attribute.
+ * The MESSAGE-INTEGRITY attribute contains an HMAC-SHA1 [10] of the
+ * STUN message.  The MESSAGE-INTEGRITY attribute can be present in any
+ * STUN message type.  Since it uses the SHA1 hash, the HMAC will be 20
+ * bytes.
+ */
+typedef struct pj_stun_msg_integrity_attr
+{
+    /**
+     * Standard STUN attribute header.
+     */
+    pj_stun_attr_hdr	hdr;
+
+    /**
+     * The 20 bytes hmac value.
+     */
+    pj_uint8_t		hmac[20];
+
+} pj_stun_msg_integrity_attr;
+
+
+/**
+ * This structure describes STUN FINGERPRINT attribute. The FINGERPRINT 
+ * attribute can be present in all STUN messages.  It is computed as 
+ * the CRC-32 of the STUN message up to (but excluding) the FINGERPRINT 
+ * attribute itself, xor-d with the 32 bit value 0x5354554e
+ */
+typedef struct pj_stun_generic_uint_attr pj_stun_fingerprint_attr;
+
+
+/**
+ * This structure represents STUN ERROR-CODE attribute. The ERROR-CODE 
+ * attribute is present in the Binding Error Response and Shared Secret 
+ * Error Response.  It is a numeric value in the range of 100 to 699 
+ * plus a textual reason phrase encoded in UTF-8
+ *
+ * \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
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                   0                     |Class|     Number    |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |      Reason Phrase (variable)                                ..
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ \endverbatim
+ */
+typedef struct pj_stun_error_code_attr
+{
+    /**
+     * Standard STUN attribute header.
+     */
+    pj_stun_attr_hdr	hdr;
+
+    /**
+     * The value must be zero.
+     */
+    pj_uint16_t		zero;
+
+    /**
+     * Error class (1-6).
+     */
+    pj_uint8_t		err_class;
+
+    /**
+     * Error number is the error number modulo 100.
+     */
+    pj_uint8_t		number;
+
+    /**
+     * The reason phrase.
+     */
+    pj_str_t		reason;
+
+} pj_stun_error_code_attr;
+
+
+/**
+ * This describes STUN REALM attribute.
+ * The REALM attribute is present in requests and responses.  It
+ * contains text which meets the grammar for "realm" as described in RFC
+ * 3261 [11], and will thus contain a quoted string (including the
+ * quotes).
+ */
+typedef struct pj_stun_generic_string_attr pj_stun_realm_attr;
+
+
+/**
+ * This describes STUN NONCE attribute. 
+ * The NONCE attribute is present in requests and in error responses.
+ * It contains a sequence of qdtext or quoted-pair, which are defined in
+ * RFC 3261 [11].  See RFC 2617 [7] for guidance on selection of nonce
+ * values in a server.
+ */
+typedef struct pj_stun_generic_string_attr pj_stun_nonce_attr;
+
+
+/**
+ * This describes STUN UNKNOWN-ATTRIBUTES attribute.
+ * The UNKNOWN-ATTRIBUTES attribute is present only in an error response
+ * when the response code in the ERROR-CODE attribute is 420.
+ * The attribute contains a list of 16 bit values, each of which
+ * represents an attribute type that was not understood by the server.
+ * If the number of unknown attributes is an odd number, one of the
+ * attributes MUST be repeated in the list, so that the total length of
+ * the list is a multiple of 4 bytes.
+ */
+typedef struct pj_stun_unknown_attr
+{
+    /**
+     * Standard STUN attribute header.
+     */
+    pj_stun_attr_hdr	hdr;
+
+    /**
+     * Number of unknown attributes in the array.
+     */
+    unsigned		attr_count;
+
+    /**
+     * Array of unknown attribute IDs.
+     */
+    pj_uint16_t	        attrs[PJ_STUN_MAX_ATTR];
+
+} pj_stun_unknown_attr;
+
+
+/**
+ * This structure describes STUN MAPPED-ADDRESS attribute.
+ * The MAPPED-ADDRESS attribute indicates the mapped transport address.
+ */
+typedef struct pj_stun_generic_ip_addr_attr pj_stun_mapped_addr_attr;
+
+
+/**
+ * This describes STUN XOR-MAPPED-ADDRESS attribute (which has the same
+ * format as STUN MAPPED-ADDRESS attribute).
+ * The XOR-MAPPED-ADDRESS attribute is present in responses.  It
+ * provides the same information that would present in the MAPPED-
+ * ADDRESS attribute but because the NAT's public IP address is
+ * obfuscated through the XOR function, STUN messages are able to pass
+ * through NATs which would otherwise interfere with STUN.
+ */
+typedef struct pj_stun_generic_ip_addr_attr pj_stun_xor_mapped_addr_attr;
+
+
+/**
+ * This describes STUN SERVER attribute.
+ * The server attribute contains a textual description of the software
+ * being used by the server, including manufacturer and version number.
+ * The attribute has no impact on operation of the protocol, and serves
+ * only as a tool for diagnostic and debugging purposes.  The value of
+ * SERVER is variable length.
+ */
+typedef struct pj_stun_generic_string_attr pj_stun_server_attr;
+
+
+/**
+ * This describes STUN ALTERNATE-SERVER attribute.
+ * The alternate server represents an alternate transport address for a
+ * different STUN server to try.  It is encoded in the same way as
+ * MAPPED-ADDRESS.
+ */
+typedef struct pj_stun_generic_ip_addr_attr pj_stun_alt_server_attr;
+
+
+/**
+ * This describes STUN REFRESH-INTERVAL attribute.
+ * The REFRESH-INTERVAL indicates the number of milliseconds that the
+ * server suggests the client should use between refreshes of the NAT
+ * bindings between the client and server.
+ */
+typedef struct pj_stun_generic_uint_attr pj_stun_refresh_interval_attr;
+
+
+/**
+ * This structure describes STUN RESPONSE-ADDRESS attribute.
+ * The RESPONSE-ADDRESS attribute indicates where the response to a
+ * Binding Request should be sent.  Its syntax is identical to MAPPED-
+ * ADDRESS.
+ *
+ * Note that the usage of this attribute has been deprecated by the 
+ * RFC 3489-bis standard.
+ */
+typedef struct pj_stun_generic_ip_addr_attr pj_stun_response_addr_attr;
+
+
+/**
+ * This structure describes STUN CHANGED-ADDRESS attribute.
+ * The CHANGED-ADDRESS attribute indicates the IP address and port where
+ * responses would have been sent from if the "change IP" and "change
+ * port" flags had been set in the CHANGE-REQUEST attribute of the
+ * Binding Request.  The attribute is always present in a Binding
+ * Response, independent of the value of the flags.  Its syntax is
+ * identical to MAPPED-ADDRESS.
+ *
+ * Note that the usage of this attribute has been deprecated by the 
+ * RFC 3489-bis standard.
+ */
+typedef struct pj_stun_generic_ip_addr_attr pj_stun_changed_addr_attr;
+
+
+/**
+ * This structure describes STUN CHANGE-REQUEST attribute.
+ * The CHANGE-REQUEST attribute is used by the client to request that
+ * the server use a different address and/or port when sending the
+ * response. 
+ *
+ * Bit 29 of the value is the "change IP" flag.  If true, it requests 
+ * the server to send the Binding Response with a different IP address 
+ * than the one the Binding Request was received on.
+ *
+ * Bit 30 of the value is the "change port" flag.  If true, it requests 
+ * the server to send the Binding Response with a different port than 
+ * the one the Binding Request was received on.
+ *
+ * Note that the usage of this attribute has been deprecated by the 
+ * RFC 3489-bis standard.
+ */
+typedef struct pj_stun_generic_uint_attr pj_stun_change_request_attr;
+
+/**
+ * This structure describes STUN SOURCE-ADDRESS attribute.
+ * The SOURCE-ADDRESS attribute is present in Binding Responses.  It
+ * indicates the source IP address and port that the server is sending
+ * the response from.  Its syntax is identical to that of MAPPED-
+ * ADDRESS.
+ *
+ * Note that the usage of this attribute has been deprecated by the 
+ * RFC 3489-bis standard.
+ */
+typedef struct pj_stun_generic_ip_addr_attr pj_stun_src_addr_attr;
+
+
+/**
+ * This describes the STUN REFLECTED-FROM attribute.
+ * The REFLECTED-FROM attribute is present only in Binding Responses,
+ * when the Binding Request contained a RESPONSE-ADDRESS attribute.  The
+ * attribute contains the identity (in terms of IP address) of the
+ * source where the request came from.  Its purpose is to provide
+ * traceability, so that a STUN server cannot be used as a reflector for
+ * denial-of-service attacks.
+ */
+typedef struct pj_stun_generic_ip_addr_attr pj_stun_reflected_from_attr;
+
+
+/**
+ * This describes STUN USERNAME attribute.
+ * The USERNAME attribute is used for message integrity.  It identifies
+ * the shared secret used in the message integrity check.  Consequently,
+ * the USERNAME MUST be included in any request that contains the
+ * MESSAGE-INTEGRITY attribute.
+ */
+typedef struct pj_stun_generic_string_attr pj_stun_username_attr;
+
+
+/**
+ * This describes STUN PASSWORD attribute.
+ * If the message type is Shared Secret Response it MUST include the
+ * PASSWORD attribute.
+ */
+typedef struct pj_stun_generic_string_attr pj_stun_password_attr;
+
+
+/**
+ * This describes STUN LIFETIME attribute.
+ * The lifetime attribute represents the duration for which the server
+ * will maintain an allocation in the absence of data traffic either
+ * from or to the client.  It is a 32 bit value representing the number
+ * of seconds remaining until expiration.
+ */
+typedef struct pj_stun_generic_uint_attr pj_stun_lifetime_attr;
+
+
+/**
+ * This describes STUN BANDWIDTH attribute.
+ * The bandwidth attribute represents the peak bandwidth, measured in
+ * kbits per second, that the client expects to use on the binding.  The
+ * value represents the sum in the receive and send directions.
+ */
+typedef struct pj_stun_generic_uint_attr pj_stun_bandwidth_attr;
+
+
+/**
+ * This describes the STUN REMOTE-ADDRESS attribute.
+ * The REMOTE-ADDRESS specifies the address and port of the peer as seen
+ * from the STUN relay server.
+ */
+typedef struct pj_stun_generic_ip_addr_attr pj_stun_remote_addr_attr;
+
+
+/**
+ * This describes the STUN DATA attribute.
+ * The DATA attribute is present in Send Indications and Data
+ * Indications.  It contains raw payload data that is to be sent (in the
+ * case of a Send Request) or was received (in the case of a Data
+ * Indication)..
+ */
+typedef struct pj_stun_binary_attr pj_stun_data_attr;
+
+
+/**
+ * This describes the STUN RELAY-ADDRESS attribute.
+ * The RELAY-ADDRESS is present in Allocate responses.  It specifies the
+ * address and port that the server allocated to the client.
+ */
+typedef struct pj_stun_generic_ip_addr_attr pj_stun_relay_addr_attr;
+
+
+/**
+ * This describes the STUN REQUESTED-PORT-PROPS attribute.
+ * This attribute allows the client to request certain properties for
+ * the port that is allocated by the server.  The attribute can be used
+ * with any transport protocol that has the notion of a 16 bit port
+ * space (including TCP and UDP).  The attribute is 32 bits long.  Its
+ * format is:
+
+ \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
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                           Reserved = 0                  |B| A |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ \endverbatim  
+ */
+typedef struct pj_stun_generic_uint_attr pj_stun_requested_port_props_attr;
+
+
+/**
+ * This describes the STUN REQUESTED-TRANSPORT attribute.
+ * This attribute is used by the client to request a specific transport
+ * protocol for the allocated transport address.  It is a 32 bit
+ * unsigned integer.  Its values are: 0x0000 for UDP and 0x0000 for TCP.
+ */
+typedef struct pj_stun_generic_uint_attr pj_stun_requested_transport_attr;
+
+
+/**
+ * This describes the STUN REQUESTED-IP attribute.
+ * The REQUESTED-IP attribute is used by the client to request that a
+ * specific IP address be allocated to it.
+ */
+typedef struct pj_stun_generic_ip_addr_attr pj_stun_requested_ip_attr;
+
+
+/**
+ * This describes the STUN TIMER-VAL attribute.
+ * The TIMER-VAL attribute is used only in conjunction with the Set
+ * Active Destination response.  It conveys from the server, to the
+ * client, the value of the timer used in the server state machine.
+ */
+typedef struct pj_stun_generic_uint_attr pj_stun_timer_val_attr;
+
+
+/**
+ * This structure describes a parsed STUN message. All integral fields
+ * in this structure (including IP addresses) will be in the host
+ * byte order.
+ */
+typedef struct pj_stun_msg
+{
+    /**
+     * STUN message header.
+     */
+    pj_stun_msg_hdr     hdr;
+
+    /**
+     * Number of attributes in the STUN message.
+     */
+    unsigned		attr_count;
+
+    /**
+     * Array of STUN attributes.
+     */
+    pj_stun_attr_hdr   *attr[PJ_STUN_MAX_ATTR];
+
+} pj_stun_msg;
+
+
+
+/**
+ * Get STUN standard reason phrase for the specified error code.
+ *
+ * @param err_code	The STUN error code.
+ *
+ * @return		The STUN error reason phrase.
+ */
+PJ_DECL(pj_str_t) pj_stun_get_err_reason(int err_code);
+
+
+/**
+ * Create a blank STUN message.
+ *
+ * @param pool		Pool to create the STUN message.
+ * @param msg_type	The 14bit message type.
+ * @param tsx_id	Optional transaction ID, or NULL to let the
+ *			function generates a random transaction ID.
+ * @param p_msg		Pointer to receive the message.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pj_stun_msg_create(pj_pool_t *pool,
+					unsigned msg_type,
+					const pj_uint8_t tsx_id[12],
+					pj_stun_msg **p_msg);
+
+/**
+ * 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[]);
+
+/**
+ * Print the message structure to a buffer.
+ *
+ * @param msg		The message to be printed to a contiguous buffer.
+ * @param pkt_buf	The buffer.
+ * @param buf_size	Size of the buffer.
+ * @param options	Options.
+ * @param p_msg_len	Upon return, it will be filed with the size of 
+ *			the packet in bytes, or negative value on error.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg,
+				        pj_uint8_t *pkt_buf,
+				        unsigned buf_size,
+				        unsigned options,
+				        unsigned *p_msg_len);
+
+
+/**
+ * Find STUN attribute in the STUN message, starting from the specified
+ * index.
+ *
+ * @param msg		The STUN message.
+ * @param attr_type	The attribute type to be found.
+ * @param start_index	The start index of the attribute in the message.
+ *			Specify zero to start searching from the first
+ *			attribute.
+ *
+ * @return		The attribute instance, or NULL if it cannot be
+ *			found.
+ */
+PJ_DECL(pj_stun_attr_hdr*) pj_stun_msg_find_attr(const pj_stun_msg *msg,
+						 int attr_type,
+						 unsigned start_index);
+
+
+/**
+ * Create a generic STUN IP address attribute for IPv4 address. Note that
+ * the port and ip_addr parameters are in host byte order.
+ *
+ * @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 p_attr	Pointer to receive the attribute.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error code.
+ */
+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,
+				    pj_stun_generic_ip_addr_attr **p_attr);
+
+
+/**
+ * Create a STUN generic string attribute.
+ *
+ * @param pool		The pool to allocate memory from.
+ * @param value		The string value to be assigned to the attribute.
+ * @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_generic_string_attr_create(pj_pool_t *pool,
+				   int attr_type,
+				   const pj_str_t *value,
+				   pj_stun_generic_string_attr **p_attr);
+
+
+/**
+ * Create a STUN generic 32bit value attribute.
+ *
+ * @param pool		The pool to allocate memory from.
+ * @param attr_type	Attribute type, from #pj_stun_attr_type.
+ * @param value		The 32bit value to be assigned to the attribute.
+ * @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_generic_uint_attr_create(pj_pool_t *pool,
+				 int attr_type,
+				 pj_uint32_t value,
+				 pj_stun_generic_uint_attr **p_attr);
+
+
+/**
+ * Create a STUN MESSAGE-INTEGRITY attribute.
+ *
+ * @param pool		The pool to allocate memory from.
+ * @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_integrity_attr_create(pj_pool_t *pool,
+				  pj_stun_msg_integrity_attr **p_attr);
+
+
+/**
+ * Create a STUN ERROR-CODE attribute.
+ *
+ * @param pool		The pool to allocate memory from.
+ * @param err_code	STUN error code.
+ * @param err_reason	Optional STUN error reason. If NULL is given, the
+ *			standard error reason will be given.
+ * @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_error_code_attr_create(pj_pool_t *pool,
+			       int err_code,
+			       const pj_str_t *err_reason,
+			       pj_stun_error_code_attr **p_attr);
+
+
+/**
+ * Create an empty instance of STUN UNKNOWN-ATTRIBUTES attribute.
+ *
+ * @param pool		The pool to allocate memory from.
+ * @param attr_cnt	Number of attributes in the array (can be zero).
+ * @param attr		Optional array of attributes.
+ * @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_unknown_attr_create(pj_pool_t *pool,
+			    unsigned attr_cnt,
+			    pj_uint16_t attr[],
+			    pj_stun_unknown_attr **p_attr);
+
+
+/**
+ * Create a blank binary attribute.
+ *
+ * @param pool		The pool to allocate memory from.
+ * @param attr_type	The attribute type.
+ * @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_binary_attr_create(pj_pool_t *pool,
+			   int attr_type,
+			   pj_stun_binary_attr **p_attr);
+
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+
+#endif	/* __PJ_STUN_MSG_H__ */
+
Index: /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_simple.h
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_simple.h	(revision 913)
+++ /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_simple.h	(revision 913)
@@ -0,0 +1,201 @@
+/* $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 __PJ_STUN_H__
+#define __PJ_STUN_H__
+
+/**
+ * @file stun.h
+ * @brief STUN client.
+ */
+
+#include <pjlib-util/types.h>
+#include <pj/sock.h>
+
+/**
+ * @defgroup PJLIB_UTIL_STUN_CLIENT Mini/Tiny STUN Client
+ * @ingroup PJLIB_UTIL
+ * @{
+ */
+
+PJ_BEGIN_DECL
+
+/**
+ * This enumeration describes STUN message types.
+ */
+typedef enum pj_stun_msg_type
+{
+    PJ_STUN_BINDING_REQUEST		    = 0x0001,
+    PJ_STUN_BINDING_RESPONSE		    = 0x0101,
+    PJ_STUN_BINDING_ERROR_RESPONSE	    = 0x0111,
+    PJ_STUN_SHARED_SECRET_REQUEST	    = 0x0002,
+    PJ_STUN_SHARED_SECRET_RESPONSE	    = 0x0102,
+    PJ_STUN_SHARED_SECRET_ERROR_RESPONSE    = 0x0112
+} pj_stun_msg_type;
+
+
+/**
+ * This enumeration describes STUN attribute types.
+ */
+typedef enum pj_stun_attr_type
+{
+    PJ_STUN_ATTR_MAPPED_ADDR = 1,
+    PJ_STUN_ATTR_RESPONSE_ADDR,
+    PJ_STUN_ATTR_CHANGE_REQUEST,
+    PJ_STUN_ATTR_SOURCE_ADDR,
+    PJ_STUN_ATTR_CHANGED_ADDR,
+    PJ_STUN_ATTR_USERNAME,
+    PJ_STUN_ATTR_PASSWORD,
+    PJ_STUN_ATTR_MESSAGE_INTEGRITY,
+    PJ_STUN_ATTR_ERROR_CODE,
+    PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES,
+    PJ_STUN_ATTR_REFLECTED_FORM
+} pj_stun_attr_type;
+
+
+/**
+ * This structre describes STUN message header.
+ */
+typedef struct pj_stun_msg_hdr
+{
+    pj_uint16_t		type;
+    pj_uint16_t		length;
+    pj_uint32_t		tsx[4];
+} pj_stun_msg_hdr;
+
+
+/**
+ * This structre describes STUN attribute header.
+ */
+typedef struct pj_stun_attr_hdr
+{
+    pj_uint16_t		type;
+    pj_uint16_t		length;
+} pj_stun_attr_hdr;
+
+
+/**
+ * This structre describes STUN MAPPED-ADDR attribute.
+ */
+typedef struct pj_stun_mapped_addr_attr
+{
+    pj_stun_attr_hdr	hdr;
+    pj_uint8_t		ignored;
+    pj_uint8_t		family;
+    pj_uint16_t		port;
+    pj_uint32_t		addr;
+} pj_stun_mapped_addr_attr;
+
+typedef pj_stun_mapped_addr_attr pj_stun_response_addr_attr;
+typedef pj_stun_mapped_addr_attr pj_stun_changed_addr_attr;
+typedef pj_stun_mapped_addr_attr pj_stun_src_addr_attr;
+typedef pj_stun_mapped_addr_attr pj_stun_reflected_form_attr;
+
+typedef struct pj_stun_change_request_attr
+{
+    pj_stun_attr_hdr	hdr;
+    pj_uint32_t		value;
+} pj_stun_change_request_attr;
+
+typedef struct pj_stun_username_attr
+{
+    pj_stun_attr_hdr	hdr;
+    pj_uint32_t		value[1];
+} pj_stun_username_attr;
+
+typedef pj_stun_username_attr pj_stun_password_attr;
+
+typedef struct pj_stun_error_code_attr
+{
+    pj_stun_attr_hdr	hdr;
+    pj_uint16_t		ignored;
+    pj_uint8_t		err_class;
+    pj_uint8_t		number;
+    char		reason[4];
+} pj_stun_error_code_attr;
+
+typedef struct pj_stun_msg
+{
+    pj_stun_msg_hdr    *hdr;
+    int			attr_count;
+    pj_stun_attr_hdr   *attr[PJ_STUN_MAX_ATTR];
+} pj_stun_msg;
+
+/* STUN message API (stun.c). */
+
+PJ_DECL(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool, 
+					      void **msg, pj_size_t *len,
+					      pj_uint32_t id_hi,
+					      pj_uint32_t id_lo);
+PJ_DECL(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len,
+				        pj_stun_msg *msg);
+PJ_DECL(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t);
+
+
+/**
+ * This is the main function to request the mapped address of local sockets
+ * to multiple STUN servers. This function is able to find the mapped 
+ * addresses of multiple sockets simultaneously, and for each socket, two
+ * requests will be sent to two different STUN servers to see if both servers
+ * get the same public address for the same socket. (Note that application can
+ * specify the same address for the two servers, but still two requests will
+ * be sent for each server).
+ *
+ * This function will perform necessary retransmissions of the requests if
+ * response is not received within a predetermined period. When all responses
+ * have been received, the function will compare the mapped addresses returned
+ * by the servers, and when both are equal, the address will be returned in
+ * \a mapped_addr argument.
+ *
+ * @param pf		The pool factory where memory will be allocated from.
+ * @param sock_cnt	Number of sockets in the socket array.
+ * @param sock		Array of local UDP sockets which public addresses are
+ *			to be queried from the STUN servers.
+ * @param srv1		Host name or IP address string of the first STUN
+ *			server.
+ * @param port1		The port number of the first STUN server. 
+ * @param srv2		Host name or IP address string of the second STUN
+ *			server.
+ * @param port2		The port number of the second STUN server. 
+ * @param mapped_addr	Array to receive the mapped public address of the local
+ *			UDP sockets, when the function returns PJ_SUCCESS.
+ *
+ * @return		This functions returns PJ_SUCCESS if responses are
+ *			received from all servers AND all servers returned the
+ *			same mapped public address. Otherwise this function may
+ *			return one of the following error codes:
+ *			- PJLIB_UTIL_ESTUNNOTRESPOND: no respons from servers.
+ *			- PJLIB_UTIL_ESTUNSYMMETRIC: different mapped addresses
+ *			  are returned by servers.
+ *			- etc.
+ *
+ */
+PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf,
+					      int sock_cnt, pj_sock_t sock[],
+					      const pj_str_t *srv1, int port1,
+					      const pj_str_t *srv2, int port2,
+					      pj_sockaddr_in mapped_addr[]);
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif	/* __PJ_STUN_H__ */
+
Index: /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_transaction.h
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_transaction.h	(revision 913)
+++ /pjproject/branches/iceproject/pjlib-util/include/pjlib-util/stun_transaction.h	(revision 913)
@@ -0,0 +1,115 @@
+/* $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 __PJ_STUN_TRANSACTION_H__
+#define __PJ_STUN_TRANSACTION_H__
+
+/**
+ * @file stun_transaction.h
+ * @brief STUN transaction
+ */
+
+#include <pjlib-util/stun_msg.h>
+#include <pjlib-util/stun_endpoint.h>
+
+
+PJ_BEGIN_DECL
+
+
+/* **************************************************************************/
+/**
+ * @defgroup PJLIB_UTIL_STUN_TRANSACTION STUN Client Transaction
+ * @brief STUN client transaction
+ * @ingroup PJLIB_UTIL_STUN
+ * @{
+ */
+
+/**
+ * Opaque declaration of STUN client transaction.
+ */
+typedef struct pj_stun_client_tsx pj_stun_client_tsx;
+
+/**
+ * STUN client transaction callback.
+ */
+typedef struct pj_stun_tsx_cb
+{
+    void	(*on_complete)(pj_stun_client_tsx *tsx,
+			       pj_status_t status, 
+			       pj_stun_msg *response);
+    pj_status_t (*on_send_msg)(pj_stun_client_tsx *tsx,
+			       const void *stun_pkt,
+			       pj_size_t pkt_size);
+
+} pj_stun_tsx_cb;
+
+
+
+/**
+ * Create a STUN client transaction.
+ */
+PJ_DECL(pj_status_t) pj_stun_client_tsx_create(	pj_stun_endpoint *endpt,
+						const pj_stun_tsx_cb *cb,
+						pj_stun_client_tsx **p_tsx);
+
+/**
+ * .
+ */
+PJ_DECL(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx);
+
+
+/**
+ * .
+ */
+PJ_DECL(pj_status_t) pj_stun_client_tsx_set_data(pj_stun_client_tsx *tsx,
+						 void *data);
+
+
+/**
+ * .
+ */
+PJ_DECL(void*) pj_stun_client_tsx_get_data(pj_stun_client_tsx *tsx);
+
+
+/**
+ * .
+ */
+PJ_DECL(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx,
+						 const pj_stun_msg *msg);
+
+
+/**
+ * .
+ */
+PJ_DECL(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx,
+						  const void *packet,
+						  pj_size_t pkt_size,
+						  unsigned *parsed_len);
+
+
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+
+#endif	/* __PJ_STUN_TRANSACTION_H__ */
+
Index: project/branches/iceproject/pjlib-util/src/pjlib-util/stun.c
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun.c	(revision 912)
+++ 	(revision )
@@ -1,125 +1,0 @@
-/* $Id$ */
-/* 
- * Copyright (C) 2003-2006 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.h>
-#include <pjlib-util/errno.h>
-#include <pj/pool.h>
-#include <pj/log.h>
-#include <pj/sock.h>
-#include <pj/os.h>
-
-#define THIS_FILE   "stun.c"
-
-PJ_DEF(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool, 
-					     void **msg, pj_size_t *len,
-					     pj_uint32_t id_hi, 
-					     pj_uint32_t id_lo)
-{
-    pj_stun_msg_hdr *hdr;
-    
-    PJ_CHECK_STACK();
-
-
-    hdr = pj_pool_calloc(pool, 1, sizeof(pj_stun_msg_hdr));
-    if (!hdr)
-	return PJ_ENOMEM;
-
-    hdr->type = pj_htons(PJ_STUN_BINDING_REQUEST);
-    hdr->tsx[2] = pj_htonl(id_hi);
-    hdr->tsx[3] = pj_htonl(id_lo);
-    *msg = hdr;
-    *len = sizeof(pj_stun_msg_hdr);
-
-    return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len, 
-				       pj_stun_msg *msg)
-{
-    pj_uint16_t msg_type, msg_len;
-    char *p_attr;
-
-    PJ_CHECK_STACK();
-
-    msg->hdr = (pj_stun_msg_hdr*)buf;
-    msg_type = pj_ntohs(msg->hdr->type);
-
-    switch (msg_type) {
-    case PJ_STUN_BINDING_REQUEST:
-    case PJ_STUN_BINDING_RESPONSE:
-    case PJ_STUN_BINDING_ERROR_RESPONSE:
-    case PJ_STUN_SHARED_SECRET_REQUEST:
-    case PJ_STUN_SHARED_SECRET_RESPONSE:
-    case PJ_STUN_SHARED_SECRET_ERROR_RESPONSE:
-	break;
-    default:
-	PJ_LOG(4,(THIS_FILE, "Error: unknown msg type %d", msg_type));
-	return PJLIB_UTIL_ESTUNINMSGTYPE;
-    }
-
-    msg_len = pj_ntohs(msg->hdr->length);
-    if (msg_len != len - sizeof(pj_stun_msg_hdr)) {
-	PJ_LOG(4,(THIS_FILE, "Error: invalid msg_len %d (expecting %d)", 
-			     msg_len, len - sizeof(pj_stun_msg_hdr)));
-	return PJLIB_UTIL_ESTUNINMSGLEN;
-    }
-
-    msg->attr_count = 0;
-    p_attr = (char*)buf + sizeof(pj_stun_msg_hdr);
-
-    while (msg_len > 0) {
-	pj_stun_attr_hdr **attr = &msg->attr[msg->attr_count];
-	pj_uint32_t len;
-
-	*attr = (pj_stun_attr_hdr*)p_attr;
-	len = pj_ntohs((pj_uint16_t) ((*attr)->length)) + sizeof(pj_stun_attr_hdr);
-
-	if (msg_len < len) {
-	    PJ_LOG(4,(THIS_FILE, "Error: length mismatch in attr %d", 
-				 msg->attr_count));
-	    return PJLIB_UTIL_ESTUNINATTRLEN;
-	}
-
-	if (pj_ntohs((*attr)->type) > PJ_STUN_ATTR_REFLECTED_FORM) {
-	    PJ_LOG(5,(THIS_FILE, "Warning: unknown attr type %x in attr %d. "
-				 "Attribute was ignored.",
-				 pj_ntohs((*attr)->type), msg->attr_count));
-	}
-
-	msg_len = (pj_uint16_t)(msg_len - len);
-	p_attr += len;
-	++msg->attr_count;
-    }
-
-    return PJ_SUCCESS;
-}
-
-PJ_DEF(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t)
-{
-    int i;
-
-    PJ_CHECK_STACK();
-
-    for (i=0; i<msg->attr_count; ++i) {
-	pj_stun_attr_hdr *attr = msg->attr[i];
-	if (pj_ntohs(attr->type) == t)
-	    return attr;
-    }
-
-    return 0;
-}
Index: project/branches/iceproject/pjlib-util/src/pjlib-util/stun_client.c
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_client.c	(revision 912)
+++ 	(revision )
@@ -1,241 +1,0 @@
-/* $Id$ */
-/* 
- * Copyright (C) 2003-2006 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.h>
-#include <pjlib-util/errno.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/rand.h>
-#include <pj/sock_select.h>
-#include <pj/string.h>
-
-
-enum { MAX_REQUEST = 3 };
-static int stun_timer[] = {1600, 1600, 1600 };
-
-#define THIS_FILE	"stun_client.c"
-#define LOG_ADDR(addr)	pj_inet_ntoa(addr.sin_addr), pj_ntohs(addr.sin_port)
-
-
-PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf,
-					      int sock_cnt, pj_sock_t sock[],
-					      const pj_str_t *srv1, int port1,
-					      const pj_str_t *srv2, int port2,
-					      pj_sockaddr_in mapped_addr[])
-{
-    pj_sockaddr_in srv_addr[2];
-    int i, j, send_cnt = 0;
-    pj_pool_t *pool;
-    struct {
-	struct {
-	    pj_uint32_t	mapped_addr;
-	    pj_uint32_t	mapped_port;
-	} srv[2];
-    } *rec;
-    void       *out_msg;
-    pj_size_t	out_msg_len;
-    int wait_resp = 0;
-    pj_status_t status;
-
-    PJ_CHECK_STACK();
-
-    /* Create pool. */
-    pool = pj_pool_create(pf, "stun%p", 1024, 1024, NULL);
-    if (!pool)
-	return PJ_ENOMEM;
-
-
-    /* Allocate client records */
-    rec = pj_pool_calloc(pool, sock_cnt, sizeof(*rec));
-    if (!rec) {
-	status = PJ_ENOMEM;
-	goto on_error;
-    }
-
-
-    /* Create the outgoing BIND REQUEST message template */
-    status = pj_stun_create_bind_req( pool, &out_msg, &out_msg_len, 
-				      pj_rand(), pj_rand());
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Resolve servers. */
-    status = pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    status = pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Init mapped addresses to zero */
-    pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in));
-
-    /* Main retransmission loop. */
-    for (send_cnt=0; send_cnt<MAX_REQUEST; ++send_cnt) {
-	pj_time_val next_tx, now;
-	pj_fd_set_t r;
-	int select_rc;
-
-	PJ_FD_ZERO(&r);
-
-	/* Send messages to servers that has not given us response. */
-	for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
-	    for (j=0; j<2 && status==PJ_SUCCESS; ++j) {
-		pj_stun_msg_hdr *msg_hdr = out_msg;
-                pj_ssize_t sent_len;
-
-		if (rec[i].srv[j].mapped_port != 0)
-		    continue;
-
-		/* Modify message so that we can distinguish response. */
-		msg_hdr->tsx[2] = pj_htonl(i);
-		msg_hdr->tsx[3] = pj_htonl(j);
-
-		/* Send! */
-                sent_len = out_msg_len;
-		status = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
-					(pj_sockaddr_t*)&srv_addr[j], 
-					sizeof(pj_sockaddr_in));
-		if (status == PJ_SUCCESS)
-		    ++wait_resp;
-	    }
-	}
-
-	/* All requests sent.
-	 * The loop below will wait for responses until all responses have
-	 * been received (i.e. wait_resp==0) or timeout occurs, which then
-	 * we'll go to the next retransmission iteration.
-	 */
-
-	/* Calculate time of next retransmission. */
-	pj_gettimeofday(&next_tx);
-	next_tx.sec += (stun_timer[send_cnt]/1000);
-	next_tx.msec += (stun_timer[send_cnt]%1000);
-	pj_time_val_normalize(&next_tx);
-
-	for (pj_gettimeofday(&now), select_rc=1; 
-	     status==PJ_SUCCESS && select_rc==1 && wait_resp>0 
-	       && PJ_TIME_VAL_LT(now, next_tx); 
-	     pj_gettimeofday(&now)) 
-	{
-	    pj_time_val timeout;
-
-	    timeout = next_tx;
-	    PJ_TIME_VAL_SUB(timeout, now);
-
-	    for (i=0; i<sock_cnt; ++i) {
-		PJ_FD_SET(sock[i], &r);
-	    }
-
-	    select_rc = pj_sock_select(FD_SETSIZE, &r, NULL, NULL, &timeout);
-	    if (select_rc < 1)
-		continue;
-
-	    for (i=0; i<sock_cnt; ++i) {
-		int sock_idx, srv_idx;
-                pj_ssize_t len;
-		pj_stun_msg msg;
-		pj_sockaddr_in addr;
-		int addrlen = sizeof(addr);
-		pj_stun_mapped_addr_attr *attr;
-		char recv_buf[128];
-
-		if (!PJ_FD_ISSET(sock[i], &r))
-		    continue;
-
-                len = sizeof(recv_buf);
-		status = pj_sock_recvfrom( sock[i], recv_buf, 
-				           &len, 0,
-				           (pj_sockaddr_t*)&addr,
-					   &addrlen);
-
-		--wait_resp;
-
-		if (status != PJ_SUCCESS)
-		    continue;
-
-		status = pj_stun_parse_msg(recv_buf, len, &msg);
-		if (status != PJ_SUCCESS) {
-		    continue;
-		}
-
-
-		sock_idx = pj_ntohl(msg.hdr->tsx[2]);
-		srv_idx = pj_ntohl(msg.hdr->tsx[3]);
-
-		if (sock_idx<0 || sock_idx>=sock_cnt || srv_idx<0 || srv_idx>=2) {
-		    status = PJLIB_UTIL_ESTUNININDEX;
-		    continue;
-		}
-
-		if (pj_ntohs(msg.hdr->type) != PJ_STUN_BINDING_RESPONSE) {
-		    status = PJLIB_UTIL_ESTUNNOBINDRES;
-		    continue;
-		}
-
-		if (pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_ERROR_CODE) != NULL) {
-		    status = PJLIB_UTIL_ESTUNRECVERRATTR;
-		    continue;
-		}
-
-		attr = (void*)pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_MAPPED_ADDR);
-		if (!attr) {
-		    status = PJLIB_UTIL_ESTUNNOMAP;
-		    continue;
-		}
-
-		rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr;
-		rec[sock_idx].srv[srv_idx].mapped_port = attr->port;
-	    }
-	}
-
-	/* The best scenario is if all requests have been replied.
-	 * Then we don't need to go to the next retransmission iteration.
-	 */
-	if (wait_resp <= 0)
-	    break;
-    }
-
-    for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
-	if (rec[i].srv[0].mapped_addr == rec[i].srv[1].mapped_addr &&
-	    rec[i].srv[0].mapped_port == rec[i].srv[1].mapped_port)
-	{
-	    mapped_addr[i].sin_family = PJ_AF_INET;
-	    mapped_addr[i].sin_addr.s_addr = rec[i].srv[0].mapped_addr;
-	    mapped_addr[i].sin_port = (pj_uint16_t)rec[i].srv[0].mapped_port;
-
-	    if (rec[i].srv[0].mapped_addr == 0 || rec[i].srv[0].mapped_port == 0) {
-		status = PJLIB_UTIL_ESTUNNOTRESPOND;
-		break;
-	    }
-	} else {
-	    status = PJLIB_UTIL_ESTUNSYMMETRIC;
-	    break;
-	}
-    }
-
-    pj_pool_release(pool);
-
-    return status;
-
-on_error:
-    if (pool) pj_pool_release(pool);
-    return status;
-}
-
Index: /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_endpoint.c
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_endpoint.c	(revision 913)
+++ /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_endpoint.c	(revision 913)
@@ -0,0 +1,68 @@
+/* $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_endpoint.h>
+#include <pjlib-util/errno.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+
+
+/*
+ * Create a STUN endpoint instance.
+ */
+PJ_DEF(pj_status_t) pj_stun_endpt_create( pj_pool_factory *factory,
+					  unsigned options,
+					  pj_ioqueue_t *ioqueue,
+					  pj_timer_heap_t *timer_heap,
+					  pj_stun_endpoint **p_endpt)
+{
+    pj_pool_t *pool;
+    pj_stun_endpoint *endpt;
+
+    PJ_ASSERT_RETURN(factory && p_endpt, PJ_EINVAL);
+
+    pool = pj_pool_create(factory, "stunendpt", 1000, 1000, NULL);
+    if (!pool)
+	return PJ_ENOMEM;
+    
+    endpt = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_endpoint);
+    endpt->pool = pool;
+    endpt->pf = factory;
+    endpt->options = options;
+    endpt->ioqueue = ioqueue;
+    endpt->timer_heap = timer_heap;
+    endpt->rto_msec = PJ_STUN_RTO_VALUE;
+
+    *p_endpt = endpt;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Destroy STUN endpoint instance.
+ */
+PJ_DEF(pj_status_t) pj_stun_endpt_destroy(pj_stun_endpoint *endpt)
+{
+    PJ_ASSERT_RETURN(endpt, PJ_EINVAL);
+
+    pj_pool_release(endpt->pool);
+
+    return PJ_SUCCESS;
+}
+
Index: /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_msg.c
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_msg.c	(revision 913)
+++ /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_msg.c	(revision 913)
@@ -0,0 +1,1199 @@
+/* $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_msg.h>
+#include <pjlib-util/errno.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+
+#define THIS_FILE   "stun_msg.c"
+
+static struct
+{
+    int err_code;
+    const char *err_msg;
+} stun_err_msg_map[] = 
+{
+    { PJ_STUN_STATUS_TRY_ALTERNATE,	    "Try Alternate"}, 
+    { PJ_STUN_STATUS_BAD_REQUEST,	    "Bad Request"},
+    { PJ_STUN_STATUS_UNAUTHORIZED,	    "Unauthorized"},
+    { PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE,	    "Unknown Attribute"},
+    { PJ_STUN_STATUS_STALE_CREDENTIALS,	    "Stale Credentials"},
+    { PJ_STUN_STATUS_INTEGRITY_CHECK_FAILURE,"Integrity Check Failure"},
+    { PJ_STUN_STATUS_MISSING_USERNAME,	    "Missing Username"},
+    { PJ_STUN_STATUS_USE_TLS,		    "Use TLS"},
+    { PJ_STUN_STATUS_MISSING_REALM,	    "Missing Realm"},
+    { PJ_STUN_STATUS_MISSING_NONCE,	    "Missing Nonce"},
+    { PJ_STUN_STATUS_UNKNOWN_USERNAME,	    "Unknown Username"},
+    { PJ_STUN_STATUS_NO_BINDING,	    "No Binding"},
+    { PJ_STUN_STATUS_STALE_NONCE,	    "Stale Nonce"},
+    { PJ_STUN_STATUS_TRANSITIONING,	    "Transitioning"},
+    { PJ_STUN_STATUS_WRONG_USERNAME,	    "Wrong Username"},
+    { PJ_STUN_STATUS_UNSUPP_TRANSPORT_PROTO,"Unsupported Transport Protocol"},
+    { PJ_STUN_STATUS_INVALID_IP_ADDR,	    "Invalid IP Address"},
+    { PJ_STUN_STATUS_INVALID_PORT,	    "Invalid Port"},
+    { PJ_STUN_STATUS_OPER_TCP_ONLY,	    "Operation for TCP Only"},
+    { PJ_STUN_STATUS_CONNECTION_FAILURE,    "Connection Failure"},
+    { PJ_STUN_STATUS_CONNECTION_TIMEOUT,    "Connection Timeout"},
+    { PJ_STUN_STATUS_SERVER_ERROR,	    "Server Error"},
+    { PJ_STUN_STATUS_GLOBAL_FAILURE,	    "Global Failure"}
+};
+
+
+/**
+ * Get STUN standard reason phrase for the specified error code.
+ */
+PJ_DEF(pj_str_t) pj_stun_get_err_reason(int err_code)
+{
+    unsigned i;
+
+    for (i=0; i<PJ_ARRAY_SIZE(stun_err_msg_map); ++i) {
+	if (stun_err_msg_map[i].err_code == err_code)
+	    return pj_str((char*)stun_err_msg_map[i].err_msg);
+    }
+    return pj_str(NULL);
+}
+
+
+
+#define INIT_ATTR(a,t,l)    (a)->hdr.type=(pj_uint16_t)(t), \
+			    (a)->hdr.length=(pj_uint16_t)(l)
+#define ATTR_HDR_LEN	    4
+
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * STUN generic IP address container
+ */
+#define STUN_GENERIC_IP_ADDR_LEN	8
+
+/*
+ * Create a generic STUN IP address attribute for IPv4 address.
+ */
+PJ_DEF(pj_status_t) 
+pj_stun_generic_ip_addr_attr_create(pj_pool_t *pool,
+				    int attr_type,
+				    pj_uint32_t ip_addr,
+				    int port,
+				    pj_stun_generic_ip_addr_attr **p_attr)
+{
+    pj_stun_generic_ip_addr_attr *attr;
+
+    PJ_ASSERT_RETURN(pool && p_attr, 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;
+
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t decode_generic_ip_addr_attr(pj_pool_t *pool, 
+					       const pj_uint8_t *buf, 
+					       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);
+
+    /* 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;
+
+    /* 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 */
+    if (attr->hdr.length != STUN_GENERIC_IP_ADDR_LEN)
+	return PJLIB_UTIL_ESTUNINATTRLEN;
+
+    /* Done */
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t encode_generic_ip_addr_attr(const void *a, pj_uint8_t *buf, 
+					       unsigned len, unsigned *printed)
+{
+    enum
+    {
+	ATTR_LEN = STUN_GENERIC_IP_ADDR_LEN + ATTR_HDR_LEN
+    };
+    pj_stun_generic_ip_addr_attr *attr;
+
+    if (len < ATTR_LEN) 
+	return PJ_ETOOSMALL;
+
+    /* Copy and convert attribute to network byte order */
+    pj_memcpy(buf, a, ATTR_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;
+
+    return PJ_SUCCESS;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * STUN generic string attribute
+ */
+
+/*
+ * Create a STUN generic string attribute.
+ */
+PJ_DEF(pj_status_t) 
+pj_stun_generic_string_attr_create(pj_pool_t *pool,
+				   int attr_type,
+				   const pj_str_t *value,
+				   pj_stun_generic_string_attr **p_attr)
+{
+    pj_stun_generic_string_attr *attr;
+
+    PJ_ASSERT_RETURN(pool && value && p_attr, PJ_EINVAL);
+
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_string_attr);
+    INIT_ATTR(attr, attr_type, value->slen);
+    pj_strdup(pool, &attr->value, value);
+
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t decode_generic_string_attr(pj_pool_t *pool, 
+					      const pj_uint8_t *buf, 
+					      void **p_attr)
+{
+    pj_stun_generic_string_attr *attr;
+    pj_str_t value;
+
+    /* Create the attribute */
+    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_generic_string_attr);
+
+    /* Copy the header */
+    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);
+
+    /* Get pointer to the string in the message */
+    value.ptr = ((char*)buf + ATTR_HDR_LEN);
+    value.slen = attr->hdr.length;
+
+    /* Copy the string to the attribute */
+    pj_strdup(pool, &attr->value, &value);
+
+    /* Done */
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+
+}
+
+
+static pj_status_t encode_generic_string_attr(const void *a, pj_uint8_t *buf, 
+					      unsigned len, unsigned *printed)
+{
+    const pj_stun_generic_string_attr *ca = 
+	(const pj_stun_generic_string_attr*)a;
+    pj_stun_attr_hdr *attr;
+
+    /* Calculated total attr_len (add padding if necessary) */
+    *printed = (ca->value.slen + ATTR_HDR_LEN + 3) & (~3);
+    if (len < *printed)
+	return PJ_ETOOSMALL;
+
+    /* Copy header */
+    pj_memcpy(buf, a, ATTR_HDR_LEN);
+
+    /* Set the correct length */
+    attr = (pj_stun_attr_hdr*)buf;
+    attr->length = (pj_uint16_t) ca->value.slen;
+
+    /* Convert to network byte order */
+    attr->type = pj_htons(attr->type);
+    attr->length = pj_htons(attr->length);
+
+    /* Copy the string */
+    pj_memcpy(buf+ATTR_HDR_LEN, ca->value.ptr, ca->value.slen);
+
+    /* Done */
+    return PJ_SUCCESS;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * STUN generic 32bit integer attribute.
+ */
+#define STUN_UINT_LEN	4
+
+/*
+ * Create a STUN generic 32bit value attribute.
+ */
+PJ_DEF(pj_status_t) 
+pj_stun_generic_uint_attr_create(pj_pool_t *pool,
+				 int attr_type,
+				 pj_uint32_t value,
+				 pj_stun_generic_uint_attr **p_attr)
+{
+    pj_stun_generic_uint_attr *attr;
+
+    PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL);
+
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_uint_attr);
+    INIT_ATTR(attr, attr_type, STUN_UINT_LEN);
+    attr->value = value;
+
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t decode_generic_uint_attr(pj_pool_t *pool, 
+					    const pj_uint8_t *buf, 
+					    void **p_attr)
+{
+    enum
+    {
+	ATTR_LEN = STUN_UINT_LEN + ATTR_HDR_LEN
+    };
+    pj_stun_generic_uint_attr *attr;
+
+    /* Check that the struct address is valid */
+    pj_assert(sizeof(pj_stun_generic_uint_attr) == ATTR_LEN);
+
+    /* Create the attribute */
+    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_generic_uint_attr);
+    pj_memcpy(attr, buf, ATTR_LEN);
+
+    /* Convert to host byte order */
+    attr->hdr.type = pj_ntohs(attr->hdr.type);
+    attr->hdr.length = pj_ntohs(attr->hdr.length);
+    attr->value = pj_ntohl(attr->value);
+
+    /* Check that the attribute length is valid */
+    if (attr->hdr.length != STUN_UINT_LEN)
+	return PJLIB_UTIL_ESTUNINATTRLEN;
+
+    /* Done */
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t encode_generic_uint_attr(const void *a, pj_uint8_t *buf, 
+					    unsigned len, unsigned *printed)
+{
+    enum
+    {
+	ATTR_LEN = STUN_UINT_LEN + ATTR_HDR_LEN
+    };
+    pj_stun_generic_uint_attr *attr;
+
+    if (len < ATTR_LEN) 
+	return PJ_ETOOSMALL;
+
+    /* Copy and convert attribute to network byte order */
+    pj_memcpy(buf, a, ATTR_LEN);
+    attr = (pj_stun_generic_uint_attr*) buf;
+    attr->hdr.type = pj_htons(attr->hdr.type);
+    attr->hdr.length = pj_htons(attr->hdr.length);
+    attr->value = pj_htonl(attr->value);
+
+    /* Done */
+    *printed = ATTR_LEN;
+
+    return PJ_SUCCESS;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * STUN MESSAGE-INTEGRITY attribute.
+ */
+
+#define STUN_MSG_INTEGRITY_LEN	20
+
+/*
+ * Create a STUN MESSAGE-INTEGRITY attribute.
+ */
+PJ_DEF(pj_status_t) 
+pj_stun_msg_integrity_attr_create(pj_pool_t *pool,
+				  pj_stun_msg_integrity_attr **p_attr)
+{
+    pj_stun_msg_integrity_attr *attr;
+
+    PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL);
+
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msg_integrity_attr);
+    INIT_ATTR(attr, PJ_STUN_ATTR_MESSAGE_INTEGRITY, STUN_MSG_INTEGRITY_LEN);
+
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t decode_msg_integrity_attr(pj_pool_t *pool, 
+					     const pj_uint8_t *buf,
+					     void **p_attr)
+{
+    enum
+    {
+	ATTR_LEN = STUN_MSG_INTEGRITY_LEN + ATTR_HDR_LEN
+    };
+    pj_stun_msg_integrity_attr *attr;
+
+    /* Check that struct size is valid */
+    pj_assert(sizeof(pj_stun_msg_integrity_attr)==STUN_MSG_INTEGRITY_LEN);
+
+    /* Create attribute */
+    attr = PJ_POOL_ALLOC_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);
+    attr->hdr.length = pj_ntohs(attr->hdr.length);
+
+    /* Check that the attribute length is valid */
+    if (attr->hdr.length != STUN_MSG_INTEGRITY_LEN)
+	return PJLIB_UTIL_ESTUNINATTRLEN;
+
+    /* Done */
+    *p_attr = attr;
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t encode_msg_integrity_attr(const void *a, pj_uint8_t *buf, 
+					     unsigned len, unsigned *printed)
+{
+    enum
+    {
+	ATTR_LEN = STUN_MSG_INTEGRITY_LEN + ATTR_HDR_LEN
+    };
+    pj_stun_msg_integrity_attr *attr;
+
+    if (len < ATTR_LEN) 
+	return PJ_ETOOSMALL;
+
+    /* Copy and convert attribute to network byte order */
+    pj_memcpy(buf, a, ATTR_LEN);
+    attr = (pj_stun_msg_integrity_attr*) buf;
+    attr->hdr.type = pj_htons(attr->hdr.type);
+    attr->hdr.length = pj_htons(attr->hdr.length);
+
+    /* Done */
+    *printed = ATTR_LEN;
+
+    return PJ_SUCCESS;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * STUN ERROR-CODE
+ */
+
+/*
+ * Create a STUN ERROR-CODE attribute.
+ */
+PJ_DEF(pj_status_t) 
+pj_stun_error_code_attr_create(pj_pool_t *pool,
+			       int err_code,
+			       const pj_str_t *err_reason,
+			       pj_stun_error_code_attr **p_attr)
+{
+    pj_stun_error_code_attr *attr;
+    char err_buf[80];
+    pj_str_t str;
+
+    PJ_ASSERT_RETURN(pool && err_code && p_attr, PJ_EINVAL);
+
+    if (err_reason == NULL) {
+	str = pj_stun_get_err_reason(err_code);
+	if (str.slen == 0) {
+	    str.slen = pj_ansi_snprintf(err_buf, sizeof(err_buf),
+				        "Unknown error %d", err_code);
+	    str.ptr = err_buf;
+	}
+	err_reason = &str;
+    }
+
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_error_code_attr);
+    INIT_ATTR(attr, PJ_STUN_ATTR_ERROR_CODE, 4+err_reason->slen);
+    attr->err_class = (pj_uint8_t)(err_code / 100);
+    attr->number = (pj_uint8_t) (err_code % 100);
+    pj_strdup(pool, &attr->reason, err_reason);
+
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t decode_error_code_attr(pj_pool_t *pool, 
+					  const pj_uint8_t *buf,
+					  void **p_attr)
+{
+    pj_stun_error_code_attr *attr;
+    pj_str_t value;
+
+    /* Create the attribute */
+    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_error_code_attr);
+
+    /* Copy the header */
+    pj_memcpy(attr, buf, ATTR_HDR_LEN + 4);
+
+    /* Convert to host byte order */
+    attr->hdr.type = pj_ntohs(attr->hdr.type);
+    attr->hdr.length = pj_ntohs(attr->hdr.length);
+
+    /* Get pointer to the string in the message */
+    value.ptr = ((char*)buf + ATTR_HDR_LEN + 4);
+    value.slen = attr->hdr.length - 4;
+
+    /* Copy the string to the attribute */
+    pj_strdup(pool, &attr->reason, &value);
+
+    /* Done */
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t encode_error_code_attr(const void *a, pj_uint8_t *buf, 
+					  unsigned len, unsigned *printed)
+{
+    const pj_stun_error_code_attr *ca = 
+	(const pj_stun_error_code_attr*)a;
+    pj_stun_error_code_attr *attr;
+
+    if (len < ATTR_HDR_LEN + 4 + (unsigned)ca->reason.slen) 
+	return PJ_ETOOSMALL;
+
+    /* Copy and convert attribute to network byte order */
+    pj_memcpy(buf, ca, ATTR_HDR_LEN + 4);
+
+    /* Update length */
+    attr = (pj_stun_error_code_attr*) buf;
+    attr->hdr.length = (pj_uint16_t)(4 + ca->reason.slen);
+
+    /* Convert fiends to network byte order */
+    attr->hdr.type = pj_htons(attr->hdr.type);
+    attr->hdr.length = pj_htons(attr->hdr.length);
+
+    /* Copy error string */
+    pj_memcpy(buf + ATTR_HDR_LEN + 4, ca->reason.ptr, ca->reason.slen);
+
+    /* Done */
+    *printed = (ATTR_HDR_LEN + 4 + ca->reason.slen + 3) & (~3);
+
+    return PJ_SUCCESS;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * STUN UNKNOWN-ATTRIBUTES attribute
+ */
+
+/*
+ * Create an empty instance of STUN UNKNOWN-ATTRIBUTES attribute.
+ *
+ * @param pool		The pool to allocate memory from.
+ * @param p_attr	Pointer to receive the attribute.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DEF(pj_status_t) 
+pj_stun_unknown_attr_create(pj_pool_t *pool,
+			    unsigned attr_cnt,
+			    pj_uint16_t attr_array[],
+			    pj_stun_unknown_attr **p_attr)
+{
+    pj_stun_unknown_attr *attr;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(pool && attr_cnt < PJ_STUN_MAX_ATTR && p_attr, PJ_EINVAL);
+
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_unknown_attr);
+    INIT_ATTR(attr, PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES, attr_cnt * 2);
+
+    attr->attr_count = attr_cnt;
+    for (i=0; i<attr_cnt; ++i) {
+	attr->attrs[i] = attr_array[i];
+    }
+
+    /* If the number of unknown attributes is an odd number, one of the
+     * attributes MUST be repeated in the list.
+     */
+    if ((attr_cnt & 0x01)) {
+	attr->attrs[attr_cnt] = attr_array[attr_cnt-1];
+    }
+
+    *p_attr = NULL;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t decode_unknown_attr(pj_pool_t *pool, 
+				       const pj_uint8_t *buf, 
+				       void **p_attr)
+{
+    pj_stun_unknown_attr *attr;
+    const pj_uint16_t *punk_attr;
+    unsigned i;
+
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_unknown_attr);
+    pj_memcpy(attr, buf, ATTR_HDR_LEN);
+
+    attr->hdr.type = pj_ntohs(attr->hdr.type);
+    attr->hdr.length = pj_ntohs(attr->hdr.length);
+
+    attr->attr_count = (attr->hdr.length >> 1);
+    if (attr->attr_count > PJ_STUN_MAX_ATTR)
+	return PJ_ETOOMANY;
+
+    punk_attr = (const pj_uint16_t*)(buf + ATTR_HDR_LEN);
+    for (i=0; i<attr->attr_count; ++i) {
+	attr->attrs[i] = pj_ntohs(punk_attr[i]);
+    }
+
+    /* Done */
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf, 
+				       unsigned len, unsigned *printed)
+{
+    const pj_stun_unknown_attr *ca = (const pj_stun_unknown_attr*) a;
+    pj_stun_unknown_attr *attr;
+    pj_uint16_t *dst_unk_attr;
+    unsigned i;
+
+    /* Check that buffer is enough */
+    if (len < ATTR_HDR_LEN + (ca->attr_count << 1))
+	return PJ_ETOOSMALL;
+
+    /* Copy to message */
+    pj_memcpy(buf, ca, ATTR_HDR_LEN);
+
+    /* Set the correct length */
+    attr = (pj_stun_unknown_attr *) buf;
+    attr->hdr.length = (pj_uint16_t)(ca->attr_count << 1);
+
+    /* Convert to network byte order */
+    attr->hdr.type = pj_htons(attr->hdr.type);
+    attr->hdr.length = pj_htons(attr->hdr.length);
+
+    /* Copy individual attribute */
+    dst_unk_attr = (pj_uint16_t*)(buf + ATTR_HDR_LEN);
+    for (i=0; i < ca->attr_count; ++i, ++dst_unk_attr) {
+	*dst_unk_attr = pj_htons(attr->attrs[i]);
+    }
+
+    /* Done */
+    *printed = (ATTR_HDR_LEN + (ca->attr_count << 1) + 3) & (~3);
+
+    return PJ_SUCCESS;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * STUN generic binary attribute
+ */
+
+/*
+ * Create a blank binary attribute.
+ */
+PJ_DEF(pj_status_t)
+pj_stun_binary_attr_create(pj_pool_t *pool,
+			   int attr_type,
+			   pj_stun_binary_attr **p_attr)
+{
+    pj_stun_binary_attr *attr;
+
+    PJ_ASSERT_RETURN(pool && attr_type && p_attr, PJ_EINVAL);
+
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_binary_attr);
+    INIT_ATTR(attr, attr_type, sizeof(pj_stun_binary_attr));
+
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t decode_binary_attr(pj_pool_t *pool, 
+				      const pj_uint8_t *buf,
+				      void **p_attr)
+{
+    pj_stun_binary_attr *attr;
+
+    /* Create the attribute */
+    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_binary_attr);
+
+    /* Copy the header */
+    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);
+
+    /* Copy the data to the attribute */
+    attr->length = attr->hdr.length;
+    attr->data = pj_pool_alloc(pool, attr->length);
+    pj_memcpy(attr->data, buf+ATTR_HDR_LEN, attr->length);
+
+    /* Done */
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+
+}
+
+
+static pj_status_t encode_binary_attr(const void *a, pj_uint8_t *buf, 
+				      unsigned len, unsigned *printed)
+{
+    const pj_stun_binary_attr *ca = (const pj_stun_binary_attr*)a;
+    pj_stun_attr_hdr *attr;
+
+    /* Calculated total attr_len (add padding if necessary) */
+    *printed = (ca->length + ATTR_HDR_LEN + 3) & (~3);
+    if (len < *printed)
+	return PJ_ETOOSMALL;
+
+    /* Copy header */
+    pj_memcpy(buf, a, ATTR_HDR_LEN);
+
+    /* Set the correct length */
+    attr = (pj_stun_attr_hdr*)buf;
+    attr->length = (pj_uint16_t) ca->length;
+
+    /* Convert to network byte order */
+    attr->type = pj_htons(attr->type);
+    attr->length = pj_htons(attr->length);
+
+    /* Copy the data */
+    pj_memcpy(buf+ATTR_HDR_LEN, ca->data, ca->length);
+
+    /* Done */
+    return PJ_SUCCESS;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+
+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);
+    pj_status_t (*encode_attr)(const void *a, pj_uint8_t *buf, 
+			       unsigned len, unsigned *printed);
+
+} attr_desc[] = 
+{
+    {
+	PJ_STUN_ATTR_MAPPED_ADDR,
+	"MAPPED-ADDRESS",
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	PJ_STUN_ATTR_RESPONSE_ADDR,
+	"RESPONSE-ADDRESS",
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	PJ_STUN_ATTR_CHANGE_REQUEST,
+	"CHANGE-REQUEST",
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	PJ_STUN_ATTR_SOURCE_ADDR,
+	"SOURCE-ADDRESS",
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	PJ_STUN_ATTR_CHANGED_ADDR,
+	"CHANGED-ADDRESS",
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	PJ_STUN_ATTR_USERNAME,
+	"USERNAME",
+	&decode_generic_string_attr,
+	&encode_generic_string_attr
+    },
+    {
+	PJ_STUN_ATTR_PASSWORD,
+	"PASSWORD",
+	&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",
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	PJ_STUN_ATTR_REALM,
+	"REALM",
+	&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",
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	PJ_STUN_ATTR_FINGERPRINT,
+	"FINGERPRINT",
+	&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;
+}
+
+
+
+/*
+ * Create a blank STUN message.
+ */
+PJ_DEF(pj_status_t) pj_stun_msg_create( pj_pool_t *pool,
+					unsigned msg_type,
+					const pj_uint8_t tsx_id[12],
+					pj_stun_msg **p_msg)
+{
+    pj_stun_msg *msg;
+
+    PJ_ASSERT_RETURN(pool && msg_type && p_msg, PJ_EINVAL);
+
+    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;
+
+    if (tsx_id) {
+	pj_memcpy(&msg->hdr.tsx_id, tsx_id, sizeof(msg->hdr.tsx_id));
+    } else {
+	struct transaction_id
+	{
+	    pj_uint32_t	    proc_id;
+	    pj_uint32_t	    random;
+	    pj_uint32_t	    counter;
+	} id;
+	static pj_uint32_t pj_stun_tsx_id_counter;
+
+	id.proc_id = pj_getpid();
+	id.random = pj_rand();
+	id.counter = pj_stun_tsx_id_counter++;
+
+	pj_memcpy(&msg->hdr.tsx_id, &id, sizeof(msg->hdr.tsx_id));
+    }
+
+    *p_msg = msg;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * 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,
+				      unsigned options)
+{
+    pj_stun_msg_hdr *hdr;
+
+    PJ_ASSERT_RETURN(pdu && pdu_len > sizeof(pj_stun_msg_hdr), 
+		     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)
+	return PJ_FALSE;
+
+    /* If magic is set, then there is great possibility that this is
+     * a STUN message.
+     */
+    if (pj_ntohl(hdr->magic) == PJ_STUN_MAGIC)
+	return PJ_TRUE;
+
+    /* Check the PDU length */
+    if (pj_ntohs(hdr->length) > pdu_len)
+	return PJ_FALSE;
+
+    /* Could be a STUN message */
+    return PJ_TRUE;
+}
+
+
+/*
+ * Parse incoming packet into STUN message.
+ */
+PJ_DEF(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 *p_uattr_cnt,
+				       pj_uint16_t uattr[])
+{
+    
+    pj_stun_msg *msg;
+    unsigned uattr_cnt;
+    const pj_uint8_t *start_pdu = pdu;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(options);
+
+    PJ_ASSERT_RETURN(pool && pdu && pdu_len && p_msg, PJ_EINVAL);
+    PJ_ASSERT_RETURN(sizeof(pj_stun_msg_hdr) == 20, PJ_EBUG);
+
+    /* Application should have checked that this is a valid STUN msg */
+    PJ_ASSERT_RETURN((status=pj_stun_msg_check(pdu, pdu_len, options))
+			== PJ_SUCCESS, status);
+
+    /* Create the message, copy the header, and convert to host byte order */
+    msg = PJ_POOL_ALLOC_TYPE(pool, pj_stun_msg);
+    pj_memcpy(&msg->hdr, pdu, sizeof(pj_stun_msg_hdr));
+    msg->hdr.type = pj_ntohs(msg->hdr.type);
+    msg->hdr.length = pj_ntohs(msg->hdr.length);
+    msg->hdr.magic = pj_ntohl(msg->hdr.magic);
+
+    pdu += sizeof(pj_stun_msg_hdr);
+    pdu_len -= sizeof(pj_stun_msg_hdr);
+
+    /* Parse attributes */
+    uattr_cnt = 0;
+    while (pdu_len > 0) {
+	unsigned attr_type, attr_val_len;
+	struct attr_desc *adesc;
+
+	/* Get attribute type and length. If length is not aligned
+	 * to 4 bytes boundary, add padding.
+	 */
+	attr_type = pj_ntohs(*(pj_uint16_t*)pdu);
+	attr_val_len = pj_ntohs(*(pj_uint16_t*)(pdu+2));
+	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;
+
+	if (adesc == NULL) {
+	    /* Unrecognized attribute */
+
+	    PJ_LOG(4,(THIS_FILE, "Unrecognized attribute type %d", 
+		      attr_type));
+
+	    /* Put to unrecognized attribute array */
+	    if (p_uattr_cnt && uattr && uattr_cnt < *p_uattr_cnt) {
+		uattr[uattr_cnt++] = (pj_uint16_t)attr_type;
+	    }
+
+	    /* Is this a fatal condition? */
+	    if (attr_type <= 0x7FFF) {
+		/* This is a mandatory attribute, we must return error
+		 * if we don't understand the attribute.
+		 */
+		if (p_err_code)
+		    *p_err_code = PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE;
+
+		return PJLIB_UTIL_ESTUNUNKNOWNATTR;
+	    }
+
+	} else {
+	    void *attr;
+
+	    /* Parse the attribute */
+	    status = (adesc->decode_attr)(pool, pdu, &attr);
+
+	    if (status != PJ_SUCCESS) {
+		PJ_LOG(4,(THIS_FILE, 
+			  "Error parsing STUN %s attribute: status=%d",
+			  adesc->attr_name, status));
+		return status;
+	    }
+	    
+	    /* Make sure we have rooms for the new attribute */
+	    if (msg->attr_count >= PJ_STUN_MAX_ATTR)
+		return PJLIB_UTIL_ESTUNTOOMANYATTR;
+
+	    /* Add the attribute */
+	    msg->attr[msg->attr_count++] = (pj_stun_attr_hdr*)attr;
+	}
+
+	pdu += (attr_val_len + 4);
+	pdu_len -= (attr_val_len + 4);
+    }
+
+    *p_msg = msg;
+
+    if (p_uattr_cnt)
+	*p_uattr_cnt = uattr_cnt;
+
+    if (p_parsed_len)
+	*p_parsed_len = (pdu - start_pdu);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Print the message structure to a buffer.
+ */
+PJ_DEF(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg,
+				       pj_uint8_t *buf, unsigned buf_size,
+				       unsigned options,
+				       unsigned *p_msg_len)
+{
+    pj_stun_msg_hdr *hdr;
+    pj_uint8_t *start = buf;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(msg && buf && buf_size, PJ_EINVAL);
+
+    PJ_UNUSED_ARG(options);
+
+    /* Copy the message header part and convert the header fields to
+     * network byte order
+     */
+    if (buf_size < sizeof(pj_stun_msg_hdr))
+	return PJ_ETOOSMALL;
+    pj_memcpy(buf, &msg->hdr, sizeof(pj_stun_msg_hdr));
+    hdr = (pj_stun_msg_hdr*) buf;
+    hdr->magic = pj_htonl(hdr->magic);
+    hdr->type = pj_htons(hdr->type);
+    /* We'll fill in the length later */
+
+    buf += sizeof(pj_stun_msg_hdr);
+    buf_size -= sizeof(pj_stun_msg_hdr);
+
+    /* Print each attribute */
+    for (i=0; i<msg->attr_count; ++i) {
+	struct attr_desc *adesc;
+	const pj_stun_attr_hdr *attr_hdr;
+	unsigned printed;
+	pj_status_t status;
+
+	attr_hdr = msg->attr[i];
+
+	adesc = find_attr_desc(attr_hdr->type);
+	PJ_ASSERT_RETURN(adesc != NULL, PJ_EBUG);
+
+	status = adesc->encode_attr(attr_hdr, buf, buf_size, &printed);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	buf += printed;
+	buf_size -= printed;
+    }
+
+    /* Update the message length in the header */
+    hdr->length = pj_htons((pj_uint16_t)(buf - start));
+
+    /* Done */
+    if (p_msg_len)
+	*p_msg_len = (buf - start);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Find STUN attribute in the STUN message, starting from the specified
+ * index.
+ */
+PJ_DEF(pj_stun_attr_hdr*) pj_stun_msg_find_attr( const pj_stun_msg *msg,
+						 int attr_type,
+						 unsigned index)
+{
+    PJ_ASSERT_RETURN(msg, NULL);
+
+    for (; index < msg->attr_count; ++index) {
+	if (msg->attr[index]->type == attr_type)
+	    return (pj_stun_attr_hdr*) &msg->attr[index];
+    }
+
+    return NULL;
+}
+
Index: /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_simple.c
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_simple.c	(revision 913)
+++ /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_simple.c	(revision 913)
@@ -0,0 +1,125 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 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_simple.h>
+#include <pjlib-util/errno.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/sock.h>
+#include <pj/os.h>
+
+#define THIS_FILE   "stun.c"
+
+PJ_DEF(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool, 
+					     void **msg, pj_size_t *len,
+					     pj_uint32_t id_hi, 
+					     pj_uint32_t id_lo)
+{
+    pj_stun_msg_hdr *hdr;
+    
+    PJ_CHECK_STACK();
+
+
+    hdr = pj_pool_calloc(pool, 1, sizeof(pj_stun_msg_hdr));
+    if (!hdr)
+	return PJ_ENOMEM;
+
+    hdr->type = pj_htons(PJ_STUN_BINDING_REQUEST);
+    hdr->tsx[2] = pj_htonl(id_hi);
+    hdr->tsx[3] = pj_htonl(id_lo);
+    *msg = hdr;
+    *len = sizeof(pj_stun_msg_hdr);
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len, 
+				       pj_stun_msg *msg)
+{
+    pj_uint16_t msg_type, msg_len;
+    char *p_attr;
+
+    PJ_CHECK_STACK();
+
+    msg->hdr = (pj_stun_msg_hdr*)buf;
+    msg_type = pj_ntohs(msg->hdr->type);
+
+    switch (msg_type) {
+    case PJ_STUN_BINDING_REQUEST:
+    case PJ_STUN_BINDING_RESPONSE:
+    case PJ_STUN_BINDING_ERROR_RESPONSE:
+    case PJ_STUN_SHARED_SECRET_REQUEST:
+    case PJ_STUN_SHARED_SECRET_RESPONSE:
+    case PJ_STUN_SHARED_SECRET_ERROR_RESPONSE:
+	break;
+    default:
+	PJ_LOG(4,(THIS_FILE, "Error: unknown msg type %d", msg_type));
+	return PJLIB_UTIL_ESTUNINMSGTYPE;
+    }
+
+    msg_len = pj_ntohs(msg->hdr->length);
+    if (msg_len != len - sizeof(pj_stun_msg_hdr)) {
+	PJ_LOG(4,(THIS_FILE, "Error: invalid msg_len %d (expecting %d)", 
+			     msg_len, len - sizeof(pj_stun_msg_hdr)));
+	return PJLIB_UTIL_ESTUNINMSGLEN;
+    }
+
+    msg->attr_count = 0;
+    p_attr = (char*)buf + sizeof(pj_stun_msg_hdr);
+
+    while (msg_len > 0) {
+	pj_stun_attr_hdr **attr = &msg->attr[msg->attr_count];
+	pj_uint32_t len;
+
+	*attr = (pj_stun_attr_hdr*)p_attr;
+	len = pj_ntohs((pj_uint16_t) ((*attr)->length)) + sizeof(pj_stun_attr_hdr);
+
+	if (msg_len < len) {
+	    PJ_LOG(4,(THIS_FILE, "Error: length mismatch in attr %d", 
+				 msg->attr_count));
+	    return PJLIB_UTIL_ESTUNINATTRLEN;
+	}
+
+	if (pj_ntohs((*attr)->type) > PJ_STUN_ATTR_REFLECTED_FORM) {
+	    PJ_LOG(5,(THIS_FILE, "Warning: unknown attr type %x in attr %d. "
+				 "Attribute was ignored.",
+				 pj_ntohs((*attr)->type), msg->attr_count));
+	}
+
+	msg_len = (pj_uint16_t)(msg_len - len);
+	p_attr += len;
+	++msg->attr_count;
+    }
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t)
+{
+    int i;
+
+    PJ_CHECK_STACK();
+
+    for (i=0; i<msg->attr_count; ++i) {
+	pj_stun_attr_hdr *attr = msg->attr[i];
+	if (pj_ntohs(attr->type) == t)
+	    return attr;
+    }
+
+    return 0;
+}
Index: /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_simple_client.c
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_simple_client.c	(revision 913)
+++ /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_simple_client.c	(revision 913)
@@ -0,0 +1,241 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 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_simple.h>
+#include <pjlib-util/errno.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/rand.h>
+#include <pj/sock_select.h>
+#include <pj/string.h>
+
+
+enum { MAX_REQUEST = 3 };
+static int stun_timer[] = {1600, 1600, 1600 };
+
+#define THIS_FILE	"stun_client.c"
+#define LOG_ADDR(addr)	pj_inet_ntoa(addr.sin_addr), pj_ntohs(addr.sin_port)
+
+
+PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf,
+					      int sock_cnt, pj_sock_t sock[],
+					      const pj_str_t *srv1, int port1,
+					      const pj_str_t *srv2, int port2,
+					      pj_sockaddr_in mapped_addr[])
+{
+    pj_sockaddr_in srv_addr[2];
+    int i, j, send_cnt = 0;
+    pj_pool_t *pool;
+    struct {
+	struct {
+	    pj_uint32_t	mapped_addr;
+	    pj_uint32_t	mapped_port;
+	} srv[2];
+    } *rec;
+    void       *out_msg;
+    pj_size_t	out_msg_len;
+    int wait_resp = 0;
+    pj_status_t status;
+
+    PJ_CHECK_STACK();
+
+    /* Create pool. */
+    pool = pj_pool_create(pf, "stun%p", 1024, 1024, NULL);
+    if (!pool)
+	return PJ_ENOMEM;
+
+
+    /* Allocate client records */
+    rec = pj_pool_calloc(pool, sock_cnt, sizeof(*rec));
+    if (!rec) {
+	status = PJ_ENOMEM;
+	goto on_error;
+    }
+
+
+    /* Create the outgoing BIND REQUEST message template */
+    status = pj_stun_create_bind_req( pool, &out_msg, &out_msg_len, 
+				      pj_rand(), pj_rand());
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Resolve servers. */
+    status = pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    status = pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Init mapped addresses to zero */
+    pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in));
+
+    /* Main retransmission loop. */
+    for (send_cnt=0; send_cnt<MAX_REQUEST; ++send_cnt) {
+	pj_time_val next_tx, now;
+	pj_fd_set_t r;
+	int select_rc;
+
+	PJ_FD_ZERO(&r);
+
+	/* Send messages to servers that has not given us response. */
+	for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
+	    for (j=0; j<2 && status==PJ_SUCCESS; ++j) {
+		pj_stun_msg_hdr *msg_hdr = out_msg;
+                pj_ssize_t sent_len;
+
+		if (rec[i].srv[j].mapped_port != 0)
+		    continue;
+
+		/* Modify message so that we can distinguish response. */
+		msg_hdr->tsx[2] = pj_htonl(i);
+		msg_hdr->tsx[3] = pj_htonl(j);
+
+		/* Send! */
+                sent_len = out_msg_len;
+		status = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
+					(pj_sockaddr_t*)&srv_addr[j], 
+					sizeof(pj_sockaddr_in));
+		if (status == PJ_SUCCESS)
+		    ++wait_resp;
+	    }
+	}
+
+	/* All requests sent.
+	 * The loop below will wait for responses until all responses have
+	 * been received (i.e. wait_resp==0) or timeout occurs, which then
+	 * we'll go to the next retransmission iteration.
+	 */
+
+	/* Calculate time of next retransmission. */
+	pj_gettimeofday(&next_tx);
+	next_tx.sec += (stun_timer[send_cnt]/1000);
+	next_tx.msec += (stun_timer[send_cnt]%1000);
+	pj_time_val_normalize(&next_tx);
+
+	for (pj_gettimeofday(&now), select_rc=1; 
+	     status==PJ_SUCCESS && select_rc==1 && wait_resp>0 
+	       && PJ_TIME_VAL_LT(now, next_tx); 
+	     pj_gettimeofday(&now)) 
+	{
+	    pj_time_val timeout;
+
+	    timeout = next_tx;
+	    PJ_TIME_VAL_SUB(timeout, now);
+
+	    for (i=0; i<sock_cnt; ++i) {
+		PJ_FD_SET(sock[i], &r);
+	    }
+
+	    select_rc = pj_sock_select(FD_SETSIZE, &r, NULL, NULL, &timeout);
+	    if (select_rc < 1)
+		continue;
+
+	    for (i=0; i<sock_cnt; ++i) {
+		int sock_idx, srv_idx;
+                pj_ssize_t len;
+		pj_stun_msg msg;
+		pj_sockaddr_in addr;
+		int addrlen = sizeof(addr);
+		pj_stun_mapped_addr_attr *attr;
+		char recv_buf[128];
+
+		if (!PJ_FD_ISSET(sock[i], &r))
+		    continue;
+
+                len = sizeof(recv_buf);
+		status = pj_sock_recvfrom( sock[i], recv_buf, 
+				           &len, 0,
+				           (pj_sockaddr_t*)&addr,
+					   &addrlen);
+
+		--wait_resp;
+
+		if (status != PJ_SUCCESS)
+		    continue;
+
+		status = pj_stun_parse_msg(recv_buf, len, &msg);
+		if (status != PJ_SUCCESS) {
+		    continue;
+		}
+
+
+		sock_idx = pj_ntohl(msg.hdr->tsx[2]);
+		srv_idx = pj_ntohl(msg.hdr->tsx[3]);
+
+		if (sock_idx<0 || sock_idx>=sock_cnt || srv_idx<0 || srv_idx>=2) {
+		    status = PJLIB_UTIL_ESTUNININDEX;
+		    continue;
+		}
+
+		if (pj_ntohs(msg.hdr->type) != PJ_STUN_BINDING_RESPONSE) {
+		    status = PJLIB_UTIL_ESTUNNOBINDRES;
+		    continue;
+		}
+
+		if (pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_ERROR_CODE) != NULL) {
+		    status = PJLIB_UTIL_ESTUNRECVERRATTR;
+		    continue;
+		}
+
+		attr = (void*)pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_MAPPED_ADDR);
+		if (!attr) {
+		    status = PJLIB_UTIL_ESTUNNOMAP;
+		    continue;
+		}
+
+		rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr;
+		rec[sock_idx].srv[srv_idx].mapped_port = attr->port;
+	    }
+	}
+
+	/* The best scenario is if all requests have been replied.
+	 * Then we don't need to go to the next retransmission iteration.
+	 */
+	if (wait_resp <= 0)
+	    break;
+    }
+
+    for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
+	if (rec[i].srv[0].mapped_addr == rec[i].srv[1].mapped_addr &&
+	    rec[i].srv[0].mapped_port == rec[i].srv[1].mapped_port)
+	{
+	    mapped_addr[i].sin_family = PJ_AF_INET;
+	    mapped_addr[i].sin_addr.s_addr = rec[i].srv[0].mapped_addr;
+	    mapped_addr[i].sin_port = (pj_uint16_t)rec[i].srv[0].mapped_port;
+
+	    if (rec[i].srv[0].mapped_addr == 0 || rec[i].srv[0].mapped_port == 0) {
+		status = PJLIB_UTIL_ESTUNNOTRESPOND;
+		break;
+	    }
+	} else {
+	    status = PJLIB_UTIL_ESTUNSYMMETRIC;
+	    break;
+	}
+    }
+
+    pj_pool_release(pool);
+
+    return status;
+
+on_error:
+    if (pool) pj_pool_release(pool);
+    return status;
+}
+
Index: /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_transaction.c
===================================================================
--- /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_transaction.c	(revision 913)
+++ /pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_transaction.c	(revision 913)
@@ -0,0 +1,317 @@
+/* $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_transaction.h>
+#include <pjlib-util/errno.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/timer.h>
+
+
+#define TIMER_ACTIVE		1
+
+
+struct pj_stun_client_tsx
+{
+    char		 obj_name[PJ_MAX_OBJ_NAME];
+    pj_pool_t		*pool;
+    pj_stun_endpoint	*endpt;
+    pj_stun_tsx_cb	 cb;
+    void		*user_data;
+
+    pj_uint32_t		 tsx_id[4];
+
+    pj_timer_entry	 timer;
+    unsigned		 transmit_count;
+    pj_time_val		 retransmit_time;
+
+    pj_uint8_t		 last_pkt[PJ_STUN_MAX_PKT_LEN];
+    unsigned		 last_pkt_size;
+};
+
+
+static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, 
+				      pj_timer_entry *timer);
+
+static void stun_perror(pj_stun_client_tsx *tsx, const char *title,
+			pj_status_t status)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+
+    pj_strerror(status, errmsg, sizeof(errmsg));
+    PJ_LOG(4,(tsx->obj_name, "%s: %s", title, errmsg));
+}
+
+
+/*
+ * Create a STUN client transaction.
+ */
+PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_endpoint *endpt,
+					      const pj_stun_tsx_cb *cb,
+					      pj_stun_client_tsx **p_tsx)
+{
+    pj_pool_t *pool;
+    pj_stun_client_tsx *tsx;
+
+    PJ_ASSERT_RETURN(endpt && cb && p_tsx, PJ_EINVAL);
+    PJ_ASSERT_RETURN(cb->on_send_msg, PJ_EINVAL);
+
+    pool = pj_pool_create(endpt->pf, "tsx", 1000, 1000, NULL);
+    tsx = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_client_tsx);
+    tsx->pool = pool;
+    tsx->endpt = endpt;
+    pj_memcpy(&tsx->cb, cb, sizeof(*cb));
+
+    tsx->timer.cb = &retransmit_timer_callback;
+    tsx->timer.user_data = tsx;
+
+    pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "stuntsx%p", pool);
+
+    *p_tsx = tsx;
+
+    PJ_LOG(4,(tsx->obj_name, "STUN client transaction created"));
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * .
+ */
+PJ_DEF(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx)
+{
+    PJ_ASSERT_RETURN(tsx, PJ_EINVAL);
+
+    if (tsx->timer.id != 0) {
+	pj_timer_heap_cancel(tsx->endpt->timer_heap, &tsx->timer);
+	tsx->timer.id = 0;
+    }
+    pj_pool_release(tsx->pool);
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * .
+ */
+PJ_DEF(pj_status_t) pj_stun_client_tsx_set_data(pj_stun_client_tsx *tsx,
+						void *data)
+{
+    PJ_ASSERT_RETURN(tsx, PJ_EINVAL);
+    tsx->user_data = data;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * .
+ */
+PJ_DEF(void*) pj_stun_client_tsx_get_data(pj_stun_client_tsx *tsx)
+{
+    PJ_ASSERT_RETURN(tsx, NULL);
+    return tsx->user_data;
+}
+
+
+/*
+ * Transmit message.
+ */
+static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(tsx->timer.id == 0, PJ_EBUSY);
+
+    /* Calculate retransmit/timeout delay */
+    if (tsx->transmit_count == 0) {
+	tsx->retransmit_time.sec = 0;
+	tsx->retransmit_time.msec = tsx->endpt->rto_msec;
+
+    } else if (tsx->transmit_count < PJ_STUN_MAX_RETRANSMIT_COUNT) {
+	unsigned msec;
+
+	msec = PJ_TIME_VAL_MSEC(tsx->retransmit_time);
+	msec = (msec >> 1) + 100;
+	tsx->retransmit_time.sec = msec / 1000;
+	tsx->retransmit_time.msec = msec % 100;
+
+    } else {
+	tsx->retransmit_time.sec = PJ_STUN_TIMEOUT_VALUE / 1000;
+	tsx->retransmit_time.msec = PJ_STUN_TIMEOUT_VALUE % 1000;
+    }
+
+    /* Schedule timer first because when send_msg() failed we can
+     * cancel it (as opposed to when schedule_timer() failed we cannot
+     * cancel transmission).
+     */
+    status = pj_timer_heap_schedule(tsx->endpt->timer_heap, &tsx->timer,
+				    &tsx->retransmit_time);
+    if (status != PJ_SUCCESS) {
+	tsx->timer.id = 0;
+	return status;
+    }
+
+
+    /* Send message */
+    status = tsx->cb.on_send_msg(tsx, tsx->last_pkt, tsx->last_pkt_size);
+    if (status != PJ_SUCCESS) {
+	pj_timer_heap_cancel(tsx->endpt->timer_heap, &tsx->timer);
+	tsx->timer.id = 0;
+	stun_perror(tsx, "STUN error sending message", status);
+	return status;
+    }
+
+    tsx->transmit_count++;
+
+    PJ_LOG(4,(tsx->obj_name, "STUN sending message (transmit count=%d)",
+	      tsx->transmit_count));
+    return status;
+}
+
+/*
+ * .
+ */
+PJ_DEF(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx,
+						const pj_stun_msg *msg)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(tsx && msg, PJ_EINVAL);
+    PJ_ASSERT_RETURN(tsx->timer.id != 0, PJ_EBUSY);
+
+    /* Encode message */
+    status = pj_stun_msg_encode(msg, tsx->last_pkt, sizeof(tsx->last_pkt), 
+				0, &tsx->last_pkt_size);
+    if (status != PJ_SUCCESS) {
+	stun_perror(tsx, "STUN msg_encode() failed", status);
+	return status;
+    }
+
+    /* Update STUN transaction ID */
+    tsx->tsx_id[0] = msg->hdr.magic;
+    pj_memcpy(&tsx->tsx_id[1], msg->hdr.tsx_id, 12);
+
+    return tsx_transmit_msg(tsx);
+}
+
+
+/* Retransmit timer callback */
+static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, 
+				      pj_timer_entry *timer)
+{
+    pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(timer_heap);
+
+    if (tsx->transmit_count >= PJ_STUN_MAX_RETRANSMIT_COUNT) {
+	/* Retransmission count exceeded. Transaction has failed */
+	tsx->timer.id = 0;
+	PJ_LOG(4,(tsx->obj_name, "STUN timeout waiting for response"));
+	if (tsx->cb.on_complete) {
+	    tsx->cb.on_complete(tsx, PJLIB_UTIL_ESTUNNOTRESPOND, NULL);
+	}
+	return;
+    }
+
+    tsx->timer.id = 0;
+    status = tsx_transmit_msg(tsx);
+    if (status != PJ_SUCCESS) {
+	tsx->timer.id = 0;
+	if (tsx->cb.on_complete) {
+	    tsx->cb.on_complete(tsx, status, NULL);
+	}
+    }
+}
+
+
+/*
+ * 
+ */
+PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx,
+						 const void *packet,
+						 pj_size_t pkt_size,
+						 unsigned *parsed_len)
+{
+    pj_stun_msg *msg;
+    pj_stun_error_code_attr *err_attr;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(tsx && packet && pkt_size, PJ_EINVAL);
+
+    /* Try to parse the message */
+    status = pj_stun_msg_decode(tsx->pool, (const pj_uint8_t*)packet,
+			        pkt_size, 0, &msg, parsed_len,
+				NULL, NULL, NULL);
+    if (status != PJ_SUCCESS) {
+	stun_perror(tsx, "STUN msg_decode() error", status);
+	return status;
+    }
+
+    /* Must be STUN response message */
+    if (!PJ_STUN_IS_RESPONSE(msg->hdr.type) && 
+	!PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
+    {
+	PJ_LOG(4,(tsx->obj_name, 
+		  "STUN rx_msg() error: not response message"));
+	return PJLIB_UTIL_ESTUNNOTRESPONSE;
+    }
+
+    /* Compare response's transaction ID */
+    if (msg->hdr.magic != tsx->tsx_id[0] ||
+	pj_memcmp(msg->hdr.tsx_id, &tsx->tsx_id[1], 12) != 0)
+    {
+	return PJLIB_UTIL_ESTUNINVALIDID;
+    }
+
+    /* We have a response with matching transaction ID. 
+     * We can cancel retransmit timer now.
+     */
+    if (tsx->timer.id) {
+	pj_timer_heap_cancel(tsx->endpt->timer_heap, &tsx->timer);
+	tsx->timer.id = 0;
+    }
+
+    /* Find STUN error code attribute */
+    err_attr = (pj_stun_error_code_attr*) 
+		pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ERROR_CODE, 0);
+    if (err_attr->err_class == 1) {
+	PJ_LOG(4,(tsx->obj_name, 
+		  "STUN rx_msg() error: received provisional %d code (%.*s)",
+		  err_attr->err_class * 100 + err_attr->number,
+		  (int)err_attr->reason.slen,
+		  err_attr->reason.ptr));
+	return PJ_SUCCESS;
+    }
+
+    if (err_attr==NULL || err_attr->err_class == 2) {
+	status = PJ_SUCCESS;
+    } else {
+	status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_class * 100 +
+					  err_attr->number);
+    }
+
+    /* Call callback */
+    if (tsx->cb.on_complete) {
+	tsx->cb.on_complete(tsx, status, msg);
+    }
+
+    return PJ_SUCCESS;
+}
+
Index: /pjproject/branches/iceproject/pjlib/include/pj/pool.h
===================================================================
--- /pjproject/branches/iceproject/pjlib/include/pj/pool.h	(revision 912)
+++ /pjproject/branches/iceproject/pjlib/include/pj/pool.h	(revision 913)
@@ -442,4 +442,6 @@
  *
  * @return pointer to the allocated memory.
+ *
+ * @see PJ_POOL_ALLOC_TYPE
  */
 PJ_IDECL(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size);
@@ -461,5 +463,4 @@
 
 /**
- * @def pj_pool_zalloc(pj_pool_t *pool, pj_size_t size)
  * Allocate storage from the pool and initialize it to zero.
  *
@@ -468,6 +469,41 @@
  *
  * @return	    Pointer to the allocated memory.
- */
-#define pj_pool_zalloc(pool, size)  pj_pool_calloc(pool, 1, size)
+ *
+ * @see PJ_POOL_ZALLOC_TYPE
+ */
+PJ_INLINE(void*) pj_pool_zalloc(pj_pool_t *pool, pj_size_t size)
+{
+    return pj_pool_calloc(pool, 1, size);
+}
+
+
+/**
+ * This macro allocates memory from the pool and returns the instance of
+ * the specified type. It provides a stricker type safety than pj_pool_alloc()
+ * since the return value of this macro will be type-casted to the specified
+ * type.
+ *
+ * @param pool	    The pool
+ * @param type	    The type of object to be allocated
+ *
+ * @return	    Memory buffer of the specified type.
+ */
+#define PJ_POOL_ALLOC_TYPE(pool,type) \
+	    ((type*)pj_pool_alloc(pool, sizeof(type)))
+
+/**
+ * This macro allocates memory from the pool, zeroes the buffer, and 
+ * returns the instance of the specified type. It provides a stricker type 
+ * safety than pj_pool_zalloc() since the return value of this macro will be 
+ * type-casted to the specified type.
+ *
+ * @param pool	    The pool
+ * @param type	    The type of object to be allocated
+ *
+ * @return	    Memory buffer of the specified type.
+ */
+#define PJ_POOL_ZALLOC_TYPE(pool,type) \
+	    ((type*)pj_pool_zalloc(pool, sizeof(type)))
+
 
 
