Index: pjproject/trunk/pjlib-util/include/pjlib-util/stun_msg.h
===================================================================
--- pjproject/trunk/pjlib-util/include/pjlib-util/stun_msg.h	(revision 1019)
+++ pjproject/trunk/pjlib-util/include/pjlib-util/stun_msg.h	(revision 1021)
@@ -1175,5 +1175,7 @@
  * the FINGERPRINT CRC attribute for the message.
  *
- * @param msg		The STUN message to be printed.
+ * @param msg		The STUN message to be printed. Upon return,
+ *			some fields in the header (such as message
+ *			length) will be updated.
  * @param pkt_buf	The buffer to be filled with the packet.
  * @param buf_size	Size of the buffer.
@@ -1187,5 +1189,5 @@
  * @return		PJ_SUCCESS on success or the appropriate error code.
  */
-PJ_DECL(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg,
+PJ_DECL(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg,
 				        pj_uint8_t *pkt_buf,
 				        unsigned buf_size,
@@ -1423,6 +1425,6 @@
  * @param xor_ed	If non-zero, the port and address will be XOR-ed
  *			with magic, to make the XOR-MAPPED-ADDRESS attribute.
+ * @param addr		A pj_sockaddr_in or pj_sockaddr_in6 structure.
  * @param addr_len	Length of \a addr parameter.
- * @param addr		A pj_sockaddr_in or pj_sockaddr_in6 structure.
  * @param p_attr	Pointer to receive the attribute.
  *
@@ -1433,6 +1435,6 @@
 				    int attr_type, 
 				    pj_bool_t xor_ed,
+				    const pj_sockaddr_t *addr,
 				    unsigned addr_len,
-				    const pj_sockaddr_t *addr,
 				    pj_stun_generic_ip_addr_attr **p_attr);
 
@@ -1448,6 +1450,6 @@
  * @param xor_ed	If non-zero, the port and address will be XOR-ed
  *			with magic, to make the XOR-MAPPED-ADDRESS attribute.
+ * @param addr		A pj_sockaddr_in or pj_sockaddr_in6 structure.
  * @param addr_len	Length of \a addr parameter.
- * @param addr		A pj_sockaddr_in or pj_sockaddr_in6 structure.
  *
  * @return		PJ_SUCCESS on success or the appropriate error code.
@@ -1458,6 +1460,6 @@
 				     int attr_type, 
 				     pj_bool_t xor_ed,
-				     unsigned addr_len,
-				     const pj_sockaddr_t *addr);
+				     const pj_sockaddr_t *addr,
+				     unsigned addr_len);
 
 /**
Index: pjproject/trunk/pjlib-util/include/pjlib-util/stun_session.h
===================================================================
--- pjproject/trunk/pjlib-util/include/pjlib-util/stun_session.h	(revision 1019)
+++ pjproject/trunk/pjlib-util/include/pjlib-util/stun_session.h	(revision 1021)
@@ -25,4 +25,15 @@
 #include <pj/list.h>
 
+PJ_BEGIN_DECL
+
+
+/* **************************************************************************/
+/**
+ * @defgroup PJLIB_UTIL_STUN_SESSION STUN Client/Server Session
+ * @brief STUN client and server session
+ * @ingroup PJLIB_UTIL_STUN
+ * @{
+ */
+
 
 /** Forward declaration for pj_stun_tx_data */
@@ -31,4 +42,5 @@
 /** Forward declaration for pj_stun_session */
 typedef struct pj_stun_session pj_stun_session;
+
 
 /**
@@ -52,13 +64,38 @@
      *			    packet sending.
      */
-    pj_status_t (*on_send_msg)(pj_stun_tx_data *tdata,
+    pj_status_t (*on_send_msg)(pj_stun_session *sess,
 			       const void *pkt,
 			       pj_size_t pkt_size,
-			       unsigned addr_len, 
-			       const pj_sockaddr_t *dst_addr);
-
-    /**
-     * Callback to be called when Binding response is received or the 
-     * transaction has timed out.
+			       const pj_sockaddr_t *dst_addr,
+			       unsigned addr_len);
+
+    /** 
+     * Callback to be called on incoming STUN request message. In the 
+     * callback processing, application MUST create a response by calling
+     * pj_stun_session_create_response() function and send the response
+     * with pj_stun_session_send_msg() function, before returning from
+     * the callback.
+     *
+     * @param sess	    The STUN session.
+     * @param pkt	    Pointer to the original STUN packet.
+     * @param pkt_len	    Length of the STUN packet.
+     * @param msg	    The parsed STUN request.
+     * @param src_addr	    Source address of the packet.
+     * @param src_addr_len  Length of the source address.
+     *
+     * @return		    The return value of this callback will be
+     *			    returned back to pj_stun_session_on_rx_pkt()
+     *			    function.
+     */
+    pj_status_t (*on_rx_request)(pj_stun_session *sess,
+				 const pj_uint8_t *pkt,
+				 unsigned pkt_len,
+				 const pj_stun_msg *msg,
+				 const pj_sockaddr_t *src_addr,
+				 unsigned src_addr_len);
+
+    /**
+     * Callback to be called when response is received or the transaction 
+     * has timed out.
      *
      * @param sess	    The STUN session.
@@ -70,59 +107,20 @@
      * @param response	    The response message, on successful transaction.
      */
-    void (*on_bind_response)(pj_stun_session *sess, 
-			     pj_status_t status, 
-			     pj_stun_tx_data *request,
-			     const pj_stun_msg *response);
-
-    /**
-     * Callback to be called when Allocate response is received or the 
-     * transaction has timed out.
-     *
-     * @param sess	    The STUN session.
-     * @param status	    Status of the request. If the value if not
-     *			    PJ_SUCCESS, the transaction has timed-out
-     *			    or other error has occurred, and the response
-     *			    argument may be NULL.
-     * @param request	    The original STUN request.
-     * @param response	    The response message, on successful transaction.
-     */
-    void (*on_allocate_response)(pj_stun_session *sess, 
-				 pj_status_t status, 
-				 pj_stun_tx_data *request,
-				 const pj_stun_msg *response);
-
-    /**
-     * Callback to be called when Set Active Destination response is received
-     * or the transaction has timed out.
-     *
-     * @param sess	    The STUN session.
-     * @param status	    Status of the request. If the value if not
-     *			    PJ_SUCCESS, the transaction has timed-out
-     *			    or other error has occurred, and the response
-     *			    argument may be NULL.
-     * @param request	    The original STUN request.
-     * @param response	    The response message, on successful transaction.
-     */
-    void (*on_set_active_destination_response)(pj_stun_session *sess, 
-					       pj_status_t status, 
-					       pj_stun_tx_data *request,
-					       const pj_stun_msg *response);
-
-    /**
-     * Callback to be called when Connect response is received or the 
-     * transaction has timed out.
-     *
-     * @param sess	    The STUN session.
-     * @param status	    Status of the request. If the value if not
-     *			    PJ_SUCCESS, the transaction has timed-out
-     *			    or other error has occurred, and the response
-     *			    argument may be NULL.
-     * @param request	    The original STUN request.
-     * @param response	    The response message, on successful transaction.
-     */
-    void (*on_connect_response)( pj_stun_session *sess, 
-				 pj_status_t status, 
-				 pj_stun_tx_data *request,
-				 const pj_stun_msg *response);
+    void (*on_request_complete)(pj_stun_session *sess,
+			        pj_status_t status,
+			        pj_stun_tx_data *tdata,
+			        const pj_stun_msg *response);
+
+
+    /**
+     * Type of callback to be called on incoming STUN indication.
+     */
+    pj_status_t (*on_rx_indication)(pj_stun_session *sess,
+				    const pj_uint8_t *pkt,
+				    unsigned pkt_len,
+				    const pj_stun_msg *msg,
+				    const pj_sockaddr_t *src_addr,
+				    unsigned src_addr_len);
+
 } pj_stun_session_cb;
 
