@ -87,6 +87,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
# include "asterisk/netsock.h"
# include "asterisk/netsock.h"
# include "asterisk/stringfields.h"
# include "asterisk/stringfields.h"
# include "asterisk/linkedlists.h"
# include "asterisk/linkedlists.h"
# include "asterisk/dlinkedlists.h"
# include "asterisk/event.h"
# include "asterisk/event.h"
# include "asterisk/astobj2.h"
# include "asterisk/astobj2.h"
@ -639,6 +640,8 @@ struct chan_iax2_pvt {
int frames_dropped ;
int frames_dropped ;
/*! received frame count: (just for stats) */
/*! received frame count: (just for stats) */
int frames_received ;
int frames_received ;
AST_DLLIST_ENTRY ( chan_iax2_pvt ) entry ;
} ;
} ;
/*!
/*!
@ -838,6 +841,17 @@ static void __attribute__((format (printf, 1, 2))) jb_debug_output(const char *f
*/
*/
static struct chan_iax2_pvt * iaxs [ IAX_MAX_CALLS ] ;
static struct chan_iax2_pvt * iaxs [ IAX_MAX_CALLS ] ;
/*!
* \ brief Another container of iax2_pvt structures
*
* Active IAX2 pvt structs are also stored in this container , if they are a part
* of an active call where we know the remote side ' s call number . The reason
* for this is that incoming media frames do not contain our call number . So ,
* instead of having to iterate the entire iaxs array , we use this container to
* look up calls where the remote side is using a given call number .
*/
static struct ao2_container * iax_peercallno_pvts ;
/*!
/*!
* \ brief chan_iax2_pvt structure locks
* \ brief chan_iax2_pvt structure locks
*
*
@ -846,6 +860,7 @@ static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS];
* local call number for the associated pvt struct .
* local call number for the associated pvt struct .
*/
*/
static ast_mutex_t iaxsl [ ARRAY_LEN ( iaxs ) ] ;
static ast_mutex_t iaxsl [ ARRAY_LEN ( iaxs ) ] ;
/*!
/*!
* \ brief The last time a call number was used
* \ brief The last time a call number was used
*
*
@ -1329,16 +1344,88 @@ static int iax2_getpeername(struct sockaddr_in sin, char *host, int len)
return res ;
return res ;
}
}
static void iax2_destroy_helper ( struct chan_iax2_pvt * pvt )
{
/* Decrement AUTHREQ count if needed */
if ( ast_test_flag ( pvt , IAX_MAXAUTHREQ ) ) {
struct iax2_user * user ;
struct iax2_user tmp_user = {
. name = pvt - > username ,
} ;
user = ao2_find ( users , & tmp_user , OBJ_POINTER ) ;
if ( user ) {
ast_atomic_fetchadd_int ( & user - > curauthreq , - 1 ) ;
user_unref ( user ) ;
}
ast_clear_flag ( pvt , IAX_MAXAUTHREQ ) ;
}
/* No more pings or lagrq's */
AST_SCHED_DEL ( sched , pvt - > pingid ) ;
AST_SCHED_DEL ( sched , pvt - > lagid ) ;
AST_SCHED_DEL ( sched , pvt - > autoid ) ;
AST_SCHED_DEL ( sched , pvt - > authid ) ;
AST_SCHED_DEL ( sched , pvt - > initid ) ;
AST_SCHED_DEL ( sched , pvt - > jbid ) ;
}
static void iax2_frame_free ( struct iax_frame * fr )
{
AST_SCHED_DEL ( sched , fr - > retrans ) ;
iax_frame_free ( fr ) ;
}
static void pvt_destructor ( void * obj )
{
struct chan_iax2_pvt * pvt = obj ;
struct iax_frame * cur = NULL ;
iax2_destroy_helper ( pvt ) ;
/* Already gone */
ast_set_flag ( pvt , IAX_ALREADYGONE ) ;
AST_LIST_LOCK ( & frame_queue ) ;
AST_LIST_TRAVERSE ( & frame_queue , cur , list ) {
/* Cancel any pending transmissions */
if ( cur - > callno = = pvt - > callno ) {
cur - > retries = - 1 ;
}
}
AST_LIST_UNLOCK ( & frame_queue ) ;
if ( pvt - > reg ) {
pvt - > reg - > callno = 0 ;
}
if ( ! pvt - > owner ) {
jb_frame frame ;
if ( pvt - > vars ) {
ast_variables_destroy ( pvt - > vars ) ;
pvt - > vars = NULL ;
}
while ( jb_getall ( pvt - > jb , & frame ) = = JB_OK ) {
iax2_frame_free ( frame . data ) ;
}
jb_destroy ( pvt - > jb ) ;
ast_string_field_free_memory ( pvt ) ;
}
}
static struct chan_iax2_pvt * new_iax ( struct sockaddr_in * sin , const char * host )
static struct chan_iax2_pvt * new_iax ( struct sockaddr_in * sin , const char * host )
{
{
struct chan_iax2_pvt * tmp ;
struct chan_iax2_pvt * tmp ;
jb_conf jbconf ;
jb_conf jbconf ;
if ( ! ( tmp = ast_calloc ( 1 , sizeof ( * tmp ) ) ) )
if ( ! ( tmp = a o2_alloc( sizeof ( * tmp ) , pvt_destructor )) ) {
return NULL ;
return NULL ;
}
if ( ast_string_field_init ( tmp , 32 ) ) {
if ( ast_string_field_init ( tmp , 32 ) ) {
ast_free ( tmp ) ;
a o2_ref( tmp , - 1 ) ;
tmp = NULL ;
tmp = NULL ;
return NULL ;
return NULL ;
}
}
@ -1481,6 +1568,26 @@ static int make_trunk(unsigned short callno, int locked)
return res ;
return res ;
}
}
static void store_by_peercallno ( struct chan_iax2_pvt * pvt )
{
if ( ! pvt - > peercallno ) {
ast_log ( LOG_ERROR , " This should not be called without a peer call number. \n " ) ;
return ;
}
ao2_link ( iax_peercallno_pvts , pvt ) ;
}
static void remove_by_peercallno ( struct chan_iax2_pvt * pvt )
{
if ( ! pvt - > peercallno ) {
ast_log ( LOG_ERROR , " This should not be called without a peer call number. \n " ) ;
return ;
}
ao2_unlink ( iax_peercallno_pvts , pvt ) ;
}
/*!
/*!
* \ todo XXX Note that this function contains a very expensive operation that
* \ todo XXX Note that this function contains a very expensive operation that
* happens for * every * incoming media frame . It iterates through every
* happens for * every * incoming media frame . It iterates through every
@ -1505,6 +1612,28 @@ static int __find_callno(unsigned short callno, unsigned short dcallno, struct s
char host [ 80 ] ;
char host [ 80 ] ;
if ( new < = NEW_ALLOW ) {
if ( new < = NEW_ALLOW ) {
if ( callno ) {
struct chan_iax2_pvt * pvt ;
struct chan_iax2_pvt tmp_pvt = {
. callno = dcallno ,
. peercallno = callno ,
/* hack!! */
. frames_received = full_frame ,
} ;
memcpy ( & tmp_pvt . addr , sin , sizeof ( tmp_pvt . addr ) ) ;
if ( ( pvt = ao2_find ( iax_peercallno_pvts , & tmp_pvt , OBJ_POINTER ) ) ) {
if ( return_locked ) {
ast_mutex_lock ( & iaxsl [ pvt - > callno ] ) ;
}
res = pvt - > callno ;
ao2_ref ( pvt , - 1 ) ;
pvt = NULL ;
return res ;
}
}
for ( x = 1 ; ! res & & x < maxnontrunkcall ; x + + ) {
for ( x = 1 ; ! res & & x < maxnontrunkcall ; x + + ) {
ast_mutex_lock ( & iaxsl [ x ] ) ;
ast_mutex_lock ( & iaxsl [ x ] ) ;
if ( iaxs [ x ] ) {
if ( iaxs [ x ] ) {
@ -1587,6 +1716,10 @@ static int __find_callno(unsigned short callno, unsigned short dcallno, struct s
ast_string_field_set ( iaxs [ x ] , mohinterpret , mohinterpret ) ;
ast_string_field_set ( iaxs [ x ] , mohinterpret , mohinterpret ) ;
ast_string_field_set ( iaxs [ x ] , mohsuggest , mohsuggest ) ;
ast_string_field_set ( iaxs [ x ] , mohsuggest , mohsuggest ) ;
ast_string_field_set ( iaxs [ x ] , parkinglot , default_parkinglot ) ;
ast_string_field_set ( iaxs [ x ] , parkinglot , default_parkinglot ) ;
if ( iaxs [ x ] - > peercallno ) {
store_by_peercallno ( iaxs [ x ] ) ;
}
} else {
} else {
ast_log ( LOG_WARNING , " Out of resources \n " ) ;
ast_log ( LOG_WARNING , " Out of resources \n " ) ;
ast_mutex_unlock ( & iaxsl [ x ] ) ;
ast_mutex_unlock ( & iaxsl [ x ] ) ;
@ -1609,12 +1742,6 @@ static int find_callno_locked(unsigned short callno, unsigned short dcallno, str
return __find_callno ( callno , dcallno , sin , new , sockfd , 1 , full_frame ) ;
return __find_callno ( callno , dcallno , sin , new , sockfd , 1 , full_frame ) ;
}
}
static void iax2_frame_free ( struct iax_frame * fr )
{
AST_SCHED_DEL ( sched , fr - > retrans ) ;
iax_frame_free ( fr ) ;
}
/*!
/*!
* \ brief Queue a frame to a call ' s owning asterisk channel
* \ brief Queue a frame to a call ' s owning asterisk channel
*
*
@ -2064,32 +2191,6 @@ static int send_packet(struct iax_frame *f)
return res ;
return res ;
}
}
static void iax2_destroy_helper ( struct chan_iax2_pvt * pvt )
{
/* Decrement AUTHREQ count if needed */
if ( ast_test_flag ( pvt , IAX_MAXAUTHREQ ) ) {
struct iax2_user * user ;
struct iax2_user tmp_user = {
. name = pvt - > username ,
} ;
user = ao2_find ( users , & tmp_user , OBJ_POINTER ) ;
if ( user ) {
ast_atomic_fetchadd_int ( & user - > curauthreq , - 1 ) ;
user_unref ( user ) ;
}
ast_clear_flag ( pvt , IAX_MAXAUTHREQ ) ;
}
/* No more pings or lagrq's */
AST_SCHED_DEL ( sched , pvt - > pingid ) ;
AST_SCHED_DEL ( sched , pvt - > lagid ) ;
AST_SCHED_DEL ( sched , pvt - > autoid ) ;
AST_SCHED_DEL ( sched , pvt - > authid ) ;
AST_SCHED_DEL ( sched , pvt - > initid ) ;
AST_SCHED_DEL ( sched , pvt - > jbid ) ;
}
/*!
/*!
* \ note Since this function calls iax2_queue_hangup ( ) , the pvt struct
* \ note Since this function calls iax2_queue_hangup ( ) , the pvt struct
* for the given call number may disappear during its execution .
* for the given call number may disappear during its execution .
@ -2120,7 +2221,6 @@ static int iax2_predestroy(int callno)
static void iax2_destroy ( int callno )
static void iax2_destroy ( int callno )
{
{
struct chan_iax2_pvt * pvt = NULL ;
struct chan_iax2_pvt * pvt = NULL ;
struct iax_frame * cur = NULL ;
struct ast_channel * owner = NULL ;
struct ast_channel * owner = NULL ;
retry :
retry :
@ -2138,53 +2238,38 @@ retry:
goto retry ;
goto retry ;
}
}
}
}
if ( ! owner )
if ( ! owner ) {
iaxs [ callno ] = NULL ;
iaxs [ callno ] = NULL ;
}
if ( pvt ) {
if ( pvt ) {
if ( ! owner )
if ( ! owner ) {
pvt - > owner = NULL ;
pvt - > owner = NULL ;
iax2_destroy_helper ( pvt ) ;
} else {
/* Already gone */
ast_set_flag ( pvt , IAX_ALREADYGONE ) ;
if ( owner ) {
/* If there's an owner, prod it to give up */
/* If there's an owner, prod it to give up */
/* It is ok to use ast_queue_hangup() here instead of iax2_queue_hangup()
/* It is ok to use ast_queue_hangup() here instead of iax2_queue_hangup()
* because we already hold the owner channel lock . */
* because we already hold the owner channel lock . */
ast_queue_hangup ( owner , - 1 ) ;
ast_queue_hangup ( owner , - 1 ) ;
}
}
AST_LIST_LOCK ( & frame_queue ) ;
if ( pvt - > peercallno ) {
AST_LIST_TRAVERSE ( & frame_queue , cur , list ) {
remove_by_peercallno ( pvt ) ;
/* Cancel any pending transmissions */
if ( cur - > callno = = pvt - > callno )
cur - > retries = - 1 ;
}
}
AST_LIST_UNLOCK ( & frame_queue ) ;
if ( pvt - > reg )
pvt - > reg - > callno = 0 ;
if ( ! owner ) {
if ( ! owner ) {
jb_frame frame ;
ao2_ref ( pvt , - 1 ) ;
if ( pvt - > vars ) {
pvt = NULL ;
ast_variables_destroy ( pvt - > vars ) ;
pvt - > vars = NULL ;
}
while ( jb_getall ( pvt - > jb , & frame ) = = JB_OK )
iax2_frame_free ( frame . data ) ;
jb_destroy ( pvt - > jb ) ;
/* gotta free up the stringfields */
ast_string_field_free_memory ( pvt ) ;
ast_free ( pvt ) ;
}
}
}
}
if ( owner ) {
if ( owner ) {
ast_channel_unlock ( owner ) ;
ast_channel_unlock ( owner ) ;
}
}
if ( callno & 0x4000 )
if ( callno & 0x4000 ) {
update_max_trunk ( ) ;
update_max_trunk ( ) ;
}
}
}
static int update_packet ( struct iax_frame * f )
static int update_packet ( struct iax_frame * f )
@ -6386,7 +6471,13 @@ static int complete_transfer(int callno, struct iax_ies *ies)
pvt - > rseqno = 0 ;
pvt - > rseqno = 0 ;
pvt - > iseqno = 0 ;
pvt - > iseqno = 0 ;
pvt - > aseqno = 0 ;
pvt - > aseqno = 0 ;
if ( pvt - > peercallno ) {
remove_by_peercallno ( pvt ) ;
}
pvt - > peercallno = peercallno ;
pvt - > peercallno = peercallno ;
store_by_peercallno ( pvt ) ;
pvt - > transferring = TRANSFER_NONE ;
pvt - > transferring = TRANSFER_NONE ;
pvt - > svoiceformat = - 1 ;
pvt - > svoiceformat = - 1 ;
pvt - > voiceformat = 0 ;
pvt - > voiceformat = 0 ;
@ -7904,7 +7995,16 @@ static int socket_process(struct iax2_thread *thread)
if ( ! inaddrcmp ( & sin , & iaxs [ fr - > callno ] - > addr ) & & ! minivid & &
if ( ! inaddrcmp ( & sin , & iaxs [ fr - > callno ] - > addr ) & & ! minivid & &
f . subclass ! = IAX_COMMAND_TXCNT & & /* for attended transfer */
f . subclass ! = IAX_COMMAND_TXCNT & & /* for attended transfer */
f . subclass ! = IAX_COMMAND_TXACC ) { /* for attended transfer */
f . subclass ! = IAX_COMMAND_TXACC ) { /* for attended transfer */
iaxs [ fr - > callno ] - > peercallno = ( unsigned short ) ( ntohs ( mh - > callno ) & ~ IAX_FLAG_FULL ) ;
unsigned short new_peercallno ;
new_peercallno = ( unsigned short ) ( ntohs ( mh - > callno ) & ~ IAX_FLAG_FULL ) ;
if ( new_peercallno & & new_peercallno ! = iaxs [ fr - > callno ] - > peercallno ) {
if ( iaxs [ fr - > callno ] - > peercallno ) {
remove_by_peercallno ( iaxs [ fr - > callno ] ) ;
}
iaxs [ fr - > callno ] - > peercallno = new_peercallno ;
store_by_peercallno ( iaxs [ fr - > callno ] ) ;
}
}
}
if ( ntohs ( mh - > callno ) & IAX_FLAG_FULL ) {
if ( ntohs ( mh - > callno ) & IAX_FLAG_FULL ) {
if ( iaxdebug )
if ( iaxdebug )
@ -11782,6 +11882,7 @@ static int __unload_module(void)
ao2_ref ( peers , - 1 ) ;
ao2_ref ( peers , - 1 ) ;
ao2_ref ( users , - 1 ) ;
ao2_ref ( users , - 1 ) ;
ao2_ref ( iax_peercallno_pvts , - 1 ) ;
con = ast_context_find ( regcontext ) ;
con = ast_context_find ( regcontext ) ;
if ( con )
if ( con )
@ -11807,6 +11908,24 @@ static int peer_set_sock_cb(void *obj, void *arg, int flags)
return 0 ;
return 0 ;
}
}
static int pvt_hash_cb ( const void * obj , const int flags )
{
const struct chan_iax2_pvt * pvt = obj ;
return pvt - > peercallno ;
}
static int pvt_cmp_cb ( void * obj , void * arg , int flags )
{
struct chan_iax2_pvt * pvt = obj , * pvt2 = arg ;
/* The frames_received field is used to hold whether we're matching
* against a full frame or not . . . */
return match ( & pvt2 - > addr , pvt2 - > peercallno , pvt2 - > callno , pvt ,
pvt2 - > frames_received ) ? CMP_MATCH : 0 ;
}
/*! \brief Load IAX2 module, load configuraiton ---*/
/*! \brief Load IAX2 module, load configuraiton ---*/
static int load_module ( void )
static int load_module ( void )
{
{
@ -11822,6 +11941,12 @@ static int load_module(void)
ao2_ref ( peers , - 1 ) ;
ao2_ref ( peers , - 1 ) ;
return AST_MODULE_LOAD_FAILURE ;
return AST_MODULE_LOAD_FAILURE ;
}
}
iax_peercallno_pvts = ao2_container_alloc ( IAX_MAX_CALLS , pvt_hash_cb , pvt_cmp_cb ) ;
if ( ! iax_peercallno_pvts ) {
ao2_ref ( peers , - 1 ) ;
ao2_ref ( users , - 1 ) ;
return AST_MODULE_LOAD_FAILURE ;
}
ast_custom_function_register ( & iaxpeer_function ) ;
ast_custom_function_register ( & iaxpeer_function ) ;
ast_custom_function_register ( & iaxvar_function ) ;
ast_custom_function_register ( & iaxvar_function ) ;