Index: /pjproject/trunk/pjnath/build/pjnath.dsw
===================================================================
--- /pjproject/trunk/pjnath/build/pjnath.dsw	(revision 1811)
+++ /pjproject/trunk/pjnath/build/pjnath.dsw	(revision 1812)
@@ -103,4 +103,25 @@
 ###############################################################################
 
+Project: "pjturn_srv"=.\pjturn_srv.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pjlib
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjlib_util
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjnath
+    End Project Dependency
+}}}
+
+###############################################################################
+
 Global:
 
Index: /pjproject/trunk/pjnath/build/pjturn_srv.dsp
===================================================================
--- /pjproject/trunk/pjnath/build/pjturn_srv.dsp	(revision 1812)
+++ /pjproject/trunk/pjnath/build/pjturn_srv.dsp	(revision 1812)
@@ -0,0 +1,114 @@
+# Microsoft Developer Studio Project File - Name="pjturn_srv" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=pjturn_srv - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "pjturn_srv.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pjturn_srv.mak" CFG="pjturn_srv - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pjturn_srv - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "pjturn_srv - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pjturn_srv - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "output/pjturn-srv-i386-win32-vc6-release"
+# PROP BASE Intermediate_Dir "output/pjturn-srv-i386-win32-vc6-release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "output/pjturn-srv-i386-win32-vc6-release"
+# PROP Intermediate_Dir "output/pjturn-srv-i386-win32-vc6-release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 netapi32.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../bin/pjturn-srv-i386-win32-vc6-release.exe"
+
+!ELSEIF  "$(CFG)" == "pjturn_srv - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "output/pjturn-srv-i386-win32-vc6-debug"
+# PROP BASE Intermediate_Dir "output/pjturn-srv-i386-win32-vc6-debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "output/pjturn-srv-i386-win32-vc6-debug"
+# PROP Intermediate_Dir "output/pjturn-srv-i386-win32-vc6-debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 netapi32.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjturn-srv-i386-win32-vc6-debug.exe" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pjturn_srv - Win32 Release"
+# Name "pjturn_srv - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE="..\src\pjturn-srv\listener_udp.c"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pjturn-srv\main.c"
+# End Source File
+# Begin Source File
+
+SOURCE="..\src\pjturn-srv\server.c"
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE="..\src\pjturn-srv\turn.h"
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
Index: /pjproject/trunk/pjnath/src/pjturn-srv/listener_udp.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjturn-srv/listener_udp.c	(revision 1812)
+++ /pjproject/trunk/pjnath/src/pjturn-srv/listener_udp.c	(revision 1812)
@@ -0,0 +1,219 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 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 "turn.h"
+
+struct read_op
+{
+    pj_ioqueue_op_key_t	op_key;
+    pjturn_pkt		pkt;
+};
+
+struct udp_listener
+{
+    pjturn_listener	     base;
+    pj_ioqueue_key_t	    *key;
+    unsigned		     read_cnt;
+    struct read_op	    **read_op;	/* Array of read_op's	*/
+};
+
+
+static pj_status_t udp_sendto(pjturn_listener *listener,
+			      const void *packet,
+			      pj_size_t size,
+			      unsigned flag,
+			      const pj_sockaddr_t *addr,
+			      int addr_len);
+static pj_status_t udp_destroy(pjturn_listener *udp);
+static void on_read_complete(pj_ioqueue_key_t *key, 
+			     pj_ioqueue_op_key_t *op_key, 
+			     pj_ssize_t bytes_read);
+
+
+/*
+ * Create a new listener on the specified port.
+ */
+PJ_DEF(pj_status_t) pjturn_listener_create_udp( pjturn_srv *srv,
+					        int af,
+					        const pj_str_t *bound_addr,
+					        unsigned port,
+						unsigned concurrency_cnt,
+						unsigned flags,
+						pjturn_listener **p_listener)
+{
+    pj_pool_t *pool;
+    struct udp_listener *udp;
+    pj_ioqueue_callback ioqueue_cb;
+    unsigned i;
+    pj_status_t status;
+
+    /* Create structure */
+    pool = pj_pool_create(srv->core.pf, "udplis%p", 1000, 1000, NULL);
+    udp = PJ_POOL_ZALLOC_T(pool, struct udp_listener);
+    udp->base.pool = pool;
+    udp->base.server = srv;
+    udp->base.tp_type = PJTURN_TP_UDP;
+    udp->base.sock = PJ_INVALID_SOCKET;
+    udp->base.sendto = &udp_sendto;
+    udp->base.destroy = &udp_destroy;
+    udp->read_cnt = concurrency_cnt;
+    udp->base.flags = flags;
+
+    /* Create socket */
+    status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &udp->base.sock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Init bind address */
+    status = pj_sockaddr_init(af, &udp->base.addr, bound_addr, 
+			      (pj_uint16_t)port);
+    if (status != PJ_SUCCESS) 
+	goto on_error;
+    
+    /* Bind socket */
+    status = pj_sock_bind(udp->base.sock, &udp->base.addr, 
+			  pj_sockaddr_get_len(&udp->base.addr));
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Register to ioqueue */
+    pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb));
+    ioqueue_cb.on_read_complete = on_read_complete;
+    status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, udp->base.sock,
+				      udp, &ioqueue_cb, &udp->key);
+
+    /* Create op keys */
+    udp->read_op = (struct read_op**)pj_pool_calloc(pool, concurrency_cnt, 
+						    sizeof(struct read_op*));
+
+    /* Create each read_op and kick off read operation */
+    for (i=0; i<concurrency_cnt; ++i) {
+	pj_pool_t *rpool = pj_pool_create(srv->core.pf, "rop%p", 
+					  1000, 1000, NULL);
+
+	udp->read_op[i] = PJ_POOL_ZALLOC_T(rpool, struct read_op);
+	udp->read_op[i]->pkt.pool = rpool;
+
+	on_read_complete(udp->key, &udp->read_op[i]->op_key, 0);
+    }
+
+    /* Done */
+    *p_listener = &udp->base;
+    return PJ_SUCCESS;
+
+
+on_error:
+    udp_destroy(&udp->base);
+    return status;
+}
+
+
+/*
+ * Destroy listener.
+ */
+static pj_status_t udp_destroy(pjturn_listener *listener)
+{
+    struct udp_listener *udp = (struct udp_listener *)listener;
+    unsigned i;
+
+    if (udp->key) {
+	pj_ioqueue_unregister(udp->key);
+	udp->key = NULL;
+	udp->base.sock = PJ_INVALID_SOCKET;
+    } else if (udp->base.sock != PJ_INVALID_SOCKET) {
+	pj_sock_close(udp->base.sock);
+	udp->base.sock = PJ_INVALID_SOCKET;
+    }
+
+    for (i=0; i<udp->read_cnt; ++i) {
+	if (udp->read_op[i]->pkt.pool) {
+	    pj_pool_t *rpool = udp->read_op[i]->pkt.pool;
+	    udp->read_op[i]->pkt.pool = NULL;
+	    pj_pool_release(rpool);
+	}
+    }
+
+    if (udp->base.pool) {
+	pj_pool_release(udp->base.pool);
+	udp->base.pool = NULL;
+    }
+    return PJ_SUCCESS;
+}
+
+/*
+ * Callback to send packet.
+ */
+static pj_status_t udp_sendto(pjturn_listener *listener,
+			      const void *packet,
+			      pj_size_t size,
+			      unsigned flag,
+			      const pj_sockaddr_t *addr,
+			      int addr_len)
+{
+    pj_ssize_t len = size;
+    return pj_sock_sendto(listener->sock, packet, &len, flag, addr,
+			  pj_sockaddr_get_len(addr));
+}
+
+/*
+ * Callback on received packet.
+ */
+static void on_read_complete(pj_ioqueue_key_t *key, 
+			     pj_ioqueue_op_key_t *op_key, 
+			     pj_ssize_t bytes_read)
+{
+    struct udp_listener *udp;
+    struct read_op *read_op = (struct read_op*) op_key;
+    pj_status_t status;
+
+    udp = (struct udp_listener*) pj_ioqueue_get_user_data(key);
+
+    do {
+	pj_pool_t *rpool;
+
+	/* Report to server */
+	if (bytes_read > 0) {
+	    read_op->pkt.len = bytes_read;
+	    pj_gettimeofday(&read_op->pkt.rx_time);
+
+	    pjturn_srv_on_rx_pkt(udp->base.server, &read_op->pkt);
+	}
+
+	/* Reset pool */
+	rpool = read_op->pkt.pool;
+	pj_pool_reset(rpool);
+	read_op->pkt.pool = rpool;
+	read_op->pkt.listener = &udp->base;
+	read_op->pkt.src.tp_type = udp->base.tp_type;
+
+	/* Read next packet */
+	bytes_read = sizeof(read_op->pkt.pkt);
+	read_op->pkt.src_addr_len = sizeof(read_op->pkt.src.clt_addr);
+	pj_bzero(&read_op->pkt.src.clt_addr, sizeof(read_op->pkt.src.clt_addr));
+
+	status = pj_ioqueue_recvfrom(udp->key, op_key,
+				     read_op->pkt.pkt, &bytes_read, 0,
+				     &read_op->pkt.src.clt_addr, 
+				     &read_op->pkt.src_addr_len);
+
+	if (status != PJ_EPENDING && status != PJ_SUCCESS)
+	    bytes_read = -status;
+
+    } while (status != PJ_EPENDING && status != PJ_ECANCELLED);
+}
+
Index: /pjproject/trunk/pjnath/src/pjturn-srv/main.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjturn-srv/main.c	(revision 1812)
+++ /pjproject/trunk/pjnath/src/pjturn-srv/main.c	(revision 1812)
@@ -0,0 +1,1 @@
+#include "turn.h"
Index: /pjproject/trunk/pjnath/src/pjturn-srv/server.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjturn-srv/server.c	(revision 1812)
+++ /pjproject/trunk/pjnath/src/pjturn-srv/server.c	(revision 1812)
@@ -0,0 +1,401 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 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 "turn.h"
+
+#define MAX_CLIENTS		32
+#define MAX_PEERS_PER_CLIENT	8
+#define MAX_HANDLES		(MAX_CLIENTS*MAX_PEERS_PER_CLIENT+MAX_LISTENERS)
+#define MAX_TIMER		(MAX_HANDLES * 2)
+#define MIN_PORT		49152
+#define MAX_PORT		65535
+#define MAX_LISTENERS		16
+#define MAX_THREADS		2
+
+#define MAX_CLIENT_BANDWIDTH	128  /* In Kbps */
+#define DEFA_CLIENT_BANDWIDTH	64
+
+#define MIN_LIFETIME		32
+#define MAX_LIFETIME		600
+#define DEF_LIFETIME		300
+
+
+/* Globals */
+PJ_DEF_DATA(int) PJTURN_TP_UDP = 1;
+PJ_DEF_DATA(int) PJTURN_TP_TCP = 2;
+PJ_DEF_DATA(int) PJTURN_TP_TLS = 3;
+
+/* Prototypes */
+static pj_status_t on_tx_stun_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 on_rx_stun_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);
+
+
+/*
+ * Create server.
+ */
+PJ_DEF(pj_status_t) pjturn_srv_create( pj_pool_factory *pf,
+				       pjturn_srv **p_srv)
+{
+    pj_pool_t *pool;
+    pjturn_srv *srv;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pf && p_srv, PJ_EINVAL);
+
+    /* Create server and init core settings */
+    pool = pj_pool_create(pf, "srv%p", 1000, 1000, NULL);
+    srv = PJ_POOL_ZALLOC_T(pool, pjturn_srv);
+    srv->core.obj_name = pool->obj_name;
+    srv->core.pf = pf;
+    srv->core.pool = pool;
+    
+    status = pj_ioqueue_create(pool, MAX_HANDLES, &srv->core.ioqueue);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    status = pj_timer_heap_create(pool, MAX_TIMER, &srv->core.timer_heap);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    srv->core.listener = pj_pool_calloc(pool, MAX_LISTENERS, 
+					sizeof(srv->core.listener[0]));
+    srv->core.stun_sess = pj_pool_calloc(pool, MAX_LISTENERS,
+					 (sizeof(srv->core.stun_sess[0])));
+
+    srv->core.thread_cnt = MAX_THREADS;
+    srv->core.thread = pj_pool_calloc(pool, srv->core.thread_cnt, 
+				      sizeof(pj_thread_t*));
+
+    status = pj_lock_create_recursive_mutex(pool, "srv%p", &srv->core.lock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Create hash tables */
+    srv->tables.alloc = pj_hash_create(pool, MAX_CLIENTS);
+    srv->tables.res = pj_hash_create(pool, MAX_CLIENTS);
+    srv->tables.peer = pj_hash_create(pool, MAX_CLIENTS*MAX_PEERS_PER_CLIENT);
+
+    /* Init ports settings */
+    srv->ports.min_udp = srv->ports.next_udp = MIN_PORT;
+    srv->ports.max_tcp = MAX_PORT;
+    srv->ports.min_tcp = srv->ports.next_tcp = MIN_PORT;
+    srv->ports.max_tcp = MAX_PORT;
+
+    /* Init STUN config */
+    pj_stun_config_init(&srv->core.stun_cfg, pf, 0, srv->core.ioqueue,
+		        srv->core.timer_heap);
+
+    *p_srv = srv;
+    return PJ_SUCCESS;
+
+on_error:
+    pjturn_srv_destroy(srv);
+    return status;
+}
+
+/** 
+ * Create server.
+ */
+PJ_DEF(pj_status_t) pjturn_srv_destroy(pjturn_srv *srv)
+{
+    return PJ_SUCCESS;
+}
+
+/** 
+ * Add listener.
+ */
+PJ_DEF(pj_status_t) pjturn_srv_add_listener(pjturn_srv *srv,
+					    pjturn_listener *lis)
+{
+    pj_stun_session_cb sess_cb;
+    unsigned index;
+    pj_stun_session *sess;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(srv && lis, PJ_EINVAL);
+    PJ_ASSERT_RETURN(srv->core.lis_cnt < MAX_LISTENERS, PJ_ETOOMANY);
+
+    /* Add to array */
+    index = srv->core.lis_cnt;
+    srv->core.listener[index] = lis;
+    lis->server = srv;
+
+    /* Create STUN session to handle new allocation */
+    pj_bzero(&sess_cb, sizeof(sess_cb));
+    sess_cb.on_rx_request = &on_rx_stun_request;
+    sess_cb.on_send_msg = &on_tx_stun_msg;
+
+    status = pj_stun_session_create(&srv->core.stun_cfg, "lis%p", &sess_cb,
+				    PJ_FALSE, &sess);
+    if (status != PJ_SUCCESS) {
+	srv->core.listener[index] = NULL;
+	return status;
+    }
+
+    pj_stun_session_set_user_data(sess, lis);
+
+    srv->core.stun_sess[index] = sess;
+    lis->id = index;
+    srv->core.lis_cnt++;
+
+    return PJ_SUCCESS;
+}
+
+
+/* Callback from our own STUN session to send packet */
+static pj_status_t on_tx_stun_msg( pj_stun_session *sess,
+				   const void *pkt,
+				   pj_size_t pkt_size,
+				   const pj_sockaddr_t *dst_addr,
+				   unsigned addr_len)
+{
+    pjturn_listener *listener;
+    
+    listener = (pjturn_listener*) pj_stun_session_get_user_data(sess);
+
+    PJ_ASSERT_RETURN(listener!=NULL, PJ_EINVALIDOP);
+
+    return pjturn_listener_sendto(listener, pkt, pkt_size, 0, 
+				  dst_addr, addr_len);
+}
+
+/* Create and send error response */
+static pj_status_t respond_error(pj_stun_sess *sess, const pj_stun_msg *req,
+				 pj_bool_t cache, int code, const char *err_msg,
+				 const pj_sockaddr_t *addr, unsigned addr_len)
+{
+    pj_status_t status;
+    pj_str_t reason;
+    pj_stun_tx_data *tdata;
+
+    status = pj_stun_session_create_res(sess, req, 
+				        code, (err_msg?pj_cstr(&reason,err_msg):NULL), 
+					&tdata);
+    if (status != PJ_SUCCESS)
+	return statys;
+
+    status = pj_stun_session_send_msg(sess, cache, dst_addr,  addr_len, tdata);
+    return status;
+
+}
+
+/* Parse ALLOCATE request */
+static pj_status_t parse_allocate_req(pjturn_allocation_req *cfg,
+				      pjturn_listener *listener,
+				      pj_stun_session *sess,
+				      const pj_stun_msg *req,
+				      const pj_sockaddr_t *src_addr,
+				      unsigned src_addr_len)
+{
+    pj_stun_bandwidth_attr *attr_bw;
+    pj_stun_req_transport_attr *attr_req_tp;
+    pj_stun_req_ip_attr *attr_req_ip;
+    pj_stun_req_port_props_attr *attr_rpp;
+    pj_stun_lifetime_attr *attr_lifetime;
+
+    pj_bzero(cfg, sizeof(*cfg));
+
+    /* Get BANDWIDTH attribute, if any. */
+    attr_bw = pj_stun_msg_find_attr(msg, PJ_STUN_BANDWIDTH_ATTR, 0);
+    if (attr_bw) {
+	cfg->bandwidth = attr_bw->value;
+    } else {
+	cfg->bandwidth = DEFA_CLIENT_BANDWIDTH;
+    }
+
+    /* Check if we can satisfy the bandwidth */
+    if (cfg->bandwidth > MAX_CLIENT_BANDWIDTH) {
+	respond_error(sess, msg, PJ_FALSE, 
+		      PJ_STUN_SC_ALLOCATION_QUOTA_REACHED, 
+		      "Invalid bandwidth", src_addr, src_addr_len);
+	return -1;
+    }
+
+    /* Get REQUESTED-TRANSPORT attribute, is any */
+    attr_req_tp = pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REQ_TRANSPORT, 0);
+    if (attr_req_tp) {
+	cfg->tp_type = PJ_STUN_GET_RT_PROTO(attr_req_tp->value);
+    } else {
+	cfg->tp_type = listener->tp_type;
+    }
+
+    /* Can only support UDP for now */
+    if (cfg->tp_type != PJTURN_TP_UDP) {
+	respond_error(sess, msg, PJ_FALSE, 
+		      PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO, 
+		      NULL, src_addr, src_addr_len);
+	return -1;
+    }
+
+    /* Get REQUESTED-IP attribute, if any */
+    attr_req_ip = pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REQ_IP, 0);
+    if (attr_req_ip) {
+	pj_memcpy(&cfg->addr, &attr_req_ip->sockaddr, 
+		  sizeof(attr_req_ip->sockaddr));
+    }
+
+    /* Get REQUESTED-PORT-PROPS attribute, if any */
+    attr_rpp = pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REQ_PORT_PROPS, 0);
+    if (attr_rpp) {
+	cfg->rpp_bits = PJ_STUN_GET_RPP_BITS(attr_rpp->value);
+	cfg->rpp_port = PJ_STUN_GET_RPP_PORT(attr_rpp->value);
+    } else {
+	cfg->rpp_bits = 0;
+	cfg->rpp_port = 0;
+    }
+
+    /* Get LIFETIME attribute */
+    attr_lifetime = pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0);
+    if (attr_lifetime) {
+	cfg->lifetime = attr_lifetime->value;
+	if (cfg->lifetime < MIN_LIFETIME || cfg->lifetime > MAX_LIFETIME) {
+	    respond_error(sess, msg, PJ_FALSE, 
+			  PJ_STUN_SC_BAD_REQUEST, 
+			  "Invalid LIFETIME value", src_addr, 
+			  src_addr_len);
+	    return -1;
+	}
+    } else {
+	cfg->lifetime = DEF_LIFETIME;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* Callback from our own STUN session when incoming request arrives */
+static pj_status_t on_rx_stun_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)
+{
+    pjturn_listener *listener;
+    pjturn_allocation_req req;
+    pj_status_t status;
+
+    listener = (pjturn_listener*) pj_stun_session_get_user_data(sess);
+
+    /* Handle strayed REFRESH request */
+    if (msg->hdr.type == PJ_STUN_REFRESH_REQUEST) {
+	return respond_error(sess, msg, PJ_FALSE, 
+			     PJ_STUN_SC_ALLOCATION_MISMATCH,
+			     NULL, src_addr, src_addr_len);
+    }
+
+    /* Respond any other requests with Bad Request response */
+    if (msg->hdr.type != PJ_STUN_ALLOCATE_REQUEST) {
+	return respond_error(sess, msg, PJ_FALSE, PJ_STUN_SC_BAD_REQUEST,
+			     NULL, src_addr, src_addr_len);
+    }
+
+    /* We have ALLOCATE request here, and it's authenticated. Parse the
+     * request.
+     */
+    status = parse_allocate_req(&req, listener, sess, msg, src_addr, 
+				src_addr_len);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Ready to allocate now */
+
+}
+
+
+/* Handle packet from new client address. */
+static void handle_new_client( pjturn_srv *srv, 
+			       pjturn_pkt *pkt)
+{
+    pj_stun_msg *req, *res;
+    unsigned options, lis_id;
+    pj_status_t status;
+
+    /* Check that this is a STUN message */
+    options = PJ_STUN_CHECK_PACKET;
+    if (pkt->listener->tp_type == PJTURN_TP_UDP)
+	options |= PJ_STUN_IS_DATAGRAM;
+
+    status = pj_stun_msg_check(pkt->pkt, pkt->len, options);
+    if (status != PJ_SUCCESS) {
+	char errmsg[PJ_ERR_MSG_SIZE];
+	char ip[PJ_INET6_ADDRSTRLEN+10];
+
+	pj_strerror(status, errmsg, sizeof(errmsg));
+	PJ_LOG(5,(srv->core.obj_name, 
+	          "Non STUN packet from %s is dropped: %s",
+		  pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3),
+		  errmsg));
+	return;
+    }
+
+    lis_id = pkt->listener->id;
+
+    /* Hand over processing to STUN session */
+    options &= ~PJ_STUN_CHECK_PACKET;
+    status = pj_stun_session_on_rx_pkt(srv->core.stun_sess[lis_id], pkt->pkt, 
+				       pkt->len, options, NULL,
+				       &pkt->src.clt_addr, 
+				       pkt->src_addr_len);
+    if (status != PJ_SUCCESS) {
+	char errmsg[PJ_ERR_MSG_SIZE];
+	char ip[PJ_INET6_ADDRSTRLEN+10];
+
+	pj_strerror(status, errmsg, sizeof(errmsg));
+	PJ_LOG(5,(srv->core.obj_name, 
+	          "Error processing STUN packet from %s: %s",
+		  pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3),
+		  errmsg));
+	return;
+    }
+}
+
+
+/*
+ * This callback is called by UDP listener on incoming packet.
+ */
+PJ_DEF(void) pjturn_srv_on_rx_pkt( pjturn_srv *srv, 
+				   pjturn_pkt *pkt)
+{
+    pjturn_allocation *alloc;
+
+    /* Get TURN allocation from the source address */
+    pj_lock_acquire(srv->core.lock);
+    alloc = pj_hash_get(srv->tables.alloc, &pkt->src, sizeof(pkt->src), NULL);
+    pj_lock_release(srv->core.lock);
+
+    /* If allocation is found, just hand over the packet to the
+     * allocation.
+     */
+    if (alloc) {
+	pjturn_allocation_on_rx_pkt(alloc, pkt);
+    } else {
+	/* Otherwise this is a new client */
+	handle_new_client(srv, pkt);
+    }
+}
+
+
Index: /pjproject/trunk/pjnath/src/pjturn-srv/turn.h
===================================================================
--- /pjproject/trunk/pjnath/src/pjturn-srv/turn.h	(revision 1812)
+++ /pjproject/trunk/pjnath/src/pjturn-srv/turn.h	(revision 1812)
@@ -0,0 +1,417 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 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 __PJTURN_SRV_TURN_H__
+#define __PJTURN_SRV_TURN_H__
+
+#include <pjlib.h>
+#include <pjnath.h>
+
+typedef struct pjturn_relay_res	    pjturn_relay_res;
+typedef struct pjturn_listener	    pjturn_listener;
+typedef struct pjturn_permission    pjturn_permission;
+typedef struct pjturn_allocation    pjturn_allocation;
+typedef struct pjturn_srv	    pjturn_srv;
+typedef struct pjturn_pkt	    pjturn_pkt;
+
+
+#define PJTURN_INVALID_CHANNEL	    0xFFFF
+#define PJTURN_NO_TIMEOUT	    ((long)0x7FFFFFFF)
+#define PJTURN_MAX_PKT_LEN	    3000
+
+/** Transport types */
+enum {
+    PJTURN_TP_UDP = 16,	    /**< UDP.	*/
+    PJTURN_TP_TCP = 6	    /**< TCP.	*/
+};
+
+
+/**
+ * This structure describes TURN relay resource. An allocation allocates
+ * one relay resource, and optionally it may reserve another resource.
+ */
+struct pjturn_relay_res
+{
+    /** Hash table key */
+    struct {
+	/** Transport type. */
+	int		    tp_type;
+
+	/** Transport/relay address */
+	pj_sockaddr	    addr;
+    } key;
+
+    /** Pool for this resource. */
+    pj_pool_t       *pool;
+
+    /** Mutex */
+    pj_lock_t	    *lock;
+
+    /** Allocation who requested or reserved this resource. */
+    pjturn_allocation *allocation;
+
+    /** Time when this resource times out */
+    pj_time_val	    timeout;
+
+    /** Username used in credential */
+    pj_str_t	    user;
+
+    /** Realm used in credential. */
+    pj_str_t	    realm;
+
+    /** Transport/relay socket */
+    pj_sock_t	    sock;
+};
+
+
+/****************************************************************************/
+/*
+ * TURN Allocation API
+ */
+
+/**
+ * This structure describes key to lookup TURN allocations in the
+ * allocation hash table.
+ */
+typedef struct pjturn_allocation_key
+{
+    int		    tp_type;	/**< Transport type.	    */
+    pj_sockaddr	    clt_addr;	/**< Client's address.	    */
+} pjturn_allocation_key;
+
+
+/**
+ * Allocation request.
+ */
+typedef struct pjturn_allocation_req
+{
+    /** Requested transport */
+    unsigned		tp_type;
+
+    /** Requested IP */
+    pj_sockaddr		addr;
+
+    /** Requested bandwidth */
+    unsigned		bandwidth;
+
+    /** Lifetime. */
+    unsigned		lifetime;
+
+    /** A bits */
+    unsigned		rpp_bits;
+
+    /** Requested port */
+    unsigned		rpp_port;
+
+} pjturn_allocation_req;
+
+
+/**
+ * This structure describes TURN pjturn_allocation session.
+ */
+struct pjturn_allocation
+{
+    /** Hash table key to identify client. */
+    pjturn_allocation_key key;
+
+    /** Pool for this allocation. */
+    pj_pool_t		*pool;
+
+    /** Mutex */
+    pj_lock_t		*lock;
+
+    /** TURN listener. */
+    pjturn_listener	*listener;
+
+    /** Client socket, if connection to client is using TCP. */
+    pj_sock_t		clt_sock;
+
+    /** The relay resource for this allocation. */
+    pjturn_relay_res	relay;
+
+    /** Relay resource reserved by this allocation, if any */
+    pjturn_relay_res	*resv;
+
+};
+
+
+/**
+ * This structure describes TURN pjturn_permission or channel.
+ */
+struct pjturn_permission
+{
+    /** Hash table key */
+    struct {
+	/** Transport type. */
+	pj_uint16_t		tp_type;
+
+	/** Transport socket. If TCP is used, the value will be the actual
+	 *  TCP socket. If UDP is used, the value will be the relay address
+	 */
+	pj_sock_t		sock;
+
+	/** Peer address. */
+	pj_sockaddr		peer_addr;
+    } key;
+
+    /** Pool for this permission. */
+    pj_pool_t	        *pool;
+
+    /** Mutex */
+    pj_lock_t		*lock;
+
+    /** TURN allocation that owns this permission/channel */
+    pjturn_allocation	*allocation;
+
+    /** Optional channel number, or PJTURN_INVALID_CHANNEL if channel number
+     *  is not requested for this permission.
+     */
+    pj_uint16_t		channel;
+
+    /** Permission timeout. */
+    pj_time_val		timeout;
+};
+
+/**
+ * Handle incoming packet.
+ */
+PJ_DECL(void) pjturn_allocation_on_rx_pkt(pjturn_allocation *alloc,
+					  pjturn_pkt *pkt);
+
+
+/****************************************************************************/
+/*
+ * TURN Listener API
+ */
+
+/**
+ * This structure describes TURN listener socket. A TURN listener socket
+ * listens for incoming connections from clients.
+ */
+struct pjturn_listener
+{
+    /** TURN server instance. */
+    pjturn_srv		*server;
+
+    /** Listener index in the server */
+    unsigned		id;
+
+    /** Pool for this listener. */
+    pj_pool_t	       *pool;
+
+    /** Transport type. */
+    int			tp_type;
+
+    /** Bound address of this listener. */
+    pj_sockaddr		addr;
+
+    /** Socket. */
+    pj_sock_t		sock;
+
+    /** Flags. */
+    unsigned		flags;
+
+    /** Sendto handler */
+    pj_status_t		(*sendto)(pjturn_listener *listener,
+				  const void *packet,
+				  pj_size_t size,
+				  unsigned flag,
+				  const pj_sockaddr_t *addr,
+				  int addr_len);
+
+    /** Destroy handler */
+    pj_status_t		(*destroy)(pjturn_listener*);
+};
+
+
+/**
+ * An incoming packet.
+ */
+struct pjturn_pkt
+{
+    /** Pool for this packet */
+    pj_pool_t		    *pool;
+
+    /** Listener that owns this. */
+    pjturn_listener	    *listener;
+
+    /** Packet buffer. */
+    pj_uint8_t		    pkt[PJTURN_MAX_PKT_LEN];
+
+    /** Size of the packet */
+    pj_size_t		    len;
+
+    /** Arrival time. */
+    pj_time_val		    rx_time;
+
+    /** Source transport type and source address. */
+    pjturn_allocation_key   src;
+
+    /** Source address length. */
+    int			    src_addr_len;
+};
+
+
+/**
+ * Create a new listener on the specified port.
+ */
+PJ_DECL(pj_status_t) pjturn_listener_create_udp(pjturn_srv *srv,
+						int af,
+					        const pj_str_t *bound_addr,
+					        unsigned port,
+						unsigned concurrency_cnt,
+						unsigned flags,
+						pjturn_listener **p_listener);
+
+/**
+ * Send packet with this listener.
+ */
+PJ_DECL(pj_status_t) pjturn_listener_sendto(pjturn_listener *listener,
+					    const void *packet,
+					    pj_size_t size,
+					    unsigned flag,
+					    const pj_sockaddr_t *addr,
+					    int addr_len);
+
+/**
+ * Destroy listener.
+ */
+PJ_DECL(pj_status_t) pjturn_listener_destroy(pjturn_listener *listener);
+
+
+/****************************************************************************/
+/*
+ * TURN Server API
+ */
+/**
+ * This structure describes TURN pjturn_srv instance.
+ */
+struct pjturn_srv
+{
+    /** Core settings */
+    struct {
+	/** Object name */
+	char		*obj_name;
+
+	/** Pool factory */
+	pj_pool_factory *pf;
+
+	/** Pool for this server instance. */
+	pj_pool_t       *pool;
+
+	/** Global Ioqueue */
+	pj_ioqueue_t    *ioqueue;
+
+	/** Mutex */
+	pj_lock_t	*lock;
+
+	/** Global timer heap instance. */
+	pj_timer_heap_t *timer_heap;
+
+	/** Number of listeners */
+	unsigned         lis_cnt;
+
+	/** Array of listeners. */
+	pjturn_listener **listener;
+
+	/** Array of STUN sessions, one for each listeners. */
+	pj_stun_session	**stun_sess;
+
+	/** Number of worker threads. */
+	unsigned        thread_cnt;
+
+	/** Array of worker threads. */
+	pj_thread_t     **thread;
+
+	/** STUN config. */
+	pj_stun_config	 stun_cfg;
+
+
+    } core;
+
+    
+    /** Hash tables */
+    struct {
+	/** Allocations hash table, indexed by transport type and
+	 *  client address. 
+	 */
+	pj_hash_table_t *alloc;
+
+	/** Relay resource hash table, indexed by transport type and
+	 *  relay address. 
+	 */
+	pj_hash_table_t *res;
+
+	/** Permission hash table, indexed by transport type, socket handle,
+	 *  and peer address.
+	 */
+	pj_hash_table_t *peer;
+
+    } tables;
+
+    /** Ports settings */
+    struct {
+	/** Minimum UDP port number. */
+	pj_uint16_t	    min_udp;
+
+	/** Maximum UDP port number. */
+	pj_uint16_t	    max_udp;
+
+	/** Next UDP port number. */
+	pj_uint16_t	    next_udp;
+
+
+	/** Minimum TCP port number. */
+	pj_uint16_t	    min_tcp;
+
+	/** Maximum TCP port number. */
+	pj_uint16_t	    max_tcp;
+
+	/** Next TCP port number. */
+	pj_uint16_t	    next_tcp;
+
+    } ports;
+};
+
+
+/** 
+ * Create server.
+ */
+PJ_DECL(pj_status_t) pjturn_srv_create(pj_pool_factory *pf,
+				       pjturn_srv **p_srv);
+
+/** 
+ * Destroy server.
+ */
+PJ_DECL(pj_status_t) pjturn_srv_destroy(pjturn_srv *srv);
+
+/** 
+ * Add listener.
+ */
+PJ_DECL(pj_status_t) pjturn_srv_add_listener(pjturn_srv *srv,
+					     pjturn_listener *lis);
+
+/**
+ * This callback is called by UDP listener on incoming packet.
+ */
+PJ_DECL(void) pjturn_srv_on_rx_pkt(pjturn_srv *srv, 
+				   pjturn_pkt *pkt);
+
+
+#endif	/* __PJTURN_SRV_TURN_H__ */
+
