summaryrefslogtreecommitdiff
path: root/pjnath/src/pjturn-srv/allocation.c
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-03-09 12:55:00 +0000
committerBenny Prijono <bennylp@teluu.com>2008-03-09 12:55:00 +0000
commit9e0ebffd26e56586f6f2ab0469144c3685fc388c (patch)
tree7c06e7a3db2b951290462c2ca9580ab5daf57b79 /pjnath/src/pjturn-srv/allocation.c
parentfee9975ecfd058c88d5dd8648ec6ff6bc6ff40c3 (diff)
More work for ticket #485: updated pjnath with TURN-07 and added authentication in the server
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1852 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjnath/src/pjturn-srv/allocation.c')
-rw-r--r--pjnath/src/pjturn-srv/allocation.c550
1 files changed, 409 insertions, 141 deletions
diff --git a/pjnath/src/pjturn-srv/allocation.c b/pjnath/src/pjturn-srv/allocation.c
index 725863ce..5698ea41 100644
--- a/pjnath/src/pjturn-srv/allocation.c
+++ b/pjnath/src/pjturn-srv/allocation.c
@@ -17,6 +17,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "turn.h"
+#include "auth.h"
+
#define THIS_FILE "allocation.c"
@@ -30,6 +32,15 @@ enum {
#define DESTROY_DELAY {0, 500}
#define PEER_TABLE_SIZE 32
+#define MAX_CLIENT_BANDWIDTH 128 /* In Kbps */
+#define DEFA_CLIENT_BANDWIDTH 64
+
+#define MIN_LIFETIME 30
+#define MAX_LIFETIME 600
+#define DEF_LIFETIME 300
+
+
+
/* ChannelData header */
typedef struct channel_data_hdr
{
@@ -38,13 +49,30 @@ typedef struct channel_data_hdr
} channel_data_hdr;
+/* Parsed Allocation request. */
+typedef struct alloc_request
+{
+ unsigned tp_type; /* Requested transport */
+ char addr[PJ_INET6_ADDRSTRLEN]; /* Requested IP */
+ unsigned bandwidth; /* Requested bandwidth */
+ unsigned lifetime; /* Lifetime. */
+ unsigned rpp_bits; /* A bits */
+ unsigned rpp_port; /* Requested port */
+} alloc_request;
+
+
+
/* Prototypes */
-static pj_status_t create_relay(pjturn_allocation *alloc,
- const pjturn_allocation_req *req);
+static void destroy_allocation(pj_turn_allocation *alloc);
+static pj_status_t create_relay(pj_turn_srv *srv,
+ pj_turn_allocation *alloc,
+ const pj_stun_msg *msg,
+ const alloc_request *req,
+ pj_turn_relay_res *relay);
+static void destroy_relay(pj_turn_relay_res *relay);
static void on_rx_from_peer(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
pj_ssize_t bytes_read);
-static void destroy_relay(pjturn_relay_res *relay);
static pj_status_t stun_on_send_msg(pj_stun_session *sess,
const void *pkt,
pj_size_t pkt_size,
@@ -64,7 +92,7 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
unsigned src_addr_len);
/* Log allocation error */
-static void alloc_err(pjturn_allocation *alloc, const char *title,
+static void alloc_err(pj_turn_allocation *alloc, const char *title,
pj_status_t status)
{
char errmsg[PJ_ERR_MSG_SIZE];
@@ -74,34 +102,225 @@ static void alloc_err(pjturn_allocation *alloc, const char *title,
title, alloc->info, errmsg));
}
+
+/* Parse ALLOCATE request */
+static pj_status_t parse_allocate_req(alloc_request *cfg,
+ 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_res_token_attr *attr_res_token;
+ pj_stun_req_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;
+ }
+
+ /* Check if we can satisfy the bandwidth */
+ if (cfg->bandwidth > MAX_CLIENT_BANDWIDTH) {
+ pj_stun_session_respond(sess, req, PJ_STUN_SC_ALLOCATION_QUOTA_REACHED,
+ "Invalid bandwidth", PJ_TRUE,
+ src_addr, src_addr_len);
+ return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_ALLOCATION_QUOTA_REACHED);
+ }
+
+ /* MUST have REQUESTED-TRANSPORT attribute */
+ attr_req_tp = (pj_stun_uint_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_TRANSPORT, 0);
+ if (attr_req_tp == NULL) {
+ pj_stun_session_respond(sess, req, PJ_STUN_SC_BAD_REQUEST,
+ "Missing REQUESTED-TRANSPORT attribute",
+ PJ_TRUE, src_addr, src_addr_len);
+ return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
+ }
+
+ cfg->tp_type = PJ_STUN_GET_RT_PROTO(attr_req_tp->value);
+
+ /* Can only support UDP for now */
+ if (cfg->tp_type != PJ_TURN_TP_UDP) {
+ pj_stun_session_respond(sess, req, PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO,
+ NULL, PJ_TRUE, src_addr, src_addr_len);
+ return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO);
+ }
+
+ /* Get RESERVATION-TOKEN attribute, if any */
+ attr_res_token = (pj_stun_res_token_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_RESERVATION_TOKEN,
+ 0);
+ if (attr_res_token) {
+ /* We don't support RESERVATION-TOKEN for now */
+ pj_stun_session_respond(sess, req,
+ PJ_STUN_SC_BAD_REQUEST,
+ "RESERVATION-TOKEN is not supported", PJ_TRUE,
+ src_addr, src_addr_len);
+ return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
+ }
+
+ /* Get REQUESTED-PROPS attribute, if any */
+ attr_rpp = (pj_stun_req_props_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_PROPS, 0);
+ if (attr_rpp) {
+ /* We don't support REQUESTED-PROPS for now */
+ pj_stun_session_respond(sess, req,
+ PJ_STUN_SC_BAD_REQUEST,
+ "REQUESTED-PROPS is not supported", PJ_TRUE,
+ src_addr, src_addr_len);
+ return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
+ }
+
+ /* 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) {
+ pj_stun_session_respond(sess, req, PJ_STUN_SC_BAD_REQUEST,
+ "LIFETIME too short", PJ_TRUE,
+ src_addr, src_addr_len);
+ return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
+ }
+ if (cfg->lifetime > MAX_LIFETIME)
+ cfg->lifetime = MAX_LIFETIME;
+ } else {
+ cfg->lifetime = DEF_LIFETIME;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/* Respond to ALLOCATE request */
+static pj_status_t send_allocate_response(pj_turn_allocation *alloc,
+ pj_stun_session *srv_sess,
+ const pj_stun_msg *msg)
+{
+ pj_stun_tx_data *tdata;
+ pj_status_t status;
+
+ /* Respond the original ALLOCATE request */
+ status = pj_stun_session_create_res(srv_sess, msg, 0, NULL, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* 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 */
+ return pj_stun_session_send_msg(srv_sess, PJ_TRUE,
+ &alloc->hkey.clt_addr,
+ pj_sockaddr_get_len(&alloc->hkey.clt_addr),
+ tdata);
+}
+
+
+/*
+ * Init credential for the allocation. We use static credential, meaning that
+ * the user's password must not change during allocation.
+ */
+static pj_status_t init_cred(pj_turn_allocation *alloc, const pj_stun_msg *req)
+{
+ const pj_stun_username_attr *user;
+ const pj_stun_realm_attr *realm;
+ const pj_stun_nonce_attr *nonce;
+ pj_status_t status;
+
+ realm = (const pj_stun_realm_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REALM, 0);
+ PJ_ASSERT_RETURN(realm != NULL, PJ_EBUG);
+
+ user = (const pj_stun_username_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0);
+ PJ_ASSERT_RETURN(user != NULL, PJ_EBUG);
+
+ nonce = (const pj_stun_nonce_attr*)
+ pj_stun_msg_find_attr(req, PJ_STUN_ATTR_NONCE, 0);
+ PJ_ASSERT_RETURN(nonce != NULL, PJ_EBUG);
+
+ /* Lookup the password */
+ status = pj_turn_get_password(NULL, NULL, &realm->value,
+ &user->value, alloc->pool,
+ &alloc->cred.data.static_cred.data_type,
+ &alloc->cred.data.static_cred.data);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Save credential */
+ alloc->cred.type = PJ_STUN_AUTH_CRED_STATIC;
+ pj_strdup(alloc->pool, &alloc->cred.data.static_cred.realm, &realm->value);
+ pj_strdup(alloc->pool, &alloc->cred.data.static_cred.username, &user->value);
+ pj_strdup(alloc->pool, &alloc->cred.data.static_cred.nonce, &nonce->value);
+
+ return PJ_SUCCESS;
+}
+
+
/*
* Create new allocation.
*/
-PJ_DEF(pj_status_t) pjturn_allocation_create(pjturn_listener *listener,
- const pj_sockaddr_t *src_addr,
- unsigned src_addr_len,
- const pj_stun_msg *msg,
- const pjturn_allocation_req *req,
- pjturn_allocation **p_alloc)
+PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len,
+ const pj_stun_msg *msg,
+ pj_stun_session *srv_sess,
+ pj_turn_allocation **p_alloc)
{
- pjturn_srv *srv = listener->server;
+ pj_turn_srv *srv = listener->server;
pj_pool_t *pool;
- pjturn_allocation *alloc;
+ alloc_request req;
+ pj_turn_allocation *alloc;
pj_stun_session_cb sess_cb;
- char relay_info[80];
+ char str_tmp[80];
pj_status_t status;
+ /* Parse ALLOCATE request */
+ status = parse_allocate_req(&req, srv_sess, msg, src_addr, src_addr_len);
+ if (status != PJ_SUCCESS)
+ return status;
+
pool = pj_pool_create(srv->core.pf, "alloc%p", 1000, 1000, NULL);
/* Init allocation structure */
- alloc = PJ_POOL_ZALLOC_T(pool, pjturn_allocation);
+ alloc = PJ_POOL_ZALLOC_T(pool, pj_turn_allocation);
alloc->pool = pool;
alloc->obj_name = pool->obj_name;
alloc->listener = listener;
alloc->clt_sock = PJ_INVALID_SOCKET;
alloc->relay.tp.sock = PJ_INVALID_SOCKET;
- alloc->bandwidth = req->bandwidth;
+ alloc->bandwidth = req.bandwidth;
alloc->hkey.tp_type = listener->tp_type;
pj_memcpy(&alloc->hkey.clt_addr, src_addr, src_addr_len);
@@ -109,8 +328,7 @@ PJ_DEF(pj_status_t) pjturn_allocation_create(pjturn_listener *listener,
status = pj_lock_create_recursive_mutex(pool, alloc->obj_name,
&alloc->lock);
if (status != PJ_SUCCESS) {
- pjturn_allocation_destroy(alloc);
- return status;
+ goto on_error;
}
/* Create peer hash table */
@@ -120,7 +338,7 @@ PJ_DEF(pj_status_t) pjturn_allocation_create(pjturn_listener *listener,
alloc->ch_table = pj_hash_create(pool, PEER_TABLE_SIZE);
/* Print info */
- pj_ansi_strcpy(alloc->info, pjturn_tp_type_name(listener->tp_type));
+ pj_ansi_strcpy(alloc->info, pj_turn_tp_type_name(listener->tp_type));
alloc->info[3] = ':';
pj_sockaddr_print(src_addr, alloc->info+4, sizeof(alloc->info)-4, 3);
@@ -132,44 +350,89 @@ PJ_DEF(pj_status_t) pjturn_allocation_create(pjturn_listener *listener,
status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name,
&sess_cb, PJ_FALSE, &alloc->sess);
if (status != PJ_SUCCESS) {
- pjturn_allocation_destroy(alloc);
- return status;
+ goto on_error;
}
/* Attach to STUN session */
pj_stun_session_set_user_data(alloc->sess, alloc);
+ /* Init authentication credential */
+ status = init_cred(alloc, msg);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Attach authentication credential to STUN session */
+ pj_stun_session_set_credential(alloc->sess, &alloc->cred);
+
/* Create the relay resource */
- status = pjturn_allocation_create_relay(srv, alloc, msg, req,
- &alloc->relay);
+ status = create_relay(srv, alloc, msg, &req, &alloc->relay);
if (status != PJ_SUCCESS) {
- pjturn_allocation_destroy(alloc);
- return status;
+ goto on_error;
}
/* Register this allocation */
- pjturn_srv_register_allocation(srv, alloc);
+ pj_turn_srv_register_allocation(srv, alloc);
- pj_sockaddr_print(&alloc->relay.hkey.addr, relay_info,
- sizeof(relay_info), 3);
+ /* Respond to ALLOCATE request */
+ status = send_allocate_response(alloc, srv_sess, msg);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Done */
+ pj_sockaddr_print(&alloc->relay.hkey.addr, str_tmp,
+ sizeof(str_tmp), 3);
PJ_LOG(4,(alloc->obj_name, "Client %s created, relay addr=%s:%s",
- alloc->info, pjturn_tp_type_name(req->tp_type), relay_info));
+ alloc->info, pj_turn_tp_type_name(req.tp_type), str_tmp));
/* Success */
*p_alloc = alloc;
return PJ_SUCCESS;
+
+on_error:
+ /* Send reply to the ALLOCATE request */
+ pj_strerror(status, str_tmp, sizeof(str_tmp));
+ pj_stun_session_respond(srv_sess, msg, PJ_STUN_SC_BAD_REQUEST, str_tmp,
+ PJ_TRUE, src_addr, src_addr_len);
+
+ /* Cleanup */
+ destroy_allocation(alloc);
+ return status;
+}
+
+
+/* Destroy relay resource */
+static void destroy_relay(pj_turn_relay_res *relay)
+{
+ if (relay->timer.id) {
+ pj_timer_heap_cancel(relay->allocation->listener->server->core.timer_heap,
+ &relay->timer);
+ relay->timer.id = PJ_FALSE;
+ }
+
+ if (relay->tp.key) {
+ pj_ioqueue_unregister(relay->tp.key);
+ relay->tp.key = NULL;
+ relay->tp.sock = PJ_INVALID_SOCKET;
+ } else if (relay->tp.sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(relay->tp.sock);
+ relay->tp.sock = PJ_INVALID_SOCKET;
+ }
+
+ /* Mark as shutdown */
+ relay->lifetime = 0;
}
/*
- * Destroy allocation.
+ * Really destroy allocation.
*/
-PJ_DECL(void) pjturn_allocation_destroy(pjturn_allocation *alloc)
+static void destroy_allocation(pj_turn_allocation *alloc)
{
pj_pool_t *pool;
/* Unregister this allocation */
- pjturn_srv_unregister_allocation(alloc->listener->server, alloc);
+ pj_turn_srv_unregister_allocation(alloc->listener->server, alloc);
/* Destroy relay */
destroy_relay(&alloc->relay);
@@ -201,36 +464,23 @@ PJ_DECL(void) pjturn_allocation_destroy(pjturn_allocation *alloc)
}
-/* Destroy relay resource */
-static void destroy_relay(pjturn_relay_res *relay)
+PJ_DECL(void) pj_turn_allocation_destroy(pj_turn_allocation *alloc)
{
- if (relay->timer.id) {
- pj_timer_heap_cancel(relay->allocation->listener->server->core.timer_heap,
- &relay->timer);
- relay->timer.id = PJ_FALSE;
- }
-
- if (relay->tp.key) {
- pj_ioqueue_unregister(relay->tp.key);
- relay->tp.key = NULL;
- relay->tp.sock = PJ_INVALID_SOCKET;
- } else if (relay->tp.sock != PJ_INVALID_SOCKET) {
- pj_sock_close(relay->tp.sock);
- relay->tp.sock = PJ_INVALID_SOCKET;
- }
-
- /* Mark as shutdown */
- relay->lifetime = 0;
+ destroy_allocation(alloc);
}
-/* Initiate shutdown sequence for this allocation */
-static void alloc_shutdown(pjturn_allocation *alloc)
+
+/* Initiate shutdown sequence for this allocation and start destroy timer.
+ * Once allocation is marked as shutting down, any packets will be
+ * rejected/discarded
+ */
+static void alloc_shutdown(pj_turn_allocation *alloc)
{
pj_time_val destroy_delay = DESTROY_DELAY;
/* Work with existing schedule */
if (alloc->relay.timer.id == TIMER_ID_TIMEOUT) {
- /* Cancel existing timer */
+ /* Cancel existing shutdown timer */
pj_timer_heap_cancel(alloc->listener->server->core.timer_heap,
&alloc->relay.timer);
alloc->relay.timer.id = TIMER_ID_NONE;
@@ -257,8 +507,9 @@ static void alloc_shutdown(pjturn_allocation *alloc)
&alloc->relay.timer, &destroy_delay);
}
+
/* Reschedule timeout using current lifetime setting */
-static pj_status_t resched_timeout(pjturn_allocation *alloc)
+static pj_status_t resched_timeout(pj_turn_allocation *alloc)
{
pj_time_val delay;
pj_status_t status;
@@ -291,10 +542,12 @@ static pj_status_t resched_timeout(pjturn_allocation *alloc)
/* Timer timeout callback */
static void relay_timeout_cb(pj_timer_heap_t *heap, pj_timer_entry *e)
{
- pjturn_relay_res *rel;
- pjturn_allocation *alloc;
+ pj_turn_relay_res *rel;
+ pj_turn_allocation *alloc;
- rel = (pjturn_relay_res*) e->user_data;
+ PJ_UNUSED_ARG(heap);
+
+ rel = (pj_turn_relay_res*) e->user_data;
alloc = rel->allocation;
if (e->id == TIMER_ID_TIMEOUT) {
@@ -313,7 +566,7 @@ static void relay_timeout_cb(pj_timer_heap_t *heap, pj_timer_entry *e)
PJ_LOG(4,(alloc->obj_name, "Client %s destroying..",
alloc->info));
- pjturn_allocation_destroy(alloc);
+ destroy_allocation(alloc);
}
}
@@ -321,11 +574,11 @@ static void relay_timeout_cb(pj_timer_heap_t *heap, pj_timer_entry *e)
/*
* Create relay.
*/
-PJ_DEF(pj_status_t) pjturn_allocation_create_relay(pjturn_srv *srv,
- pjturn_allocation *alloc,
- const pj_stun_msg *msg,
- const pjturn_allocation_req *req,
- pjturn_relay_res *relay)
+static pj_status_t create_relay(pj_turn_srv *srv,
+ pj_turn_allocation *alloc,
+ const pj_stun_msg *msg,
+ const alloc_request *req,
+ pj_turn_relay_res *relay)
{
enum { RETRY = 40 };
pj_pool_t *pool = alloc->pool;
@@ -365,9 +618,9 @@ PJ_DEF(pj_status_t) pjturn_allocation_create_relay(pjturn_srv *srv,
relay->hkey.tp_type = req->tp_type;
/* Create the socket */
- if (req->tp_type == PJTURN_TP_UDP) {
+ if (req->tp_type == PJ_TURN_TP_UDP) {
sock_type = pj_SOCK_DGRAM();
- } else if (req->tp_type == PJTURN_TP_TCP) {
+ } else if (req->tp_type == PJ_TURN_TP_TCP) {
sock_type = pj_SOCK_STREAM();
} else {
pj_assert(!"Unknown transport");
@@ -395,16 +648,17 @@ PJ_DEF(pj_status_t) pjturn_allocation_create_relay(pjturn_srv *srv,
if (req->rpp_port) {
port = (pj_uint16_t) req->rpp_port;
- } else if (req->tp_type == PJTURN_TP_UDP) {
+ } else if (req->tp_type == PJ_TURN_TP_UDP) {
port = (pj_uint16_t) srv->ports.next_udp++;
if (srv->ports.next_udp > srv->ports.max_udp)
srv->ports.next_udp = srv->ports.min_udp;
- } else if (req->tp_type == PJTURN_TP_TCP) {
+ } else if (req->tp_type == PJ_TURN_TP_TCP) {
port = (pj_uint16_t) srv->ports.next_tcp++;
if (srv->ports.next_tcp > srv->ports.max_tcp)
srv->ports.next_tcp = srv->ports.min_tcp;
} else {
pj_assert(!"Invalid transport");
+ port = 0;
}
pj_lock_release(srv->core.lock);
@@ -463,27 +717,16 @@ PJ_DEF(pj_status_t) pjturn_allocation_create_relay(pjturn_srv *srv,
}
/* Create and send error response */
-static void send_reply_err(pjturn_allocation *alloc,
+static void send_reply_err(pj_turn_allocation *alloc,
const pj_stun_msg *req,
pj_bool_t cache,
int code, const char *errmsg)
{
pj_status_t status;
- pj_str_t reason;
- pj_stun_tx_data *tdata;
- status = pj_stun_session_create_res(alloc->sess, req,
- code, (errmsg?pj_cstr(&reason,errmsg):NULL),
- &tdata);
- if (status != PJ_SUCCESS) {
- alloc_err(alloc, "Error creating STUN error response", status);
- return;
- }
-
- status = pj_stun_session_send_msg(alloc->sess, cache,
- &alloc->hkey.clt_addr,
- pj_sockaddr_get_len(&alloc->hkey.clt_addr),
- tdata);
+ status = pj_stun_session_respond(alloc->sess, req, code, errmsg, cache,
+ &alloc->hkey.clt_addr,
+ pj_sockaddr_get_len(&alloc->hkey.clt_addr.addr));
if (status != PJ_SUCCESS) {
alloc_err(alloc, "Error sending STUN error response", status);
return;
@@ -491,7 +734,7 @@ static void send_reply_err(pjturn_allocation *alloc,
}
/* Create and send successful response */
-static void send_reply_ok(pjturn_allocation *alloc,
+static void send_reply_ok(pj_turn_allocation *alloc,
const pj_stun_msg *req)
{
pj_status_t status;
@@ -534,16 +777,16 @@ static void send_reply_ok(pjturn_allocation *alloc,
/* Create new permission */
-static pjturn_permission *create_permission(pjturn_allocation *alloc,
+static pj_turn_permission *create_permission(pj_turn_allocation *alloc,
const pj_sockaddr_t *peer_addr,
unsigned addr_len)
{
- pjturn_permission *perm;
+ pj_turn_permission *perm;
- perm = PJ_POOL_ZALLOC_T(alloc->pool, pjturn_permission);
+ perm = PJ_POOL_ZALLOC_T(alloc->pool, pj_turn_permission);
pj_memcpy(&perm->hkey.peer_addr, peer_addr, addr_len);
- if (alloc->listener->tp_type == PJTURN_TP_UDP) {
+ if (alloc->listener->tp_type == PJ_TURN_TP_UDP) {
perm->sock = alloc->listener->sock;
} else {
pj_assert(!"TCP is not supported yet");
@@ -551,18 +794,18 @@ static pjturn_permission *create_permission(pjturn_allocation *alloc,
}
perm->allocation = alloc;
- perm->channel = PJTURN_INVALID_CHANNEL;
+ perm->channel = PJ_TURN_INVALID_CHANNEL;
pj_gettimeofday(&perm->expiry);
- perm->expiry.sec += PJTURN_PERM_TIMEOUT;
+ perm->expiry.sec += PJ_TURN_PERM_TIMEOUT;
return perm;
}
/* Check if a permission isn't expired. Return NULL if expired. */
-static pjturn_permission *check_permission_expiry(pjturn_permission *perm)
+static pj_turn_permission *check_permission_expiry(pj_turn_permission *perm)
{
- pjturn_allocation *alloc = perm->allocation;
+ pj_turn_allocation *alloc = perm->allocation;
pj_time_val now;
pj_gettimeofday(&now);
@@ -576,7 +819,7 @@ static pjturn_permission *check_permission_expiry(pjturn_permission *perm)
0, NULL);
/* Remove from channel hash table, if assigned a channel number */
- if (perm->channel != PJTURN_INVALID_CHANNEL) {
+ if (perm->channel != PJ_TURN_INVALID_CHANNEL) {
pj_hash_set(NULL, alloc->ch_table, &perm->channel,
sizeof(perm->channel), 0, NULL);
}
@@ -585,33 +828,33 @@ static pjturn_permission *check_permission_expiry(pjturn_permission *perm)
}
/* Lookup permission in hash table by the peer address */
-static pjturn_permission*
-lookup_permission_by_addr(pjturn_allocation *alloc,
+static pj_turn_permission*
+lookup_permission_by_addr(pj_turn_allocation *alloc,
const pj_sockaddr_t *peer_addr,
unsigned addr_len)
{
- pjturn_permission_key key;
- pjturn_permission *perm;
+ pj_turn_permission_key key;
+ pj_turn_permission *perm;
pj_bzero(&key, sizeof(key));
pj_memcpy(&key, peer_addr, addr_len);
/* Lookup in peer hash table */
- perm = (pjturn_permission*) pj_hash_get(alloc->peer_table, &key,
+ perm = (pj_turn_permission*) pj_hash_get(alloc->peer_table, &key,
sizeof(key), NULL);
return check_permission_expiry(perm);
}
/* Lookup permission in hash table by the channel number */
-static pjturn_permission*
-lookup_permission_by_chnum(pjturn_allocation *alloc,
+static pj_turn_permission*
+lookup_permission_by_chnum(pj_turn_allocation *alloc,
unsigned chnum)
{
pj_uint16_t chnum16 = (pj_uint16_t)chnum;
- pjturn_permission *perm;
+ pj_turn_permission *perm;
/* Lookup in peer hash table */
- perm = (pjturn_permission*) pj_hash_get(alloc->peer_table, &chnum16,
+ perm = (pj_turn_permission*) pj_hash_get(alloc->peer_table, &chnum16,
sizeof(chnum16), NULL);
return check_permission_expiry(perm);
}
@@ -619,25 +862,29 @@ lookup_permission_by_chnum(pjturn_allocation *alloc,
/* Update permission because of data from client to peer.
* Return PJ_TRUE is permission is found.
*/
-static pj_bool_t refresh_permission(pjturn_permission *perm)
+static pj_bool_t refresh_permission(pj_turn_permission *perm)
{
pj_gettimeofday(&perm->expiry);
- if (perm->channel == PJTURN_INVALID_CHANNEL)
- perm->expiry.sec += PJTURN_PERM_TIMEOUT;
+ if (perm->channel == PJ_TURN_INVALID_CHANNEL)
+ perm->expiry.sec += PJ_TURN_PERM_TIMEOUT;
else
- perm->expiry.sec += PJTURN_CHANNEL_TIMEOUT;
+ perm->expiry.sec += PJ_TURN_CHANNEL_TIMEOUT;
return PJ_TRUE;
}
/*
- * Handle incoming packet from client.
+ * Handle incoming packet from client. This would have been called by
+ * server upon receiving packet from a listener.
*/
-PJ_DEF(void) pjturn_allocation_on_rx_client_pkt( pjturn_allocation *alloc,
- pjturn_pkt *pkt)
+PJ_DEF(void) pj_turn_allocation_on_rx_client_pkt(pj_turn_allocation *alloc,
+ pj_turn_pkt *pkt)
{
pj_bool_t is_stun;
pj_status_t status;
+ /* Lock this allocation */
+ pj_lock_acquire(alloc->lock);
+
/* Quickly check if this is STUN message */
is_stun = ((*((pj_uint8_t*)pkt->pkt) & 0xC0) == 0);
@@ -649,7 +896,7 @@ PJ_DEF(void) pjturn_allocation_on_rx_client_pkt( pjturn_allocation *alloc,
* callbacks.
*/
unsigned options = PJ_STUN_CHECK_PACKET;
- if (pkt->listener->tp_type == PJTURN_TP_UDP)
+ if (pkt->listener->tp_type == PJ_TURN_TP_UDP)
options |= PJ_STUN_IS_DATAGRAM;
status = pj_stun_session_on_rx_pkt(alloc->sess, pkt->pkt, pkt->len,
@@ -658,7 +905,7 @@ PJ_DEF(void) pjturn_allocation_on_rx_client_pkt( pjturn_allocation *alloc,
pkt->src_addr_len);
if (status != PJ_SUCCESS) {
alloc_err(alloc, "Error handling STUN packet", status);
- return;
+ goto on_return;
}
} else {
@@ -666,20 +913,20 @@ PJ_DEF(void) pjturn_allocation_on_rx_client_pkt( pjturn_allocation *alloc,
* This is not a STUN packet, must be ChannelData packet.
*/
channel_data_hdr *cd = (channel_data_hdr*)pkt->pkt;
- pjturn_permission *perm;
+ pj_turn_permission *perm;
pj_ssize_t len;
/* For UDP check the packet length */
- if (alloc->listener->tp_type == PJTURN_TP_UDP) {
+ if (alloc->listener->tp_type == PJ_TURN_TP_UDP) {
if (pkt->len < pj_ntohs(cd->length)+sizeof(*cd)) {
PJ_LOG(4,(alloc->obj_name,
"ChannelData from %s discarded: UDP size error",
alloc->info));
- return;
+ goto on_return;
}
} else {
pj_assert(!"Unsupported transport");
- return;
+ goto on_return;
}
perm = lookup_permission_by_chnum(alloc, pj_ntohs(cd->ch_number));
@@ -688,7 +935,7 @@ PJ_DEF(void) pjturn_allocation_on_rx_client_pkt( pjturn_allocation *alloc,
PJ_LOG(4,(alloc->obj_name,
"ChannelData from %s discarded: not found",
alloc->info));
- return;
+ goto on_return;
}
/* Relay the data */
@@ -700,18 +947,23 @@ PJ_DEF(void) pjturn_allocation_on_rx_client_pkt( pjturn_allocation *alloc,
/* Refresh permission */
refresh_permission(perm);
}
+
+on_return:
+ /* Release lock */
+ pj_lock_release(alloc->lock);
}
+
/*
* Handle incoming packet from peer. This function is called by
* on_rx_from_peer().
*/
-static void on_rx_peer_pkt(pjturn_allocation *alloc,
- pjturn_relay_res *rel,
- char *pkt, pj_size_t len,
- const pj_sockaddr *src_addr)
+static void handle_peer_pkt(pj_turn_allocation *alloc,
+ pj_turn_relay_res *rel,
+ char *pkt, pj_size_t len,
+ const pj_sockaddr *src_addr)
{
- pjturn_permission *perm;
+ pj_turn_permission *perm;
/* Lookup permission */
perm = lookup_permission_by_addr(alloc, src_addr,
@@ -724,14 +976,14 @@ static void on_rx_peer_pkt(pjturn_allocation *alloc,
/* Send Data Indication or ChannelData, depends on whether
* this permission is attached to a channel number.
*/
- if (perm->channel != PJTURN_INVALID_CHANNEL) {
+ if (perm->channel != PJ_TURN_INVALID_CHANNEL) {
/* Send ChannelData */
channel_data_hdr *cd = (channel_data_hdr*)rel->tp.tx_pkt;
- if (len > PJTURN_MAX_PKT_LEN) {
+ if (len > PJ_TURN_MAX_PKT_LEN) {
char peer_addr[80];
pj_sockaddr_print(src_addr, peer_addr, sizeof(peer_addr), 3);
- PJ_LOG(1,(alloc->obj_name, "Client %s: discarded data from %s "
+ PJ_LOG(4,(alloc->obj_name, "Client %s: discarded data from %s "
"because it's too long (%d bytes)",
alloc->info, peer_addr, len));
return;
@@ -745,7 +997,7 @@ static void on_rx_peer_pkt(pjturn_allocation *alloc,
pj_memcpy(rel->tp.rx_pkt+sizeof(channel_data_hdr), pkt, len);
/* Send to client */
- pjturn_listener_sendto(alloc->listener, rel->tp.tx_pkt,
+ pj_turn_listener_sendto(alloc->listener, rel->tp.tx_pkt,
len+sizeof(channel_data_hdr), 0,
&alloc->hkey.clt_addr,
pj_sockaddr_get_len(&alloc->hkey.clt_addr));
@@ -770,15 +1022,18 @@ static void on_rx_from_peer(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
pj_ssize_t bytes_read)
{
- pjturn_relay_res *rel;
+ pj_turn_relay_res *rel;
pj_status_t status;
- rel = (pjturn_relay_res*) pj_ioqueue_get_user_data(key);
+ rel = (pj_turn_relay_res*) pj_ioqueue_get_user_data(key);
+
+ /* Lock the allocation */
+ pj_lock_acquire(rel->allocation->lock);
do {
if (bytes_read > 0) {
- on_rx_peer_pkt(rel->allocation, rel, rel->tp.rx_pkt,
- bytes_read, &rel->tp.src_addr);
+ handle_peer_pkt(rel->allocation, rel, rel->tp.rx_pkt,
+ bytes_read, &rel->tp.src_addr);
}
/* Read next packet */
@@ -794,6 +1049,8 @@ static void on_rx_from_peer(pj_ioqueue_key_t *key,
} while (status != PJ_EPENDING && status != PJ_ECANCELLED);
+ /* Release allocation lock */
+ pj_lock_release(rel->allocation->lock);
}
/*
@@ -806,18 +1063,18 @@ static pj_status_t stun_on_send_msg(pj_stun_session *sess,
const pj_sockaddr_t *dst_addr,
unsigned addr_len)
{
- pjturn_allocation *alloc;
+ pj_turn_allocation *alloc;
- alloc = (pjturn_allocation*) pj_stun_session_get_user_data(sess);
+ alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
- return pjturn_listener_sendto(alloc->listener, pkt, pkt_size, 0,
+ return pj_turn_listener_sendto(alloc->listener, pkt, pkt_size, 0,
dst_addr, addr_len);
}
/*
* Callback notification from STUN session when it receives STUN
* requests. This callback was trigger by STUN incoming message
- * processing in pjturn_allocation_on_rx_client_pkt().
+ * processing in pj_turn_allocation_on_rx_client_pkt().
*/
static pj_status_t stun_on_rx_request(pj_stun_session *sess,
const pj_uint8_t *pkt,
@@ -826,12 +1083,18 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
- pjturn_allocation *alloc;
+ pj_turn_allocation *alloc;
- alloc = (pjturn_allocation*) pj_stun_session_get_user_data(sess);
+ PJ_UNUSED_ARG(pkt);
+ PJ_UNUSED_ARG(pkt_len);
+ PJ_UNUSED_ARG(src_addr);
+ PJ_UNUSED_ARG(src_addr_len);
+
+ alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
/* Refuse to serve any request if we've been shutdown */
if (alloc->relay.lifetime == 0) {
+ /* Reject with 437 if we're shutting down */
send_reply_err(alloc, msg, PJ_TRUE,
PJ_STUN_SC_ALLOCATION_MISMATCH, NULL);
return PJ_SUCCESS;
@@ -894,7 +1157,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess,
*/
pj_stun_channel_number_attr *ch_attr;
pj_stun_peer_addr_attr *peer_attr;
- pjturn_permission *p1, *p2;
+ pj_turn_permission *p1, *p2;
ch_attr = (pj_stun_channel_number_attr*)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CHANNEL_NUMBER, 0);
@@ -933,7 +1196,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess,
*/
p2 = lookup_permission_by_addr(alloc, &peer_attr->sockaddr,
pj_sockaddr_get_len(&peer_attr->sockaddr));
- if (p2 && p2->channel != PJTURN_INVALID_CHANNEL) {
+ if (p2 && p2->channel != PJ_TURN_INVALID_CHANNEL) {
send_reply_err(alloc, msg, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST,
"Peer address already assigned a channel number");
return PJ_SUCCESS;
@@ -976,7 +1239,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess,
/*
* Callback notification from STUN session when it receives STUN
* indications. This callback was trigger by STUN incoming message
- * processing in pjturn_allocation_on_rx_client_pkt().
+ * processing in pj_turn_allocation_on_rx_client_pkt().
*/
static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
const pj_uint8_t *pkt,
@@ -987,10 +1250,15 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
{
pj_stun_peer_addr_attr *peer_attr;
pj_stun_data_attr *data_attr;
- pjturn_allocation *alloc;
- pjturn_permission *perm;
+ pj_turn_allocation *alloc;
+ pj_turn_permission *perm;
+
+ PJ_UNUSED_ARG(pkt);
+ PJ_UNUSED_ARG(pkt_len);
+ PJ_UNUSED_ARG(src_addr);
+ PJ_UNUSED_ARG(src_addr_len);
- alloc = (pjturn_allocation*) pj_stun_session_get_user_data(sess);
+ alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess);
/* Only expect Send Indication */
if (msg->hdr.type != PJ_STUN_SEND_INDICATION) {
@@ -1024,7 +1292,7 @@ static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
return PJ_SUCCESS;
/* Relay the data to client */
- if (alloc->hkey.tp_type == PJTURN_TP_UDP) {
+ if (alloc->hkey.tp_type == PJ_TURN_TP_UDP) {
pj_ssize_t len = data_attr->length;
pj_sock_sendto(alloc->listener->sock, data_attr->data,
&len, 0, &peer_attr->sockaddr,