Index: pjproject/trunk/pjlib-util/build/pjstun_srv_test.dsp
===================================================================
--- pjproject/trunk/pjlib-util/build/pjstun_srv_test.dsp	(revision 1048)
+++ pjproject/trunk/pjlib-util/build/pjstun_srv_test.dsp	(revision 1049)
@@ -100,4 +100,8 @@
 # Begin Source File
 
+SOURCE="..\src\pjstun-srv-test\turn_usage.c"
+# End Source File
+# Begin Source File
+
 SOURCE="..\src\pjstun-srv-test\usage.c"
 # End Source File
Index: pjproject/trunk/pjlib-util/include/pjlib-util/stun_msg.h
===================================================================
--- pjproject/trunk/pjlib-util/include/pjlib-util/stun_msg.h	(revision 1048)
+++ pjproject/trunk/pjlib-util/include/pjlib-util/stun_msg.h	(revision 1049)
@@ -339,11 +339,11 @@
     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_ADDR_TYPE= 0x0017,/**< REQUESTED-ADDRESS-TYPE    */
-    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_RELAY_ADDR	    = 0x0016,/**< RELAY-ADDRESS attribute.  */
+    PJ_STUN_ATTR_REQ_ADDR_TYPE	    = 0x0017,/**< REQUESTED-ADDRESS-TYPE    */
+    PJ_STUN_ATTR_REQ_PORT_PROPS	    = 0x0018,/**< REQUESTED-PORT-PROPS	    */
+    PJ_STUN_ATTR_REQ_TRANSPORT	    = 0x0019,/**< REQUESTED-TRANSPORT	    */
+    PJ_STUN_ATTR_XOR_MAPPED_ADDR    = 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_REQ_IP		    = 0x0022,/**< REQUESTED-IP attribute    */
     PJ_STUN_ATTR_XOR_REFLECTED_FROM = 0x0023,/**< XOR-REFLECTED-FROM	    */
     PJ_STUN_ATTR_PRIORITY	    = 0x0024,/**< PRIORITY		    */
@@ -391,5 +391,7 @@
     PJ_STUN_STATUS_CONNECTION_FAILURE	    = 446,  /**< Connection Failure */
     PJ_STUN_STATUS_CONNECTION_TIMEOUT	    = 447,  /**< Connection Timeout */
+    PJ_STUN_STATUS_ALLOCATION_QUOTA_REACHED = 486,  /**< Allocation Quota Reached (TURN) */
     PJ_STUN_STATUS_SERVER_ERROR		    = 500,  /**< Server Error	    */
+    PJ_STUN_STATUS_INSUFFICIENT_CAPACITY    = 507,  /**< Insufficient Capacity (TURN) */
     PJ_STUN_STATUS_GLOBAL_FAILURE	    = 600   /**< Global Failure	    */
 } pj_stun_status;
@@ -934,5 +936,5 @@
  \endverbatim
  */
-typedef struct pj_stun_uint_attr pj_stun_requested_addr_type;
+typedef struct pj_stun_uint_attr pj_stun_req_addr_type;
 
 /**
@@ -954,5 +956,5 @@
  \endverbatim  
  */
-typedef struct pj_stun_uint_attr pj_stun_requested_port_props_attr;
+typedef struct pj_stun_uint_attr pj_stun_req_port_props_attr;
 
 
@@ -963,5 +965,5 @@
  * unsigned integer.  Its values are: 0x0000 for UDP and 0x0000 for TCP.
  */
-typedef struct pj_stun_uint_attr pj_stun_requested_transport_attr;
+typedef struct pj_stun_uint_attr pj_stun_req_transport_attr;
 
 
@@ -971,5 +973,5 @@
  * specific IP address be allocated to it.
  */