@@ -360,4 +358,30 @@
 PJ_DECL(pj_status_t) pj_stun_session_create_data_ind(pj_stun_session *sess,
 						     pj_stun_tx_data **p_tdata);
+
+/**
+ * Create a STUN response message. After the message has been 
+ * successfully created, application can send the message by calling 
+ * pj_stun_session_send_msg().
+ *
+ * @param sess	    The STUN session instance.
+ * @param req	    The STUN request where the response is to be created.
+ * @param err_code  Error code to be set in the response, if error response
+ *		    is to be created, according to pj_stun_status enumeration.
+ *		    This argument MUST be zero if successful response is
+ *		    to be created.
+ * @param err_msg   Optional pointer for the error message string, when
+ *		    creating error response. If the value is NULL and the
+ *		    \a err_code is non-zero, then default error message will
+ *		    be used.
+ * @param p_tdata   Pointer to receive the response message created.
+ *
+ * @return	    PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_stun_session_create_response(pj_stun_session *sess,
+						     const pj_stun_msg *req,
+						     unsigned err_code,
+						     const pj_str_t *err_msg,
+						     pj_stun_tx_data **p_tdata);
+
 
 /**
@@ -373,6 +397,6 @@
  * @param sess	    The STUN session instance.
  * @param options   Optional flags, from pj_stun_session_option.
+ * @param dst_addr  The destination socket address.
  * @param addr_len  Length of destination address.
- * @param dst_addr  The destination socket address.
  * @param tdata	    The STUN transmit data containing the STUN message to
  *		    be sent.
@@ -382,6 +406,6 @@
 PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess,
 					      unsigned options,
+					      const pj_sockaddr_t *dst_addr,
 					      unsigned addr_len,
-					      const pj_sockaddr_t *dst_addr,
 					      pj_stun_tx_data *tdata);
 
@@ -403,4 +427,5 @@
  * @param packet     The packet containing STUN message.
  * @param pkt_size   Size of the packet.
+ * @param options    Options, from pj_stun_options.
  * @param parsed_len Optional pointer to receive the size of the parsed
  *		     STUN message (useful if packet is received via a
@@ -412,7 +437,29 @@
 					       const void *packet,
 					       pj_size_t pkt_size,
-					       unsigned *parsed_len);
-
-
+					       unsigned options,
+					       unsigned *parsed_len,
+					       const pj_sockaddr_t *src_addr,
+					       unsigned src_addr_len);
+
+/**
+ * Destroy the transmit data. Call this function only when tdata has been
+ * created but application doesn't want to send the message (perhaps
+ * because of other error).
+ *
+ * @param sess	    The STUN session.
+ * @param tdata	    The transmit data.
+ *
+ * @return	    PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(void) pj_stun_msg_destroy_tdata(pj_stun_session *sess,
+					pj_stun_tx_data *tdata);
+
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
 
 #endif	/* __PJLIB_UTIL_STUN_SESSION_H__ */
Index: pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg.c	(revision 1019)
+++ pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg.c	(revision 1021)
@@ -518,6 +518,6 @@
 				    int attr_type,
 				    pj_bool_t xor_ed,
+				    const pj_sockaddr_t *addr,
 				    unsigned addr_len,
-				    const pj_sockaddr_t *addr,
 				    pj_stun_generic_ip_addr_attr **p_attr)
 {
@@ -560,6 +560,6 @@
 				     int attr_type, 
 				     pj_bool_t xor_ed,
-				     unsigned addr_len,
-				     const pj_sockaddr_t *addr)
+				     const pj_sockaddr_t *addr,
+				     unsigned addr_len)
 {
     pj_stun_generic_ip_addr_attr *attr;
@@ -567,5 +567,5 @@
 
     status = pj_stun_generic_ip_addr_attr_create(pool, attr_type, xor_ed,
-					         addr_len, addr, &attr);
+					         addr, addr_len, &attr);
     if (status != PJ_SUCCESS)
 	return status;
@@ -1435,15 +1435,13 @@
 PJ_INLINE(pj_uint16_t) GET_VAL16(const pj_uint8_t *pdu, unsigned pos)
 {
-    pj_uint16_t val = (pj_uint16_t) ((pdu[pos] << 8) + pdu[pos+1]);
-    return pj_ntohs(val);
+    return (pj_uint16_t) ((pdu[pos] << 8) + pdu[pos+1]);
 }
 
 PJ_INLINE(pj_uint32_t) GET_VAL32(const pj_uint8_t *pdu, unsigned pos)
 {
-    pj_uint32_t val = (pdu[pos+0] << 24) +
-		      (pdu[pos+1] << 16) +
-		      (pdu[pos+2] <<  8) +
-		      (pdu[pos+3]);
-    return pj_ntohl(val);
+    return (pdu[pos+0] << 24) +
+	   (pdu[pos+1] << 16) +
+	   (pdu[pos+2] <<  8) +
+	   (pdu[pos+3]);
 }
 
@@ -1466,32 +1464,32 @@
 	return PJLIB_UTIL_ESTUNINMSGTYPE;
 
+    /* Check the PDU length */
+    msg_len = GET_VAL16(pdu, 2);
+    if ((msg_len + 20 > pdu_len) || 
+	((options & PJ_STUN_IS_DATAGRAM) && msg_len + 20 != pdu_len))
+    {
+	return PJLIB_UTIL_ESTUNINMSGLEN;
+    }
+
     /* If magic is set, then there is great possibility that this is
      * a STUN message.
      */
-    if (GET_VAL32(pdu, 4) != PJ_STUN_MAGIC)
-	return PJLIB_UTIL_ESTUNNOTMAGIC;
-
-    /* Check the PDU length */
-    msg_len = GET_VAL16(pdu, 2);
-    if ((msg_len > pdu_len) || 
-	((options & PJ_STUN_IS_DATAGRAM) && msg_len != pdu_len))
-    {
-	return PJLIB_UTIL_ESTUNINMSGLEN;
-    }
-
-    /* Check if FINGERPRINT attribute is present */
-    if (GET_VAL16(pdu, msg_len + 20) == PJ_STUN_ATTR_FINGERPRINT) {
-	pj_uint16_t attr_len = GET_VAL16(pdu, msg_len + 22);
-	pj_uint32_t fingerprint = GET_VAL32(pdu, msg_len + 24);
-	pj_uint32_t crc;
-
-	if (attr_len != 4)
-	    return PJLIB_UTIL_ESTUNINATTRLEN;
-
-	crc = pj_crc32_calc(pdu, msg_len + 20);
-	crc ^= STUN_XOR_FINGERPRINT;
-
-	if (crc != fingerprint)
-	    return PJLIB_UTIL_ESTUNFINGERPRINT;
+    if (GET_VAL32(pdu, 4) == PJ_STUN_MAGIC) {
+
+	/* Check if FINGERPRINT attribute is present */
+	if (GET_VAL16(pdu, msg_len + 20) == PJ_STUN_ATTR_FINGERPRINT) {
+	    pj_uint16_t attr_len = GET_VAL16(pdu, msg_len + 22);
+	    pj_uint32_t fingerprint = GET_VAL32(pdu, msg_len + 24);
+	    pj_uint32_t crc;
+
+	    if (attr_len != 4)
+		return PJLIB_UTIL_ESTUNINATTRLEN;
+
+	    crc = pj_crc32_calc(pdu, msg_len + 20);
+	    crc ^= STUN_XOR_FINGERPRINT;
+
+	    if (crc != fingerprint)
+		return PJLIB_UTIL_ESTUNFINGERPRINT;
+	}
     }
 
@@ -1820,5 +1818,5 @@
  * Print the message structure to a buffer.
  */
-PJ_DEF(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg,
+PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg,
 				       pj_uint8_t *buf, unsigned buf_size,
 				       unsigned options,
@@ -1834,5 +1832,5 @@
     unsigned printed;
     pj_status_t status;
-    unsigned i, length;
+    unsigned i;
 
 
@@ -1899,16 +1897,16 @@
      */
     if (amsg_integrity && afingerprint) {
-	length = (pj_uint16_t)((buf - start) - 20 + 24 + 8);
+	msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 24 + 8);
     } else if (amsg_integrity) {
-	length = (pj_uint16_t)((buf - start) - 20 + 24);
+	msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 24);
     } else if (afingerprint) {
-	length = (pj_uint16_t)((buf - start) - 20 + 8);
+	msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 8);
     } else {
-	length = (pj_uint16_t)((buf - start) - 20);
+	msg->hdr.length = (pj_uint16_t)((buf - start) - 20);
     }
 
     /* hdr->length = pj_htons(length); */
