2017-04-19 07:56:45 +00:00
/*
Copyright ( c ) 2007 , Adobe Systems , Incorporated
All rights reserved .
Redistribution and use in source and binary forms , with or without
modification , are permitted provided that the following conditions are
met :
* Redistributions of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
* Redistributions in binary form must reproduce the above copyright
notice , this list of conditions and the following disclaimer in the
documentation and / or other materials provided with the distribution .
* Neither the name of Adobe Systems , Network Resonance nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
" AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
static char * RCSSTRING __UNUSED__ = " $Id: ice_component.c,v 1.2 2008/04/28 17:59:01 ekr Exp $ " ;
# include <string.h>
# include <assert.h>
# include <nr_api.h>
# include <registry.h>
# include <async_timer.h>
# include "ice_ctx.h"
# include "ice_codeword.h"
# include "stun.h"
# include "nr_socket_local.h"
# include "nr_socket_turn.h"
# include "nr_socket_wrapper.h"
# include "nr_socket_buffered_stun.h"
# include "nr_socket_multi_tcp.h"
# include "ice_reg.h"
# include "nr_crypto.h"
static int nr_ice_component_stun_server_default_cb ( void * cb_arg , nr_stun_server_ctx * stun_ctx , nr_socket * sock , nr_stun_server_request * req , int * dont_free , int * error ) ;
static int nr_ice_pre_answer_request_destroy ( nr_ice_pre_answer_request * * parp ) ;
/* This function takes ownership of the contents of req (but not req itself) */
static int nr_ice_pre_answer_request_create ( nr_socket * sock , nr_stun_server_request * req , nr_ice_pre_answer_request * * parp )
{
int r , _status ;
nr_ice_pre_answer_request * par = 0 ;
nr_stun_message_attribute * attr ;
if ( ! ( par = RCALLOC ( sizeof ( nr_ice_pre_answer_request ) ) ) )
ABORT ( R_NO_MEMORY ) ;
par - > req = * req ; /* Struct assignment */
memset ( req , 0 , sizeof ( * req ) ) ; /* Zero contents to avoid confusion */
if ( r = nr_socket_getaddr ( sock , & par - > local_addr ) )
ABORT ( r ) ;
if ( ! nr_stun_message_has_attribute ( par - > req . request , NR_STUN_ATTR_USERNAME , & attr ) )
ABORT ( R_INTERNAL ) ;
if ( ! ( par - > username = r_strdup ( attr - > u . username ) ) )
ABORT ( R_NO_MEMORY ) ;
* parp = par ;
_status = 0 ;
abort :
if ( _status ) {
/* Erase the request so we don't free it */
memset ( & par - > req , 0 , sizeof ( nr_stun_server_request ) ) ;
nr_ice_pre_answer_request_destroy ( & par ) ;
}
return ( _status ) ;
}
static int nr_ice_pre_answer_request_destroy ( nr_ice_pre_answer_request * * parp )
{
nr_ice_pre_answer_request * par ;
if ( ! parp | | ! * parp )
return ( 0 ) ;
par = * parp ;
* parp = 0 ;
nr_stun_message_destroy ( & par - > req . request ) ;
nr_stun_message_destroy ( & par - > req . response ) ;
RFREE ( par - > username ) ;
RFREE ( par ) ;
return ( 0 ) ;
}
int nr_ice_component_create ( nr_ice_media_stream * stream , int component_id , nr_ice_component * * componentp )
{
int _status ;
nr_ice_component * comp = 0 ;
if ( ! ( comp = RCALLOC ( sizeof ( nr_ice_component ) ) ) )
ABORT ( R_NO_MEMORY ) ;
comp - > state = NR_ICE_COMPONENT_UNPAIRED ;
comp - > component_id = component_id ;
comp - > stream = stream ;
comp - > ctx = stream - > ctx ;
STAILQ_INIT ( & comp - > sockets ) ;
TAILQ_INIT ( & comp - > candidates ) ;
STAILQ_INIT ( & comp - > pre_answer_reqs ) ;
STAILQ_INSERT_TAIL ( & stream - > components , comp , entry ) ;
_status = 0 ;
abort :
return ( _status ) ;
}
int nr_ice_component_destroy ( nr_ice_component * * componentp )
{
nr_ice_component * component ;
nr_ice_socket * s1 , * s2 ;
nr_ice_candidate * c1 , * c2 ;
nr_ice_pre_answer_request * r1 , * r2 ;
if ( ! componentp | | ! * componentp )
return ( 0 ) ;
component = * componentp ;
* componentp = 0 ;
/* Detach ourselves from the sockets */
if ( component - > local_component ) {
nr_ice_socket * isock = STAILQ_FIRST ( & component - > local_component - > sockets ) ;
while ( isock ) {
nr_stun_server_remove_client ( isock - > stun_server , component ) ;
isock = STAILQ_NEXT ( isock , entry ) ;
}
}
/* candidates MUST be destroyed before the sockets so that
they can deregister */
TAILQ_FOREACH_SAFE ( c1 , & component - > candidates , entry_comp , c2 ) {
TAILQ_REMOVE ( & component - > candidates , c1 , entry_comp ) ;
nr_ice_candidate_destroy ( & c1 ) ;
}
STAILQ_FOREACH_SAFE ( s1 , & component - > sockets , entry , s2 ) {
STAILQ_REMOVE ( & component - > sockets , s1 , nr_ice_socket_ , entry ) ;
nr_ice_socket_destroy ( & s1 ) ;
}
STAILQ_FOREACH_SAFE ( r1 , & component - > pre_answer_reqs , entry , r2 ) {
STAILQ_REMOVE ( & component - > pre_answer_reqs , r1 , nr_ice_pre_answer_request_ , entry ) ;
nr_ice_pre_answer_request_destroy ( & r1 ) ;
}
if ( component - > keepalive_timer )
NR_async_timer_cancel ( component - > keepalive_timer ) ;
nr_stun_client_ctx_destroy ( & component - > keepalive_ctx ) ;
RFREE ( component ) ;
return ( 0 ) ;
}
static int nr_ice_component_create_stun_server_ctx ( nr_ice_component * component , nr_ice_socket * isock , nr_socket * sock , nr_transport_addr * addr , char * lufrag , Data * pwd )
{
char label [ 256 ] ;
int r , _status ;
/* Create a STUN server context for this socket */
snprintf ( label , sizeof ( label ) , " server(%s) " , addr - > as_string ) ;
if ( r = nr_stun_server_ctx_create ( label , sock , & isock - > stun_server ) )
ABORT ( r ) ;
if ( r = nr_ice_socket_register_stun_server ( isock , isock - > stun_server , & isock - > stun_server_handle ) )
ABORT ( r ) ;
/* Add the default STUN credentials so that we can respond before
we hear about the peer . */
if ( r = nr_stun_server_add_default_client ( isock - > stun_server , lufrag , pwd , nr_ice_component_stun_server_default_cb , component ) )
ABORT ( r ) ;
_status = 0 ;
abort :
return ( _status ) ;
}
static int nr_ice_component_initialize_udp ( struct nr_ice_ctx_ * ctx , nr_ice_component * component , nr_local_addr * addrs , int addr_ct , char * lufrag , Data * pwd )
{
nr_socket * sock ;
nr_ice_socket * isock = 0 ;
nr_ice_candidate * cand = 0 ;
int i ;
int j ;
int r , _status ;
/* Now one ice_socket for each address */
for ( i = 0 ; i < addr_ct ; i + + ) {
char suppress ;
if ( r = NR_reg_get2_char ( NR_ICE_REG_SUPPRESS_INTERFACE_PRFX , addrs [ i ] . addr . ifname , & suppress ) ) {
if ( r ! = R_NOT_FOUND )
ABORT ( r ) ;
}
else {
if ( suppress )
continue ;
}
r_log ( LOG_ICE , LOG_DEBUG , " ICE(%s): host address %s " , ctx - > label , addrs [ i ] . addr . as_string ) ;
if ( ( r = nr_socket_factory_create_socket ( ctx - > socket_factory , & addrs [ i ] . addr , & sock ) ) ) {
r_log ( LOG_ICE , LOG_WARNING , " ICE(%s): couldn't create socket for address %s " , ctx - > label , addrs [ i ] . addr . as_string ) ;
continue ;
}
if ( r = nr_ice_socket_create ( ctx , component , sock , NR_ICE_SOCKET_TYPE_DGRAM , & isock ) )
ABORT ( r ) ;
if ( ! ( ctx - > flags & NR_ICE_CTX_FLAGS_RELAY_ONLY ) ) {
/* Create one host candidate */
if ( r = nr_ice_candidate_create ( ctx , component , isock , sock , HOST , 0 , 0 ,
component - > component_id , & cand ) )
ABORT ( r ) ;
TAILQ_INSERT_TAIL ( & component - > candidates , cand , entry_comp ) ;
component - > candidate_ct + + ;
cand = 0 ;
/* And a srvrflx candidate for each STUN server */
for ( j = 0 ; j < ctx - > stun_server_ct ; j + + ) {
/* Skip non-UDP */
if ( ctx - > stun_servers [ j ] . transport ! = IPPROTO_UDP )
continue ;
if ( r = nr_ice_candidate_create ( ctx , component ,
isock , sock , SERVER_REFLEXIVE , 0 ,
& ctx - > stun_servers [ j ] , component - > component_id , & cand ) )
ABORT ( r ) ;
TAILQ_INSERT_TAIL ( & component - > candidates , cand , entry_comp ) ;
component - > candidate_ct + + ;
cand = 0 ;
}
}
# ifdef USE_TURN
/* And both a srvrflx and relayed candidate for each TURN server (unless
we ' re in relay - only mode , in which case just the relayed one ) */
for ( j = 0 ; j < ctx - > turn_server_ct ; j + + ) {
nr_socket * turn_sock ;
nr_ice_candidate * srvflx_cand = 0 ;
/* Skip non-UDP */
if ( ctx - > turn_servers [ j ] . turn_server . transport ! = IPPROTO_UDP )
continue ;
if ( ! ( ctx - > flags & NR_ICE_CTX_FLAGS_RELAY_ONLY ) ) {
/* srvrflx */
if ( r = nr_ice_candidate_create ( ctx , component ,
isock , sock , SERVER_REFLEXIVE , 0 ,
& ctx - > turn_servers [ j ] . turn_server , component - > component_id , & cand ) )
ABORT ( r ) ;
cand - > state = NR_ICE_CAND_STATE_INITIALIZING ; /* Don't start */
cand - > done_cb = nr_ice_gather_finished_cb ;
cand - > cb_arg = cand ;
TAILQ_INSERT_TAIL ( & component - > candidates , cand , entry_comp ) ;
component - > candidate_ct + + ;
srvflx_cand = cand ;
cand = 0 ;
}
/* relayed*/
if ( r = nr_socket_turn_create ( sock , & turn_sock ) )
ABORT ( r ) ;
if ( r = nr_ice_candidate_create ( ctx , component ,
isock , turn_sock , RELAYED , 0 ,
& ctx - > turn_servers [ j ] . turn_server , component - > component_id , & cand ) )
ABORT ( r ) ;
if ( srvflx_cand ) {
cand - > u . relayed . srvflx_candidate = srvflx_cand ;
srvflx_cand - > u . srvrflx . relay_candidate = cand ;
}
cand - > u . relayed . server = & ctx - > turn_servers [ j ] ;
TAILQ_INSERT_TAIL ( & component - > candidates , cand , entry_comp ) ;
component - > candidate_ct + + ;
cand = 0 ;
}
# endif /* USE_TURN */
/* Create a STUN server context for this socket */
if ( ( r = nr_ice_component_create_stun_server_ctx ( component , isock , sock , & addrs [ i ] . addr , lufrag , pwd ) ) )
ABORT ( r ) ;
STAILQ_INSERT_TAIL ( & component - > sockets , isock , entry ) ;
}
_status = 0 ;
abort :
return ( _status ) ;
}
static int nr_ice_component_get_port_from_ephemeral_range ( uint16_t * port )
{
int _status , r ;
void * buf = port ;
if ( r = nr_crypto_random_bytes ( buf , 2 ) )
ABORT ( r ) ;
* port | = 49152 ; /* make it fit into IANA ephemeral port range >= 49152 */
_status = 0 ;
abort :
return ( _status ) ;
}
static int nr_ice_component_create_tcp_host_candidate ( struct nr_ice_ctx_ * ctx ,
nr_ice_component * component , nr_transport_addr * interface_addr , nr_socket_tcp_type tcp_type ,
int backlog , int so_sock_ct , char * lufrag , Data * pwd , nr_ice_socket * * isock )
{
int r , _status ;
nr_ice_candidate * cand = 0 ;
int tries = 3 ;
nr_ice_socket * isock_tmp = 0 ;
nr_socket * nrsock = 0 ;
nr_transport_addr addr ;
uint16_t local_port ;
if ( ( r = nr_transport_addr_copy ( & addr , interface_addr ) ) )
ABORT ( r ) ;
addr . protocol = IPPROTO_TCP ;
do {
if ( ! tries - - )
ABORT ( r ) ;
if ( ( r = nr_ice_component_get_port_from_ephemeral_range ( & local_port ) ) )
ABORT ( r ) ;
if ( ( r = nr_transport_addr_set_port ( & addr , local_port ) ) )
ABORT ( r ) ;
if ( ( r = nr_transport_addr_fmt_addr_string ( & addr ) ) )
ABORT ( r ) ;
/* It would be better to stop trying if there is error other than
port already used , but it ' d require significant work to support this . */
r = nr_socket_multi_tcp_create ( ctx , & addr , tcp_type , so_sock_ct , NR_STUN_MAX_MESSAGE_SIZE , & nrsock ) ;
} while ( r ) ;
if ( ( tcp_type = = TCP_TYPE_PASSIVE ) & & ( r = nr_socket_listen ( nrsock , backlog ) ) )
ABORT ( r ) ;
if ( ( r = nr_ice_socket_create ( ctx , component , nrsock , NR_ICE_SOCKET_TYPE_STREAM_TCP , & isock_tmp ) ) )
ABORT ( r ) ;
/* nr_ice_socket took ownership of nrsock */
nrsock = NULL ;
/* Create a STUN server context for this socket */
if ( ( r = nr_ice_component_create_stun_server_ctx ( component , isock_tmp , isock_tmp - > sock , & addr , lufrag , pwd ) ) )
ABORT ( r ) ;
if ( ( r = nr_ice_candidate_create ( ctx , component , isock_tmp , isock_tmp - > sock , HOST , tcp_type , 0 ,
component - > component_id , & cand ) ) )
ABORT ( r ) ;
if ( isock )
* isock = isock_tmp ;
TAILQ_INSERT_TAIL ( & component - > candidates , cand , entry_comp ) ;
component - > candidate_ct + + ;
STAILQ_INSERT_TAIL ( & component - > sockets , isock_tmp , entry ) ;
_status = 0 ;
abort :
if ( _status ) {
nr_ice_socket_destroy ( & isock_tmp ) ;
nr_socket_destroy ( & nrsock ) ;
}
return ( _status ) ;
}
static int nr_ice_component_initialize_tcp ( struct nr_ice_ctx_ * ctx , nr_ice_component * component , nr_local_addr * addrs , int addr_ct , char * lufrag , Data * pwd )
{
nr_ice_candidate * cand = 0 ;
int i ;
int j ;
int r , _status ;
int so_sock_ct = 0 ;
int backlog = 10 ;
char ice_tcp_disabled = 1 ;
r_log ( LOG_ICE , LOG_DEBUG , " nr_ice_component_initialize_tcp " ) ;
if ( r = NR_reg_get_int4 ( NR_ICE_REG_ICE_TCP_SO_SOCK_COUNT , & so_sock_ct ) ) {
if ( r ! = R_NOT_FOUND )
ABORT ( r ) ;
}
if ( r = NR_reg_get_int4 ( NR_ICE_REG_ICE_TCP_LISTEN_BACKLOG , & backlog ) ) {
if ( r ! = R_NOT_FOUND )
ABORT ( r ) ;
}
if ( ( r = NR_reg_get_char ( NR_ICE_REG_ICE_TCP_DISABLE , & ice_tcp_disabled ) ) ) {
if ( r ! = R_NOT_FOUND )
ABORT ( r ) ;
}
if ( ctx - > flags & NR_ICE_CTX_FLAGS_RELAY_ONLY ) {
ice_tcp_disabled = 1 ;
}
for ( i = 0 ; i < addr_ct ; i + + ) {
char suppress ;
nr_ice_socket * isock_psv = 0 ;
nr_ice_socket * isock_so = 0 ;
if ( r = NR_reg_get2_char ( NR_ICE_REG_SUPPRESS_INTERFACE_PRFX , addrs [ i ] . addr . ifname , & suppress ) ) {
if ( r ! = R_NOT_FOUND )
ABORT ( r ) ;
}
else if ( suppress ) {
continue ;
}
if ( ! ice_tcp_disabled ) {
/* passive host candidate */
if ( ( r = nr_ice_component_create_tcp_host_candidate ( ctx , component , & addrs [ i ] . addr ,
TCP_TYPE_PASSIVE , backlog , 0 , lufrag , pwd , & isock_psv ) ) )
ABORT ( r ) ;
/* active host candidate */
if ( ( r = nr_ice_component_create_tcp_host_candidate ( ctx , component , & addrs [ i ] . addr ,
TCP_TYPE_ACTIVE , 0 , 0 , lufrag , pwd , NULL ) ) )
ABORT ( r ) ;
/* simultaneous-open host candidate */
if ( so_sock_ct ) {
if ( ( r = nr_ice_component_create_tcp_host_candidate ( ctx , component , & addrs [ i ] . addr ,
TCP_TYPE_SO , 0 , so_sock_ct , lufrag , pwd , & isock_so ) ) )
ABORT ( r ) ;
}
/* And srvrflx candidates for each STUN server */
for ( j = 0 ; j < ctx - > stun_server_ct ; j + + ) {
if ( ctx - > stun_servers [ j ] . transport ! = IPPROTO_TCP )
continue ;
if ( r = nr_ice_candidate_create ( ctx , component ,
isock_psv , isock_psv - > sock , SERVER_REFLEXIVE , TCP_TYPE_PASSIVE ,
& ctx - > stun_servers [ j ] , component - > component_id , & cand ) )
ABORT ( r ) ;
TAILQ_INSERT_TAIL ( & component - > candidates , cand , entry_comp ) ;
component - > candidate_ct + + ;
cand = 0 ;
if ( so_sock_ct ) {
if ( r = nr_ice_candidate_create ( ctx , component ,
isock_so , isock_so - > sock , SERVER_REFLEXIVE , TCP_TYPE_SO ,
& ctx - > stun_servers [ j ] , component - > component_id , & cand ) )
ABORT ( r ) ;
TAILQ_INSERT_TAIL ( & component - > candidates , cand , entry_comp ) ;
component - > candidate_ct + + ;
cand = 0 ;
}
}
}
# ifdef USE_TURN
/* Create a new relayed candidate for each addr/TURN server pair */
for ( j = 0 ; j < ctx - > turn_server_ct ; j + + ) {
nr_transport_addr addr ;
nr_socket * local_sock ;
nr_socket * buffered_sock ;
nr_socket * turn_sock ;
nr_ice_socket * turn_isock ;
/* Skip non-TCP */
if ( ctx - > turn_servers [ j ] . turn_server . transport ! = IPPROTO_TCP )
continue ;
if ( ! ice_tcp_disabled ) {
/* Use TURN server to get srflx candidates */
if ( r = nr_ice_candidate_create ( ctx , component ,
isock_psv , isock_psv - > sock , SERVER_REFLEXIVE , TCP_TYPE_PASSIVE ,
& ctx - > turn_servers [ j ] . turn_server , component - > component_id , & cand ) )
ABORT ( r ) ;
TAILQ_INSERT_TAIL ( & component - > candidates , cand , entry_comp ) ;
component - > candidate_ct + + ;
cand = 0 ;
if ( so_sock_ct ) {
if ( r = nr_ice_candidate_create ( ctx , component ,
isock_so , isock_so - > sock , SERVER_REFLEXIVE , TCP_TYPE_SO ,
& ctx - > turn_servers [ j ] . turn_server , component - > component_id , & cand ) )
ABORT ( r ) ;
TAILQ_INSERT_TAIL ( & component - > candidates , cand , entry_comp ) ;
component - > candidate_ct + + ;
cand = 0 ;
}
}
/* Create relay candidate */
if ( ( r = nr_transport_addr_copy ( & addr , & addrs [ i ] . addr ) ) )
ABORT ( r ) ;
addr . protocol = IPPROTO_TCP ;
if ( ( r = nr_transport_addr_fmt_addr_string ( & addr ) ) )
ABORT ( r ) ;
/* Create a local socket */
if ( ( r = nr_socket_factory_create_socket ( ctx - > socket_factory , & addr , & local_sock ) ) ) {
r_log ( LOG_ICE , LOG_DEBUG , " ICE(%s): couldn't create socket for address %s " , ctx - > label , addr . as_string ) ;
continue ;
}
r_log ( LOG_ICE , LOG_DEBUG , " nr_ice_component_initialize_tcp create " ) ;
if ( ctx - > turn_tcp_socket_wrapper ) {
/* Wrap it */
if ( ( r = nr_socket_wrapper_factory_wrap ( ctx - > turn_tcp_socket_wrapper , local_sock , & local_sock ) ) )
ABORT ( r ) ;
}
/* Wrap it */
if ( ( r = nr_socket_buffered_stun_create ( local_sock , NR_STUN_MAX_MESSAGE_SIZE , TURN_TCP_FRAMING , & buffered_sock ) ) )
ABORT ( r ) ;
/* The TURN socket */
if ( r = nr_socket_turn_create ( buffered_sock , & turn_sock ) )
ABORT ( r ) ;
/* Create an ICE socket */
if ( ( r = nr_ice_socket_create ( ctx , component , buffered_sock , NR_ICE_SOCKET_TYPE_STREAM_TURN , & turn_isock ) ) )
ABORT ( r ) ;
/* Attach ourselves to it */
if ( r = nr_ice_candidate_create ( ctx , component ,
turn_isock , turn_sock , RELAYED , TCP_TYPE_NONE ,
& ctx - > turn_servers [ j ] . turn_server , component - > component_id , & cand ) )
ABORT ( r ) ;
cand - > u . relayed . srvflx_candidate = NULL ;
cand - > u . relayed . server = & ctx - > turn_servers [ j ] ;
TAILQ_INSERT_TAIL ( & component - > candidates , cand , entry_comp ) ;
component - > candidate_ct + + ;
cand = 0 ;
/* Create a STUN server context for this socket */
if ( ( r = nr_ice_component_create_stun_server_ctx ( component , turn_isock , local_sock , & addr , lufrag , pwd ) ) )
ABORT ( r ) ;
STAILQ_INSERT_TAIL ( & component - > sockets , turn_isock , entry ) ;
}
# endif /* USE_TURN */
}
_status = 0 ;
abort :
return ( _status ) ;
}
/* Make all the candidates we can make at the beginning */
int nr_ice_component_initialize ( struct nr_ice_ctx_ * ctx , nr_ice_component * component )
{
int r , _status ;
nr_local_addr * addrs = ctx - > local_addrs ;
int addr_ct = ctx - > local_addr_ct ;
char * lufrag ;
char * lpwd ;
Data pwd ;
nr_ice_candidate * cand ;
if ( component - > candidate_ct ) {
r_log ( LOG_ICE , LOG_DEBUG , " ICE(%s): component with id %d already has candidates, probably restarting gathering because of a new stream " , ctx - > label , component - > component_id ) ;
return ( 0 ) ;
}
r_log ( LOG_ICE , LOG_DEBUG , " ICE(%s): initializing component with id %d " , ctx - > label , component - > component_id ) ;
if ( addr_ct = = 0 ) {
r_log ( LOG_ICE , LOG_ERR , " ICE(%s): no local addresses available " , ctx - > label ) ;
ABORT ( R_NOT_FOUND ) ;
}
/* Note: we need to recompute these because
we have not yet computed the values in the peer media stream . */
lufrag = component - > stream - > ufrag ? component - > stream - > ufrag : ctx - > ufrag ;
assert ( lufrag ) ;
if ( ! lufrag )
ABORT ( R_INTERNAL ) ;
lpwd = component - > stream - > pwd ? component - > stream - > pwd : ctx - > pwd ;
assert ( lpwd ) ;
if ( ! lpwd )
ABORT ( R_INTERNAL ) ;
INIT_DATA ( pwd , ( UCHAR * ) lpwd , strlen ( lpwd ) ) ;
/* Initialize the UDP candidates */
if ( r = nr_ice_component_initialize_udp ( ctx , component , addrs , addr_ct , lufrag , & pwd ) )
r_log ( LOG_ICE , LOG_INFO , " ICE(%s): failed to create UDP candidates with error %d " , ctx - > label , r ) ;
/* And the TCP candidates */
if ( r = nr_ice_component_initialize_tcp ( ctx , component , addrs , addr_ct , lufrag , & pwd ) )
r_log ( LOG_ICE , LOG_INFO , " ICE(%s): failed to create TCP candidates with error %d " , ctx - > label , r ) ;
/* count the candidates that will be initialized */
cand = TAILQ_FIRST ( & component - > candidates ) ;
if ( ! cand ) {
r_log ( LOG_ICE , LOG_ERR , " ICE(%s): couldn't create any valid candidates " , ctx - > label ) ;
ABORT ( R_NOT_FOUND ) ;
}
while ( cand ) {
ctx - > uninitialized_candidates + + ;
cand = TAILQ_NEXT ( cand , entry_comp ) ;
}
/* Now initialize all the candidates */
cand = TAILQ_FIRST ( & component - > candidates ) ;
while ( cand ) {
if ( cand - > state ! = NR_ICE_CAND_STATE_INITIALIZING ) {
nr_ice_candidate_initialize ( cand , nr_ice_gather_finished_cb , cand ) ;
}
cand = TAILQ_NEXT ( cand , entry_comp ) ;
}
_status = 0 ;
abort :
return ( _status ) ;
}
static int nr_ice_any_peer_paired ( nr_ice_candidate * cand ) {
nr_ice_peer_ctx * pctx = STAILQ_FIRST ( & cand - > ctx - > peers ) ;
while ( pctx & & pctx - > state = = NR_ICE_PEER_STATE_UNPAIRED ) {
/* Is it worth actually looking through the check lists? Probably not. */
pctx = STAILQ_NEXT ( pctx , entry ) ;
}
return pctx ! = NULL ;
}
/*
Compare this newly initialized candidate against the other initialized
candidates and discard the lower - priority one if they are redundant .
This algorithm combined with the other algorithms , favors
host > srflx > relay
*/
int nr_ice_component_maybe_prune_candidate ( nr_ice_ctx * ctx , nr_ice_component * comp , nr_ice_candidate * c1 , int * was_pruned )
{
nr_ice_candidate * c2 , * tmp = NULL ;
* was_pruned = 0 ;
c2 = TAILQ_FIRST ( & comp - > candidates ) ;
while ( c2 ) {
if ( ( c1 ! = c2 ) & &
( c2 - > state = = NR_ICE_CAND_STATE_INITIALIZED ) & &
! nr_transport_addr_cmp ( & c1 - > base , & c2 - > base , NR_TRANSPORT_ADDR_CMP_MODE_ALL ) & &
! nr_transport_addr_cmp ( & c1 - > addr , & c2 - > addr , NR_TRANSPORT_ADDR_CMP_MODE_ALL ) ) {
if ( ( c1 - > type = = c2 - > type ) | |
( ! ( ctx - > flags & NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS ) & &
( ( c1 - > type = = HOST & & c2 - > type = = SERVER_REFLEXIVE ) | |
( c2 - > type = = HOST & & c1 - > type = = SERVER_REFLEXIVE ) ) ) ) {
/*
These are redundant . Remove the lower pri one , or if pairing has
already occurred , remove the newest one .
Since this algorithmis run whenever a new candidate
is initialized , there should at most one duplicate .
*/
if ( ( c1 - > priority < = c2 - > priority ) | | nr_ice_any_peer_paired ( c2 ) ) {
tmp = c1 ;
* was_pruned = 1 ;
}
else {
tmp = c2 ;
}
break ;
}
}
c2 = TAILQ_NEXT ( c2 , entry_comp ) ;
}
if ( tmp ) {
r_log ( LOG_ICE , LOG_DEBUG , " ICE(%s)/CAND(%s): Removing redundant candidate " ,
ctx - > label , tmp - > label ) ;
TAILQ_REMOVE ( & comp - > candidates , tmp , entry_comp ) ;
comp - > candidate_ct - - ;
TAILQ_REMOVE ( & tmp - > isock - > candidates , tmp , entry_sock ) ;
nr_ice_candidate_destroy ( & tmp ) ;
}
return 0 ;
}
static int nr_ice_component_pair_matches_check ( nr_ice_component * comp , nr_ice_cand_pair * pair , nr_transport_addr * local_addr , nr_stun_server_request * req )
{
if ( pair - > remote - > component - > component_id ! = comp - > component_id )
return ( 0 ) ;
if ( nr_transport_addr_cmp ( & pair - > local - > base , local_addr , NR_TRANSPORT_ADDR_CMP_MODE_ALL ) )
return ( 0 ) ;
if ( nr_transport_addr_cmp ( & pair - > remote - > addr , & req - > src_addr , NR_TRANSPORT_ADDR_CMP_MODE_ALL ) )
return ( 0 ) ;
return ( 1 ) ;
}
static int nr_ice_component_handle_triggered_check ( nr_ice_component * comp , nr_ice_cand_pair * pair , nr_stun_server_request * req , int * error )
{
nr_stun_message * sreq = req - > request ;
int r = 0 , _status ;
if ( nr_stun_message_has_attribute ( sreq , NR_STUN_ATTR_USE_CANDIDATE , 0 ) ) {
if ( comp - > stream - > pctx - > controlling ) {
r_log ( LOG_ICE , LOG_WARNING , " ICE-PEER(%s)/CAND_PAIR(%s): Peer sent USE-CANDIDATE but is controlled " , comp - > stream - > pctx - > label , pair - > codeword ) ;
}
else {
/* If this is the first time we've noticed this is nominated...*/
pair - > peer_nominated = 1 ;
if ( pair - > state = = NR_ICE_PAIR_STATE_SUCCEEDED & & ! pair - > nominated ) {
pair - > nominated = 1 ;
if ( r = nr_ice_component_nominated_pair ( pair - > remote - > component , pair ) ) {
* error = ( r = = R_NO_MEMORY ) ? 500 : 400 ;
ABORT ( r ) ;
}
}
}
}
/* Note: the RFC says to trigger first and then nominate. But in that case
* the canceled trigger pair would get nominated and the cloned trigger pair
* would not get the nomination status cloned with it . */
if ( r = nr_ice_candidate_pair_do_triggered_check ( comp - > stream - > pctx , pair ) ) {
* error = ( r = = R_NO_MEMORY ) ? 500 : 400 ;
ABORT ( r ) ;
}
_status = 0 ;
abort :
return ( r ) ;
}
/* Section 7.2.1 */
static int nr_ice_component_process_incoming_check ( nr_ice_component * comp , nr_transport_addr * local_addr , nr_stun_server_request * req , int * error )
{
nr_ice_cand_pair * pair ;
nr_ice_candidate * pcand = 0 ;
nr_stun_message * sreq = req - > request ;
nr_stun_message_attribute * attr ;
int r = 0 , _status ;
int found_valid = 0 ;
r_log ( LOG_ICE , LOG_DEBUG , " ICE-PEER(%s)/STREAM(%s)/COMP(%d): received request from %s " , comp - > stream - > pctx - > label , comp - > stream - > label , comp - > component_id , req - > src_addr . as_string ) ;
if ( comp - > state = = NR_ICE_COMPONENT_DISABLED )
ABORT ( R_REJECTED ) ;
/* Check for role conficts (7.2.1.1) */
if ( comp - > stream - > pctx - > controlling ) {
if ( nr_stun_message_has_attribute ( sreq , NR_STUN_ATTR_ICE_CONTROLLING , & attr ) ) {
/* OK, there is a conflict. Who's right? */
r_log ( LOG_ICE , LOG_INFO , " ICE-PEER(%s): role conflict, both controlling " , comp - > stream - > pctx - > label ) ;
if ( attr - > u . ice_controlling > comp - > stream - > pctx - > tiebreaker ) {
/* Update the peer ctx. This will propagate to all candidate pairs
in the context . */
nr_ice_peer_ctx_switch_controlling_role ( comp - > stream - > pctx ) ;
}
else {
/* We are: throw an error */
r_log ( LOG_ICE , LOG_WARNING , " ICE-PEER(%s): returning 487 role conflict " , comp - > stream - > pctx - > label ) ;
* error = 487 ;
ABORT ( R_REJECTED ) ;
}
}
}
else {
if ( nr_stun_message_has_attribute ( sreq , NR_STUN_ATTR_ICE_CONTROLLED , & attr ) ) {
/* OK, there is a conflict. Who's right? */
r_log ( LOG_ICE , LOG_INFO , " ICE-PEER(%s): role conflict, both controlled " , comp - > stream - > pctx - > label ) ;
if ( attr - > u . ice_controlled < comp - > stream - > pctx - > tiebreaker ) {
/* Update the peer ctx. This will propagate to all candidate pairs
in the context . */
nr_ice_peer_ctx_switch_controlling_role ( comp - > stream - > pctx ) ;
}
else {
/* We are: throw an error */
r_log ( LOG_ICE , LOG_WARNING , " ICE-PEER(%s): returning 487 role conflict " , comp - > stream - > pctx - > label ) ;
* error = 487 ;
ABORT ( R_REJECTED ) ;
}
}
}
r_log ( LOG_ICE , LOG_DEBUG , " ICE-PEER(%s): This STUN request appears to map to local addr %s " , comp - > stream - > pctx - > label , local_addr - > as_string ) ;
pair = TAILQ_FIRST ( & comp - > stream - > check_list ) ;
while ( pair ) {
/* Since triggered checks create duplicate pairs (in this implementation)
* we are willing to handle multiple matches here . */
if ( nr_ice_component_pair_matches_check ( comp , pair , local_addr , req ) ) {
r_log ( LOG_ICE , LOG_DEBUG , " ICE-PEER(%s)/CAND_PAIR(%s): Found a matching pair for received check: %s " , comp - > stream - > pctx - > label , pair - > codeword , pair - > as_string ) ;
if ( r = nr_ice_component_handle_triggered_check ( comp , pair , req , error ) )
ABORT ( r ) ;
+ + found_valid ;
}
pair = TAILQ_NEXT ( pair , check_queue_entry ) ;
}
if ( ! found_valid ) {
/* There were no matching pairs, so we need to create a new peer
* reflexive candidate pair . */
if ( ! nr_stun_message_has_attribute ( sreq , NR_STUN_ATTR_PRIORITY , & attr ) ) {
r_log ( LOG_ICE , LOG_WARNING , " ICE-PEER(%s): Rejecting stun request without priority " , comp - > stream - > pctx - > label ) ;
* error = 400 ;
ABORT ( R_BAD_DATA ) ;
}
/* Find our local component candidate */
nr_ice_candidate * cand ;
r_log ( LOG_ICE , LOG_DEBUG , " ICE-PEER(%s): no matching pair " , comp - > stream - > pctx - > label ) ;
cand = TAILQ_FIRST ( & comp - > local_component - > candidates ) ;
while ( cand ) {
if ( ! nr_transport_addr_cmp ( & cand - > addr , local_addr , NR_TRANSPORT_ADDR_CMP_MODE_ALL ) )
break ;
cand = TAILQ_NEXT ( cand , entry_comp ) ;
}
/* Well, this really shouldn't happen, but it's an error from the
other side , so we just throw an error and keep going */
if ( ! cand ) {
r_log ( LOG_ICE , LOG_WARNING , " ICE-PEER(%s): stun request to unknown local address %s, discarding " , comp - > stream - > pctx - > label , local_addr - > as_string ) ;
* error = 400 ;
ABORT ( R_NOT_FOUND ) ;
}
/* Now make a peer reflexive (remote) candidate */
if ( r = nr_ice_peer_peer_rflx_candidate_create ( comp - > stream - > pctx - > ctx , " prflx " , comp , & req - > src_addr , & pcand ) ) {
* error = ( r = = R_NO_MEMORY ) ? 500 : 400 ;
ABORT ( r ) ;
}
pcand - > priority = attr - > u . priority ;
pcand - > state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED ;
/* Finally, create the candidate pair, insert into the check list, and
* apply the incoming check to it . */
if ( r = nr_ice_candidate_pair_create ( comp - > stream - > pctx , cand , pcand ,
& pair ) ) {
* error = ( r = = R_NO_MEMORY ) ? 500 : 400 ;
ABORT ( r ) ;
}
nr_ice_candidate_pair_set_state ( pair - > pctx , pair , NR_ICE_PAIR_STATE_FROZEN ) ;
if ( r = nr_ice_component_insert_pair ( comp , pair ) ) {
* error = ( r = = R_NO_MEMORY ) ? 500 : 400 ;
ABORT ( r ) ;
}
/* Do this last, since any call to ABORT will destroy pcand */
TAILQ_INSERT_TAIL ( & comp - > candidates , pcand , entry_comp ) ;
pcand = 0 ;
/* Finally start the trigger check if needed */
if ( r = nr_ice_component_handle_triggered_check ( comp , pair , req , error ) )
ABORT ( r ) ;
}
_status = 0 ;
abort :
if ( _status ) {
nr_ice_candidate_destroy ( & pcand ) ;
assert ( * error ! = 0 ) ;
if ( r ! = R_NO_MEMORY ) assert ( * error ! = 500 ) ;
}
return ( _status ) ;
}
static int nr_ice_component_stun_server_cb ( void * cb_arg , nr_stun_server_ctx * stun_ctx , nr_socket * sock , nr_stun_server_request * req , int * dont_free , int * error )
{
nr_ice_component * comp = cb_arg ;
nr_transport_addr local_addr ;
int r , _status ;
/* Find the candidate pair that this maps to */
if ( r = nr_socket_getaddr ( sock , & local_addr ) ) {
* error = 500 ;
ABORT ( r ) ;
}
if ( r = nr_ice_component_process_incoming_check ( comp , & local_addr , req , error ) )
ABORT ( r ) ;
_status = 0 ;
abort :
return ( _status ) ;
}
int nr_ice_component_service_pre_answer_requests ( nr_ice_peer_ctx * pctx , nr_ice_component * pcomp , char * username , int * serviced )
{
nr_ice_pre_answer_request * r1 , * r2 ;
nr_ice_component * comp = pcomp - > local_component ;
int r , _status ;
if ( serviced )
* serviced = 0 ;
r_log ( LOG_ICE , LOG_DEBUG , " ICE-PEER(%s)/STREAM(%s)/COMP(%d): looking for pre-answer requests " , pctx - > label , comp - > stream - > label , comp - > component_id ) ;
STAILQ_FOREACH_SAFE ( r1 , & comp - > pre_answer_reqs , entry , r2 ) {
if ( ! strcmp ( r1 - > username , username ) ) {
int error = 0 ;
r_log ( LOG_ICE , LOG_DEBUG , " ICE-PEER(%s)/STREAM(%s)/COMP(%d): found pre-answer request " , pctx - > label , comp - > stream - > label , comp - > component_id ) ;
r = nr_ice_component_process_incoming_check ( pcomp , & r1 - > local_addr , & r1 - > req , & error ) ;
if ( r ) {
r_log ( LOG_ICE , LOG_INFO , " ICE-PEER(%s)/STREAM(%s)/COMP(%d): error processing pre-answer request. Would have returned %d " , pctx - > label , comp - > stream - > label , comp - > component_id , error ) ;
}
( * serviced ) + + ;
STAILQ_REMOVE ( & comp - > pre_answer_reqs , r1 , nr_ice_pre_answer_request_ , entry ) ;
nr_ice_pre_answer_request_destroy ( & r1 ) ;
}
}
_status = 0 ;
return ( _status ) ;
}
int nr_ice_component_can_candidate_tcptype_pair ( nr_socket_tcp_type left , nr_socket_tcp_type right )
{
if ( left & & ! right )
return ( 0 ) ;
if ( ! left & & right )
return ( 0 ) ;
if ( left = = TCP_TYPE_ACTIVE & & right ! = TCP_TYPE_PASSIVE )
return ( 0 ) ;
if ( left = = TCP_TYPE_SO & & right ! = TCP_TYPE_SO )
return ( 0 ) ;
if ( left = = TCP_TYPE_PASSIVE )
return ( 0 ) ;
return ( 1 ) ;
}
/* local vs. remote matters here because we allow private -> public pairing,
* but discourage public - > private pairing . */
int nr_ice_component_can_candidate_addr_pair ( nr_transport_addr * local , nr_transport_addr * remote )
{
int remote_range ;
if ( local - > ip_version ! = remote - > ip_version )
return ( 0 ) ;
if ( nr_transport_addr_is_link_local ( local ) ! =
nr_transport_addr_is_link_local ( remote ) )
return ( 0 ) ;
/* This prevents our ice_unittest (or broken clients) from pairing a
* loopback with a host candidate . */
if ( nr_transport_addr_is_loopback ( local ) ! =
nr_transport_addr_is_loopback ( remote ) )
return ( 0 ) ;
remote_range = nr_transport_addr_get_private_addr_range ( remote ) ;
if ( remote_range & & ( nr_transport_addr_get_private_addr_range ( local ) ! =
remote_range ) )
return ( 0 ) ;
return ( 1 ) ;
}
int nr_ice_component_pair_candidate ( nr_ice_peer_ctx * pctx , nr_ice_component * pcomp , nr_ice_candidate * lcand , int pair_all_remote )
{
int r , _status ;
nr_ice_candidate * pcand ;
nr_ice_cand_pair * pair = 0 ;
char codeword [ 5 ] ;
nr_ice_compute_codeword ( lcand - > label , strlen ( lcand - > label ) , codeword ) ;
r_log ( LOG_ICE , LOG_DEBUG , " ICE-PEER(%s)/CAND(%s): Pairing local candidate %s " , pctx - > label , codeword , lcand - > label ) ;
switch ( lcand - > type ) {
case HOST :
break ;
case SERVER_REFLEXIVE :
case PEER_REFLEXIVE :
/* Don't actually pair these candidates */
goto done ;
break ;
case RELAYED :
break ;
default :
assert ( 0 ) ;
ABORT ( R_INTERNAL ) ;
break ;
}
TAILQ_FOREACH ( pcand , & pcomp - > candidates , entry_comp ) {
if ( ! nr_ice_component_can_candidate_addr_pair ( & lcand - > addr , & pcand - > addr ) )
continue ;
if ( ! nr_ice_component_can_candidate_tcptype_pair ( lcand - > tcp_type , pcand - > tcp_type ) )
continue ;
/*
Two modes , depending on | pair_all_remote |
1. Pair remote candidates which have not been paired
( used in initial pairing or in processing the other side ' s
trickle candidates ) .
2. Pair any remote candidate ( used when processing our own
trickle candidates ) .
*/
if ( pair_all_remote | | ( pcand - > state = = NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED ) ) {
if ( pair_all_remote ) {
/* When a remote candidate arrives after the start of checking, but
* before the gathering of local candidates , it can be in UNPAIRED */
pcand - > state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED ;
}
nr_ice_compute_codeword ( pcand - > label , strlen ( pcand - > label ) , codeword ) ;
r_log ( LOG_ICE , LOG_DEBUG , " ICE-PEER(%s)/CAND(%s): Pairing with peer candidate %s " , pctx - > label , codeword , pcand - > label ) ;
if ( r = nr_ice_candidate_pair_create ( pctx , lcand , pcand , & pair ) )
ABORT ( r ) ;
if ( r = nr_ice_component_insert_pair ( pcomp , pair ) )
ABORT ( r ) ;
}
}
done :
_status = 0 ;
abort :
return ( _status ) ;
}
int nr_ice_component_pair_candidates ( nr_ice_peer_ctx * pctx , nr_ice_component * lcomp , nr_ice_component * pcomp )
{
nr_ice_candidate * lcand , * pcand ;
nr_ice_socket * isock ;
int r , _status ;
r_log ( LOG_ICE , LOG_DEBUG , " Pairing candidates====== " ) ;
/* Create the candidate pairs */
lcand = TAILQ_FIRST ( & lcomp - > candidates ) ;
while ( lcand ) {
if ( lcand - > state = = NR_ICE_CAND_STATE_INITIALIZED ) {
if ( ( r = nr_ice_component_pair_candidate ( pctx , pcomp , lcand , 0 ) ) )
ABORT ( r ) ;
}
lcand = TAILQ_NEXT ( lcand , entry_comp ) ;
}
/* Mark all peer candidates as paired */
pcand = TAILQ_FIRST ( & pcomp - > candidates ) ;
while ( pcand ) {
pcand - > state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED ;
pcand = TAILQ_NEXT ( pcand , entry_comp ) ;
}
/* Now register the STUN server callback for this component.
Note that this is a per - component CB so we only need to
do this once .
*/
if ( pcomp - > state ! = NR_ICE_COMPONENT_RUNNING ) {
isock = STAILQ_FIRST ( & lcomp - > sockets ) ;
while ( isock ) {
if ( r = nr_stun_server_add_client ( isock - > stun_server , pctx - > label ,
pcomp - > stream - > r2l_user , & pcomp - > stream - > r2l_pass , nr_ice_component_stun_server_cb , pcomp ) ) {
ABORT ( r ) ;
}
isock = STAILQ_NEXT ( isock , entry ) ;
}
}
pcomp - > state = NR_ICE_COMPONENT_RUNNING ;
_status = 0 ;
abort :
return ( _status ) ;
}
/* Fires when we have an incoming candidate that doesn't correspond to an existing
remote peer . This is either pre - answer or just spurious . Store it in the
component for use when we see the actual answer , at which point we need
to do the procedures from S 7.2 .1 in nr_ice_component_stun_server_cb .
*/
static int nr_ice_component_stun_server_default_cb ( void * cb_arg , nr_stun_server_ctx * stun_ctx , nr_socket * sock , nr_stun_server_request * req , int * dont_free , int * error )
{
int r , _status ;
nr_ice_component * comp = ( nr_ice_component * ) cb_arg ;
nr_ice_pre_answer_request * par = 0 ;
r_log ( LOG_ICE , LOG_DEBUG , " ICE(%s)/STREAM(%s)/COMP(%d): Received STUN request pre-answer from %s " ,
comp - > ctx - > label , comp - > stream - > label , comp - > component_id , req - > src_addr . as_string ) ;
if ( r = nr_ice_pre_answer_request_create ( sock , req , & par ) )
ABORT ( r ) ;
* dont_free = 1 ;
STAILQ_INSERT_TAIL ( & comp - > pre_answer_reqs , par , entry ) ;
_status = 0 ;
abort :
return ( _status ) ;
}
int nr_ice_component_nominated_pair ( nr_ice_component * comp , nr_ice_cand_pair * pair )
{
int r , _status ;
nr_ice_cand_pair * p2 ;
/* Are we changing what the nominated pair is? */
if ( comp - > nominated ) {
if ( comp - > nominated - > priority > = pair - > priority )
return ( 0 ) ;
r_log ( LOG_ICE , LOG_INFO , " ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): replacing pair %s with CAND-PAIR(%s) " , comp - > stream - > pctx - > label , comp - > stream - > label , comp - > component_id , comp - > nominated - > codeword , comp - > nominated - > as_string , pair - > codeword ) ;
}
/* Set the new nominated pair */
r_log ( LOG_ICE , LOG_INFO , " ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): nominated pair is %s " , comp - > stream - > pctx - > label , comp - > stream - > label , comp - > component_id , pair - > codeword , pair - > as_string ) ;
comp - > state = NR_ICE_COMPONENT_NOMINATED ;
comp - > nominated = pair ;
comp - > active = pair ;
r_log ( LOG_ICE , LOG_INFO , " ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling all pairs but %s " , comp - > stream - > pctx - > label , comp - > stream - > label , comp - > component_id , pair - > codeword , pair - > as_string ) ;
/* Cancel checks in WAITING and FROZEN per ICE S 8.1.2 */
p2 = TAILQ_FIRST ( & comp - > stream - > trigger_check_queue ) ;
while ( p2 ) {
if ( ( p2 ! = pair ) & &
( p2 - > remote - > component - > component_id = = comp - > component_id ) ) {
assert ( p2 - > state = = NR_ICE_PAIR_STATE_WAITING | |
p2 - > state = = NR_ICE_PAIR_STATE_CANCELLED ) ;
r_log ( LOG_ICE , LOG_INFO , " ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling FROZEN/WAITING pair %s in trigger check queue because CAND-PAIR(%s) was nominated. " , comp - > stream - > pctx - > label , comp - > stream - > label , comp - > component_id , p2 - > codeword , p2 - > as_string , pair - > codeword ) ;
if ( r = nr_ice_candidate_pair_cancel ( pair - > pctx , p2 , 0 ) )
ABORT ( r ) ;
}
p2 = TAILQ_NEXT ( p2 , triggered_check_queue_entry ) ;
}
p2 = TAILQ_FIRST ( & comp - > stream - > check_list ) ;
while ( p2 ) {
if ( ( p2 ! = pair ) & &
( p2 - > remote - > component - > component_id = = comp - > component_id ) & &
( ( p2 - > state = = NR_ICE_PAIR_STATE_FROZEN ) | |
( p2 - > state = = NR_ICE_PAIR_STATE_WAITING ) ) ) {
r_log ( LOG_ICE , LOG_INFO , " ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling FROZEN/WAITING pair %s because CAND-PAIR(%s) was nominated. " , comp - > stream - > pctx - > label , comp - > stream - > label , comp - > component_id , p2 - > codeword , p2 - > as_string , pair - > codeword ) ;
if ( r = nr_ice_candidate_pair_cancel ( pair - > pctx , p2 , 0 ) )
ABORT ( r ) ;
}
p2 = TAILQ_NEXT ( p2 , check_queue_entry ) ;
}
r_log ( LOG_ICE , LOG_DEBUG , " ICE-PEER(%s)/STREAM(%s)/COMP(%d): cancelling done " , comp - > stream - > pctx - > label , comp - > stream - > label , comp - > component_id ) ;
if ( r = nr_ice_media_stream_component_nominated ( comp - > stream , comp ) )
ABORT ( r ) ;
_status = 0 ;
abort :
return ( _status ) ;
}
static int nr_ice_component_have_all_pairs_failed ( nr_ice_component * comp )
{
nr_ice_cand_pair * p2 ;
p2 = TAILQ_FIRST ( & comp - > stream - > check_list ) ;
while ( p2 ) {
if ( comp - > component_id = = p2 - > local - > component_id ) {
switch ( p2 - > state ) {
case NR_ICE_PAIR_STATE_FROZEN :
case NR_ICE_PAIR_STATE_WAITING :
case NR_ICE_PAIR_STATE_IN_PROGRESS :
case NR_ICE_PAIR_STATE_SUCCEEDED :
return ( 0 ) ;
case NR_ICE_PAIR_STATE_FAILED :
case NR_ICE_PAIR_STATE_CANCELLED :
/* states that will never be recovered from */
break ;
default :
assert ( 0 ) ;
break ;
}
}
p2 = TAILQ_NEXT ( p2 , check_queue_entry ) ;
}
return ( 1 ) ;
}
int nr_ice_component_failed_pair ( nr_ice_component * comp , nr_ice_cand_pair * pair )
{
return nr_ice_component_check_if_failed ( comp ) ;
}
int nr_ice_component_check_if_failed ( nr_ice_component * comp )
{
if ( comp - > state = = NR_ICE_COMPONENT_RUNNING ) {
/* Don't do anything to streams that aren't currently running */
r_log ( LOG_ICE , LOG_DEBUG , " ICE-PEER(%s)/STREAM(%s)/COMP(%d): Checking whether component needs to be marked failed. " , comp - > stream - > pctx - > label , comp - > stream - > label , comp - > component_id ) ;
if ( ! comp - > stream - > pctx - > trickle_grace_period_timer & &
nr_ice_component_have_all_pairs_failed ( comp ) ) {
r_log ( LOG_ICE , LOG_INFO , " ICE-PEER(%s)/STREAM(%s)/COMP(%d): All pairs are failed, and grace period has elapsed. Marking component as failed. " , comp - > stream - > pctx - > label , comp - > stream - > label , comp - > component_id ) ;
return nr_ice_media_stream_component_failed ( comp - > stream , comp ) ;
}
}
return ( 0 ) ;
}
int nr_ice_component_select_pair ( nr_ice_peer_ctx * pctx , nr_ice_component * comp )
{
nr_ice_cand_pair * * pairs = 0 ;
int ct = 0 ;
nr_ice_cand_pair * pair ;
int r , _status ;
/* Size the array */
pair = TAILQ_FIRST ( & comp - > stream - > check_list ) ;
while ( pair ) {
if ( comp - > component_id = = pair - > local - > component_id )
ct + + ;
pair = TAILQ_NEXT ( pair , check_queue_entry ) ;
}
/* Make and fill the array */
if ( ! ( pairs = RCALLOC ( sizeof ( nr_ice_cand_pair * ) * ct ) ) )
ABORT ( R_NO_MEMORY ) ;
ct = 0 ;
pair = TAILQ_FIRST ( & comp - > stream - > check_list ) ;
while ( pair ) {
if ( comp - > component_id = = pair - > local - > component_id )
pairs [ ct + + ] = pair ;
pair = TAILQ_NEXT ( pair , check_queue_entry ) ;
}
if ( pctx - > handler ) {
if ( r = pctx - > handler - > vtbl - > select_pair ( pctx - > handler - > obj ,
comp - > stream , comp - > component_id , pairs , ct ) )
ABORT ( r ) ;
}
_status = 0 ;
abort :
RFREE ( pairs ) ;
return ( _status ) ;
}
static void nr_ice_component_keepalive_cb ( NR_SOCKET s , int how , void * cb_arg )
{
nr_ice_component * comp = cb_arg ;
UINT4 keepalive_timeout ;
assert ( comp - > keepalive_ctx ) ;
if ( NR_reg_get_uint4 ( NR_ICE_REG_KEEPALIVE_TIMER , & keepalive_timeout ) ) {
keepalive_timeout = 15000 ; /* Default */
}
if ( comp - > keepalive_needed )
nr_stun_client_force_retransmit ( comp - > keepalive_ctx ) ;
comp - > keepalive_needed = 1 ;
NR_ASYNC_TIMER_SET ( keepalive_timeout , nr_ice_component_keepalive_cb , cb_arg , & comp - > keepalive_timer ) ;
}
/* Close the underlying sockets for everything but the nominated candidate */
int nr_ice_component_finalize ( nr_ice_component * lcomp , nr_ice_component * rcomp )
{
nr_ice_socket * isock = 0 ;
int r , _status ;
nr_ice_socket * s1 , * s2 ;
if ( rcomp - > state = = NR_ICE_COMPONENT_NOMINATED ) {
assert ( rcomp - > active = = rcomp - > nominated ) ;
isock = rcomp - > nominated - > local - > isock ;
}
STAILQ_FOREACH_SAFE ( s1 , & lcomp - > sockets , entry , s2 ) {
if ( s1 ! = isock ) {
STAILQ_REMOVE ( & lcomp - > sockets , s1 , nr_ice_socket_ , entry ) ;
nr_ice_socket_destroy ( & s1 ) ;
}
}
/* Set up the keepalives for the chosen socket */
if ( r = nr_stun_client_ctx_create ( " keepalive " , rcomp - > nominated - > local - > osock ,
& rcomp - > nominated - > remote - > addr , 0 , & rcomp - > keepalive_ctx ) )
ABORT ( r ) ;
if ( r = nr_stun_client_start ( rcomp - > keepalive_ctx , NR_STUN_CLIENT_MODE_KEEPALIVE , 0 , 0 ) )
ABORT ( r ) ;
nr_ice_component_keepalive_cb ( 0 , 0 , rcomp ) ;
_status = 0 ;
abort :
return ( _status ) ;
}
int nr_ice_component_insert_pair ( nr_ice_component * pcomp , nr_ice_cand_pair * pair )
{
2020-06-17 03:18:17 +00:00
int _status ;
2017-04-19 07:56:45 +00:00
/* Pairs for peer reflexive are marked SUCCEEDED immediately */
if ( pair - > state ! = NR_ICE_PAIR_STATE_FROZEN & &
pair - > state ! = NR_ICE_PAIR_STATE_SUCCEEDED ) {
assert ( 0 ) ;
ABORT ( R_BAD_ARGS ) ;
}
2020-06-17 03:18:17 +00:00
/* We do not throw an error after this, because we've inserted the pair. */
nr_ice_candidate_pair_insert ( & pair - > remote - > stream - > check_list , pair ) ;
2019-10-17 05:25:44 +00:00
2017-04-19 07:56:45 +00:00
/* Make sure the check timer is running, if the stream was previously
* started . We will not start streams just because a pair was created ,
* unless it is the first pair to be created across all streams . */
r_log ( LOG_ICE , LOG_DEBUG , " ICE-PEER(%s)/CAND-PAIR(%s): Ensure that check timer is running for new pair %s. " , pair - > remote - > stream - > pctx - > label , pair - > codeword , pair - > as_string ) ;
if ( pair - > remote - > stream - > ice_state = = NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE | |
( pair - > remote - > stream - > ice_state = = NR_ICE_MEDIA_STREAM_CHECKS_FROZEN & &
! pair - > remote - > stream - > pctx - > checks_started ) ) {
if ( nr_ice_media_stream_start_checks ( pair - > remote - > stream - > pctx , pair - > remote - > stream ) ) {
r_log ( LOG_ICE , LOG_WARNING , " ICE-PEER(%s)/CAND-PAIR(%s): Could not restart checks for new pair %s. " , pair - > remote - > stream - > pctx - > label , pair - > codeword , pair - > as_string ) ;
}
}
_status = 0 ;
abort :
2020-06-17 03:18:17 +00:00
if ( _status ) {
2019-10-17 05:25:44 +00:00
nr_ice_candidate_pair_destroy ( & pair ) ;
}
2017-04-19 07:56:45 +00:00
return ( _status ) ;
}
int nr_ice_component_get_default_candidate ( nr_ice_component * comp , nr_ice_candidate * * candp , int ip_version )
{
int _status ;
nr_ice_candidate * cand ;
nr_ice_candidate * best_cand = NULL ;
/* We have the component. Now find the "best" candidate, making
use of the fact that more " reliable " candidate types have
higher numbers . So , we sort by type and then priority within
type
*/
cand = TAILQ_FIRST ( & comp - > candidates ) ;
while ( cand ) {
if ( ! nr_ice_ctx_hide_candidate ( comp - > ctx , cand ) & &
cand - > addr . ip_version = = ip_version ) {
if ( ! best_cand ) {
best_cand = cand ;
}
else if ( best_cand - > type < cand - > type ) {
best_cand = cand ;
} else if ( best_cand - > type = = cand - > type & &
best_cand - > priority < cand - > priority ) {
best_cand = cand ;
}
}
cand = TAILQ_NEXT ( cand , entry_comp ) ;
}
/* No candidates */
if ( ! best_cand )
ABORT ( R_NOT_FOUND ) ;
* candp = best_cand ;
_status = 0 ;
abort :
return ( _status ) ;
}