-typedef struct pj_stun_ip_addr_attr pj_stun_requested_ip_attr;
+typedef struct pj_stun_ip_addr_attr pj_stun_req_ip_attr;
 
 /**
Index: pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg.c	(revision 1048)
+++ pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg.c	(revision 1049)
@@ -72,5 +72,7 @@
     { PJ_STUN_STATUS_CONNECTION_FAILURE,    "Connection Failure"},
     { PJ_STUN_STATUS_CONNECTION_TIMEOUT,    "Connection Timeout"},
+    { PJ_STUN_STATUS_ALLOCATION_QUOTA_REACHED, "Allocation Quota Reached"},
     { PJ_STUN_STATUS_SERVER_ERROR,	    "Server Error"},
+    { PJ_STUN_STATUS_INSUFFICIENT_CAPACITY, "Insufficient Capacity"},
     { PJ_STUN_STATUS_GLOBAL_FAILURE,	    "Global Failure"}
 };
Index: pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg_dump.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg_dump.c	(revision 1048)
+++ pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg_dump.c	(revision 1049)
@@ -70,7 +70,7 @@
     case PJ_STUN_ATTR_REFLECTED_FROM:
     case PJ_STUN_ATTR_REMOTE_ADDRESS:
-    case PJ_STUN_ATTR_RELAY_ADDRESS:
-    case PJ_STUN_ATTR_XOR_MAPPED_ADDRESS:
-    case PJ_STUN_ATTR_REQUESTED_IP:
+    case PJ_STUN_ATTR_RELAY_ADDR:
+    case PJ_STUN_ATTR_XOR_MAPPED_ADDR:
+    case PJ_STUN_ATTR_REQ_IP:
     case PJ_STUN_ATTR_XOR_REFLECTED_FROM:
     case PJ_STUN_ATTR_XOR_INTERNAL_ADDR:
@@ -100,7 +100,7 @@
     case PJ_STUN_ATTR_LIFETIME:
     case PJ_STUN_ATTR_BANDWIDTH:
-    case PJ_STUN_ATTR_REQUESTED_ADDR_TYPE:
-    case PJ_STUN_ATTR_REQUESTED_PORT_PROPS:
-    case PJ_STUN_ATTR_REQUESTED_TRANSPORT:
+    case PJ_STUN_ATTR_REQ_ADDR_TYPE:
+    case PJ_STUN_ATTR_REQ_PORT_PROPS:
+    case PJ_STUN_ATTR_REQ_TRANSPORT:
     case PJ_STUN_ATTR_TIMER_VAL:
     case PJ_STUN_ATTR_PRIORITY:
Index: pjproject/trunk/pjlib-util/src/pjlib-util/stun_session.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjlib-util/stun_session.c	(revision 1048)
+++ pjproject/trunk/pjlib-util/src/pjlib-util/stun_session.c	(revision 1049)
@@ -177,4 +177,5 @@
 			     &tdata->res_timer);
 	tdata->res_timer.id = PJ_FALSE;
+	pj_list_erase(tdata);
     }
     pj_pool_release(tdata->pool);
@@ -348,4 +349,15 @@
 {
     PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+
+    pj_mutex_lock(sess->mutex);
+    while (!pj_list_empty(&sess->pending_request_list)) {
+	pj_stun_tx_data *tdata = sess->pending_request_list.next;
+	destroy_tdata(tdata);
+    }
+    while (!pj_list_empty(&sess->cached_response_list)) {
+	pj_stun_tx_data *tdata = sess->cached_response_list.next;
+	destroy_tdata(tdata);
+    }
+    pj_mutex_unlock(sess->mutex);
 
     pj_mutex_destroy(sess->mutex);
Index: pjproject/trunk/pjlib-util/src/pjstun-srv-test/bind_usage.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjstun-srv-test/bind_usage.c	(revision 1048)
+++ pjproject/trunk/pjlib-util/src/pjstun-srv-test/bind_usage.c	(revision 1049)
@@ -26,4 +26,5 @@
 			     const pj_sockaddr_t *src_addr,
 			     unsigned src_addr_len);
+static void usage_on_destroy(pj_stun_usage *usage);
 static pj_status_t sess_on_send_msg(pj_stun_session *sess,
 				    const void *pkt,
@@ -38,4 +39,12 @@
 				      unsigned src_addr_len);
 
+struct bind_usage
+{
+    pj_pool_t	    *pool;
+    pj_stun_usage   *usage;
+    pj_stun_session *session;
+};
+
+
 PJ_DEF(pj_status_t) pj_stun_bind_usage_create(pj_stun_server *srv,
 					      const pj_str_t *ip_addr,
@@ -43,7 +52,7 @@
 					      pj_stun_usage **p_bu)
 {
+    pj_pool_t *pool;
+    struct bind_usage *bu;
     pj_stun_server_info *si;
-    pj_stun_session *session;
-    pj_stun_usage *usage;
     pj_stun_usage_cb usage_cb;
     pj_stun_session_cb sess_cb;
@@ -53,4 +62,8 @@
     si = pj_stun_server_get_info(srv);
 
+    pool = pj_pool_create(si->pf, "bind%p", 128, 128, NULL);
+    bu = PJ_POOL_ZALLOC_T(pool, struct bind_usage);
+    bu->pool = pool;
+
     status = pj_sockaddr_in_init(&local_addr, ip_addr, (pj_uint16_t)port);
     if (status != PJ_SUCCESS)
@@ -59,11 +72,14 @@
     pj_bzero(&usage_cb, sizeof(usage_cb));
     usage_cb.on_rx_data = &usage_on_rx_data;
+    usage_cb.on_destroy = &usage_on_destroy;
 
     status = pj_stun_usage_create(srv, "bind%p", &usage_cb,
 				  PJ_AF_INET, PJ_SOCK_DGRAM, 0,
 				  &local_addr, sizeof(local_addr),
-				  &usage);
-    if (status != PJ_SUCCESS)
-	return status;
+				  &bu->usage);
+    if (status != PJ_SUCCESS) {
+	pj_pool_release(pool);
+	return status;
+    }
 
     pj_bzero(&sess_cb, sizeof(sess_cb));
@@ -71,14 +87,15 @@
     sess_cb.on_rx_request = &sess_on_rx_request;
     status = pj_stun_session_create(si->endpt, "bind%p", &sess_cb, PJ_FALSE,
-				    &session);
-    if (status != PJ_SUCCESS) {
-	pj_stun_usage_destroy(usage);
-	return status;
-    }
-
-    pj_stun_usage_set_user_data(usage, session);
-    pj_stun_session_set_user_data(session, usage);
-
-    *p_bu = usage;
+				    &bu->session);
+    if (status != PJ_SUCCESS) {
+	pj_stun_usage_destroy(bu->usage);
+	return status;
+    }
+
+    pj_stun_usage_set_user_data(bu->usage, bu);
+    pj_stun_session_set_user_data(bu->session, bu);
+
+    if (p_bu)
+	*p_bu = bu->usage;
 
     return PJ_SUCCESS;
@@ -92,8 +109,10 @@
 			     unsigned src_addr_len)
 {
+    struct bind_usage *bu;
     pj_stun_session *session;
     pj_status_t status;
 
-    session = (pj_stun_session*) pj_stun_usage_get_user_data(usage);
+    bu = (struct bind_usage*) pj_stun_usage_get_user_data(usage);
+    session = bu->session;
 
     /* Handle packet to session */