-    *(buf+2) = (pj_uint8_t)((length >> 8) & 0x00FF);
-    *(buf+3) = (pj_uint8_t)(length & 0x00FF);
+    start[2] = (pj_uint8_t)((msg->hdr.length >> 8) & 0x00FF);
+    start[3] = (pj_uint8_t)(msg->hdr.length & 0x00FF);
 
     /* Calculate message integrity, if present */
@@ -2089,5 +2087,5 @@
 	*p_response = NULL;
 
-    if (PJ_STUN_IS_REQUEST(msg->hdr.type))
+    if (!PJ_STUN_IS_REQUEST(msg->hdr.type))
 	p_response = NULL;
 
Index: pjproject/trunk/pjlib-util/src/pjlib-util/stun_session.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjlib-util/stun_session.c	(revision 1019)
+++ pjproject/trunk/pjlib-util/src/pjlib-util/stun_session.c	(revision 1021)
@@ -24,4 +24,5 @@
     pj_stun_endpoint	*endpt;
     pj_pool_t		*pool;
+    pj_mutex_t		*mutex;
     pj_stun_session_cb	 cb;
     void		*user_data;
@@ -48,5 +49,5 @@
 
 #if PJ_LOG_MAX_LEVEL >= 4
-#   define LOG_ERR_(sess, title, rc)
+#   define LOG_ERR_(sess, title, rc) stun_perror(sess, title, rc)
 static void stun_perror(pj_stun_session *sess, const char *title, 
 			pj_status_t status)
@@ -60,5 +61,5 @@
 
 #else
-#   define ERR_(sess, title, rc)
+#   define LOG_ERR_(sess, title, rc)
 #endif
 
