summaryrefslogtreecommitdiff
path: root/pjnath/src/pjnath/stun_sock.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjnath/src/pjnath/stun_sock.c')
-rw-r--r--pjnath/src/pjnath/stun_sock.c217
1 files changed, 160 insertions, 57 deletions
diff --git a/pjnath/src/pjnath/stun_sock.c b/pjnath/src/pjnath/stun_sock.c
index ff7dc16..cc7bd2c 100644
--- a/pjnath/src/pjnath/stun_sock.c
+++ b/pjnath/src/pjnath/stun_sock.c
@@ -1,4 +1,4 @@
-/* $Id: stun_sock.c 3999 2012-03-30 07:10:13Z bennylp $ */
+/* $Id: stun_sock.c 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -28,16 +28,24 @@
#include <pj/assert.h>
#include <pj/ip_helper.h>
#include <pj/log.h>
+#include <pj/os.h>
#include <pj/pool.h>
#include <pj/rand.h>
+#if 1
+# define TRACE_(x) PJ_LOG(5,x)
+#else
+# define TRACE_(x)
+#endif
+
+enum { MAX_BIND_RETRY = 100 };
struct pj_stun_sock
{
char *obj_name; /* Log identification */
pj_pool_t *pool; /* Pool */
void *user_data; /* Application user data */
-
+ pj_bool_t is_destroying; /* Destroy already called */
int af; /* Address family */
pj_stun_config stun_cfg; /* STUN config (ioqueue etc)*/
pj_stun_sock_cb cb; /* Application callbacks */
@@ -56,13 +64,16 @@ struct pj_stun_sock
pj_uint16_t tsx_id[6]; /* .. to match STUN msg */
pj_stun_session *stun_sess; /* STUN session */
-
+ pj_grp_lock_t *grp_lock; /* Session group lock */
};
/*
* Prototypes for static functions
*/
+/* Destructor for group lock */
+static void stun_sock_destructor(void *obj);
+
/* This callback is called by the STUN session to send packet */
static pj_status_t sess_on_send_msg(pj_stun_session *sess,
void *token,
@@ -162,7 +173,9 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
pj_pool_t *pool;
pj_stun_sock *stun_sock;
pj_stun_sock_cfg default_cfg;
+ pj_sockaddr bound_addr;
unsigned i;
+ pj_uint16_t max_bind_retry;
pj_status_t status;
PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL);
@@ -198,6 +211,20 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
if (stun_sock->ka_interval == 0)
stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC;
+ if (cfg && cfg->grp_lock) {
+ stun_sock->grp_lock = cfg->grp_lock;
+ } else {
+ status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
+ }
+
+ pj_grp_lock_add_ref(stun_sock->grp_lock);
+ pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock,
+ &stun_sock_destructor);
+
/* Create socket and bind socket */
status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &stun_sock->sock_fd);
if (status != PJ_SUCCESS)
@@ -211,17 +238,17 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
goto on_error;
/* Bind socket */
- if (pj_sockaddr_has_addr(&cfg->bound_addr)) {
- status = pj_sock_bind(stun_sock->sock_fd, &cfg->bound_addr,
- pj_sockaddr_get_len(&cfg->bound_addr));
- } else {
- pj_sockaddr bound_addr;
-
- pj_sockaddr_init(af, &bound_addr, NULL, 0);
- status = pj_sock_bind(stun_sock->sock_fd, &bound_addr,
- pj_sockaddr_get_len(&bound_addr));
+ max_bind_retry = MAX_BIND_RETRY;
+ if (cfg->port_range && cfg->port_range < max_bind_retry)
+ max_bind_retry = cfg->port_range;
+ pj_sockaddr_init(af, &bound_addr, NULL, 0);
+ if (cfg->bound_addr.addr.sa_family == pj_AF_INET() ||
+ cfg->bound_addr.addr.sa_family == pj_AF_INET6())
+ {
+ pj_sockaddr_cp(&bound_addr, &cfg->bound_addr);
}
-
+ status = pj_sock_bind_random(stun_sock->sock_fd, &bound_addr,
+ cfg->port_range, max_bind_retry);
if (status != PJ_SUCCESS)
goto on_error;
@@ -248,6 +275,7 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
pj_activesock_cb activesock_cb;
pj_activesock_cfg_default(&activesock_cfg);
+ activesock_cfg.grp_lock = stun_sock->grp_lock;
activesock_cfg.async_cnt = cfg->async_cnt;
activesock_cfg.concurrency = 0;
@@ -286,6 +314,7 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
status = pj_stun_session_create(&stun_sock->stun_cfg,
stun_sock->obj_name,
&sess_cb, PJ_FALSE,
+ stun_sock->grp_lock,
&stun_sock->stun_sess);
if (status != PJ_SUCCESS)
goto on_error;
@@ -328,6 +357,8 @@ PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock,
PJ_ASSERT_RETURN(stun_sock && domain && default_port, PJ_EINVAL);
+ pj_grp_lock_acquire(stun_sock->grp_lock);
+
/* Check whether the domain contains IP address */
stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)stun_sock->af;
status = pj_inet_pton(stun_sock->af, domain,
@@ -356,7 +387,6 @@ PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock,
&stun_sock->q);
/* Processing will resume when the DNS SRV callback is called */
- return status;
} else {
@@ -374,40 +404,29 @@ PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock,
pj_sockaddr_set_port(&stun_sock->srv_addr, (pj_uint16_t)default_port);
/* Start sending Binding request */
- return get_mapped_addr(stun_sock);
+ status = get_mapped_addr(stun_sock);
}
+
+ pj_grp_lock_release(stun_sock->grp_lock);
+ return status;
}
-/* Destroy */
-PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock)
+/* Destructor */
+static void stun_sock_destructor(void *obj)
{
+ pj_stun_sock *stun_sock = (pj_stun_sock*)obj;
+
if (stun_sock->q) {
pj_dns_srv_cancel_query(stun_sock->q, PJ_FALSE);
stun_sock->q = NULL;
}
- /* Destroy the active socket first just in case we'll get
- * stray callback.
- */
- if (stun_sock->active_sock != NULL) {
- pj_activesock_close(stun_sock->active_sock);
- stun_sock->active_sock = NULL;
- stun_sock->sock_fd = PJ_INVALID_SOCKET;
- } else if (stun_sock->sock_fd != PJ_INVALID_SOCKET) {
- pj_sock_close(stun_sock->sock_fd);
- stun_sock->sock_fd = PJ_INVALID_SOCKET;
- }
-
- if (stun_sock->ka_timer.id != 0) {
- pj_timer_heap_cancel(stun_sock->stun_cfg.timer_heap,
- &stun_sock->ka_timer);
- stun_sock->ka_timer.id = 0;
- }
-
+ /*
if (stun_sock->stun_sess) {
pj_stun_session_destroy(stun_sock->stun_sess);
stun_sock->stun_sess = NULL;
}
+ */
if (stun_sock->pool) {
pj_pool_t *pool = stun_sock->pool;
@@ -415,6 +434,40 @@ PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock)
pj_pool_release(pool);
}
+ TRACE_(("", "STUN sock %p destroyed", stun_sock));
+
+}
+
+/* Destroy */
+PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock)
+{
+ TRACE_((stun_sock->obj_name, "STUN sock %p request, ref_cnt=%d",
+ stun_sock, pj_grp_lock_get_ref(stun_sock->grp_lock)));
+
+ pj_grp_lock_acquire(stun_sock->grp_lock);
+ if (stun_sock->is_destroying) {
+ /* Destroy already called */
+ pj_grp_lock_release(stun_sock->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ stun_sock->is_destroying = PJ_TRUE;
+ pj_timer_heap_cancel_if_active(stun_sock->stun_cfg.timer_heap,
+ &stun_sock->ka_timer, 0);
+
+ if (stun_sock->active_sock != NULL) {
+ stun_sock->sock_fd = PJ_INVALID_SOCKET;
+ pj_activesock_close(stun_sock->active_sock);
+ } else if (stun_sock->sock_fd != PJ_INVALID_SOCKET) {
+ pj_sock_close(stun_sock->sock_fd);
+ stun_sock->sock_fd = PJ_INVALID_SOCKET;
+ }
+
+ if (stun_sock->stun_sess) {
+ pj_stun_session_destroy(stun_sock->stun_sess);
+ }
+ pj_grp_lock_dec_ref(stun_sock->grp_lock);
+ pj_grp_lock_release(stun_sock->grp_lock);
return PJ_SUCCESS;
}
@@ -458,12 +511,15 @@ static void dns_srv_resolver_cb(void *user_data,
{
pj_stun_sock *stun_sock = (pj_stun_sock*) user_data;
+ pj_grp_lock_acquire(stun_sock->grp_lock);
+
/* Clear query */
stun_sock->q = NULL;
/* Handle error */
if (status != PJ_SUCCESS) {
sess_fail(stun_sock, PJ_STUN_SOCK_DNS_OP, status);
+ pj_grp_lock_release(stun_sock->grp_lock);
return;
}
@@ -480,6 +536,8 @@ static void dns_srv_resolver_cb(void *user_data,
/* Start sending Binding request */
get_mapped_addr(stun_sock);
+
+ pj_grp_lock_release(stun_sock->grp_lock);
}
@@ -523,6 +581,8 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock,
PJ_ASSERT_RETURN(stun_sock && info, PJ_EINVAL);
+ pj_grp_lock_acquire(stun_sock->grp_lock);
+
/* Copy STUN server address and mapped address */
pj_memcpy(&info->srv_addr, &stun_sock->srv_addr,
sizeof(pj_sockaddr));
@@ -533,8 +593,10 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock,
addr_len = sizeof(info->bound_addr);
status = pj_sock_getsockname(stun_sock->sock_fd, &info->bound_addr,
&addr_len);
- if (status != PJ_SUCCESS)
+ if (status != PJ_SUCCESS) {
+ pj_grp_lock_release(stun_sock->grp_lock);
return status;
+ }
/* If socket is bound to a specific interface, then only put that
* interface in the alias list. Otherwise query all the interfaces
@@ -550,8 +612,10 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock,
/* Get the default address */
status = pj_gethostip(stun_sock->af, &def_addr);
- if (status != PJ_SUCCESS)
+ if (status != PJ_SUCCESS) {
+ pj_grp_lock_release(stun_sock->grp_lock);
return status;
+ }
pj_sockaddr_set_port(&def_addr, port);
@@ -559,8 +623,10 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock,
info->alias_cnt = PJ_ARRAY_SIZE(info->aliases);
status = pj_enum_ip_interface(stun_sock->af, &info->alias_cnt,
info->aliases);
- if (status != PJ_SUCCESS)
+ if (status != PJ_SUCCESS) {
+ pj_grp_lock_release(stun_sock->grp_lock);
return status;
+ }
/* Set the port number for each address.
*/
@@ -580,6 +646,7 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock,
}
}
+ pj_grp_lock_release(stun_sock->grp_lock);
return PJ_SUCCESS;
}
@@ -593,14 +660,29 @@ PJ_DEF(pj_status_t) pj_stun_sock_sendto( pj_stun_sock *stun_sock,
unsigned addr_len)
{
pj_ssize_t size;
+ pj_status_t status;
+
PJ_ASSERT_RETURN(stun_sock && pkt && dst_addr && addr_len, PJ_EINVAL);
+ pj_grp_lock_acquire(stun_sock->grp_lock);
+
+ if (!stun_sock->active_sock) {
+ /* We have been shutdown, but this callback may still get called
+ * by retransmit timer.
+ */
+ pj_grp_lock_release(stun_sock->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
if (send_key==NULL)
send_key = &stun_sock->send_key;
size = pkt_len;
- return pj_activesock_sendto(stun_sock->active_sock, send_key,
- pkt, &size, flag, dst_addr, addr_len);
+ status = pj_activesock_sendto(stun_sock->active_sock, send_key,
+ pkt, &size, flag, dst_addr, addr_len);
+
+ pj_grp_lock_release(stun_sock->grp_lock);
+ return status;
}
/* This callback is called by the STUN session to send packet */
@@ -615,12 +697,18 @@ static pj_status_t sess_on_send_msg(pj_stun_session *sess,
pj_ssize_t size;
stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess);
+ if (!stun_sock || !stun_sock->active_sock) {
+ /* We have been shutdown, but this callback may still get called
+ * by retransmit timer.
+ */
+ return PJ_EINVALIDOP;
+ }
pj_assert(token==INTERNAL_MSG_TOKEN);
PJ_UNUSED_ARG(token);
size = pkt_size;
- return pj_activesock_sendto(stun_sock->active_sock,
+ return pj_activesock_sendto(stun_sock->active_sock,
&stun_sock->int_send_key,
pkt, &size, 0, dst_addr, addr_len);
}
@@ -643,6 +731,8 @@ static void sess_on_request_complete(pj_stun_session *sess,
pj_bool_t resched = PJ_TRUE;
stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess);
+ if (!stun_sock)
+ return;
PJ_UNUSED_ARG(tdata);
PJ_UNUSED_ARG(token);
@@ -712,25 +802,20 @@ on_return:
/* Schedule keep-alive timer */
static void start_ka_timer(pj_stun_sock *stun_sock)
{
- if (stun_sock->ka_timer.id != 0) {
- pj_timer_heap_cancel(stun_sock->stun_cfg.timer_heap,
- &stun_sock->ka_timer);
- stun_sock->ka_timer.id = 0;
- }
+ pj_timer_heap_cancel_if_active(stun_sock->stun_cfg.timer_heap,
+ &stun_sock->ka_timer, 0);
pj_assert(stun_sock->ka_interval != 0);
- if (stun_sock->ka_interval > 0) {
+ if (stun_sock->ka_interval > 0 && !stun_sock->is_destroying) {
pj_time_val delay;
delay.sec = stun_sock->ka_interval;
delay.msec = 0;
- if (pj_timer_heap_schedule(stun_sock->stun_cfg.timer_heap,
- &stun_sock->ka_timer,
- &delay) == PJ_SUCCESS)
- {
- stun_sock->ka_timer.id = PJ_TRUE;
- }
+ pj_timer_heap_schedule_w_grp_lock(stun_sock->stun_cfg.timer_heap,
+ &stun_sock->ka_timer,
+ &delay, PJ_TRUE,
+ stun_sock->grp_lock);
}
}
@@ -742,14 +827,18 @@ static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
stun_sock = (pj_stun_sock *) te->user_data;
PJ_UNUSED_ARG(th);
+ pj_grp_lock_acquire(stun_sock->grp_lock);
/* Time to send STUN Binding request */
- if (get_mapped_addr(stun_sock) != PJ_SUCCESS)
+ if (get_mapped_addr(stun_sock) != PJ_SUCCESS) {
+ pj_grp_lock_release(stun_sock->grp_lock);
return;
+ }
/* Next keep-alive timer will be scheduled once the request
* is complete.
*/
+ pj_grp_lock_release(stun_sock->grp_lock);
}
/* Callback from active socket when incoming packet is received */
@@ -765,6 +854,8 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
pj_uint16_t type;
stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock);
+ if (!stun_sock)
+ return PJ_FALSE;
/* Log socket error */
if (status != PJ_SUCCESS) {
@@ -772,6 +863,8 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
return PJ_TRUE;
}
+ pj_grp_lock_acquire(stun_sock->grp_lock);
+
/* Check that this is STUN message */
status = pj_stun_msg_check((const pj_uint8_t*)data, size,
PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET);
@@ -807,7 +900,10 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, data, size,
PJ_STUN_IS_DATAGRAM, NULL, NULL,
src_addr, addr_len);
- return status!=PJNATH_ESTUNDESTROYED ? PJ_TRUE : PJ_FALSE;
+
+ status = pj_grp_lock_release(stun_sock->grp_lock);
+
+ return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE;
process_app_data:
if (stun_sock->cb.on_rx_data) {
@@ -815,10 +911,12 @@ process_app_data:
ret = (*stun_sock->cb.on_rx_data)(stun_sock, data, size,
src_addr, addr_len);
- return ret;
+ status = pj_grp_lock_release(stun_sock->grp_lock);
+ return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE;
}
- return PJ_TRUE;
+ status = pj_grp_lock_release(stun_sock->grp_lock);
+ return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE;
}
/* Callback from active socket about send status */
@@ -829,6 +927,8 @@ static pj_bool_t on_data_sent(pj_activesock_t *asock,
pj_stun_sock *stun_sock;
stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock);
+ if (!stun_sock)
+ return PJ_FALSE;
/* Don't report to callback if this is internal message */
if (send_key == &stun_sock->int_send_key) {
@@ -839,6 +939,8 @@ static pj_bool_t on_data_sent(pj_activesock_t *asock,
if (stun_sock->cb.on_data_sent) {
pj_bool_t ret;
+ pj_grp_lock_acquire(stun_sock->grp_lock);
+
/* If app gives NULL send_key in sendto() function, then give
* NULL in the callback too
*/
@@ -848,6 +950,7 @@ static pj_bool_t on_data_sent(pj_activesock_t *asock,
/* Call callback */
ret = (*stun_sock->cb.on_data_sent)(stun_sock, send_key, sent);
+ pj_grp_lock_release(stun_sock->grp_lock);
return ret;
}