@@ -114,7 +133,9 @@
 				    unsigned addr_len)
 {
+    struct bind_usage *bu;
     pj_stun_usage *usage;
 
-    usage = pj_stun_session_get_user_data(sess);
+    bu = (struct bind_usage*) pj_stun_session_get_user_data(sess);
+    usage = bu->usage;
 
     return pj_stun_usage_sendto(usage, pkt, pkt_size, 0,
@@ -156,5 +177,5 @@
 	status = 
 	    pj_stun_msg_add_ip_addr_attr(tdata->pool, tdata->msg,
-					 PJ_STUN_ATTR_XOR_MAPPED_ADDRESS,
+					 PJ_STUN_ATTR_XOR_MAPPED_ADDR,
 					 PJ_TRUE,
 					 src_addr, src_addr_len);
@@ -173,2 +194,13 @@
 }
 
+static void usage_on_destroy(pj_stun_usage *usage)
+{
+    struct bind_usage *bu;
+
+    bu = (struct bind_usage*) pj_stun_usage_get_user_data(usage);
+    if (bu==NULL)
+	return;
+
+    pj_stun_session_destroy(bu->session);
+    pj_pool_release(bu->pool);
+}
Index: pjproject/trunk/pjlib-util/src/pjstun-srv-test/main.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjstun-srv-test/main.c	(revision 1048)
+++ pjproject/trunk/pjlib-util/src/pjstun-srv-test/main.c	(revision 1049)
@@ -18,4 +18,6 @@
  */
 #include "server.h"
+
+#define THIS_FILE	"main.c"
 
 struct options
@@ -42,5 +44,5 @@
 
 
-static void server_main(void)
+static void server_main(pj_stun_server *srv)
 {
     int quit = 0;
@@ -50,10 +52,15 @@
 
 	printf("Menu:\n"
+	       "  d     Dump status\n"
 	       "  q     Quit\n"
 	       "Choice:");
 
 	fgets(line, sizeof(line), stdin);
-	if (line[0] == 'q')
+	if (line[0] == 'q') {
 	    quit = 1;
+	} else if (line[0] == 'd') {
+	    pj_stun_server_info *si = pj_stun_server_get_info(srv);
+	    pj_pool_factory_dump(si->pf, PJ_TRUE);
+	}
     }
 }
@@ -72,5 +79,4 @@
     pj_caching_pool cp;
     pj_stun_server *srv;
-    pj_stun_usage *bu;
     pj_status_t status;
 
@@ -117,5 +123,5 @@
     }
 
-    status = pj_stun_bind_usage_create(srv, NULL, 3478, &bu);
+    status = pj_stun_bind_usage_create(srv, NULL, 3478, NULL);
     if (status != PJ_SUCCESS) {
 	pj_stun_perror(THIS_FILE, "Unable to create bind usage", status);
@@ -123,7 +129,6 @@
     }
 
-    server_main();
+    server_main(srv);
 
-    pj_stun_usage_destroy(bu);
     pj_stun_server_destroy(srv);
     pj_pool_factory_dump(&cp.factory, PJ_TRUE);
Index: pjproject/trunk/pjlib-util/src/pjstun-srv-test/server.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjstun-srv-test/server.c	(revision 1048)
+++ pjproject/trunk/pjlib-util/src/pjstun-srv-test/server.c	(revision 1049)
@@ -30,4 +30,7 @@
     pj_bool_t		 thread_quit_flag;
     pj_thread_t	       **threads;
+
+    unsigned		 usage_cnt;
+    pj_stun_usage	*usage[32];
 };
 
