diff options
Diffstat (limited to 'pjnath/src/pjnath/stun_sock.c')
-rw-r--r-- | pjnath/src/pjnath/stun_sock.c | 217 |
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; } |