@@ -116,10 +117,8 @@
 
 static pj_status_t create_tdata(pj_stun_session *sess,
-				unsigned msg_type,
 				void *user_data,
 			        pj_stun_tx_data **p_tdata)
 {
     pj_pool_t *pool;
-    pj_status_t status;
     pj_stun_tx_data *tdata;
 
@@ -134,20 +133,33 @@
     tdata->user_data = user_data;
 
+    *p_tdata = tdata;
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t create_request_tdata(pj_stun_session *sess,
+					unsigned msg_type,
+					void *user_data,
+					pj_stun_tx_data **p_tdata)
+{
+    pj_status_t status;
+    pj_stun_tx_data *tdata;
+
+    status = create_tdata(sess, user_data, &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
     /* Create STUN message */
-    status = pj_stun_msg_create(pool, msg_type,  PJ_STUN_MAGIC, 
+    status = pj_stun_msg_create(tdata->pool, msg_type,  PJ_STUN_MAGIC, 
 				NULL, &tdata->msg);
     if (status != PJ_SUCCESS) {
-	pj_pool_release(pool);
-	return status;
-    }
-
-    /* If this is a request, then copy the request's transaction ID
-     * as the transaction key.
-     */
-    if (PJ_STUN_IS_REQUEST(msg_type)) {
-	pj_assert(sizeof(tdata->client_key)==sizeof(tdata->msg->hdr.tsx_id));
-	pj_memcpy(tdata->client_key, tdata->msg->hdr.tsx_id,
-		  sizeof(tdata->msg->hdr.tsx_id));
-    }
+	pj_pool_release(tdata->pool);
+	return status;
+    }
+
+    /* copy the request's transaction ID as the transaction key. */
+    pj_assert(sizeof(tdata->client_key)==sizeof(tdata->msg->hdr.tsx_id));
+    pj_memcpy(tdata->client_key, tdata->msg->hdr.tsx_id,
+	      sizeof(tdata->msg->hdr.tsx_id));
 
     *p_tdata = tdata;
@@ -167,8 +179,19 @@
 }
 
-static pj_status_t session_apply_req(pj_stun_session *sess,
+/*
+ * Destroy the transmit data.
+ */
+PJ_DEF(void) pj_stun_msg_destroy_tdata( pj_stun_session *sess,
+					pj_stun_tx_data *tdata)
+{
+    PJ_UNUSED_ARG(sess);
+    destroy_tdata(tdata);
+}
+
+static pj_status_t apply_msg_options(pj_stun_session *sess,
 				     pj_pool_t *pool,
 				     unsigned options,
-				     pj_stun_msg *msg)
+				     pj_stun_msg *msg,
+				     pj_str_t **p_passwd)
 {
     pj_status_t status;
@@ -182,6 +205,8 @@
 	pj_stun_generic_string_attr *arealm;
 
+	*p_passwd = &sess->l_password;
+
 	/* Create and add USERNAME attribute */
-	status = pj_stun_generic_string_attr_create(sess->pool, 
+	status = pj_stun_generic_string_attr_create(pool, 
 						    PJ_STUN_ATTR_USERNAME,
 						    &sess->l_username,
@@ -193,5 +218,5 @@
 
 	/* Add REALM only when long term credential is used */
-	status = pj_stun_generic_string_attr_create(sess->pool, 
+	status = pj_stun_generic_string_attr_create(pool, 
 						    PJ_STUN_ATTR_REALM,
 						    &sess->l_realm,
@@ -203,11 +228,9 @@
 
 	/* Add MESSAGE-INTEGRITY attribute */
-	status = pj_stun_msg_integrity_attr_create(sess->pool, &amsgi);
+	status = pj_stun_msg_integrity_attr_create(pool, &amsgi);
 	PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
 
 	status = pj_stun_msg_add_attr(msg, &amsgi->hdr);
 	PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
-
-	PJ_TODO(COMPUTE_MESSAGE_INTEGRITY1);
 
     } else if (options & PJ_STUN_USE_SHORT_TERM_CRED) {
@@ -215,6 +238,8 @@
 	pj_stun_msg_integrity_attr *amsgi;
 
+	*p_passwd = &sess->s_password;
+
 	/* Create and add USERNAME attribute */
-	status = pj_stun_generic_string_attr_create(sess->pool, 
+	status = pj_stun_generic_string_attr_create(pool, 
 						    PJ_STUN_ATTR_USERNAME,
 						    &sess->s_username,
@@ -226,5 +251,5 @@
 
 	/* Add MESSAGE-INTEGRITY attribute */
-	status = pj_stun_msg_integrity_attr_create(sess->pool, &amsgi);
+	status = pj_stun_msg_integrity_attr_create(pool, &amsgi);
 	PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
 
@@ -232,5 +257,6 @@
 	PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
 
-	PJ_TODO(COMPUTE_MESSAGE_INTEGRITY2);
+    } else {
+	*p_passwd = NULL;
     }
 
@@ -239,5 +265,5 @@
 	pj_stun_fingerprint_attr *af;
 
-	status = pj_stun_generic_uint_attr_create(sess->pool, 
+	status = pj_stun_generic_uint_attr_create(pool, 
 						  PJ_STUN_ATTR_FINGERPRINT,
 						  0, &af);
@@ -260,23 +286,7 @@
     tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
 
-    switch (PJ_STUN_GET_METHOD(tdata->msg->hdr.type)) {
-    case PJ_STUN_BINDING_METHOD:
-	tdata->sess->cb.on_bind_response(tdata->sess, status, tdata, response);
-	break;
-    case PJ_STUN_ALLOCATE_METHOD:
-	tdata->sess->cb.on_allocate_response(tdata->sess, status,
-					     tdata, response);
-	break;
-    case PJ_STUN_SET_ACTIVE_DESTINATION_METHOD:
-	tdata->sess->cb.on_set_active_destination_response(tdata->sess, status,
-							   tdata, response);
-	break;
-    case PJ_STUN_CONNECT_METHOD:
-	tdata->sess->cb.on_connect_response(tdata->sess, status, tdata,
-					    response);
-	break;
-    default:
-	pj_assert(!"Unknown method");
-	break;
+    if (tdata->sess->cb.on_request_complete) {
+	(*tdata->sess->cb.on_request_complete)(tdata->sess, status, tdata, 
+					       response);
     }
 }
@@ -290,6 +300,6 @@
     tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
 
-    return tdata->sess->cb.on_send_msg(tdata, stun_pkt, pkt_size,
-				       tdata->addr_len, tdata->dst_addr);
+    return tdata->sess->cb.on_send_msg(tdata->sess, stun_pkt, pkt_size,
+				       tdata->dst_addr, tdata->addr_len);
 }
 
@@ -303,6 +313,10 @@
     pj_pool_t	*pool;
     pj_stun_session *sess;
+    pj_status_t status;
 
     PJ_ASSERT_RETURN(endpt && cb && p_sess, PJ_EINVAL);
+
+    if (name==NULL)
+	name = "sess%p";
 
     pool = pj_pool_create(endpt->pf, name, 4000, 4000, NULL);
@@ -316,8 +330,12 @@
     pj_list_init(&sess->pending_request_list);
 
+    status = pj_mutex_create_recursive(pool, name, &sess->mutex);
+    if (status != PJ_SUCCESS) {
+	pj_pool_release(pool);
+	return status;
+    }
+
     *p_sess = sess;
 
-    PJ_TODO(MUTEX_PROTECTION);
-
     return PJ_SUCCESS;
 }
@@ -327,4 +345,5 @@
     PJ_ASSERT_RETURN(sess, PJ_EINVAL);
 
+    pj_mutex_destroy(sess->mutex);
     pj_pool_release(sess->pool);
 
@@ -337,5 +356,7 @@
 {
     PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+    pj_mutex_lock(sess->mutex);
     sess->user_data = user_data;
+    pj_mutex_unlock(sess->mutex);
     return PJ_SUCCESS;
 }
@@ -356,7 +377,10 @@
 
     PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+
+    pj_mutex_lock(sess->mutex);
     pj_strdup_with_null(sess->pool, &sess->l_realm, realm ? realm : &nil);
     pj_strdup_with_null(sess->pool, &sess->l_username, user ? user : &nil);
     pj_strdup_with_null(sess->pool, &sess->l_password, passwd ? passwd : &nil);
+    pj_mutex_unlock(sess->mutex);
 
     return PJ_SUCCESS;
@@ -372,6 +396,9 @@
 
     PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+
+    pj_mutex_lock(sess->mutex);
     pj_strdup_with_null(sess->pool, &sess->s_username, user ? user : &nil);
     pj_strdup_with_null(sess->pool, &sess->s_password, passwd ? passwd : &nil);
+    pj_mutex_unlock(sess->mutex);
 
     return PJ_SUCCESS;
@@ -387,5 +414,6 @@
     PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL);
 
-    status = create_tdata(sess, PJ_STUN_BINDING_REQUEST, NULL, &tdata);
+    status = create_request_tdata(sess, PJ_STUN_BINDING_REQUEST, NULL, 
+				  &tdata);
     if (status != PJ_SUCCESS)
 	return status;
@@ -398,4 +426,6 @@
 							pj_stun_tx_data **p_tdata)
 {
+    PJ_UNUSED_ARG(sess);
+    PJ_UNUSED_ARG(p_tdata);
     PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
 }
@@ -406,4 +436,6 @@
 						  pj_stun_tx_data **p_tdata)
 {
+    PJ_UNUSED_ARG(sess);
+    PJ_UNUSED_ARG(p_tdata);
     PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
 }
@@ -412,4 +444,6 @@
 						        pj_stun_tx_data **p_tdata)
 {
+    PJ_UNUSED_ARG(sess);
+    PJ_UNUSED_ARG(p_tdata);
     PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
 }
@@ -419,4 +453,6 @@
 					     pj_stun_tx_data **p_tdata)
 {
+    PJ_UNUSED_ARG(sess);
+    PJ_UNUSED_ARG(p_tdata);
     PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
 }
@@ -425,4 +461,6 @@
 						     pj_stun_tx_data **p_tdata)
 {
+    PJ_UNUSED_ARG(sess);
+    PJ_UNUSED_ARG(p_tdata);
     PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
 }
@@ -431,13 +469,84 @@
 						     pj_stun_tx_data **p_tdata)
 {
+    PJ_UNUSED_ARG(sess);
+    PJ_UNUSED_ARG(p_tdata);
     PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP);
 }
+
+
+/*
+ * Create a STUN response message.
+ */
+PJ_DEF(pj_status_t) pj_stun_session_create_response( pj_stun_session *sess,
+						     const pj_stun_msg *req,
+						     unsigned err_code,
+						     const pj_str_t *err_msg,
+						     pj_stun_tx_data **p_tdata)
+{
+    pj_status_t status;
+    pj_stun_tx_data *tdata;
+
+    status = create_tdata(sess, NULL, &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Create STUN response message */
+    status = pj_stun_msg_create_response(tdata->pool, req, err_code, err_msg,
+					 &tdata->msg);
+    if (status != PJ_SUCCESS) {
+	pj_pool_release(tdata->pool);
+	return status;
+    }
+
+    /* copy the request's transaction ID as the transaction key. */
+    pj_assert(sizeof(tdata->client_key)==sizeof(req->hdr.tsx_id));
+    pj_memcpy(tdata->client_key, req->hdr.tsx_id, sizeof(req->hdr.tsx_id));
+
+    *p_tdata = tdata;
+
+    return PJ_SUCCESS;
+}
+
+
+/* Print outgoing message to log */
+static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg,
+			unsigned pkt_size, const pj_sockaddr_t *addr)
+{
+    const char *dst_name;
+    int dst_port;
+    const pj_sockaddr *dst = (const pj_sockaddr*)addr;
+    char buf[512];
+    
+    if (dst->sa_family == PJ_AF_INET) {
+	const pj_sockaddr_in *dst4 = (const pj_sockaddr_in*)dst;
+	dst_name = pj_inet_ntoa(dst4->sin_addr);
+	dst_port = pj_ntohs(dst4->sin_port);
+    } else if (dst->sa_family == PJ_AF_INET6) {
+	const pj_sockaddr_in6 *dst6 = (const pj_sockaddr_in6*)dst;
+	dst_name = "IPv6";
+	dst_port = pj_ntohs(dst6->sin6_port);
+    } else {
+	LOG_ERR_(sess, "Invalid address family", PJ_EINVAL);
+	return;
+    }
+
+    PJ_LOG(5,(SNAME(sess), 
+	      "TX %d bytes STUN message to %s:%d:\n"
+	      "--- begin STUN message ---\n"
+	      "%s"
+	      "--- end of STUN message ---\n",
+	      pkt_size, dst_name, dst_port,
+	      pj_stun_msg_dump(msg, buf, sizeof(buf), NULL)));
+
+}
+
 
 PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
 					      unsigned options,