@@ -110,7 +113,59 @@
 
 
+pj_status_t pj_stun_server_register_usage(pj_stun_server *srv,
+					  pj_stun_usage *usage)
+{
+    unsigned i;
+
+    for (i=0; i<PJ_ARRAY_SIZE(srv->usage); ++i) {
+	if (srv->usage[i] == usage)
+	    return PJ_SUCCESS;
+    }
+
+    for (i=0; i<PJ_ARRAY_SIZE(srv->usage); ++i) {
+	if (srv->usage[i] == NULL)
+	    break;
+    }
+
+    if (i == PJ_ARRAY_SIZE(srv->usage))
+	return PJ_ETOOMANY;
+
+    srv->usage[i] = usage;
+    ++srv->usage_cnt;
+
+    return PJ_SUCCESS;
+}
+
+pj_status_t pj_stun_server_unregister_usage(pj_stun_server *srv,
+					    pj_stun_usage *usage)
+{
+    unsigned i;
+
+    for (i=0; i<PJ_ARRAY_SIZE(srv->usage); ++i) {
+	if (srv->usage[i] == usage)
+	    break;
+    }
+
+    if (i != PJ_ARRAY_SIZE(srv->usage)) {
+	srv->usage[i] = NULL;
+	--srv->usage_cnt;
+	return PJ_SUCCESS;
+    }
+
+    return PJ_ENOTFOUND;
+}
+
+
 PJ_DEF(pj_status_t) pj_stun_server_destroy(pj_stun_server *srv)
 {
     unsigned i;
+
+    for (i=0; i<PJ_ARRAY_SIZE(srv->usage); ++i) {
+	if (!srv->usage[i])
+	    continue;
+
+	pj_stun_usage_destroy(srv->usage[i]);
+	pj_stun_server_unregister_usage(srv, srv->usage[i]);
+    }
 
     srv->thread_quit_flag = PJ_TRUE;
Index: pjproject/trunk/pjlib-util/src/pjstun-srv-test/server.h
===================================================================
--- pjproject/trunk/pjlib-util/src/pjstun-srv-test/server.h	(revision 1048)
+++ pjproject/trunk/pjlib-util/src/pjstun-srv-test/server.h	(revision 1049)
@@ -48,4 +48,5 @@
 		       const pj_sockaddr_t *src_addr,
 		       unsigned src_addr_len);
+    void (*on_destroy)(pj_stun_usage *usage);
 } pj_stun_usage_cb;
 
@@ -118,4 +119,11 @@
 
 
+
+pj_status_t pj_stun_server_register_usage(pj_stun_server *srv,
+					  pj_stun_usage *usage);
+pj_status_t pj_stun_server_unregister_usage(pj_stun_server *srv,
+					    pj_stun_usage *usage);
+
+
 #endif	/* __STUN_SERVER_H__ */
 
