summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-02-21 15:54:27 +0000
committerBenny Prijono <bennylp@teluu.com>2008-02-21 15:54:27 +0000
commit806767dde5dcdf2fb599ca37249f06fc50818483 (patch)
tree8e20a6f4deddd8d3ca082a073a8fc055d1dfb0c5
parent8d3fdf02c0b6764cb3beacac8601f2574dc97b74 (diff)
Ticket #485: initial TURN server implementation (not yet compilable)
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1812 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjnath/build/pjnath.dsw21
-rw-r--r--pjnath/build/pjturn_srv.dsp114
-rw-r--r--pjnath/src/pjturn-srv/listener_udp.c219
-rw-r--r--pjnath/src/pjturn-srv/main.c1
-rw-r--r--pjnath/src/pjturn-srv/server.c401
-rw-r--r--pjnath/src/pjturn-srv/turn.h417
6 files changed, 1173 insertions, 0 deletions
diff --git a/pjnath/build/pjnath.dsw b/pjnath/build/pjnath.dsw
index 33252433..8eaa0776 100644
--- a/pjnath/build/pjnath.dsw
+++ b/pjnath/build/pjnath.dsw
@@ -102,6 +102,27 @@ Package=<4>
###############################################################################
+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:
Package=<5>
diff --git a/pjnath/build/pjturn_srv.dsp b/pjnath/build/pjturn_srv.dsp
new file mode 100644
index 00000000..4a322345
--- /dev/null
+++ b/pjnath/build/pjturn_srv.dsp
@@ -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
diff --git a/pjnath/src/pjturn-srv/listener_udp.c b/pjnath/src/pjturn-srv/listener_udp.c
new file mode 100644
index 00000000..d8f90ca4
--- /dev/null
+++ b/pjnath/src/pjturn-srv/listener_udp.c
@@ -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);
+}
+
diff --git a/pjnath/src/pjturn-srv/main.c b/pjnath/src/pjturn-srv/main.c
new file mode 100644
index 00000000..823eb28a
--- /dev/null
+++ b/pjnath/src/pjturn-srv/main.c
@@ -0,0 +1 @@
+#include "turn.h"
diff --git a/pjnath/src/pjturn-srv/server.c b/pjnath/src/pjturn-srv/server.c
new file mode 100644
index 00000000..15268348
--- /dev/null
+++ b/pjnath/src/pjturn-srv/server.c
@@ -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);
+ }
+}
+
+
diff --git a/pjnath/src/pjturn-srv/turn.h b/pjnath/src/pjturn-srv/turn.h
new file mode 100644
index 00000000..39a17fae
--- /dev/null
+++ b/pjnath/src/pjturn-srv/turn.h
@@ -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__ */
+