+					      const pj_sockaddr_t *server,
 					      unsigned addr_len,
-					      const pj_sockaddr_t *server,
 					      pj_stun_tx_data *tdata)
 {
+    pj_str_t *password;
     pj_status_t status;
 
@@ -448,37 +557,14 @@
     tdata->pkt = pj_pool_alloc(tdata->pool, tdata->max_len);
 
-    if (PJ_LOG_MAX_LEVEL >= 5) {
-	char *buf = (char*) tdata->pkt;
-	const char *dst_name;
-	int dst_port;
-	const pj_sockaddr *dst = (const pj_sockaddr*)server;
-	
-	if (dst->sa_family == PJ_AF_INET) {
-	    const pj_sockaddr_in *dst4 = (const pj_sockaddr_in*)dst;
-	    dst_name = pj_inet_ntoa(dst4->sin_addr);
-	    dst_port = pj_ntohs(dst4->sin_port);
-	} else if (dst->sa_family == PJ_AF_INET6) {
-	    const pj_sockaddr_in6 *dst6 = (const pj_sockaddr_in6*)dst;
-	    dst_name = "IPv6";
-	    dst_port = pj_ntohs(dst6->sin6_port);
-	} else {
-	    LOG_ERR_(sess, "Invalid address family", PJ_EINVAL);
-	    return PJ_EINVAL;
-	}
-
-	PJ_LOG(5,(SNAME(sess), 
-		  "Sending STUN message to %s:%d:\n"
-		  "--- begin STUN message ---\n"
-		  "%s"
-		  "--- end of STUN message ---\n",
-		  dst_name, dst_port,
-		  pj_stun_msg_dump(tdata->msg, buf, tdata->max_len, NULL)));
-    }
+    /* Start locking the session now */
+    pj_mutex_lock(sess->mutex);
 
     /* Apply options */
-    status = session_apply_req(sess, tdata->pool, options, tdata->msg);
+    status = apply_msg_options(sess, tdata->pool, options, 
+			       tdata->msg, &password);
     if (status != PJ_SUCCESS) {
+	pj_stun_msg_destroy_tdata(sess, tdata);
+	pj_mutex_unlock(sess->mutex);
 	LOG_ERR_(sess, "Error applying options", status);
-	destroy_tdata(tdata);
 	return status;
     }
@@ -488,8 +574,12 @@
 			        0, NULL, &tdata->pkt_size);
     if (status != PJ_SUCCESS) {
+	pj_stun_msg_destroy_tdata(sess, tdata);
+	pj_mutex_unlock(sess->mutex);
 	LOG_ERR_(sess, "STUN encode() error", status);
-	destroy_tdata(tdata);
-	return status;
-    }
+	return status;
+    }
+
+    /* Dump packet */
+    dump_tx_msg(sess, tdata->msg, tdata->pkt_size, server);
 
     /* If this is a STUN request message, then send the request with
@@ -512,6 +602,7 @@
 					     tdata->pkt, tdata->pkt_size);
 	if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	    pj_stun_msg_destroy_tdata(sess, tdata);
+	    pj_mutex_unlock(sess->mutex);
 	    LOG_ERR_(sess, "Error sending STUN request", status);
-	    destroy_tdata(tdata);
 	    return status;
 	}
@@ -522,16 +613,139 @@
     } else {
 	/* Otherwise for non-request message, send directly to transport. */
-	status = sess->cb.on_send_msg(tdata, tdata->pkt, tdata->pkt_size,
-				      addr_len, server);
+	status = sess->cb.on_send_msg(sess, tdata->pkt, tdata->pkt_size,
+				      server, addr_len);
 
 	if (status != PJ_SUCCESS && status != PJ_EPENDING) {
 	    LOG_ERR_(sess, "Error sending STUN request", status);
-	    destroy_tdata(tdata);
-	    return status;
 	}
-    }
-
-
+
+	/* Destroy */
+	pj_stun_msg_destroy_tdata(sess, tdata);
+    }
+
+
+    pj_mutex_unlock(sess->mutex);
     return status;
+}
+
+
+/* Handle incoming response */
+static pj_status_t on_incoming_response(pj_stun_session *sess,
+					pj_stun_msg *msg)
+{
+    pj_stun_tx_data *tdata;
+    pj_status_t status;
+
+    /* Lookup pending client transaction */
+    tdata = tsx_lookup(sess, msg);
+    if (tdata == NULL) {
+	LOG_ERR_(sess, "STUN error finding transaction", PJ_ENOTFOUND);
+	return PJ_ENOTFOUND;
+    }
+
+    /* Pass the response to the transaction. 
+     * If the message is accepted, transaction callback will be called,
+     * and this will call the session callback too.
+     */
+    status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg);
+    if (status != PJ_SUCCESS) {
+	return status;
+    }
+
+    /* If transaction has completed, destroy the transmit data.
+     * This will remove the transaction from the pending list too.
+     */
+    if (pj_stun_client_tsx_is_complete(tdata->client_tsx)) {
+	pj_stun_msg_destroy_tdata(sess, tdata);
+	tdata = NULL;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/* Send response */
+static pj_status_t send_response(pj_stun_session *sess, unsigned options,
+				 pj_pool_t *pool, pj_stun_msg *response,
+				 const pj_sockaddr_t *addr, unsigned addr_len)
+{
+    pj_uint8_t *out_pkt;
+    unsigned out_max_len, out_len;
+    pj_str_t *passwd;
+    pj_status_t status;
+
+    /* Alloc packet buffer */
+    out_max_len = PJ_STUN_MAX_PKT_LEN;
+    out_pkt = pj_pool_alloc(pool, out_max_len);
+
+    /* Apply options */
+    apply_msg_options(sess, pool, options, response, &passwd);
+
+    /* Encode */
+    status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0, 
+				passwd, &out_len);
+    if (status != PJ_SUCCESS) {
+	LOG_ERR_(sess, "Error encoding message", status);
+	return status;
+    }
+
+    /* Print log */
+    dump_tx_msg(sess, response, out_len, addr);
+
+    /* Send packet */
+    status = sess->cb.on_send_msg(sess, out_pkt, out_len, addr, addr_len);
+
+    return status;
+}
+
+/* Handle incoming request */
+static pj_status_t on_incoming_request(pj_stun_session *sess,
+				       pj_pool_t *tmp_pool,
+				       const pj_uint8_t *in_pkt,
+				       unsigned in_pkt_len,
+				       const pj_stun_msg *msg,
+				       const pj_sockaddr_t *src_addr,
+				       unsigned src_addr_len)
+{
+    pj_status_t status;
+
+    /* Distribute to handler, or respond with Bad Request */
+    if (sess->cb.on_rx_request) {
+	status = (*sess->cb.on_rx_request)(sess, in_pkt, in_pkt_len, msg,
+					   src_addr, src_addr_len);
+    } else {
+	pj_stun_msg *response = NULL;
+
+	status = pj_stun_msg_create_response(tmp_pool, msg, 
+					     PJ_STUN_STATUS_BAD_REQUEST, NULL,
+					     &response);
+	if (status == PJ_SUCCESS && response) {
+	    status = send_response(sess, 0, tmp_pool, response, 
+				   src_addr, src_addr_len);
+	}
+    }
+
+    return status;    
+}
+
+
+/* Handle incoming indication */
+static pj_status_t on_incoming_indication(pj_stun_session *sess,
+					  pj_pool_t *tmp_pool,
+					  const pj_uint8_t *in_pkt,
+					  unsigned in_pkt_len,
+					  const pj_stun_msg *msg,
+					  const pj_sockaddr_t *src_addr,
+					  unsigned src_addr_len)
+{
+    PJ_UNUSED_ARG(tmp_pool);
+
+    /* Distribute to handler */
+    if (sess->cb.on_rx_indication) {
+	return (*sess->cb.on_rx_indication)(sess, in_pkt, in_pkt_len, msg,
+					    src_addr, src_addr_len);
+    } else {
+	return PJ_SUCCESS;
+    }
 }
 