Index: pjproject/trunk/pjlib-util/src/pjstun-srv-test/turn_usage.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjstun-srv-test/turn_usage.c	(revision 1049)
+++ pjproject/trunk/pjlib-util/src/pjstun-srv-test/turn_usage.c	(revision 1049)
@@ -0,0 +1,662 @@
+/* $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 "server.h"
+
+#define THIS_FILE   "turn_usage.c"
+
+#define MAX_CLIENTS		8000
+#define MAX_PEER_PER_CLIENTS	16
+#define START_PORT		2000
+#define END_PORT		65530
+
+static void tu_on_rx_data(pj_stun_usage *usage,
+			  void *pkt,
+			  pj_size_t pkt_size,
+			  const pj_sockaddr_t *src_addr,
+			  unsigned src_addr_len);
+static void tu_on_destroy(pj_stun_usage *usage);
+
+static pj_status_t sess_on_send_msg(pj_stun_session *sess,
+				    const void *pkt,
+				    pj_size_t pkt_size,
+				    const pj_sockaddr_t *dst_addr,
+				    unsigned addr_len);
+static pj_status_t sess_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);
+
+
+struct turn_usage
+{
+    pj_pool_factory	*pf;
+    pj_stun_endpoint	*endpt;
+    pj_ioqueue_t	*ioqueue;
+    pj_pool_t		*pool;
+    pj_stun_usage	*usage;
+    int			 type;
+    pj_stun_session	*default_session;
+    pj_hash_table_t	*client_htable;
+    pj_hash_table_t	*peer_htable;
+
+    unsigned		 max_bw_kbps;
+    unsigned		 max_lifetime;
+
+    unsigned		 next_port;
+};
+
+struct peer;
+
+struct turn_client
+{
+    struct turn_usage	*tu;
+    pj_pool_t		*pool;
+    pj_sockaddr_in	 addr;
+    pj_stun_session	*session;
+
+    unsigned		 bw_kbps;
+    unsigned		 lifetime;
+
+    pj_sock_t		 sock;
+    pj_ioqueue_key_t	*key;
+    char		 packet[4000];
+    pj_sockaddr_in	 src_addr;
+    int			 src_addr_len;
+    pj_ioqueue_op_key_t	 read_key;
+    pj_ioqueue_op_key_t  write_key;
+
+    pj_bool_t		 has_ad;
+    pj_bool_t		 ad_is_pending;
+    pj_sockaddr_in	 ad;
+};
+
+struct peer
+{
+    struct turn_client   *client;
+    pj_sockaddr_in	  addr;
+};
+
+struct session_data
+{
+    struct turn_usage	*tu;
+    struct turn_client	*client;
+};
+
+
+PJ_DEF(pj_status_t) pj_stun_turn_usage_create(pj_stun_server *srv,
+					      int type,
+					      const pj_str_t *ip_addr,
+					      unsigned port,
+					      pj_stun_usage **p_bu)
+{
+    pj_pool_t *pool;
+    struct turn_usage *tu;
+    pj_stun_server_info *si;
+    pj_stun_usage_cb usage_cb;
+    pj_stun_session_cb sess_cb;
+    struct session_data *sd;
+    pj_sockaddr_in local_addr;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(srv && (type==PJ_SOCK_DGRAM||type==PJ_SOCK_STREAM) &&
+		     p_bu, PJ_EINVAL);
+    si = pj_stun_server_get_info(srv);
+
+    pool = pj_pool_create(si->pf, "turn%p", 4000, 4000, NULL);
+    tu = PJ_POOL_ZALLOC_T(pool, struct turn_usage);
+    tu->pool = pool;
+    tu->type = type;
+    tu->pf = si->pf;
+    tu->endpt = si->endpt;
+    tu->next_port = START_PORT;
+
+    status = pj_sockaddr_in_init(&local_addr, ip_addr, (pj_uint16_t)port);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Create usage */
+    pj_bzero(&usage_cb, sizeof(usage_cb));
+    usage_cb.on_rx_data = &tu_on_rx_data;
+    usage_cb.on_destroy = &tu_on_destroy;
+    status = pj_stun_usage_create(srv, "turn%p", &usage_cb,
+				  PJ_AF_INET, tu->type, 0,
+				  &local_addr, sizeof(local_addr),
+				  &tu->usage);
+    if (status != PJ_SUCCESS) {
+	pj_pool_release(pool);
+	return status;
+    }
+    pj_stun_usage_set_user_data(tu->usage, tu);
+
+    /* Init hash tables */
+    tu->client_htable = pj_hash_create(tu->pool, MAX_CLIENTS);
+    tu->peer_htable = pj_hash_create(tu->pool, MAX_CLIENTS);
+
+    /* Create default session */
+    pj_bzero(&sess_cb, sizeof(sess_cb));
+    sess_cb.on_send_msg = &sess_on_send_msg;
+    sess_cb.on_rx_request = &sess_on_rx_request;
+    status = pj_stun_session_create(si->endpt, "turns%p", &sess_cb, PJ_FALSE,
+				    &tu->default_session);
+    if (status != PJ_SUCCESS) {
+	pj_stun_usage_destroy(tu->usage);
+	return status;
+    }
+
+    sd = PJ_POOL_ZALLOC_T(pool, struct session_data);
+    sd->tu = tu;
+    pj_stun_session_set_user_data(tu->default_session, sd);
+
+    *p_bu = tu->usage;
+
+    return PJ_SUCCESS;
+}
+
+
+static void tu_on_destroy(pj_stun_usage *usage)
+{
+    struct turn_usage *tu;
+
+    tu = (struct turn_usage*) pj_stun_usage_get_user_data(usage);
+    PJ_TODO(DESTROY_ALL_CLIENTS);
+    pj_pool_release(tu->pool);
+}
+
+
+static void tu_on_rx_data(pj_stun_usage *usage,
+			  void *pkt,
+			  pj_size_t pkt_size,
+			  const pj_sockaddr_t *src_addr,
+			  unsigned src_addr_len)
+{
+    struct turn_usage *tu;
+    pj_stun_session *session;
+    struct turn_client *client;
+    unsigned flags;
+    pj_status_t status;
+
+    /* Which usage instance is this */
+    tu = (struct turn_usage*) pj_stun_usage_get_user_data(usage);
+
+    /* Lookup client structure based on source address */
+    client = (struct turn_client*) pj_hash_get(tu->client_htable, src_addr,
+					       src_addr_len, NULL);
+
+    if (client == NULL) {
+	session = tu->default_session;
+    } else {
+	session = client->session;
+    }
+    
+    /* Handle packet to session */
+    flags = PJ_STUN_CHECK_PACKET;
+    if (tu->type == PJ_SOCK_DGRAM)
+	flags |= PJ_STUN_IS_DATAGRAM;
+
+    status = pj_stun_session_on_rx_pkt(session, (pj_uint8_t*)pkt, pkt_size,
+				       flags, NULL, src_addr, src_addr_len);
+    if (status != PJ_SUCCESS) {
+	pj_stun_perror(THIS_FILE, "Error handling incoming packet", status);
+	return;
+    }
+}
+
+
+static pj_status_t tu_alloc_port(struct turn_usage *tu, 
+				 int type, 
+				 unsigned rpp_bits, 
+				 const pj_sockaddr_in *req_addr,
+				 pj_sock_t *p_sock,
+				 int *err_code)
+{
+    enum { RETRY = 100 };
+    pj_sockaddr_in addr;
+    pj_sock_t sock = PJ_INVALID_SOCKET;
+    unsigned retry;
+    pj_status_t status;
+
+    if (req_addr && req_addr->sin_port != 0) {
+
+	*err_code = PJ_STUN_STATUS_INVALID_PORT;
+
+	/* Allocate specific port */
+	status = pj_sock_socket(PJ_AF_INET, type, 0, &sock);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	/* Bind */
+	status = pj_sock_bind(sock, req_addr, sizeof(pj_sockaddr_in));
+	if (status != PJ_SUCCESS) {
+	    pj_sock_close(sock);
+	    return status;
+	}
+
+	/* Success */
+	*p_sock = sock;
+	return PJ_SUCCESS;
+
+    } else {
+	*err_code = PJ_STUN_STATUS_INSUFFICIENT_CAPACITY;
+
+	if (req_addr && req_addr->sin_addr.s_addr) {
+	    *err_code = PJ_STUN_STATUS_INVALID_IP_ADDR;
+	    pj_memcpy(&addr, req_addr, sizeof(pj_sockaddr_in));
+	} else {
+	    pj_sockaddr_in_init(&addr, NULL, 0);
+	}
+
+	for (retry=0; retry<RETRY && sock == PJ_INVALID_SOCKET; ++retry) {
+	    switch (rpp_bits) {
+	    case 1:
+		if ((tu->next_port & 0x01)==0)
+		    tu->next_port++;
+		break;
+	    case 2:
+	    case 3:
+		if ((tu->next_port & 0x01)==1)
+		    tu->next_port++;
+		break;
+	    }
+
+	    status = pj_sock_socket(PJ_AF_INET, type, 0, &sock);
+	    if (status != PJ_SUCCESS)
+		return status;
+
+	    addr.sin_port = pj_htons((pj_uint16_t)tu->next_port);
+
+	    if (++tu->next_port > END_PORT)
+		tu->next_port = START_PORT;
+
+	    status = pj_sock_bind(sock, &addr, sizeof(addr));
+	    if (status != PJ_SUCCESS) {
+		pj_sock_close(sock);
+		sock = PJ_INVALID_SOCKET;
+
+		/* If client requested specific IP address, assume that
+		 * bind failed because the IP address is not valid. We
+		 * don't want to retry that since it will exhaust our
+		 * port space.
+		 */
+		if (req_addr && req_addr->sin_addr.s_addr)
+		    break;
+	    }
+	}
+
+	if (sock == PJ_INVALID_SOCKET) {
+	    return status;
+	}
+
+	return PJ_SUCCESS;
+    }
+}
+
+/****************************************************************************/
+
+static pj_status_t client_create(struct turn_usage *tu,
+				 const pj_stun_msg *msg,
+				 const pj_sockaddr_t *src_addr,
+				 unsigned src_addr_len,
+				 struct turn_client **p_client)
+{
+    pj_pool_t *pool;
+    struct turn_client *client;
+    pj_stun_session_cb sess_cb;
+    struct session_data *sd;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(msg);
+
+    pool = pj_pool_create(tu->pf, "turnc%p", 4000, 4000, NULL);
+    client = PJ_POOL_ZALLOC_T(pool, struct turn_client);
+    client->pool = pool;
+    client->tu = tu;
+    client->sock = PJ_INVALID_SOCKET;
+
+    /* Create session */
+    pj_bzero(&sess_cb, sizeof(sess_cb));
+    sess_cb.on_send_msg = &sess_on_send_msg;
+    sess_cb.on_rx_request = &sess_on_rx_request;
+    status = pj_stun_session_create(tu->endpt, "turnc%p", &sess_cb, PJ_FALSE,
+				    &client->session);
+    if (status != PJ_SUCCESS) {
+	pj_pool_release(pool);
+	return status;
+    }
+
+    sd = PJ_POOL_ZALLOC_T(pool, struct session_data);
+    sd->tu = tu;
+    sd->client = client;
+    pj_stun_session_set_user_data(client->session, sd);
+
+    /* Register to hash table */
+    pj_hash_set(pool, tu->client_htable, src_addr, src_addr_len, 0, client);
+
+    *p_client = client;
+    return PJ_SUCCESS;
+}
+
+static pj_status_t client_destroy(struct turn_client *client)
+{
+}
+
+static void client_on_read_complete(pj_ioqueue_key_t *key, 
+				    pj_ioqueue_op_key_t *op_key, 
+				    pj_ssize_t bytes_read)
+{
+}
+
+static void client_on_write_complete(pj_ioqueue_key_t *key, 
+				     pj_ioqueue_op_key_t *op_key, 
+				     pj_ssize_t bytes_sent)
+{
+}
+
+static pj_status_t client_create_relay(struct turn_client *client)
+{
+    pj_ioqueue_callback client_ioq_cb;
+    pj_status_t status;
+
+    /* Register to ioqueue */
+    pj_bzero(&client_ioq_cb, sizeof(client_ioq_cb));
+    client_ioq_cb.on_read_complete = &client_on_read_complete;
+    client_ioq_cb.on_write_complete = &client_on_write_complete;
+    status = pj_ioqueue_register_sock(client->pool, client->tu->ioqueue, 
+				      client->sock, client,
+				      &client_ioq_cb, &client->key);
+    if (status != PJ_SUCCESS) {
+	pj_sock_close(client->sock);
+	client->sock = PJ_INVALID_SOCKET;
+	return status;
+    }
+
+    pj_ioqueue_op_key_init(&client->read_key, sizeof(client->read_key));
+    pj_ioqueue_op_key_init(&client->write_key, sizeof(client->write_key));
+
+    /* Trigger the first read */
+    client_on_read_complete(client->key, &client->read_key, 0);
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t client_handle_allocate_req(struct turn_client *client,
+					      const pj_uint8_t *pkt,
+					      unsigned pkt_len,
+					      const pj_stun_msg *msg,
+					      const pj_sockaddr_t *src_addr,
+					      unsigned src_addr_len)
+{
+    const pj_stun_bandwidth_attr *a_bw;
+    const pj_stun_lifetime_attr *a_lf;
+    const pj_stun_req_port_props_attr *a_rpp;
+    const pj_stun_req_transport_attr *a_rt;
+    const pj_stun_req_ip_attr *a_rip;
+    pj_stun_tx_data *response;
+    pj_sockaddr_in req_addr;
+    int addr_len;
+    unsigned type;
+    unsigned rpp_bits;
+    pj_status_t status;
+
+    a_bw = (const pj_stun_bandwidth_attr *)
+	   pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_BANDWIDTH, 0);
+    a_lf = (const pj_stun_lifetime_attr*)
+	    pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0);
+    a_rpp = (const pj_stun_req_port_props_attr*)
+	    pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REQ_PORT_PROPS, 0);
+    a_rt = (const pj_stun_req_transport_attr*)
+	   pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REQ_TRANSPORT, 0);
+    a_rip = (const pj_stun_req_ip_attr*)
+	    pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REQ_IP, 0);
+
+    /* Init requested local address */
+    pj_sockaddr_in_init(&req_addr, NULL, 0);
+
+    /* Process BANDWIDTH attribute */
+    if (a_bw && a_bw->value > client->tu->max_bw_kbps) {
+	status = pj_stun_session_create_response(client->session, msg, 
+						 PJ_STUN_STATUS_INSUFFICIENT_CAPACITY, 
+						 NULL, &response);
+	if (status == PJ_SUCCESS && response) {
+	    pj_stun_session_send_msg(client->session, PJ_TRUE, 
+				     src_addr, src_addr_len, response);
+	}
+	return -1;
+    } else if (a_bw) {
+	client->bw_kbps = a_bw->value;
+    } else {
+	client->bw_kbps = client->tu->max_bw_kbps;
+    }
+
+    /* Process REQUESTED-TRANSPORT attribute */
+    if (a_rt && a_rt->value != 0) {
+	status = pj_stun_session_create_response(client->session, msg, 
+						 PJ_STUN_STATUS_UNSUPP_TRANSPORT_PROTO, 
+						 NULL, &response);
+	if (status == PJ_SUCCESS && response) {
+	    pj_stun_session_send_msg(client->session, PJ_TRUE, 
+				     src_addr, src_addr_len, response);
+	}
+	return -1;
+    } else if (a_rt) {
+	type = a_rt->value ? PJ_SOCK_STREAM : PJ_SOCK_DGRAM;
+    } else {
+	type = client->tu->type;;
+    }
+
+    /* Process REQUESTED-IP attribute */
+    if (a_rip && a_rip->addr.addr.sa_family != PJ_AF_INET) {
+	status = pj_stun_session_create_response(client->session, msg, 
+						 PJ_STUN_STATUS_INVALID_IP_ADDR, 
+						 NULL, &response);
+	if (status == PJ_SUCCESS && response) {
+	    pj_stun_session_send_msg(client->session, PJ_TRUE, 
+				     src_addr, src_addr_len, response);
+	}
+	return -1;
+	
+    } else if (a_rip) {
+	req_addr.sin_addr.s_addr = a_rip->addr.ipv4.sin_addr.s_addr;
+    }
+
+    /* Process REQUESTED-PORT-PROPS attribute */
+    if (a_rpp) {
+	unsigned port;
+
+	rpp_bits = (a_rpp->value & 0x00030000) >> 16;
+	port = (a_rpp->value & 0xFFFF);
+	req_addr.sin_port = pj_htons((pj_uint8_t)port);
+    } else {
+	rpp_bits = 0;
+    }
+
+    /* Process LIFETIME attribute */
+    if (a_lf && a_lf->value > client->tu->max_lifetime) {
+	client->lifetime = client->tu->max_lifetime;
+    } else if (a_lf) {
+	client->lifetime = a_lf->value;
+    } else {
+	client->lifetime = client->tu->max_lifetime;
+    }
+
+    /* Allocate socket if we don't have one */
+    if (client->sock == PJ_INVALID_SOCKET) {
+	int err_code;
+
+	status = tu_alloc_port(client->tu, type, rpp_bits, &req_addr, 
+			       &client->sock, &err_code);
+	if (status != PJ_SUCCESS) {
+
+	    status = pj_stun_session_create_response(client->session, msg, 
+						     err_code, NULL, 
+						     &response);
+	    if (status == PJ_SUCCESS && response) {
+		pj_stun_session_send_msg(client->session, PJ_TRUE, 
+					 src_addr, src_addr_len, response);
+	    }
+	    return -1;
+	}
+
+	status = client_create_relay(client);
+	if (status != PJ_SUCCESS) {
+	    status = pj_stun_session_create_response(client->session, msg, 
+						     PJ_STUN_STATUS_SERVER_ERROR, 
+						     NULL, &response);
+	    if (status == PJ_SUCCESS && response) {
+		pj_stun_session_send_msg(client->session, PJ_TRUE, 
+					 src_addr, src_addr_len, response);
+	    }
+	    return -1;
+	}
+    } else {
+	/* Otherwise check if the port parameter stays the same */
+	/* TODO */
+    }
+
+    /* Done successfully, create and send success response */
+    status = pj_stun_session_create_response(client->session, msg, 
+					     0, NULL, &response);
+    if (status != PJ_SUCCESS) {
+	return -1;
+    }
+
+    pj_stun_msg_add_uint_attr(response->pool, response->msg, 
+			      PJ_STUN_ATTR_BANDWIDTH, client->bw_kbps);
+    pj_stun_msg_add_uint_attr(response->pool, response->msg,
+			      PJ_STUN_ATTR_LIFETIME, client->lifetime);
+    pj_stun_msg_add_ip_addr_attr(response->pool, response->msg,
+				 PJ_STUN_ATTR_MAPPED_ADDR, PJ_FALSE,
+				 src_addr, src_addr_len);
+    pj_stun_msg_add_ip_addr_attr(response->pool, response->msg,
+				 PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE,
+				 src_addr, src_addr_len);
+
+    addr_len = sizeof(req_addr);
+    pj_sock_getsockname(client->sock, &req_addr, &addr_len);
+    pj_stun_msg_add_ip_addr_attr(response->pool, response->msg,
+				 PJ_STUN_ATTR_RELAY_ADDR, PJ_FALSE,
+				 &req_addr, addr_len);
+
+    return pj_stun_session_send_msg(client->session, PJ_TRUE, 
+				    src_addr, src_addr_len, response);
+}
+
+static pj_status_t client_handle_send_ind(struct turn_client *client,
+					  const pj_uint8_t *pkt,
+					  unsigned pkt_len,
+					  const pj_stun_msg *msg,
+					  const pj_sockaddr_t *src_addr,
+					  unsigned src_addr_len)
+{
+}
+
+static pj_status_t client_handle_unknown_msg(struct turn_client *client,
+					     const pj_uint8_t *pkt,
+					     unsigned pkt_len,
+					     const pj_stun_msg *msg,
+					     const pj_sockaddr_t *src_addr,
+					     unsigned src_addr_len)
+{
+}
+
+static pj_status_t client_handle_stun_msg(struct turn_client *client,
+					  const pj_uint8_t *pkt,
+					  unsigned pkt_len,
+					  const pj_stun_msg *msg,
+					  const pj_sockaddr_t *src_addr,
+					  unsigned src_addr_len)
+{
+    pj_status_t status;
+
+    switch (msg->hdr.type) {
+    case PJ_STUN_ALLOCATE_REQUEST:
+	return client_handle_allocate_req(client, pkt, pkt_len, msg,
+					  src_addr, src_addr_len);
+
+    case PJ_STUN_SEND_INDICATION:
+	return client_handle_send_ind(client, pkt, pkt_len, msg,
+				      src_addr, src_addr_len);
+
+    default:
+	return client_handle_unknown_msg(client, pkt, pkt_len, msg,
+					 src_addr, src_addr_len);
+    }
+}
+
+
+static pj_status_t sess_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)
+{
+    struct session_data *sd;
+    struct turn_client *client;
+    pj_stun_tx_data *tdata;
+    pj_status_t status;
+
+    sd = (struct session_data*) pj_stun_session_get_user_data(sess);
+
+    if (sd->client == NULL) {
+	/* No client is associated with this source address. Create a new
+	 * one if this is an Allocate request.
+	 */
+	if (msg->hdr.type != PJ_STUN_ALLOCATE_REQUEST) {
+	    PJ_LOG(4,(THIS_FILE, "Received first packet not Allocate request"));
+	    return PJ_SUCCESS;
+	}
+
+	PJ_TODO(SUPPORT_MOVE);
+
+	status = client_create(sd->tu, msg, src_addr, src_addr_len, &client);
+	if (status != PJ_SUCCESS) {
+	    pj_stun_perror(THIS_FILE, "Error creating new TURN client", status);
+	    return status;
+	}
+
+    } else {
+	client = sd->client;
+    }
+
+    return client_handle_stun_msg(client, pkt, pkt_len, msg, 
+				  src_addr, src_addr_len);
+}
+
+static pj_status_t sess_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 session_data *sd;
+
+    sd = (struct session_data*) pj_stun_session_get_user_data(sess);
+
+    if (sd->tu->type == PJ_SOCK_DGRAM) {
+	return pj_stun_usage_sendto(sd->tu->usage, pkt, pkt_size, 0,
+				    dst_addr, addr_len);
+    } else {
+	return PJ_ENOTSUP;
+    }
+}
+
+
+
Index: pjproject/trunk/pjlib-util/src/pjstun-srv-test/usage.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjstun-srv-test/usage.c	(revision 1048)
+++ pjproject/trunk/pjlib-util/src/pjstun-srv-test/usage.c	(revision 1049)
@@ -31,4 +31,5 @@
 {
     pj_pool_t		*pool;
+    pj_stun_server	*srv;
     pj_mutex_t		*mutex;
     pj_stun_usage_cb	 cb;
@@ -76,4 +77,5 @@
     usage = PJ_POOL_ZALLOC_T(pool, pj_stun_usage);
     usage->pool = pool;
+    usage->srv = srv;
 
     status = pj_mutex_create_simple(pool, name, &usage->mutex);
@@ -130,4 +132,6 @@
     }
 
+    pj_stun_server_register_usage(srv, usage);
+
     *p_usage = usage;
     return PJ_SUCCESS;
@@ -144,4 +148,8 @@
 PJ_DEF(pj_status_t) pj_stun_usage_destroy(pj_stun_usage *usage)
 {
+    pj_stun_server_unregister_usage(usage->srv, usage);
+    if (usage->cb.on_destroy)
+	(*usage->cb.on_destroy)(usage);
+
     if (usage->key) {
 	pj_ioqueue_unregister(usage->key);
