summaryrefslogtreecommitdiff
path: root/pjnath/src/pjturn-srv/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjnath/src/pjturn-srv/server.c')
-rw-r--r--pjnath/src/pjturn-srv/server.c743
1 files changed, 479 insertions, 264 deletions
diff --git a/pjnath/src/pjturn-srv/server.c b/pjnath/src/pjturn-srv/server.c
index c9fc40cf..6765b3ca 100644
--- a/pjnath/src/pjturn-srv/server.c
+++ b/pjnath/src/pjturn-srv/server.c
@@ -17,25 +17,21 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "turn.h"
+#include "auth.h"
#define MAX_CLIENTS 32
#define MAX_PEERS_PER_CLIENT 8
-#define MAX_HANDLES (MAX_CLIENTS*MAX_PEERS_PER_CLIENT+MAX_LISTENERS)
+//#define MAX_HANDLES (MAX_CLIENTS*MAX_PEERS_PER_CLIENT+MAX_LISTENERS)
+#define MAX_HANDLES PJ_IOQUEUE_MAX_HANDLES
#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
-
+#define MAX_NET_EVENTS 10
/* Prototypes */
+static int server_thread_proc(void *arg);
static pj_status_t on_tx_stun_msg( pj_stun_session *sess,
const void *pkt,
pj_size_t pkt_size,
@@ -48,60 +44,91 @@ static pj_status_t on_rx_stun_request(pj_stun_session *sess,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len);
+struct saved_cred
+{
+ pj_str_t realm;
+ pj_str_t username;
+ pj_str_t nonce;
+ int data_type;
+ pj_str_t data;
+};
+
+
/*
- * Get transport type name.
+ * Get transport type name, normally for logging purpose only.
*/
-PJ_DEF(const char*) pjturn_tp_type_name(int tp_type)
+PJ_DEF(const char*) pj_turn_tp_type_name(int tp_type)
{
/* Must be 3 characters long! */
- if (tp_type == PJTURN_TP_UDP)
+ if (tp_type == PJ_TURN_TP_UDP) {
return "UDP";
- else if (tp_type == PJTURN_TP_TCP)
+ } else if (tp_type == PJ_TURN_TP_TCP) {
return "TCP";
- else
+ } else {
+ pj_assert(!"Unsupported transport");
return "???";
+ }
}
/*
* Create server.
*/
-PJ_DEF(pj_status_t) pjturn_srv_create( pj_pool_factory *pf,
- pjturn_srv **p_srv)
+PJ_DEF(pj_status_t) pj_turn_srv_create(pj_pool_factory *pf,
+ pj_turn_srv **p_srv)
{
pj_pool_t *pool;
- pjturn_srv *srv;
+ pj_turn_srv *srv;
+ unsigned i;
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 = PJ_POOL_ZALLOC_T(pool, pj_turn_srv);
+ srv->obj_name = pool->obj_name;
srv->core.pf = pf;
srv->core.pool = pool;
+ srv->core.tls_key = srv->core.tls_data = -1;
+ /* Create ioqueue */
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);
+ /* Server mutex */
+ status = pj_lock_create_recursive_mutex(pool, srv->obj_name,
+ &srv->core.lock);
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*));
+ /* Allocate TLS */
+ status = pj_thread_local_alloc(&srv->core.tls_key);
+ if (status != PJ_SUCCESS)
+ goto on_error;
- status = pj_lock_create_recursive_mutex(pool, "srv%p", &srv->core.lock);
+ status = pj_thread_local_alloc(&srv->core.tls_data);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Create timer heap */
+ status = pj_timer_heap_create(pool, MAX_TIMER, &srv->core.timer_heap);
if (status != PJ_SUCCESS)
goto on_error;
+ /* Configure lock for the timer heap */
+ pj_timer_heap_set_lock(srv->core.timer_heap, srv->core.lock, PJ_FALSE);
+
+ /* Array of listeners */
+ srv->core.listener = (pj_turn_listener**)
+ pj_pool_calloc(pool, MAX_LISTENERS,
+ sizeof(srv->core.listener[0]));
+
+ /* Array of STUN sessions, one for each listener */
+ srv->core.stun_sess = (pj_stun_session**)
+ pj_pool_calloc(pool, MAX_LISTENERS,
+ (sizeof(srv->core.stun_sess[0])));
+
/* Create hash tables */
srv->tables.alloc = pj_hash_create(pool, MAX_CLIENTS);
srv->tables.res = pj_hash_create(pool, MAX_CLIENTS);
@@ -116,27 +143,204 @@ PJ_DEF(pj_status_t) pjturn_srv_create( pj_pool_factory *pf,
pj_stun_config_init(&srv->core.stun_cfg, pf, 0, srv->core.ioqueue,
srv->core.timer_heap);
+ /* Init STUN credential */
+ srv->core.cred.type = PJ_STUN_AUTH_CRED_DYNAMIC;
+ srv->core.cred.data.dyn_cred.user_data = srv;
+ srv->core.cred.data.dyn_cred.get_auth = &pj_turn_get_auth;
+ srv->core.cred.data.dyn_cred.get_cred = &pj_turn_srv_get_cred;
+ srv->core.cred.data.dyn_cred.get_password = &pj_turn_get_password;
+ srv->core.cred.data.dyn_cred.verify_nonce = &pj_turn_verify_nonce;
+
+ /* Array of worker threads */
+ srv->core.thread_cnt = MAX_THREADS;
+ srv->core.thread = (pj_thread_t**)
+ pj_pool_calloc(pool, srv->core.thread_cnt,
+ sizeof(pj_thread_t*));
+
+ /* Start the worker threads */
+ for (i=0; i<srv->core.thread_cnt; ++i) {
+ status = pj_thread_create(pool, srv->obj_name, &server_thread_proc,
+ srv, 0, 0, &srv->core.thread[i]);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+
+ /* We're done. Application should add listeners now */
+ PJ_LOG(4,(srv->obj_name, "TURN server v%s is running",
+ pj_get_version()));
+
*p_srv = srv;
return PJ_SUCCESS;
on_error:
- pjturn_srv_destroy(srv);
+ pj_turn_srv_destroy(srv);
return status;
}
-/**
- * Create server.
+
+/*
+ * Handle timer and network events
+ */
+static void srv_handle_events(pj_turn_srv *srv, const pj_time_val *max_timeout)
+{
+ /* timeout is 'out' var. This just to make compiler happy. */
+ pj_time_val timeout = { 0, 0};
+ unsigned net_event_count = 0;
+ int c;
+
+ /* Poll the timer. The timer heap has its own mutex for better
+ * granularity, so we don't need to lock the server.
+ */
+ timeout.sec = timeout.msec = 0;
+ c = pj_timer_heap_poll( srv->core.timer_heap, &timeout );
+
+ /* timer_heap_poll should never ever returns negative value, or otherwise
+ * ioqueue_poll() will block forever!
+ */
+ pj_assert(timeout.sec >= 0 && timeout.msec >= 0);
+ if (timeout.msec >= 1000) timeout.msec = 999;
+
+ /* If caller specifies maximum time to wait, then compare the value with
+ * the timeout to wait from timer, and use the minimum value.
+ */
+ if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) {
+ timeout = *max_timeout;
+ }
+
+ /* Poll ioqueue.
+ * Repeat polling the ioqueue while we have immediate events, because
+ * timer heap may process more than one events, so if we only process
+ * one network events at a time (such as when IOCP backend is used),
+ * the ioqueue may have trouble keeping up with the request rate.
+ *
+ * For example, for each send() request, one network event will be
+ * reported by ioqueue for the send() completion. If we don't poll
+ * the ioqueue often enough, the send() completion will not be
+ * reported in timely manner.
+ */
+ do {
+ c = pj_ioqueue_poll( srv->core.ioqueue, &timeout);
+ if (c < 0) {
+ pj_thread_sleep(PJ_TIME_VAL_MSEC(timeout));
+ return;
+ } else if (c == 0) {
+ break;
+ } else {
+ net_event_count += c;
+ timeout.sec = timeout.msec = 0;
+ }
+ } while (c > 0 && net_event_count < MAX_NET_EVENTS);
+
+}
+
+/*
+ * Server worker thread proc.
+ */
+static int server_thread_proc(void *arg)
+{
+ pj_turn_srv *srv = (pj_turn_srv*)arg;
+
+ while (!srv->core.quit) {
+ pj_time_val timeout_max = {0, 500};
+ srv_handle_events(srv, &timeout_max);
+ }
+
+ return 0;
+}
+
+/*
+ * Destroy the server.
*/
-PJ_DEF(pj_status_t) pjturn_srv_destroy(pjturn_srv *srv)
+PJ_DEF(pj_status_t) pj_turn_srv_destroy(pj_turn_srv *srv)
{
+ pj_hash_iterator_t itbuf, *it;
+ unsigned i;
+
+ /* Stop all worker threads */
+ srv->core.quit = PJ_TRUE;
+ for (i=0; i<srv->core.thread_cnt; ++i) {
+ if (srv->core.thread[i]) {
+ pj_thread_join(srv->core.thread[i]);
+ pj_thread_destroy(srv->core.thread[i]);
+ srv->core.thread[i] = NULL;
+ }
+ }
+
+ /* Destroy all listeners and STUN sessions associated with them. */
+ for (i=0; i<srv->core.lis_cnt; ++i) {
+ if (srv->core.listener[i]) {
+ pj_turn_listener_destroy(srv->core.listener[i]);
+ srv->core.listener[i] = NULL;
+ }
+ if (srv->core.stun_sess[i]) {
+ pj_stun_session_destroy(srv->core.stun_sess[i]);
+ srv->core.stun_sess[i] = NULL;
+ }
+ }
+
+ /* Destroy all allocations */
+ if (srv->tables.alloc) {
+ it = pj_hash_first(srv->tables.alloc, &itbuf);
+ while (it != NULL) {
+ pj_turn_allocation *alloc = (pj_turn_allocation*)
+ pj_hash_this(srv->tables.alloc, it);
+ pj_turn_allocation_destroy(alloc);
+ it = pj_hash_next(srv->tables.alloc, it);
+ }
+ }
+
+
+ /* Destroy hash tables (well, sort of) */
+ if (srv->tables.alloc) {
+ srv->tables.alloc = NULL;
+ srv->tables.res = NULL;
+ }
+
+ /* Destroy timer heap */
+ if (srv->core.timer_heap) {
+ pj_timer_heap_destroy(srv->core.timer_heap);
+ srv->core.timer_heap = NULL;
+ }
+
+ /* Destroy ioqueue */
+ if (srv->core.ioqueue) {
+ pj_ioqueue_destroy(srv->core.ioqueue);
+ srv->core.ioqueue = NULL;
+ }
+
+ /* Destroy thread local IDs */
+ if (srv->core.tls_key != -1) {
+ pj_thread_local_free(srv->core.tls_key);
+ srv->core.tls_key = -1;
+ }
+ if (srv->core.tls_data != -1) {
+ pj_thread_local_free(srv->core.tls_data);
+ srv->core.tls_data = -1;
+ }
+
+ /* Destroy server lock */
+ if (srv->core.lock) {
+ pj_lock_destroy(srv->core.lock);
+ srv->core.lock = NULL;
+ }
+
+ /* Release pool */
+ if (srv->core.pool) {
+ pj_pool_t *pool = srv->core.pool;
+ srv->core.pool = NULL;
+ pj_pool_release(pool);
+ }
+
+ /* Done */
return PJ_SUCCESS;
}
-/**
+
+/*
* Add listener.
*/
-PJ_DEF(pj_status_t) pjturn_srv_add_listener(pjturn_srv *srv,
- pjturn_listener *lis)
+PJ_DEF(pj_status_t) pj_turn_srv_add_listener(pj_turn_srv *srv,
+ pj_turn_listener *lis)
{
pj_stun_session_cb sess_cb;
unsigned index;
@@ -156,27 +360,76 @@ PJ_DEF(pj_status_t) pjturn_srv_add_listener(pjturn_srv *srv,
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);
+ status = pj_stun_session_create(&srv->core.stun_cfg, lis->obj_name,
+ &sess_cb, PJ_FALSE, &sess);
if (status != PJ_SUCCESS) {
srv->core.listener[index] = NULL;
return status;
}
pj_stun_session_set_user_data(sess, lis);
+ pj_stun_session_set_credential(sess, &srv->core.cred);
srv->core.stun_sess[index] = sess;
lis->id = index;
srv->core.lis_cnt++;
+ PJ_LOG(4,(srv->obj_name, "Listener %s/%s added at index %d",
+ lis->obj_name, lis->info, lis->id));
+
return PJ_SUCCESS;
}
-/**
- * Register an allocation.
+
+/*
+ * Send packet with this listener.
*/
-PJ_DEF(pj_status_t) pjturn_srv_register_allocation(pjturn_srv *srv,
- pjturn_allocation *alloc)
+PJ_DEF(pj_status_t) pj_turn_listener_sendto(pj_turn_listener *listener,
+ const void *packet,
+ pj_size_t size,
+ unsigned flag,
+ const pj_sockaddr_t *addr,
+ int addr_len)
+{
+ pj_assert(listener->id != PJ_TURN_INVALID_LIS_ID);
+ return listener->sendto(listener, packet, size, flag, addr, addr_len);
+}
+
+
+/*
+ * Destroy listener.
+ */
+PJ_DEF(pj_status_t) pj_turn_listener_destroy(pj_turn_listener *listener)
+{
+ pj_turn_srv *srv = listener->server;
+ unsigned i;
+
+ /* Remove from our listener list */
+ pj_lock_acquire(srv->core.lock);
+ for (i=0; i<srv->core.lis_cnt; ++i) {
+ if (srv->core.listener[i] == listener) {
+ srv->core.listener[i] = NULL;
+ srv->core.lis_cnt--;
+ listener->id = PJ_TURN_INVALID_LIS_ID;
+ if (srv->core.stun_sess[i]) {
+ pj_stun_session_destroy(srv->core.stun_sess[i]);
+ srv->core.stun_sess[i] = NULL;
+ }
+ break;
+ }
+ }
+ pj_lock_release(srv->core.lock);
+
+ /* Destroy */
+ return listener->destroy(listener);
+}
+
+
+/*
+ * Register an allocation to the hash tables.
+ */
+PJ_DEF(pj_status_t) pj_turn_srv_register_allocation(pj_turn_srv *srv,
+ pj_turn_allocation *alloc)
{
/* Add to hash tables */
pj_lock_acquire(srv->core.lock);
@@ -190,11 +443,12 @@ PJ_DEF(pj_status_t) pjturn_srv_register_allocation(pjturn_srv *srv,
return PJ_SUCCESS;
}
-/**
- * Unregister an allocation.
+
+/*
+ * Unregister an allocation from the hash tables.
*/
-PJ_DEF(pj_status_t) pjturn_srv_unregister_allocation(pjturn_srv *srv,
- pjturn_allocation *alloc)
+PJ_DEF(pj_status_t) pj_turn_srv_unregister_allocation(pj_turn_srv *srv,
+ pj_turn_allocation *alloc)
{
/* Unregister from hash tables */
pj_lock_acquire(srv->core.lock);
@@ -208,285 +462,204 @@ PJ_DEF(pj_status_t) pjturn_srv_unregister_allocation(pjturn_srv *srv,
}
-/* Callback from our own STUN session to send packet */
+/* Callback from our own STUN session whenever it needs to send
+ * outgoing STUN 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;
+ pj_turn_listener *listener;
- listener = (pjturn_listener*) pj_stun_session_get_user_data(sess);
+ listener = (pj_turn_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);
+ return pj_turn_listener_sendto(listener, pkt, pkt_size, 0,
+ dst_addr, addr_len);
}
-/* Create and send error response */
-static pj_status_t respond_error(pj_stun_session *sess, const pj_stun_msg *req,
- pj_bool_t cache, int code, const char *errmsg,
- const pj_sockaddr_t *dst_addr,
- unsigned addr_len)
+
+/* Respond to STUN request */
+static pj_status_t stun_respond(pj_turn_srv *srv,
+ pj_stun_session *sess,
+ const pj_stun_msg *req,
+ unsigned code,
+ const char *errmsg,
+ pj_bool_t cache,
+ const pj_sockaddr_t *dst_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, (errmsg?pj_cstr(&reason,errmsg):NULL),
+ /* Create response */
+ status = pj_stun_session_create_res(sess, req, code,
+ (errmsg?pj_cstr(&reason,errmsg):NULL),
&tdata);
if (status != PJ_SUCCESS)
return status;
- status = pj_stun_session_send_msg(sess, cache, dst_addr, addr_len, tdata);
- return status;
+ /* Store the credential for future lookup. */
+ if (pj_stun_auth_valid_for_msg(tdata->msg)) {
+ pj_turn_srv_put_cred(srv, req, tdata);
+ }
+ /* Send the response */
+ return pj_stun_session_send_msg(sess, cache, dst_addr, addr_len, tdata);
}
-/* 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)
+
+/*
+ * Store the credential to put placed for the specified message for
+ * future retrieval.
+ */
+PJ_DEF(pj_status_t) pj_turn_srv_put_cred(pj_turn_srv *srv,
+ const pj_stun_msg *req,
+ pj_stun_tx_data *response)
{
- 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_uint_attr*)
- pj_stun_msg_find_attr(req, PJ_STUN_ATTR_BANDWIDTH, 0);
- if (attr_bw) {
- cfg->bandwidth = attr_bw->value;
- } else {
- cfg->bandwidth = DEFA_CLIENT_BANDWIDTH;
- }
+ pj_stun_username_attr *user;
+ pj_stun_realm_attr *realm;
+ pj_stun_nonce_attr *nonce;
+ struct saved_cred *saved_cred;
+ pj_status_t status;
- /* Check if we can satisfy the bandwidth */
- if (cfg->bandwidth > MAX_CLIENT_BANDWIDTH) {
- respond_error(sess, req, PJ_FALSE,
- PJ_STUN_SC_ALLOCATION_QUOTA_REACHED,
- "Invalid bandwidth", src_addr, src_addr_len);
- return -1;
- }
+ realm = (pj_stun_realm_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REALM, 0);
+ PJ_ASSERT_RETURN(realm != NULL, PJ_EBUG);
- /* Get REQUESTED-TRANSPORT attribute, is any */
- attr_req_tp = (pj_stun_uint_attr*)
- pj_stun_msg_find_attr(req, 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;
- }
+ user = (pj_stun_username_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0);
+ PJ_ASSERT_RETURN(user != NULL, PJ_EBUG);
- /* Can only support UDP for now */
- if (cfg->tp_type != PJTURN_TP_UDP) {
- respond_error(sess, req, PJ_FALSE,
- PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO,
- NULL, src_addr, src_addr_len);
- return -1;
- }
+ nonce = (pj_stun_nonce_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_NONCE, 0);
+ PJ_ASSERT_RETURN(nonce != NULL, PJ_EBUG);
- /* Get REQUESTED-IP attribute, if any */
- attr_req_ip = (pj_stun_sockaddr_attr*)
- pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_IP, 0);
- if (attr_req_ip) {
- pj_sockaddr_print(&attr_req_ip->sockaddr, cfg->addr,
- sizeof(cfg->addr), 0);
- }
+ saved_cred = PJ_POOL_ALLOC_T(response->pool, struct saved_cred);
- /* Get REQUESTED-PORT-PROPS attribute, if any */
- attr_rpp = (pj_stun_uint_attr*)
- pj_stun_msg_find_attr(req, 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;
- }
+ /* Lookup the password */
+ status = pj_turn_get_password(response->msg, NULL, &realm->value,
+ &user->value, response->pool,
+ &saved_cred->data_type,
+ &saved_cred->data);
+ if (status != PJ_SUCCESS)
+ return status;
- /* Get LIFETIME attribute */
- attr_lifetime = (pj_stun_uint_attr*)
- pj_stun_msg_find_attr(req, 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, req, PJ_FALSE,
- PJ_STUN_SC_BAD_REQUEST,
- "Invalid LIFETIME value", src_addr,
- src_addr_len);
- return -1;
- }
- } else {
- cfg->lifetime = DEF_LIFETIME;
- }
+ /* Store credential */
+ pj_strdup(response->pool, &saved_cred->username, &user->value);
+ pj_strdup(response->pool, &saved_cred->realm, &realm->value);
+ pj_strdup(response->pool, &saved_cred->nonce, &nonce->value);
+
+ pj_thread_local_set(srv->core.tls_key, response->msg);
+ pj_thread_local_set(srv->core.tls_data, saved_cred);
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)
+
+/**
+ * Retrieve previously stored credential for the specified message.
+ */
+PJ_DEF(pj_status_t) pj_turn_srv_get_cred(const pj_stun_msg *msg,
+ void *user_data,
+ pj_pool_t *pool,
+ pj_str_t *realm,
+ pj_str_t *username,
+ pj_str_t *nonce,
+ int *data_type,
+ pj_str_t *data)
{
- pjturn_listener *listener;
- pjturn_srv *srv;
- pjturn_allocation_req req;
- pjturn_allocation *alloc;
- pj_stun_tx_data *tdata;
- pj_status_t status;
+ pj_turn_srv *srv;
+ const pj_stun_msg *saved_msg;
+ struct saved_cred *saved_cred;
- listener = (pjturn_listener*) pj_stun_session_get_user_data(sess);
- srv = listener->server;
+ PJ_UNUSED_ARG(pool);
- /* 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);
- }
+ srv = (pj_turn_srv*)user_data;
- /* 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);
- }
+ /* Lookup stored message and make sure it's for the same message */
+ saved_msg = (const pj_stun_msg*)
+ pj_thread_local_get(srv->core.tls_key);
+ PJ_ASSERT_RETURN(saved_msg==msg, PJ_ENOTFOUND);
- /* 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;
+ /* Lookup saved credential */
+ saved_cred = (struct saved_cred*)
+ pj_thread_local_get(srv->core.tls_data);
+ PJ_ASSERT_RETURN(saved_cred != NULL, PJ_ENOTFOUND);
- /* Create new allocation. The relay resource will be allocated
- * in this function.
- */
- status = pjturn_allocation_create(listener, src_addr, src_addr_len,
- msg, &req, &alloc);
- if (status != PJ_SUCCESS) {
- char errmsg[PJ_ERR_MSG_SIZE];
- pj_strerror(status, errmsg, sizeof(errmsg));
- return respond_error(sess, msg, PJ_FALSE, PJ_STUN_SC_SERVER_ERROR,
- errmsg, src_addr, src_addr_len);
- }
+ *realm = saved_cred->realm;
+ *username = saved_cred->username;
+ *nonce = saved_cred->nonce;
+ *data_type = saved_cred->data_type;
+ *data = saved_cred->data;
- /* Respond the original ALLOCATE request */
- status = pj_stun_session_create_res(srv->core.stun_sess[listener->id],
- msg, 0, NULL, &tdata);
- if (status != PJ_SUCCESS) {
- char errmsg[PJ_ERR_MSG_SIZE];
-
- pjturn_allocation_destroy(alloc);
- pj_strerror(status, errmsg, sizeof(errmsg));
- return respond_error(sess, msg, PJ_FALSE, PJ_STUN_SC_SERVER_ERROR,
- errmsg, src_addr, src_addr_len);
- }
-
- /* Add RELAYED-ADDRESS attribute */
- pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
- PJ_STUN_ATTR_RELAY_ADDR, PJ_TRUE,
- &alloc->relay.hkey.addr,
- pj_sockaddr_get_len(&alloc->relay.hkey.addr));
-
- /* Add LIFETIME. */
- pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
- PJ_STUN_ATTR_LIFETIME,
- (unsigned)alloc->relay.lifetime);
-
- /* Add BANDWIDTH */
- pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
- PJ_STUN_ATTR_BANDWIDTH,
- alloc->bandwidth);
-
- /* Add RESERVATION-TOKEN */
- PJ_TODO(ADD_RESERVATION_TOKEN);
-
- /* Add XOR-MAPPED-ADDRESS */
- pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
- PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE,
- &alloc->hkey.clt_addr,
- pj_sockaddr_get_len(&alloc->hkey.clt_addr));
-
- /* Send the response */
- pj_stun_session_send_msg(srv->core.stun_sess[listener->id], PJ_TRUE,
- src_addr, src_addr_len, tdata);
+ /* Don't clear saved_cred as this may be called more than once */
- /* Done. */
return PJ_SUCCESS;
}
-/* Handle packet from new client address. */
-static void handle_new_client( pjturn_srv *srv,
- pjturn_pkt *pkt)
+/* Callback from our own STUN session when incoming request arrives.
+ * This function is triggered by pj_stun_session_on_rx_pkt() call in
+ * pj_turn_srv_on_rx_pkt() function below.
+ */
+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)
{
- unsigned options, lis_id;
+ pj_turn_listener *listener;
+ pj_turn_srv *srv;
+ pj_turn_allocation *alloc;
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;
+ PJ_UNUSED_ARG(pkt);
+ PJ_UNUSED_ARG(pkt_len);
- 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;
- }
+ listener = (pj_turn_listener*) pj_stun_session_get_user_data(sess);
+ srv = listener->server;
- lis_id = pkt->listener->id;
+ /* Respond any requests other than ALLOCATE with 437 response */
+ if (msg->hdr.type != PJ_STUN_ALLOCATE_REQUEST) {
+ stun_respond(srv, sess, msg, PJ_STUN_SC_ALLOCATION_MISMATCH,
+ NULL, PJ_FALSE, src_addr, src_addr_len);
+ return PJ_SUCCESS;
+ }
- /* 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);
+ /* Create new allocation. The relay resource will be allocated
+ * in this function.
+ */
+ status = pj_turn_allocation_create(listener, src_addr, src_addr_len,
+ msg, sess, &alloc);
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;
+ /* STUN response has been sent, no need to reply here */
+ return PJ_SUCCESS;
}
+
+ /* Done. */
+ return PJ_SUCCESS;
}
/*
- * This callback is called by UDP listener on incoming packet.
+ * This callback is called by UDP listener on incoming packet. This is
+ * the first entry for incoming packet (from client) to the server. From
+ * here, the packet may be handed over to an allocation if an allocation
+ * is found for the client address, or handed over to owned STUN session
+ * if an allocation is not found.
*/
-PJ_DEF(void) pjturn_srv_on_rx_pkt( pjturn_srv *srv,
- pjturn_pkt *pkt)
+PJ_DEF(void) pj_turn_srv_on_rx_pkt(pj_turn_srv *srv,
+ pj_turn_pkt *pkt)
{
- pjturn_allocation *alloc;
+ pj_turn_allocation *alloc;
/* Get TURN allocation from the source address */
pj_lock_acquire(srv->core.lock);
@@ -497,10 +670,52 @@ PJ_DEF(void) pjturn_srv_on_rx_pkt( pjturn_srv *srv,
* allocation.
*/
if (alloc) {
- pjturn_allocation_on_rx_client_pkt(alloc, pkt);
+ pj_turn_allocation_on_rx_client_pkt(alloc, pkt);
} else {
/* Otherwise this is a new client */
- handle_new_client(srv, pkt);
+ 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 == PJ_TURN_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->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. This will trigger
+ * on_rx_stun_request() callback to be called if the STUN
+ * message is a request.
+ */
+ 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->obj_name,
+ "Error processing STUN packet from %s: %s",
+ pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3),
+ errmsg));
+ return;
+ }
}
}