@@ -540,5 +754,8 @@
 					      const void *packet,
 					      pj_size_t pkt_size,
-					      unsigned *parsed_len)
+					      unsigned options,
+					      unsigned *parsed_len,
+					      const pj_sockaddr_t *src_addr,
+					      unsigned src_addr_len)
 {
     pj_stun_msg *msg, *response;
@@ -555,10 +772,11 @@
     /* Try to parse the message */
     status = pj_stun_msg_decode(tmp_pool, (const pj_uint8_t*)packet,
-			        pkt_size, 0, &msg, parsed_len,
-				&response);
+			        pkt_size, options, 
+				&msg, parsed_len, &response);
     if (status != PJ_SUCCESS) {
 	LOG_ERR_(sess, "STUN msg_decode() error", status);
 	if (response) {
-	    PJ_TODO(SEND_RESPONSE);
+	    send_response(sess, 0, tmp_pool, response, 
+			  src_addr, src_addr_len);
 	}
 	pj_pool_release(tmp_pool);
@@ -568,60 +786,38 @@
     dump = pj_pool_alloc(tmp_pool, PJ_STUN_MAX_PKT_LEN);
 
-    PJ_LOG(4,(SNAME(sess), 
+    PJ_LOG(4,(SNAME(sess),
 	      "RX STUN message:\n"
-	      "--- begin STUN message ---"
+	      "--- begin STUN message ---\n"
 	      "%s"
 	      "--- end of STUN message ---\n",
 	      pj_stun_msg_dump(msg, dump, PJ_STUN_MAX_PKT_LEN, NULL)));
 
+    pj_mutex_lock(sess->mutex);
 
     if (PJ_STUN_IS_RESPONSE(msg->hdr.type) ||
 	PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
     {
-	pj_stun_tx_data *tdata;
-
-	/* Lookup pending client transaction */
-	tdata = tsx_lookup(sess, msg);
-	if (tdata == NULL) {
-	    LOG_ERR_(sess, "STUN error finding transaction", PJ_ENOTFOUND);
-	    pj_pool_release(tmp_pool);
-	    return PJ_ENOTFOUND;
-	}
-
-	/* Pass the response to the transaction. 
-	 * If the message is accepted, transaction callback will be called,
-	 * and this will call the session callback too.
-	 */
-	status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg);
-	if (status != PJ_SUCCESS) {
-	    pj_pool_release(tmp_pool);
-	    return status;
-	}
-
-	/* If transaction has completed, destroy the transmit data.
-	 * This will remove the transaction from the pending list too.
-	 */
-	if (pj_stun_client_tsx_is_complete(tdata->client_tsx)) {
-	    destroy_tdata(tdata);
-	    tdata = NULL;
-	}
-
-	pj_pool_release(tmp_pool);
-	return PJ_SUCCESS;
+	status = on_incoming_response(sess, msg);
 
     } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
 
-	PJ_TODO(HANDLE_INCOMING_STUN_REQUEST);
+	status = on_incoming_request(sess, tmp_pool, packet, pkt_size, msg,
+				     src_addr, src_addr_len);
 
     } else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) {
 
-	PJ_TODO(HANDLE_INCOMING_STUN_INDICATION);
+	status = on_incoming_indication(sess, tmp_pool, packet, pkt_size,
+					msg, src_addr, src_addr_len);
 
     } else {
 	pj_assert(!"Unexpected!");
-    }
+	status = PJ_EBUG;
+    }
+
+    pj_mutex_unlock(sess->mutex);
 
     pj_pool_release(tmp_pool);
-    return PJ_ENOTSUP;
-}
-
+    return status;
+}
+
+
Index: pjproject/trunk/pjlib-util/src/pjstun-srv-test/server.h
===================================================================
--- pjproject/trunk/pjlib-util/src/pjstun-srv-test/server.h	(revision 1019)
+++ 	(revision )
@@ -1,61 +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 __PJSTUN_SERVER_H__
-#define __PJSTUN_SERVER_H__
-
-
-#define MAX_SERVICE	16
-#define MAX_PKT_LEN	512
-
-struct service
-{
-    unsigned		 index;
-    pj_uint16_t		 port;
-    pj_bool_t		 is_stream;
-    pj_sock_t		 sock;
-    pj_ioqueue_key_t	*key;
-    pj_ioqueue_op_key_t  recv_opkey,
-			 send_opkey;
-
-    int		         src_addr_len;
-    pj_sockaddr_in	 src_addr;
-    pj_ssize_t		 rx_pkt_len;
-    pj_uint8_t		 rx_pkt[MAX_PKT_LEN];
-    pj_uint8_t		 tx_pkt[MAX_PKT_LEN];
-};
-
-struct stun_server_tag
-{
-    pj_caching_pool	 cp;
-    pj_pool_t		*pool;
-    pj_ioqueue_t	*ioqueue;
-    unsigned		 service_cnt;
-    struct service	 services[MAX_SERVICE];
-
-    pj_bool_t		 thread_quit_flag;
-    unsigned		 thread_cnt;
-    pj_thread_t		*threads[16];
-
-};
-
-extern struct stun_server_tag server;
-
-
-#endif	/* __PJSTUN_SERVER_H__ */
-
Index: pjproject/trunk/pjlib-util/src/pjstun-srv-test/server_main.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjstun-srv-test/server_main.c	(revision 1019)
+++ pjproject/trunk/pjlib-util/src/pjstun-srv-test/server_main.c	(revision 1021)
@@ -19,5 +19,4 @@
 #include <pjlib-util.h>
 #include <pjlib.h>
-#include "server.h"
 
 #include <stdio.h>
@@ -27,10 +26,45 @@
 #define THIS_FILE	"server_main.c"
 #define MAX_THREADS	8
