@ -31,6 +31,82 @@
# include "include/res_pjsip_private.h"
# include "asterisk/http_websocket.h"
# define MAX_POINTER_STRING 33
/*! \brief Default number of state container buckets */
# define DEFAULT_STATE_BUCKETS 53
static struct ao2_container * transport_states ;
struct internal_state {
char * id ;
/*! Set if there was a change detected */
int change_detected ;
/*! \brief Transport configuration object */
struct ast_sip_transport * transport ;
/*! \brief Transport state information */
struct ast_sip_transport_state * state ;
} ;
static void temp_state_store_cleanup ( void * data )
{
struct ast_sip_transport_state * * temp_state = data ;
ao2_cleanup ( * temp_state ) ;
ast_free ( data ) ;
}
AST_THREADSTORAGE_CUSTOM ( temp_state_store , NULL , temp_state_store_cleanup ) ;
/*! \brief hashing function for state objects */
static int internal_state_hash ( const void * obj , const int flags )
{
const struct internal_state * object ;
const char * key ;
switch ( flags & OBJ_SEARCH_MASK ) {
case OBJ_SEARCH_KEY :
key = obj ;
break ;
case OBJ_SEARCH_OBJECT :
object = obj ;
key = object - > id ;
break ;
default :
ast_assert ( 0 ) ;
return 0 ;
}
return ast_str_hash ( key ) ;
}
/*! \brief comparator function for state objects */
static int internal_state_cmp ( void * obj , void * arg , int flags )
{
const struct internal_state * object_left = obj ;
const struct internal_state * object_right = arg ;
const char * right_key = arg ;
int cmp ;
switch ( flags & OBJ_SEARCH_MASK ) {
case OBJ_SEARCH_OBJECT :
right_key = object_right - > id ;
/* Fall through */
case OBJ_SEARCH_KEY :
cmp = strcmp ( object_left - > id , right_key ) ;
break ;
case OBJ_SEARCH_PARTIAL_KEY :
/* Not supported by container. */
ast_assert ( 0 ) ;
return 0 ;
default :
cmp = 0 ;
break ;
}
if ( cmp ) {
return 0 ;
}
return CMP_MATCH ;
}
static int sip_transport_to_ami ( const struct ast_sip_transport * transport ,
struct ast_str * * buf )
{
@ -75,69 +151,206 @@ struct ast_sip_endpoint_formatter endpoint_transport_formatter = {
. format_ami = format_ami_endpoint_transport
} ;
static int destroy_transport_state ( void * data )
static void set_qos ( struct ast_sip_transport * transport , pj_qos_params * qos )
{
int tos_as_dscp = transport - > tos > > 2 ;
if ( transport - > tos ) {
qos - > flags | = PJ_QOS_PARAM_HAS_DSCP ;
qos - > dscp_val = tos_as_dscp ;
}
if ( transport - > cos ) {
qos - > flags | = PJ_QOS_PARAM_HAS_SO_PRIO ;
qos - > so_prio = transport - > cos ;
}
}
/*! \brief Destructor for transport */
static void sip_transport_destroy ( void * obj )
{
struct ast_sip_transport * transport = obj ;
ast_string_field_free_memory ( transport ) ;
}
/*! \brief Allocator for transport */
static void * sip_transport_alloc ( const char * name )
{
struct ast_sip_transport * transport = ast_sorcery_generic_alloc ( sizeof ( * transport ) , sip_transport_destroy ) ;
if ( ! transport ) {
return NULL ;
}
if ( ast_string_field_init ( transport , 256 ) ) {
ao2_cleanup ( transport ) ;
return NULL ;
}
return transport ;
}
static int destroy_sip_transport_state ( void * data )
{
pjsip_transport * transport = data ;
pjsip_transport_shutdown ( transport ) ;
struct ast_sip_transport_state * transport_state = data ;
ast_free ( transport_state - > id ) ;
ast_free_ha ( transport_state - > localnet ) ;
if ( transport_state - > external_address_refresher ) {
ast_dnsmgr_release ( transport_state - > external_address_refresher ) ;
}
if ( transport_state - > transport ) {
pjsip_transport_shutdown ( transport_state - > transport ) ;
}
return 0 ;
}
/*! \brief Destructor for transport state information */
static void transport_state_destroy ( void * obj )
/*! \brief Destructor for ast_sip_ transport state information */
static void sip_ transport_state_destroy( void * obj )
{
struct ast_sip_transport_state * state = obj ;
if ( state - > transport ) {
ast_sip_push_task_synchronous ( NULL , destroy_transport_state , state - > transport ) ;
ast_sip_push_task_synchronous ( NULL , destroy_sip_transport_state , state ) ;
}
/*! \brief Destructor for ast_sip_transport state information */
static void internal_state_destroy ( void * obj )
{
struct internal_state * state = obj ;
ast_free ( state - > id ) ;
ao2_cleanup ( state - > transport ) ;
ao2_cleanup ( state - > state ) ;
}
static struct internal_state * find_internal_state_by_transport ( const struct ast_sip_transport * transport )
{
const char * key = ast_sorcery_object_get_id ( transport ) ;
return ao2_find ( transport_states , key , OBJ_SEARCH_KEY | OBJ_NOLOCK ) ;
}
static struct ast_sip_transport_state * find_state_by_transport ( const struct ast_sip_transport * transport )
{
struct internal_state * state ;
state = find_internal_state_by_transport ( transport ) ;
if ( ! state ) {
return NULL ;
}
ao2_bump ( state - > state ) ;
ao2_cleanup ( state ) ;
return state - > state ;
}
/*! \brief Destructor for transport */
static void transport_destroy ( void * obj )
static int remove_temporary_state ( void )
{
struct ast_sip_transport * transport = obj ;
struct ast_sip_transport _state * * state ;
ast_string_field_free_memory ( transport ) ;
ast_free_ha ( transport - > localnet ) ;
state = ast_threadstorage_get ( & temp_state_store , sizeof ( state ) ) ;
if ( ! state ) {
return - 1 ;
}
ao2_cleanup ( * state ) ;
* state = NULL ;
return 0 ;
}
if ( transport - > external_address_refresher ) {
ast_dnsmgr_release ( transport - > external_address_refresher ) ;
static struct ast_sip_transport_state * find_temporary_state ( struct ast_sip_transport * transport )
{
struct ast_sip_transport_state * * state ;
state = ast_threadstorage_get ( & temp_state_store , sizeof ( state ) ) ;
if ( state & & * state ) {
ao2_ref ( * state , + 1 ) ;
return * state ;
}
ao2_cleanup ( transport - > state ) ;
return NULL ;
}
/*! \brief Allocator for transport */
static void * transport_alloc ( const char * name )
static struct internal_state * internal_state_alloc ( struct ast_sip_transport * transport )
{
struct ast_sip_transport * transport = ast_sorcery_generic_alloc ( sizeof ( * transport ) , transport_destroy ) ;
struct internal_state * internal_state ;
if ( ! transport ) {
internal_state = ao2_alloc ( sizeof ( * internal_state ) , internal_state_destroy ) ;
if ( ! internal_state ) {
return NULL ;
}
if ( ast_string_field_init ( transport , 256 ) ) {
ao2_cleanup ( transport ) ;
internal_state - > id = ast_strdup ( ast_sorcery_object_get_id ( transport ) ) ;
if ( ! internal_state - > id ) {
ao2_cleanup ( internal_state ) ;
return NULL ;
}
pjsip_tls_setting_default ( & transport - > tls ) ;
transport - > tls . ciphers = transport - > ciphers ;
/* We're transferring the reference from find_temporary_state */
internal_state - > state = find_temporary_state ( transport ) ;
if ( ! internal_state - > state ) {
ao2_cleanup ( internal_state ) ;
return NULL ;
}
internal_state - > transport = ao2_bump ( transport ) ;
internal_state - > transport - > state = internal_state - > state ;
remove_temporary_state ( ) ;
return transport ;
return internal_state ;
}
static void set_qos ( struct ast_sip_transport * transport , pj_qos_params * qos )
/*!
* \ internal
* \ brief Should only be called by the individual field handlers
*/
static struct ast_sip_transport_state * find_or_create_temporary_state ( struct ast_sip_transport * transport )
{
int tos_as_dscp = transport - > tos > > 2 ;
struct ast_sip_transport_state * * state ;
struct ast_sip_transport_state * new_state ;
if ( transport - > tos ) {
qos - > flags | = PJ_QOS_PARAM_HAS_DSCP ;
qos - > dscp_val = tos_as_dscp ;
if ( ( new_state = find_temporary_state ( transport ) ) ) {
return new_state ;
}
if ( transport - > cos ) {
qos - > flags | = PJ_QOS_PARAM_HAS_SO_PRIO ;
qos - > so_prio = transport - > cos ;
state = ast_threadstorage_get ( & temp_state_store , sizeof ( state ) ) ;
if ( ! state | | * state ) {
return NULL ;
}
new_state = ao2_alloc ( sizeof ( * * state ) , sip_transport_state_destroy ) ;
if ( ! new_state ) {
return NULL ;
}
new_state - > id = ast_strdup ( ast_sorcery_object_get_id ( transport ) ) ;
new_state - > type = transport - > type ;
pjsip_tls_setting_default ( & new_state - > tls ) ;
new_state - > tls . ciphers = new_state - > ciphers ;
ao2_ref ( new_state , + 1 ) ;
* state = new_state ;
return new_state ;
}
static void copy_state_to_transport ( struct ast_sip_transport * transport )
{
ast_assert ( transport & & transport - > state ) ;
memcpy ( & transport - > host , & transport - > state - > host , sizeof ( transport - > host ) ) ;
memcpy ( & transport - > tls , & transport - > state - > tls , sizeof ( transport - > tls ) ) ;
memcpy ( & transport - > ciphers , & transport - > state - > ciphers , sizeof ( transport - > ciphers ) ) ;
transport - > localnet = transport - > state - > localnet ;
transport - > external_address_refresher = transport - > state - > external_address_refresher ;
memcpy ( & transport - > external_address , & transport - > state - > external_address , sizeof ( transport - > external_address ) ) ;
}
static void states_cleanup ( void * states )
{
if ( states ) {
ao2_unlock ( states ) ;
}
}
@ -145,64 +358,87 @@ static void set_qos(struct ast_sip_transport *transport, pj_qos_params *qos)
static int transport_apply ( const struct ast_sorcery * sorcery , void * obj )
{
struct ast_sip_transport * transport = obj ;
RAII_VAR ( struct ast_sip_transport * , existing , ast_sorcery_retrieve_by_id ( sorcery , " transport " , ast_sorcery_object_get_id ( obj ) ) , ao2_cleanup ) ;
const char * transport_id = ast_sorcery_object_get_id ( obj ) ;
RAII_VAR ( struct ao2_container * , states , transport_states , states_cleanup ) ;
RAII_VAR ( struct internal_state * , temp_state , NULL , ao2_cleanup ) ;
RAII_VAR ( struct internal_state * , perm_state , NULL , ao2_cleanup ) ;
RAII_VAR ( struct ast_variable * , changes , NULL , ast_variables_destroy ) ;
pj_status_t res = - 1 ;
if ( ! existing | | ! existing - > state ) {
if ( ! ( transport - > state = ao2_alloc ( sizeof ( * transport - > state ) , transport_state_destroy ) ) ) {
ast_log ( LOG_ERROR , " Transport state for '%s' could not be allocated \n " , ast_sorcery_object_get_id ( obj ) ) ;
return - 1 ;
if ( ! states ) {
return - 1 ;
}
/*
* transport_apply gets called for EVERY retrieval of a transport when using realtime .
* We need to prevent multiple threads from trying to mess with underlying transports
* at the same time . The container is the only thing we have to lock on .
*/
ao2_wrlock ( states ) ;
perm_state = find_internal_state_by_transport ( transport ) ;
if ( perm_state ) {
ast_sorcery_diff ( sorcery , perm_state - > transport , transport , & changes ) ;
if ( changes ) {
if ( ! perm_state - > change_detected ) {
perm_state - > change_detected = 1 ;
ast_log ( LOG_WARNING , " Transport '%s' is not reloadable, maintaining previous values \n " , transport_id ) ;
}
}
} else {
transport - > state = existing - > state ;
ao2_ref ( transport - > state , + 1 ) ;
/* In case someone is using the deprecated fields, reset them */
transport - > state = perm_state - > state ;
copy_state_to_transport ( transport ) ;
ao2_replace ( perm_state - > transport , transport ) ;
return 0 ;
}
/* Once active a transport can not be reconfigured */
if ( transport - > state - > transport | | transport - > state - > factory ) {
return - 1 ;
temp_state = internal_state_alloc ( transport ) ;
if ( ! temp_state ) {
ast_log ( LOG_ERROR , " Transport '%s' failed to allocate memory \n " , transport_id ) ;
goto error ;
}
if ( transport - > host . addr . sa_family ! = PJ_AF_INET & & transport - > host . addr . sa_family ! = PJ_AF_INET6 ) {
ast_log ( LOG_ERROR , " Transport '%s' could not be started as binding not specified \n " , ast_sorcery_object_get_id ( obj ) ) ;
return - 1 ;
if ( t emp_state- > state - > host . addr . sa_family ! = PJ_AF_INET & & t emp_state- > state - > host . addr . sa_family ! = PJ_AF_INET6 ) {
ast_log ( LOG_ERROR , " Transport '%s' could not be started as binding not specified \n " , transport_id ) ;
goto error ;
}
/* Set default port if not present */
if ( ! pj_sockaddr_get_port ( & t ransport - > host ) ) {
pj_sockaddr_set_port ( & t ransport - > host , ( transport - > type = = AST_TRANSPORT_TLS ) ? 5061 : 5060 ) ;
if ( ! pj_sockaddr_get_port ( & t emp_state- > state - > host ) ) {
pj_sockaddr_set_port ( & t emp_state- > state - > host , ( transport - > type = = AST_TRANSPORT_TLS ) ? 5061 : 5060 ) ;
}
/* Now that we know what address family we can set up a dnsmgr refresh for the external media address if present */
if ( ! ast_strlen_zero ( transport - > external_signaling_address ) ) {
if ( t ransport - > host . addr . sa_family = = pj_AF_INET ( ) ) {
t ransport - > external_address . ss . ss_family = AF_INET ;
} else if ( t ransport - > host . addr . sa_family = = pj_AF_INET6 ( ) ) {
t ransport - > external_address . ss . ss_family = AF_INET6 ;
if ( t emp_state- > state - > host . addr . sa_family = = pj_AF_INET ( ) ) {
t emp_state- > state - > external_address . ss . ss_family = AF_INET ;
} else if ( t emp_state- > state - > host . addr . sa_family = = pj_AF_INET6 ( ) ) {
t emp_state- > state - > external_address . ss . ss_family = AF_INET6 ;
} else {
ast_log ( LOG_ERROR , " Unknown address family for transport '%s', could not get external signaling address \n " ,
ast_sorcery_object_get_id ( obj ) ) ;
return - 1 ;
transport_id ) ;
goto error ;
}
if ( ast_dnsmgr_lookup ( transport - > external_signaling_address , & t ransport- > external_address , & transport - > external_address_refresher , NULL ) < 0 ) {
ast_log ( LOG_ERROR , " Could not create dnsmgr for external signaling address on '%s' \n " , ast_sorcery_object_get_id ( obj ) ) ;
return - 1 ;
if ( ast_dnsmgr_lookup ( transport - > external_signaling_address , & t emp_state- > state - > external_address , & temp_state - > state - > external_address_refresher , NULL ) < 0 ) {
ast_log ( LOG_ERROR , " Could not create dnsmgr for external signaling address on '%s' \n " , transport_id ) ;
goto error ;
}
}
if ( transport - > type = = AST_TRANSPORT_UDP ) {
if ( t ransport - > host . addr . sa_family = = pj_AF_INET ( ) ) {
res = pjsip_udp_transport_start ( ast_sip_get_pjsip_endpoint ( ) , & t ransport - > host . ipv4 , NULL , transport - > async_operations , & t ransport - > state - > transport ) ;
} else if ( t ransport - > host . addr . sa_family = = pj_AF_INET6 ( ) ) {
res = pjsip_udp_transport_start6 ( ast_sip_get_pjsip_endpoint ( ) , & t ransport - > host . ipv6 , NULL , transport - > async_operations , & t ransport - > state - > transport ) ;
if ( t emp_state- > state - > host . addr . sa_family = = pj_AF_INET ( ) ) {
res = pjsip_udp_transport_start ( ast_sip_get_pjsip_endpoint ( ) , & t emp_state- > state - > host . ipv4 , NULL , transport - > async_operations , & t emp_state - > state - > transport ) ;
} else if ( t emp_state- > state - > host . addr . sa_family = = pj_AF_INET6 ( ) ) {
res = pjsip_udp_transport_start6 ( ast_sip_get_pjsip_endpoint ( ) , & t emp_state- > state - > host . ipv6 , NULL , transport - > async_operations , & t emp_state - > state - > transport ) ;
}
if ( res = = PJ_SUCCESS & & ( transport - > tos | | transport - > cos ) ) {
pj_sock_t sock ;
pj_qos_params qos_params ;
sock = pjsip_udp_transport_get_socket ( t ransport - > state - > transport ) ;
sock = pjsip_udp_transport_get_socket ( t emp_state - > state - > transport ) ;
pj_sock_get_qos_params ( sock , & qos_params ) ;
set_qos ( transport , & qos_params ) ;
pj_sock_set_qos_params ( sock , & qos_params ) ;
@ -210,61 +446,23 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
} else if ( transport - > type = = AST_TRANSPORT_TCP ) {
pjsip_tcp_transport_cfg cfg ;
pjsip_tcp_transport_cfg_default ( & cfg , t ransport - > host . addr . sa_family ) ;
cfg . bind_addr = t ransport - > host ;
pjsip_tcp_transport_cfg_default ( & cfg , t emp_state- > state - > host . addr . sa_family ) ;
cfg . bind_addr = t emp_state- > state - > host ;
cfg . async_cnt = transport - > async_operations ;
set_qos ( transport , & cfg . qos_params ) ;
res = pjsip_tcp_transport_start3 ( ast_sip_get_pjsip_endpoint ( ) , & cfg , & t ransport - > state - > factory ) ;
res = pjsip_tcp_transport_start3 ( ast_sip_get_pjsip_endpoint ( ) , & cfg , & t emp_state - > state - > factory ) ;
} else if ( transport - > type = = AST_TRANSPORT_TLS ) {
if ( transport - > async_operations > 1 & & ast_compare_versions ( pj_get_version ( ) , " 2.5.0 " ) < 0 ) {
ast_log ( LOG_ERROR , " Transport: %s: When protocol=tls and pjproject version < 2.5.0, async_operations can't be > 1 \n " ,
ast_sorcery_object_get_id ( obj ) ) ;
return - 1 ;
}
if ( ! ast_strlen_zero ( transport - > ca_list_file ) ) {
if ( ! ast_file_is_readable ( transport - > ca_list_file ) ) {
ast_log ( LOG_ERROR , " Transport: %s: ca_list_file %s is either missing or not readable \n " ,
ast_sorcery_object_get_id ( obj ) , transport - > ca_list_file ) ;
return - 1 ;
}
}
transport - > tls . ca_list_file = pj_str ( ( char * ) transport - > ca_list_file ) ;
# ifdef HAVE_PJ_SSL_CERT_LOAD_FROM_FILES2
if ( ! ast_strlen_zero ( transport - > ca_list_path ) ) {
if ( ! ast_file_is_readable ( transport - > ca_list_path ) ) {
ast_log ( LOG_ERROR , " Transport: %s: ca_list_path %s is either missing or not readable \n " ,
ast_sorcery_object_get_id ( obj ) , transport - > ca_list_path ) ;
return - 1 ;
}
}
transport - > tls . ca_list_path = pj_str ( ( char * ) transport - > ca_list_path ) ;
# else
if ( ! ast_strlen_zero ( transport - > ca_list_path ) ) {
ast_log ( LOG_WARNING , " Asterisk has been built against a version of pjproject that does not "
" support the 'ca_list_path' option. Please upgrade to version 2.4 or later. \n " ) ;
goto error ;
}
# endif
if ( ! ast_strlen_zero ( transport - > cert_file ) ) {
if ( ! ast_file_is_readable ( transport - > cert_file ) ) {
ast_log ( LOG_ERROR , " Transport: %s: cert_file %s is either missing or not readable \n " ,
ast_sorcery_object_get_id ( obj ) , transport - > cert_file ) ;
return - 1 ;
}
}
transport - > tls . cert_file = pj_str ( ( char * ) transport - > cert_file ) ;
if ( ! ast_strlen_zero ( transport - > privkey_file ) ) {
if ( ! ast_file_is_readable ( transport - > privkey_file ) ) {
ast_log ( LOG_ERROR , " Transport: %s: privkey_file %s is either missing or not readable \n " ,
ast_sorcery_object_get_id ( obj ) , transport - > privkey_file ) ;
return - 1 ;
}
}
transport - > tls . privkey_file = pj_str ( ( char * ) transport - > privkey_file ) ;
transport - > tls . password = pj_str ( ( char * ) transport - > password ) ;
set_qos ( transport , & transport - > tls . qos_params ) ;
res = pjsip_tls_transport_start2 ( ast_sip_get_pjsip_endpoint ( ) , & transport - > tls , & transport - > host , NULL , transport - > async_operations , & transport - > state - > factory ) ;
temp_state - > state - > tls . password = pj_str ( ( char * ) transport - > password ) ;
set_qos ( transport , & temp_state - > state - > tls . qos_params ) ;
res = pjsip_tls_transport_start2 ( ast_sip_get_pjsip_endpoint ( ) , & temp_state - > state - > tls , & temp_state - > state - > host , NULL , transport - > async_operations , & temp_state - > state - > factory ) ;
} else if ( ( transport - > type = = AST_TRANSPORT_WS ) | | ( transport - > type = = AST_TRANSPORT_WSS ) ) {
if ( transport - > cos | | transport - > tos ) {
ast_log ( LOG_WARNING , " TOS and COS values ignored for websocket transport \n " ) ;
@ -277,8 +475,101 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
pj_strerror ( res , msg , sizeof ( msg ) ) ;
ast_log ( LOG_ERROR , " Transport '%s' could not be started: %s \n " , ast_sorcery_object_get_id ( obj ) , msg ) ;
goto error ;
}
copy_state_to_transport ( transport ) ;
ao2_link ( states , temp_state ) ;
return 0 ;
error :
ao2_unlink ( states , temp_state ) ;
return - 1 ;
}
/*! \brief Custom handler for type just makes sure the state is created */
static int transport_state_init ( const struct aco_option * opt , struct ast_variable * var , void * obj )
{
struct ast_sip_transport * transport = obj ;
struct ast_sip_transport_state * state = find_or_create_temporary_state ( transport ) ;
ao2_cleanup ( state ) ;
return 0 ;
}
/*! \brief Custom handler for TLS method setting */
static int transport_tls_file_handler ( const struct aco_option * opt , struct ast_variable * var , void * obj )
{
struct ast_sip_transport * transport = obj ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_or_create_temporary_state ( transport ) , ao2_cleanup ) ;
if ( ! state ) {
return - 1 ;
}
if ( ! ast_file_is_readable ( var - > value ) ) {
ast_log ( LOG_ERROR , " Transport: %s: %s %s is either missing or not readable \n " ,
ast_sorcery_object_get_id ( obj ) , var - > name , var - > value ) ;
return - 1 ;
}
if ( ! strcasecmp ( var - > name , " ca_list_file " ) ) {
state - > tls . ca_list_file = pj_str ( ( char * ) var - > value ) ;
ast_string_field_set ( transport , ca_list_file , var - > value ) ;
} else if ( ! strcasecmp ( var - > name , " ca_list_path " ) ) {
# ifdef HAVE_PJ_SSL_CERT_LOAD_FROM_FILES2
state - > tls . ca_list_path = pj_str ( ( char * ) var - > value ) ;
ast_string_field_set ( transport , ca_list_path , var - > value ) ;
# else
ast_log ( LOG_WARNING , " Asterisk has been built against a version of pjproject that does not "
" support the 'ca_list_path' option. Please upgrade to version 2.4 or later. \n " ) ;
# endif
} else if ( ! strcasecmp ( var - > name , " cert_file " ) ) {
state - > tls . cert_file = pj_str ( ( char * ) var - > value ) ;
ast_string_field_set ( transport , cert_file , var - > value ) ;
} else if ( ! strcasecmp ( var - > name , " priv_key_file " ) ) {
state - > tls . privkey_file = pj_str ( ( char * ) var - > value ) ;
ast_string_field_set ( transport , privkey_file , var - > value ) ;
}
return 0 ;
}
static int ca_list_file_to_str ( const void * obj , const intptr_t * args , char * * buf )
{
const struct ast_sip_transport * transport = obj ;
* buf = ast_strdup ( transport - > ca_list_file ) ;
return 0 ;
}
static int ca_list_path_to_str ( const void * obj , const intptr_t * args , char * * buf )
{
const struct ast_sip_transport * transport = obj ;
* buf = ast_strdup ( transport - > ca_list_path ) ;
return 0 ;
}
static int cert_file_to_str ( const void * obj , const intptr_t * args , char * * buf )
{
const struct ast_sip_transport * transport = obj ;
* buf = ast_strdup ( transport - > cert_file ) ;
return 0 ;
}
static int privkey_file_to_str ( const void * obj , const intptr_t * args , char * * buf )
{
const struct ast_sip_transport * transport = obj ;
* buf = ast_strdup ( transport - > privkey_file ) ;
return 0 ;
}
@ -328,7 +619,14 @@ static int transport_bind_handler(const struct aco_option *opt, struct ast_varia
{
struct ast_sip_transport * transport = obj ;
pj_str_t buf ;
int rc = pj_sockaddr_parse ( pj_AF_UNSPEC ( ) , 0 , pj_cstr ( & buf , var - > value ) , & transport - > host ) ;
int rc ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_or_create_temporary_state ( transport ) , ao2_cleanup ) ;
if ( ! state ) {
return - 1 ;
}
rc = pj_sockaddr_parse ( pj_AF_UNSPEC ( ) , 0 , pj_cstr ( & buf , var - > value ) , & state - > host ) ;
return rc ! = PJ_SUCCESS ? - 1 : 0 ;
}
@ -336,13 +634,18 @@ static int transport_bind_handler(const struct aco_option *opt, struct ast_varia
static int transport_bind_to_str ( const void * obj , const intptr_t * args , char * * buf )
{
const struct ast_sip_transport * transport = obj ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_state_by_transport ( transport ) , ao2_cleanup ) ;
if ( ! state ) {
return - 1 ;
}
if ( ! ( * buf = ast_calloc ( MAX_OBJECT_FIELD , sizeof ( char ) ) ) ) {
return - 1 ;
}
/* include port as well as brackets if IPv6 */
pj_sockaddr_print ( & tr anspor t- > host , * buf , MAX_OBJECT_FIELD , 1 | 2 ) ;
pj_sockaddr_print ( & s tate - > host , * buf , MAX_OBJECT_FIELD , 1 | 2 ) ;
return 0 ;
}
@ -351,13 +654,18 @@ static int transport_bind_to_str(const void *obj, const intptr_t *args, char **b
static int transport_tls_bool_handler ( const struct aco_option * opt , struct ast_variable * var , void * obj )
{
struct ast_sip_transport * transport = obj ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_or_create_temporary_state ( transport ) , ao2_cleanup ) ;
if ( ! state ) {
return - 1 ;
}
if ( ! strcasecmp ( var - > name , " verify_server " ) ) {
transport - > tls . verify_server = ast_true ( var - > value ) ? PJ_TRUE : PJ_FALSE ;
s tate - > tls . verify_server = ast_true ( var - > value ) ? PJ_TRUE : PJ_FALSE ;
} else if ( ! strcasecmp ( var - > name , " verify_client " ) ) {
transport - > tls . verify_client = ast_true ( var - > value ) ? PJ_TRUE : PJ_FALSE ;
s tate - > tls . verify_client = ast_true ( var - > value ) ? PJ_TRUE : PJ_FALSE ;
} else if ( ! strcasecmp ( var - > name , " require_client_cert " ) ) {
tr anspor t- > tls . require_client_cert = ast_true ( var - > value ) ? PJ_TRUE : PJ_FALSE ;
s tate - > tls . require_client_cert = ast_true ( var - > value ) ? PJ_TRUE : PJ_FALSE ;
} else {
return - 1 ;
}
@ -368,21 +676,42 @@ static int transport_tls_bool_handler(const struct aco_option *opt, struct ast_v
static int verify_server_to_str ( const void * obj , const intptr_t * args , char * * buf )
{
const struct ast_sip_transport * transport = obj ;
* buf = ast_strdup ( AST_YESNO ( transport - > tls . verify_server ) ) ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_state_by_transport ( transport ) , ao2_cleanup ) ;
if ( ! state ) {
return - 1 ;
}
* buf = ast_strdup ( AST_YESNO ( state - > tls . verify_server ) ) ;
return 0 ;
}
static int verify_client_to_str ( const void * obj , const intptr_t * args , char * * buf )
{
const struct ast_sip_transport * transport = obj ;
* buf = ast_strdup ( AST_YESNO ( transport - > tls . verify_client ) ) ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_state_by_transport ( transport ) , ao2_cleanup ) ;
if ( ! state ) {
return - 1 ;
}
* buf = ast_strdup ( AST_YESNO ( state - > tls . verify_client ) ) ;
return 0 ;
}
static int require_client_cert_to_str ( const void * obj , const intptr_t * args , char * * buf )
{
const struct ast_sip_transport * transport = obj ;
* buf = ast_strdup ( AST_YESNO ( transport - > tls . require_client_cert ) ) ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_state_by_transport ( transport ) , ao2_cleanup ) ;
if ( ! state ) {
return - 1 ;
}
* buf = ast_strdup ( AST_YESNO ( state - > tls . require_client_cert ) ) ;
return 0 ;
}
@ -390,19 +719,24 @@ static int require_client_cert_to_str(const void *obj, const intptr_t *args, cha
static int transport_tls_method_handler ( const struct aco_option * opt , struct ast_variable * var , void * obj )
{
struct ast_sip_transport * transport = obj ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_or_create_temporary_state ( transport ) , ao2_cleanup ) ;
if ( ! state ) {
return - 1 ;
}
if ( ast_strlen_zero ( var - > value ) | | ! strcasecmp ( var - > value , " default " ) ) {
transport - > tls . method = PJSIP_SSL_DEFAULT_METHOD ;
s tate - > tls . method = PJSIP_SSL_DEFAULT_METHOD ;
} else if ( ! strcasecmp ( var - > value , " unspecified " ) ) {
transport - > tls . method = PJSIP_SSL_UNSPECIFIED_METHOD ;
s tate - > tls . method = PJSIP_SSL_UNSPECIFIED_METHOD ;
} else if ( ! strcasecmp ( var - > value , " tlsv1 " ) ) {
tr anspor t- > tls . method = PJSIP_TLSV1_METHOD ;
s tate - > tls . method = PJSIP_TLSV1_METHOD ;
} else if ( ! strcasecmp ( var - > value , " sslv2 " ) ) {
tr anspor t- > tls . method = PJSIP_SSLV2_METHOD ;
s tate - > tls . method = PJSIP_SSLV2_METHOD ;
} else if ( ! strcasecmp ( var - > value , " sslv3 " ) ) {
tr anspor t- > tls . method = PJSIP_SSLV3_METHOD ;
s tate - > tls . method = PJSIP_SSLV3_METHOD ;
} else if ( ! strcasecmp ( var - > value , " sslv23 " ) ) {
tr anspor t- > tls . method = PJSIP_SSLV23_METHOD ;
s tate - > tls . method = PJSIP_SSLV23_METHOD ;
} else {
return - 1 ;
}
@ -421,9 +755,16 @@ static const char *tls_method_map[] = {
static int tls_method_to_str ( const void * obj , const intptr_t * args , char * * buf )
{
const struct ast_sip_transport * transport = obj ;
if ( ARRAY_IN_BOUNDS ( transport - > tls . method , tls_method_map ) ) {
* buf = ast_strdup ( tls_method_map [ transport - > tls . method ] ) ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_state_by_transport ( transport ) , ao2_cleanup ) ;
if ( ! state ) {
return - 1 ;
}
if ( ARRAY_IN_BOUNDS ( state - > tls . method , tls_method_map ) ) {
* buf = ast_strdup ( tls_method_map [ state - > tls . method ] ) ;
}
return 0 ;
}
@ -463,7 +804,7 @@ static pj_ssl_cipher cipher_name_to_id(const char *name)
* \ retval 0 on success .
* \ retval - 1 on error .
*/
static int transport_cipher_add ( struct ast_sip_transport * tr anspor t, const char * name )
static int transport_cipher_add ( struct ast_sip_transport _state * s tate , const char * name )
{
pj_ssl_cipher cipher ;
int idx ;
@ -480,13 +821,13 @@ static int transport_cipher_add(struct ast_sip_transport *transport, const char
}
if ( pj_ssl_cipher_is_supported ( cipher ) ) {
for ( idx = tr anspor t- > tls . ciphers_num ; idx - - ; ) {
if ( tr anspor t- > ciphers [ idx ] = = cipher ) {
for ( idx = s tate - > tls . ciphers_num ; idx - - ; ) {
if ( s tate - > ciphers [ idx ] = = cipher ) {
/* The cipher is already in the list. */
return 0 ;
}
}
tr anspor t- > ciphers [ tr anspor t- > tls . ciphers_num + + ] = cipher ;
s tate - > ciphers [ s tate - > tls . ciphers_num + + ] = cipher ;
return 0 ;
} else {
ast_log ( LOG_ERROR , " Cipher '%s' is unsupported \n " , name ) ;
@ -501,6 +842,11 @@ static int transport_tls_cipher_handler(const struct aco_option *opt, struct ast
char * parse ;
char * name ;
int res = 0 ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_or_create_temporary_state ( transport ) , ao2_cleanup ) ;
if ( ! state ) {
return - 1 ;
}
parse = ast_strdupa ( S_OR ( var - > value , " " ) ) ;
while ( ( name = strsep ( & parse , " , " ) ) ) {
@ -508,12 +854,12 @@ static int transport_tls_cipher_handler(const struct aco_option *opt, struct ast
if ( ast_strlen_zero ( name ) ) {
continue ;
}
if ( ARRAY_LEN ( tr anspor t- > ciphers ) < = tr anspor t- > tls . ciphers_num ) {
if ( ARRAY_LEN ( s tate - > ciphers ) < = s tate - > tls . ciphers_num ) {
ast_log ( LOG_ERROR , " Too many ciphers specified \n " ) ;
res = - 1 ;
break ;
}
res | = transport_cipher_add ( tr anspor t, name ) ;
res | = transport_cipher_add ( s tate , name ) ;
}
return res ? - 1 : 0 ;
}
@ -543,8 +889,13 @@ static void cipher_to_str(char **buf, const pj_ssl_cipher *ciphers, unsigned int
static int transport_tls_cipher_to_str ( const void * obj , const intptr_t * args , char * * buf )
{
const struct ast_sip_transport * transport = obj ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_state_by_transport ( transport ) , ao2_cleanup ) ;
if ( ! state ) {
return - 1 ;
}
cipher_to_str ( buf , transport - > ciphers , transport - > tls . ciphers_num ) ;
cipher_to_str ( buf , s tate - > ciphers , s tate - > tls . ciphers_num ) ;
return * buf ? 0 : - 1 ;
}
@ -584,14 +935,19 @@ static int transport_localnet_handler(const struct aco_option *opt, struct ast_v
{
struct ast_sip_transport * transport = obj ;
int error = 0 ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_or_create_temporary_state ( transport ) , ao2_cleanup ) ;
if ( ! state ) {
return - 1 ;
}
if ( ast_strlen_zero ( var - > value ) ) {
ast_free_ha ( transport - > localnet ) ;
transport - > localnet = NULL ;
ast_free_ha ( s tate - > localnet ) ;
s tate - > localnet = NULL ;
return 0 ;
}
if ( ! ( tr anspor t- > localnet = ast_append_ha ( " d " , var - > value , tr anspor t- > localnet , & error ) ) ) {
if ( ! ( s tate - > localnet = ast_append_ha ( " d " , var - > value , s tate - > localnet , & error ) ) ) {
return - 1 ;
}
@ -601,12 +957,16 @@ static int transport_localnet_handler(const struct aco_option *opt, struct ast_v
static int localnet_to_vl ( const void * obj , struct ast_variable * * fields )
{
const struct ast_sip_transport * transport = obj ;
char str [ MAX_OBJECT_FIELD ] ;
struct ast_variable * head = NULL ;
struct ast_ha * ha = transport - > localnet ;
struct ast_ha * ha ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_state_by_transport ( transport ) , ao2_cleanup ) ;
for ( ; ha ; ha = ha - > next ) {
if ( ! state ) {
return - 1 ;
}
for ( ha = state - > localnet ; ha ; ha = ha - > next ) {
const char * addr = ast_strdupa ( ast_sockaddr_stringify_addr ( & ha - > addr ) ) ;
snprintf ( str , MAX_OBJECT_FIELD , " %s%s/%s " , ha - > sense = = AST_SENSE_ALLOW ? " ! " : " " ,
addr , ast_sockaddr_stringify_addr ( & ha - > netmask ) ) ;
@ -625,8 +985,13 @@ static int localnet_to_str(const void *obj, const intptr_t *args, char **buf)
{
RAII_VAR ( struct ast_str * , str , ast_str_create ( MAX_OBJECT_FIELD ) , ast_free ) ;
const struct ast_sip_transport * transport = obj ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_state_by_transport ( transport ) , ao2_cleanup ) ;
ast_ha_join ( transport - > localnet , & str ) ;
if ( ! state ) {
return - 1 ;
}
ast_ha_join ( state - > localnet , & str ) ;
* buf = ast_strdup ( ast_str_buffer ( str ) ) ;
return 0 ;
}
@ -730,10 +1095,15 @@ static int cli_print_body(void *obj, void *arg, int flags)
struct ast_sip_transport * transport = obj ;
struct ast_sip_cli_context * context = arg ;
char hoststr [ PJ_INET6_ADDRSTRLEN ] ;
RAII_VAR ( struct ast_sip_transport_state * , state , find_state_by_transport ( transport ) , ao2_cleanup ) ;
if ( ! state ) {
return - 1 ;
}
ast_assert ( context - > output_buffer ! = NULL ) ;
pj_sockaddr_print ( & transport - > host , hoststr , sizeof ( hoststr ) , 3 ) ;
pj_sockaddr_print ( & s tate - > host , hoststr , sizeof ( hoststr ) , 3 ) ;
ast_str_append ( & context - > output_buffer , 0 , " %*s: %-21s %6s %5u %5u %s \n " ,
CLI_INDENT_TO_SPACES ( context - > indent_level ) , " Transport " ,
@ -770,25 +1140,61 @@ static struct ast_cli_entry cli_commands[] = {
static struct ast_sip_cli_formatter_entry * cli_formatter ;
struct ast_sip_transport_state * ast_sip_get_transport_state ( const char * transport_id )
{
struct internal_state * state = NULL ;
if ( ! transport_states ) {
return NULL ;
}
state = ao2_find ( transport_states , transport_id , OBJ_SEARCH_KEY ) ;
if ( ! state | | ! state - > state ) {
ao2_cleanup ( state ) ;
return NULL ;
}
ao2_ref ( state - > state , + 1 ) ;
ao2_ref ( state , - 1 ) ;
return state - > state ;
}
struct ao2_container * ast_sip_get_transport_states ( void )
{
return ao2_container_clone ( transport_states , 0 ) ;
}
/*! \brief Initialize sorcery with transport support */
int ast_sip_initialize_sorcery_transport ( void )
{
struct ast_sorcery * sorcery = ast_sip_get_sorcery ( ) ;
struct ao2_container * transports = NULL ;
/* Create outbound registration states container. */
transport_states = ao2_container_alloc ( DEFAULT_STATE_BUCKETS , internal_state_hash , internal_state_cmp ) ;
if ( ! transport_states ) {
ast_log ( LOG_ERROR , " Unable to allocate transport states container \n " ) ;
return AST_MODULE_LOAD_FAILURE ;
}
ast_sorcery_apply_default ( sorcery , " transport " , " config " , " pjsip.conf,criteria=type=transport " ) ;
if ( ast_sorcery_object_register_no_reload ( sorcery , " transport " , transport_alloc , NULL , transport_apply ) ) {
if ( ast_sorcery_object_register ( sorcery , " transport " , sip_ transport_alloc, NULL , transport_apply ) ) {
return - 1 ;
}
ast_sorcery_object_field_register ( sorcery , " transport " , " type " , " " , OPT_NOOP_T , 0 , 0 ) ;
/* Normally type is a OPT_NOOP_T but we're using it to make sure that state is created */
ast_sorcery_object_field_register_custom ( sorcery , " transport " , " type " , " " , transport_state_init , NULL , NULL , 0 , 0 ) ;
ast_sorcery_object_field_register_custom ( sorcery , " transport " , " protocol " , " udp " , transport_protocol_handler , transport_protocol_to_str , NULL , 0 , 0 ) ;
ast_sorcery_object_field_register_custom ( sorcery , " transport " , " bind " , " " , transport_bind_handler , transport_bind_to_str , NULL , 0 , 0 ) ;
ast_sorcery_object_field_register ( sorcery , " transport " , " async_operations " , " 1 " , OPT_UINT_T , 0 , FLDSET ( struct ast_sip_transport , async_operations ) ) ;
ast_sorcery_object_field_register ( sorcery , " transport " , " ca_list_file " , " " , OPT_STRINGFIELD_T , 0 , STRFLDSET ( struct ast_sip_transport , ca_list_file ) ) ;
ast_sorcery_object_field_register ( sorcery , " transport " , " ca_list_path " , " " , OPT_STRINGFIELD_T , 0 , STRFLDSET ( struct ast_sip_transport , ca_list_path ) ) ;
ast_sorcery_object_field_register ( sorcery , " transport " , " cert_file " , " " , OPT_STRINGFIELD_T , 0 , STRFLDSET ( struct ast_sip_transport , cert_file ) ) ;
ast_sorcery_object_field_register ( sorcery , " transport " , " priv_key_file " , " " , OPT_STRINGFIELD_T , 0 , STRFLDSET ( struct ast_sip_transport , privkey_file ) ) ;
ast_sorcery_object_field_register_custom ( sorcery , " transport " , " ca_list_file " , " " , transport_tls_file_handler , ca_list_file_to_str , NULL , 0 , 0 ) ;
ast_sorcery_object_field_register_custom ( sorcery , " transport " , " ca_list_path " , " " , transport_tls_file_handler , ca_list_path_to_str , NULL , 0 , 0 ) ;
ast_sorcery_object_field_register_custom ( sorcery , " transport " , " cert_file " , " " , transport_tls_file_handler , cert_file_to_str , NULL , 0 , 0 ) ;
ast_sorcery_object_field_register_custom ( sorcery , " transport " , " priv_key_file " , " " , transport_tls_file_handler , privkey_file_to_str , NULL , 0 , 0 ) ;
ast_sorcery_object_field_register ( sorcery , " transport " , " password " , " " , OPT_STRINGFIELD_T , 0 , STRFLDSET ( struct ast_sip_transport , password ) ) ;
ast_sorcery_object_field_register ( sorcery , " transport " , " external_signaling_address " , " " , OPT_STRINGFIELD_T , 0 , STRFLDSET ( struct ast_sip_transport , external_signaling_address ) ) ;
ast_sorcery_object_field_register ( sorcery , " transport " , " external_signaling_port " , " 0 " , OPT_UINT_T , PARSE_IN_RANGE , FLDSET ( struct ast_sip_transport , external_signaling_port ) , 0 , 65535 ) ;
@ -822,6 +1228,10 @@ int ast_sip_initialize_sorcery_transport(void)
ast_sip_register_cli_formatter ( cli_formatter ) ;
ast_cli_register_multiple ( cli_commands , ARRAY_LEN ( cli_commands ) ) ;
/* trigger load of transports from realtime by trying to revrieve them all */
transports = ast_sorcery_retrieve_by_fields ( sorcery , " transport " , AST_RETRIEVE_FLAG_ALL | AST_RETRIEVE_FLAG_MULTIPLE , NULL ) ;
ao2_cleanup ( transports ) ;
return 0 ;
}
@ -832,5 +1242,8 @@ int ast_sip_destroy_sorcery_transport(void)
internal_sip_unregister_endpoint_formatter ( & endpoint_transport_formatter ) ;
ao2_ref ( transport_states , - 1 ) ;
transport_states = NULL ;
return 0 ;
}