-
-struct stun_server_tag server;
-
-
-pj_status_t server_perror(const char *sender, const char *title, 
-			  pj_status_t status)
+#define MAX_SERVICE	16
+#define MAX_PKT_LEN	512
+
+struct service
+{
+    unsigned		 index;
+    pj_uint16_t		 port;
+    pj_bool_t		 is_stream;
+    pj_sock_t		 sock;
+    pj_ioqueue_key_t	*key;
+    pj_ioqueue_op_key_t  recv_opkey,
+			 send_opkey;
+
+    pj_stun_session	*sess;
+
+    int		         src_addr_len;
+    pj_sockaddr_in	 src_addr;
+    pj_ssize_t		 rx_pkt_len;
+    pj_uint8_t		 rx_pkt[MAX_PKT_LEN];
+    pj_uint8_t		 tx_pkt[MAX_PKT_LEN];
+};
+
+static struct stun_server
+{
+    pj_caching_pool	 cp;
+    pj_pool_t		*pool;
+    pj_stun_endpoint	*endpt;
+    pj_ioqueue_t	*ioqueue;
+    pj_timer_heap_t	*timer_heap;
+    unsigned		 service_cnt;
+    struct service	 services[MAX_SERVICE];
+
+    pj_bool_t		 thread_quit_flag;
+    unsigned		 thread_cnt;
+    pj_thread_t		*threads[16];
+
+} server;
+
+
+static pj_status_t server_perror(const char *sender, const char *title, 
+				 pj_status_t status)
 {
     char errmsg[PJ_ERR_MSG_SIZE];
@@ -43,72 +77,35 @@
 
 
-static pj_status_t create_response(pj_pool_t *pool,
-				   const pj_stun_msg *req_msg,
-				   unsigned err_code,
-				   unsigned uattr_cnt,
-				   pj_uint16_t uattr_types[],
-				   pj_stun_msg **p_response)
-{
-    pj_uint32_t msg_type = req_msg->hdr.type;
-    pj_stun_msg *response;
-    pj_status_t status;
-
-    status = pj_stun_msg_create_response(pool, req_msg, err_code, NULL,
-					 &response);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    /* Add unknown_attribute attributes if err_code is 420 */
-    if (err_code == PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE) {
-	pj_stun_unknown_attr *uattr;
-
-	status = pj_stun_unknown_attr_create(pool, uattr_cnt, uattr_types,
-					     &uattr);
-	if (status != PJ_SUCCESS)
-	    return status;
-
-	pj_stun_msg_add_attr(response, &uattr->hdr);
-    }
-
-    *p_response = response;
-    return PJ_SUCCESS;
-}
-
-
-static pj_status_t send_msg(struct service *svc, const pj_stun_msg *msg)
-{
-    unsigned tx_pkt_len;
+/* Callback to be called to send outgoing message */
+static pj_status_t on_send_msg(pj_stun_session *sess,
+			       const void *pkt,
+			       pj_size_t pkt_size,
+			       const pj_sockaddr_t *dst_addr,
+			       unsigned addr_len)
+{
+    struct service *svc;
     pj_ssize_t length;
     pj_status_t status;
 
-    /* Print to log */
-    PJ_LOG(4,(THIS_FILE, "TX STUN message: \n"
-			 "--- begin STUN message ---\n"
-			 "%s"
-			 "--- end of STUN message ---\n", 
-	      pj_stun_msg_dump(msg, svc->tx_pkt, sizeof(svc->tx_pkt), NULL)));
-
-    /* Encode packet */
-    tx_pkt_len = sizeof(svc->tx_pkt);
-    status = pj_stun_msg_encode(msg, svc->tx_pkt, tx_pkt_len, 0,
-				NULL, &tx_pkt_len);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    length = tx_pkt_len;
+    svc = (struct service*) pj_stun_session_get_user_data(sess);
 
     /* Send packet */
+    length = pkt_size;
     if (svc->is_stream) {
-	status = pj_ioqueue_send(svc->key, &svc->send_opkey, svc->tx_pkt,
-				 &length, 0);
+	status = pj_ioqueue_send(svc->key, &svc->send_opkey, pkt, &length, 0);
     } else {
-	status = pj_ioqueue_sendto(svc->key, &svc->send_opkey, svc->tx_pkt,
-				   &length, 0, &svc->src_addr, 
-				   svc->src_addr_len);
-    }
-
-    PJ_LOG(4,(THIS_FILE, "Sending STUN %s %s",
-	      pj_stun_get_method_name(msg->hdr.type),
-	      pj_stun_get_class_name(msg->hdr.type)));
+#if 0
+	pj_pool_t *pool;
+	char *buf;
+	pj_stun_msg *msg;
+
+	pool = pj_pool_create(&server.cp.factory, "", 4000, 4000, NULL);
+	status = pj_stun_msg_decode(pool, pkt, pkt_size, PJ_STUN_CHECK_PACKET, &msg, NULL, NULL);
+	buf = pj_pool_alloc(pool, 512);
+	PJ_LOG(3,("", "%s", pj_stun_msg_dump(msg, buf, 512, NULL)));
+#endif
+	status = pj_ioqueue_sendto(svc->key, &svc->send_opkey, pkt, &length, 
+				   0, dst_addr, addr_len);
+    }
 
     return (status == PJ_SUCCESS || status == PJ_EPENDING) ? 
@@ -117,86 +114,110 @@
 
 
-static pj_status_t err_respond(struct service *svc,
-			       pj_pool_t *pool,
-			       const pj_stun_msg *req_msg,
-			       unsigned err_code,
-			       unsigned uattr_cnt,
-			       pj_uint16_t uattr_types[])
-{
-    pj_stun_msg *response;
-    pj_status_t status;
-
-    /* Create the error response */
-    status = create_response(pool, req_msg, err_code,
-			     uattr_cnt, uattr_types, &response);
+/* Handle STUN binding request */
+static pj_status_t on_rx_binding_request(pj_stun_session *sess,
+					 const pj_uint8_t *pkt,
+					 unsigned pkt_len,
+					 const pj_stun_msg *msg,
+					 const pj_sockaddr_t *src_addr,
+					 unsigned src_addr_len)
+{
+    struct service *svc = (struct service *) pj_stun_session_get_user_data(sess);
+    pj_stun_tx_data *tdata;
+    pj_stun_auth_policy pol;
+    pj_status_t status;
+
+    /* Create response */
+    status = pj_stun_session_create_response(sess, msg, 0, NULL, &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
+#if 1
+    pj_memset(&pol, 0, sizeof(pol));
+    pol.type = PJ_STUN_POLICY_STATIC_LONG_TERM;
+    pol.user_data = NULL;
+    pol.data.static_long_term.realm = pj_str("realm");
+    pol.data.static_long_term.username = pj_str("user");
+    pol.data.static_long_term.password = pj_str("password");
+    pol.data.static_long_term.nonce = pj_str("nonce");
+    status = pj_stun_verify_credential(pkt, pkt_len, msg, &pol, tdata->pool, 
+				       &tdata->msg);
+    if (!tdata->msg)
+	return status;
+#endif
+
+    /* Create MAPPED-ADDRESS attribute */
+    status = pj_stun_msg_add_generic_ip_addr_attr(tdata->pool, tdata->msg,
+						  PJ_STUN_ATTR_MAPPED_ADDR,
+						  PJ_FALSE,
+					          src_addr, src_addr_len);
     if (status != PJ_SUCCESS) {
 	server_perror(THIS_FILE, "Error creating response", status);
+	pj_stun_msg_destroy_tdata(sess, tdata);
 	return status;
     }
 
-    /* Send response */
-    status = send_msg(svc, response);
-    if (status != PJ_SUCCESS) {
-	server_perror(THIS_FILE, "Error sending response", status);
-	return status;
-    }
-
-    return PJ_SUCCESS;
-}
-
-
-static void handle_binding_request(struct service *svc, pj_pool_t *pool, 
-				   const pj_stun_msg *rx_msg)
-{
-    pj_stun_msg *response;
-    pj_stun_generic_ip_addr_attr *m_attr;
-    pj_status_t status;
-
-    status = create_response(pool, rx_msg, 0, 0, NULL, &response);
-    if (status != PJ_SUCCESS) {
-	server_perror(THIS_FILE, "Error creating response", status);
-	return;
-    }
-
-    /* Create MAPPED-ADDRESS attribute */
-    status = pj_stun_generic_ip_addr_attr_create(pool,
-						 PJ_STUN_ATTR_MAPPED_ADDR,
-					         PJ_FALSE,
-					         svc->src_addr_len,
-					         &svc->src_addr, &m_attr);
-    if (status != PJ_SUCCESS) {
-	server_perror(THIS_FILE, "Error creating response", status);
-	return;
-    }
-    pj_stun_msg_add_attr(response, &m_attr->hdr);
-
     /* On the presence of magic, create XOR-MAPPED-ADDRESS attribute */
-    if (rx_msg->hdr.magic == PJ_STUN_MAGIC) {
+    if (msg->hdr.magic == PJ_STUN_MAGIC) {
 	status = 
-	    pj_stun_generic_ip_addr_attr_create(pool, 
-						PJ_STUN_ATTR_XOR_MAPPED_ADDRESS,
-						PJ_TRUE,
-						svc->src_addr_len,
-						&svc->src_addr, &m_attr);
+	    pj_stun_msg_add_generic_ip_addr_attr(tdata->pool, tdata->msg,
+						 PJ_STUN_ATTR_XOR_MAPPED_ADDRESS,
+						 PJ_TRUE,
+						 src_addr, src_addr_len);
 	if (status != PJ_SUCCESS) {
 	    server_perror(THIS_FILE, "Error creating response", status);
-	    return;
+	    pj_stun_msg_destroy_tdata(sess, tdata);
+	    return status;
 	}
     }
 
     /* Send */
-    status = send_msg(svc, response);
-    if (status != PJ_SUCCESS)
-	server_perror(THIS_FILE, "Error sending response", status);
-}
-
-
-static void handle_unknown_request(struct service *svc, pj_pool_t *pool, 
-				   pj_stun_msg *rx_msg)
-{
-    err_respond(svc, pool, rx_msg, PJ_STUN_STATUS_BAD_REQUEST, 0, NULL);
-}
-
-
+    status = pj_stun_session_send_msg(sess, 0, src_addr, src_addr_len, tdata);
+    return status;
+}
+
+
+/* Handle unknown request */
+static pj_status_t on_rx_unknown_request(pj_stun_session *sess,
+					 const pj_uint8_t *pkt,
+					 unsigned pkt_len,
+					 const pj_stun_msg *msg,
+					 const pj_sockaddr_t *src_addr,
+					 unsigned src_addr_len)
+{
+    pj_stun_tx_data *tdata;
+    pj_status_t status;
+
+    /* Create response */
+    status = pj_stun_session_create_response(sess, msg, 
+					     PJ_STUN_STATUS_BAD_REQUEST,
+					     NULL, &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Send */
+    status = pj_stun_session_send_msg(sess, 0, src_addr, src_addr_len, tdata);
+    return status;
+}
+
+/* Callback to be called by STUN session on incoming STUN requests */
+static pj_status_t on_rx_request(pj_stun_session *sess,
+				 const pj_uint8_t *pkt,
+				 unsigned pkt_len,
+				 const pj_stun_msg *msg,
+				 const pj_sockaddr_t *src_addr,
+				 unsigned src_addr_len)
+{
+    switch (PJ_STUN_GET_METHOD(msg->hdr.type)) {
+    case PJ_STUN_BINDING_METHOD:
+	return on_rx_binding_request(sess, pkt, pkt_len, msg, 
+				     src_addr, src_addr_len);
+    default:
+	return on_rx_unknown_request(sess, pkt, pkt_len, msg,
+				     src_addr, src_addr_len);
+    }
+}
+
+
+/* Callback on ioqueue read completion */
 static void on_read_complete(pj_ioqueue_key_t *key, 
 			     pj_ioqueue_op_key_t *op_key, 
@@ -204,7 +225,4 @@
 {
     struct service *svc = (struct service *) pj_ioqueue_get_user_data(key);
-    pj_pool_t *pool = NULL;
-    pj_stun_msg *rx_msg, *response;
-    char dump[512];
     pj_status_t status;
 
@@ -212,38 +230,13 @@
 	goto next_read;
 
-    pool = pj_pool_create(&server.cp.factory, "service", 4000, 4000, NULL);
-
-    rx_msg = NULL;
-    status = pj_stun_msg_decode(pool, svc->rx_pkt, bytes_read, 0, &rx_msg,
-				NULL, &response);
+    /* Handle packet to session */
+    status = pj_stun_session_on_rx_pkt(svc->sess, svc->rx_pkt, bytes_read,
+				       PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET,
+				       NULL, &svc->src_addr, svc->src_addr_len);
     if (status != PJ_SUCCESS) {
-	server_perror(THIS_FILE, "STUN msg_decode() error", status);
-	if (response) {
-	    send_msg(svc, response);
-	}
-	goto next_read;
-    }
-
-    PJ_LOG(4,(THIS_FILE, "RX STUN message: \n"
-			 "--- begin STUN message ---\n"
-			 "%s"
-			 "--- end of STUN message ---\n", 
-	      pj_stun_msg_dump(rx_msg, dump, sizeof(dump), NULL)));
-
-    if (PJ_STUN_IS_REQUEST(rx_msg->hdr.type)) {
-	switch (rx_msg->hdr.type) {
-	case PJ_STUN_BINDING_REQUEST:
-	    handle_binding_request(svc, pool, rx_msg);
-	    break;
-	default:
-	    handle_unknown_request(svc, pool, rx_msg);
-	}
-	
+	server_perror(THIS_FILE, "Error processing incoming packet", status);
     }
 
 next_read:
-    if (pool != NULL)
-	pj_pool_release(pool);
-
     if (bytes_read < 0) {
 	server_perror(THIS_FILE, "on_read_complete()", -bytes_read);
@@ -266,4 +259,5 @@
     pj_status_t status;
     pj_ioqueue_callback service_callback;
+    pj_stun_session_cb sess_cb;
     pj_sockaddr_in addr;
 
@@ -279,4 +273,14 @@
     if (status != PJ_SUCCESS)
 	goto on_error;
+
+    pj_bzero(&sess_cb, sizeof(sess_cb));
+    sess_cb.on_send_msg = &on_send_msg;
+    sess_cb.on_rx_request = &on_rx_request;
+    status = pj_stun_session_create(server.endpt, "session", 
+				    &sess_cb, &svc->sess);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    pj_stun_session_set_user_data(svc->sess, (void*)svc);
 
     pj_bzero(&service_callback, sizeof(service_callback));
@@ -317,4 +321,5 @@
     while (!server.thread_quit_flag) {
 	pj_time_val timeout = { 0, 50 };
+	pj_timer_heap_poll(server.timer_heap, NULL);
 	pj_ioqueue_poll(server.ioqueue, &timeout);
     }
@@ -347,4 +352,14 @@
     if (status != PJ_SUCCESS)
 	return server_perror(THIS_FILE, "pj_ioqueue_create()", status);
+
+    status = pj_timer_heap_create(server.pool, 1024, &server.timer_heap);
+    if (status != PJ_SUCCESS)
+	return server_perror(THIS_FILE, "Error creating timer heap", status);
+
+    status = pj_stun_endpoint_create(&server.cp.factory, 0, 
+				     server.ioqueue, server.timer_heap, 
+				     &server.endpt);
+    if (status != PJ_SUCCESS)
+	return server_perror(THIS_FILE, "Error creating endpoint", status);
 
     server.service_cnt = 1;
@@ -362,7 +377,8 @@
 pj_status_t server_main(void)
 {
-#if 1
+#if 0
     for (;;) {
 	pj_time_val timeout = { 0, 50 };
+	pj_timer_heap_poll(server.timer_heap, NULL);
 	pj_ioqueue_poll(server.ioqueue, &timeout);
 
@@ -414,6 +430,10 @@
     }
 
+    pj_stun_session_destroy(server.services[0].sess);
+    pj_stun_endpoint_destroy(server.endpt);
     pj_ioqueue_destroy(server.ioqueue);
     pj_pool_release(server.pool);
+
+    pj_pool_factory_dump(&server.cp.factory, PJ_TRUE);
     pj_caching_pool_destroy(&server.cp);
 
