2020-09-27 12:17:11 +00:00
/* call handling
*
* ( C ) 2020 by Andreas Eversberg < jolly @ eversberg . eu >
* All Rights Reserved
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
/* Call forking process
*
* A call can be forwarded to one or multiple endpoints ( call forking ) .
*
* In case of call forking , each terminating endpoint that disconnects or
* releases , is removed from the list of terminating endpoints . The cause is
* collected according to mulitpoint process at Q .931 . If all endpoints have
* disconnected or released , the originating endpoint is released with the
* collected cause .
*
* If one terminating endpoint answers the call , all other endpoints are
* released with the cause " non-selected user clearing " .
*
* If the originating endpoint disconnectes or releases prior answer , all
* terminating endpoints are released with the same cause .
*/
/* SDP negotiation process
*
* The originating endpoint sends a CC - SETUP - REQ with SDP included .
*
* If no RTP proxy is enabled , the SDP is forwarded towards terminating
* endpoint or multiple terminating endpoints ( in case of call forking ) . The
* first reply with SDP from the terminating endpoint is stored . In case of
* single terminating endpoint , it is forwarded towards the originating
* endpoint with progress indicator set to 1 or 8. In case of multiple
* terminating endpoints ( call forking ) the SDP is forwarded as soon a
* CC - SETUP - RSP is received from the first terminating endpoint that answers .
* The SDP negotiation is complete .
*
* If RTP proxy is enabled , the SDP is negotiated with the supported codecs of
* this router . The first reply to the CC - SETUP - REQ message will include the
* SDP reply as well as progress indicator set to 8 ( if not a CC - SETUP - CNF
2021-01-01 21:14:42 +00:00
* message ) towards originating endpoint . The SDP negotiation on the
2020-09-27 12:17:11 +00:00
* originating side is complete . If the call gets forwarded to a single or
2021-01-01 21:14:42 +00:00
* multiple terminating endpoints , an SDP is generated with the supported
* codecs of this router . In case of single terminating endpoint , the SDP of the
2020-09-27 12:17:11 +00:00
* first reply to the CC - SETUP - IND message is used to negotiate the codec . In
* case of multiple terminating endpoints ( call forking ) the SDP reply is
* stored and processed when a CC - SETUP - RSP is received from the first
* terminating endpoint that answers . The SDP negotiation on the terminating
* side is complete .
*/
# include <stdio.h>
# include <unistd.h>
# include <errno.h>
# include <string.h>
# include <stdint.h>
# include <stdlib.h>
# include <math.h>
# include <sys/types.h>
# include <sys/wait.h>
# include "../libdebug/debug.h"
# include "../libg711/g711.h"
# include "call.h"
# include "audio.h"
# include "display.h"
call_t * call_list = NULL ;
struct timer status_timer ;
static osmo_cc_endpoint_t * cc_ep ;
static const char * routing_script , * routing_shell ;
2021-02-27 11:07:32 +00:00
static struct osmo_cc_helper_audio_codecs codecs_def [ ] = {
2020-09-27 12:17:11 +00:00
{ " PCMA " , 8000 , 1 , g711_encode_alaw , g711_decode_alaw } ,
{ " PCMU " , 8000 , 1 , g711_encode_ulaw , g711_decode_ulaw } ,
2021-02-27 11:07:32 +00:00
{ " telephone-event " , 8000 , 1 , encode_te , decode_te } ,
{ " L16 " , 8000 , 1 , encode_l16 , decode_l16 } ,
2020-09-27 12:17:11 +00:00
{ NULL , 0 , 0 , NULL , NULL } ,
} ;
static void refresh_status ( void )
{
osmo_cc_call_t * cc_call ;
int from_count , to_count ;
call_t * call ;
call_relation_t * relation ;
display_status_start ( ) ;
for ( cc_call = cc_ep - > call_list ; cc_call ; cc_call = cc_call - > next ) {
if ( cc_call - > state ! = OSMO_CC_STATE_ATTACH_IN )
continue ;
if ( ! cc_call - > attached_name )
continue ;
from_count = 0 ;
for ( call = call_list ; call ; call = call - > next ) {
if ( ! call - > relation_list )
continue ;
if ( ! ! strcmp ( cc_call - > attached_name , call - > relation_list - > interface ) )
continue ;
to_count = 0 ;
for ( relation = call - > relation_list - > next ; relation ; relation = relation - > next ) {
display_status_line ( cc_call - > attached_name , from_count , call - > relation_list - > id , to_count , relation - > interface , relation - > id , relation - > state ) ;
to_count + + ;
}
if ( ! to_count )
display_status_line ( cc_call - > attached_name , from_count , call - > relation_list - > id , 0 , NULL , NULL , 0 ) ;
from_count + + ;
}
if ( ! from_count )
display_status_line ( cc_call - > attached_name , 0 , NULL , 0 , NULL , NULL , 0 ) ;
}
display_status_end ( ) ;
}
static int status_needs_update = 0 ;
static void status_timeout ( struct timer * timer )
{
static int last_interfaces = - 1 ;
osmo_cc_call_t * cc_call ;
int interfaces = 0 ;
for ( cc_call = cc_ep - > call_list ; cc_call ; cc_call = cc_call - > next ) {
if ( cc_call - > state ! = OSMO_CC_STATE_ATTACH_IN )
continue ;
interfaces + + ;
}
if ( interfaces ! = last_interfaces ) {
last_interfaces = interfaces ;
status_needs_update = 1 ;
}
if ( status_needs_update ) {
refresh_status ( ) ;
status_needs_update = 0 ;
}
timer_start ( timer , 0.1 ) ;
}
static const char * state_names [ ] = {
" IDLE " ,
" SETUP " ,
" OVERLAP " ,
" PROCEEDING " ,
" ALERTING " ,
" CONNECT " ,
" DISC-FROM-ORIG " ,
" DISC-FROM-TERM " ,
} ;
static void new_state ( call_t * call , enum call_state state )
{
PDEBUG ( DROUTER , DEBUG_DEBUG , " (call #%d) Changing state %s -> %s. \n " , call - > num , state_names [ call - > state ] , state_names [ state ] ) ;
call - > state = state ;
}
static const char * relation_name ( call_relation_t * relation )
{
static char text [ 128 ] ;
if ( relation - > num = = 0 )
sprintf ( text , " (call #%d, originator) " , relation - > call - > num ) ;
else
sprintf ( text , " (call #%d, terminator #%d) " , relation - > call - > num , relation - > num ) ;
return text ;
}
static call_t * call_create ( void )
{
call_t * call , * * call_p ;
static int call_num = 0 ;
call = calloc ( 1 , sizeof ( * call ) ) ;
if ( ! call ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " No memory! \n " ) ;
abort ( ) ;
}
call - > num = + + call_num ;
call - > routing . call = call ;
/* append to list */
call_p = & call_list ;
while ( * call_p )
call_p = & ( ( * call_p ) - > next ) ;
* call_p = call ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " (call #%d) Created new call instance. \n " , call - > num ) ;
return call ;
}
static void relation_destroy ( call_relation_t * relation ) ;
static void call_destroy ( call_t * call )
{
call_t * * call_p ;
new_state ( call , CALL_STATE_IDLE ) ;
/* remove setup message */
free ( call - > setup_msg ) ;
/* destroy all relations */
while ( call - > relation_list )
relation_destroy ( call - > relation_list ) ;
/* destroy routing */
routing_stop ( & call - > routing ) ;
routing_env_free ( & call - > routing ) ;
/* detach */
call_p = & call_list ;
while ( * call_p ) {
if ( * call_p = = call )
break ;
call_p = & ( ( * call_p ) - > next ) ;
}
* call_p = call - > next ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " (call #%d) Destroyed call instance. \n " , call - > num ) ;
free ( call ) ;
}
static call_relation_t * relation_create ( call_t * call )
{
call_relation_t * relation , * * relation_p ;
int rc ;
relation = calloc ( 1 , sizeof ( * relation ) ) ;
if ( ! relation ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " No memory! \n " ) ;
abort ( ) ;
}
relation - > call = call ;
/* allocate jitter buffer */
rc = jitter_create ( & relation - > orig_dejitter , 8000 / 10 ) ; // FIXME: size
if ( rc < 0 )
abort ( ) ;
rc = jitter_create ( & relation - > term_dejitter , 8000 / 10 ) ; // FIXME: size
if ( rc < 0 )
abort ( ) ;
/* append to list, count number of relation */
relation_p = & call - > relation_list ;
while ( * relation_p ) {
relation - > num + + ;
relation_p = & ( ( * relation_p ) - > next ) ;
}
* relation_p = relation ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s Created new endpoint relation instance. \n " , relation_name ( relation ) ) ;
return relation ;
}
static void relation_destroy ( call_relation_t * relation )
{
call_relation_t * * relation_p ;
/* playback and record */
wave_destroy_playback ( & relation - > play ) ;
wave_destroy_record ( & relation - > rec ) ;
/* dtmf decoder */
dtmf_decode_exit ( & relation - > dtmf_dec ) ;
/* SDP */
free ( ( char * ) relation - > sdp ) ;
/* session */
if ( relation - > cc_session ) {
osmo_cc_free_session ( relation - > cc_session ) ;
relation - > cc_session = NULL ;
}
/* destroy jitter buffer */
jitter_destroy ( & relation - > orig_dejitter ) ;
jitter_destroy ( & relation - > term_dejitter ) ;
/* detach */
relation_p = & relation - > call - > relation_list ;
while ( * relation_p ) {
if ( * relation_p = = relation )
break ;
relation_p = & ( ( * relation_p ) - > next ) ;
}
* relation_p = relation - > next ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s Destroyed endpoint relation instance. \n " , relation_name ( relation ) ) ;
free ( relation ) ;
/* refresh status: we lost a call (originating and/or terminating) */
status_needs_update = 1 ;
}
int call_init ( osmo_cc_endpoint_t * ep , const char * _routing_script , const char * _routing_shell )
{
cc_ep = ep ;
routing_script = _routing_script ;
routing_shell = _routing_shell ;
timer_init ( & status_timer , status_timeout , NULL ) ;
status_timeout ( & status_timer ) ;
return 0 ;
}
void call_exit ( void )
{
timer_exit ( & status_timer ) ;
/* destroy all calls */
while ( call_list )
call_destroy ( call_list ) ;
}
/* handle all calls, if it returns 1, work has been done, so it must be called again */
int call_handle ( void )
{
int w ;
call_t * call ;
int status ;
pid_t pid ;
for ( call = call_list ; call ; call = call - > next ) {
/* must return, call may be destroyed */
w = routing_handle ( & call - > routing ) ;
if ( w )
return 1 ;
}
/* eat zombies */
while ( ( pid = waitpid ( - 1 , & status , WNOHANG ) ) > 0 ) {
PDEBUG ( DROUTER , DEBUG_DEBUG , " Script child %d terminated. \n " , pid ) ;
for ( call = call_list ; call ; call = call - > next ) {
if ( call - > routing . script_pid = = pid ) {
/* tell routing that script has terminated */
call - > routing . script_pid = 0 ;
}
}
}
return 0 ;
}
/*
* RTP - Proxy
*/
/* send SDP answer to originator */
static void proxy_send_sdp_answer ( call_relation_t * relation , osmo_cc_msg_t * msg )
{
const char * sdp ;
/* no proxy */
if ( ! relation - > rtp_proxy )
return ;
/* NOTE: The SDP was removed at cc_message() */
/* add progreess if not connect message, but for first message or disconnect message */
if ( msg - > type ! = OSMO_CC_MSG_SETUP_CNF
& & ( msg - > type = = OSMO_CC_MSG_DISC_IND | | ! relation - > codec_negotiated ) ) {
/* process */
PDEBUG ( DROUTER , DEBUG_DEBUG , " Sending progress indicator 8 to allow audio. \n " ) ;
osmo_cc_add_ie_progress ( msg , OSMO_CC_CODING_ITU_T , OSMO_CC_LOCATION_BEYOND_INTERWORKING , OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE ) ;
}
// if (!relation->codec_negotiated || msg->type == OSMO_CC_MSG_SETUP_CNF) { gibt einen crash, da codec vor der antwort schon gesetzt ist. warum sollten wir nach einer antwort denn nochmal den codec schicken?
if ( ! relation - > codec_negotiated ) {
2021-02-27 11:07:32 +00:00
sdp = osmo_cc_helper_audio_accept ( relation , relation - > orig_codecs , receive_originator , relation - > call - > setup_msg , & relation - > cc_session , & relation - > codec , 0 ) ;
2020-09-27 12:17:11 +00:00
if ( sdp ) {
relation - > codec_negotiated = 1 ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " Sending SDP answer to originator with supported codecs. \n " ) ;
/* SDP */
osmo_cc_add_ie_sdp ( msg , sdp ) ;
} else {
relation - > rtp_proxy = 0 ;
PDEBUG ( DROUTER , DEBUG_ERROR , " Originator's setup message does not contain a codec we support, cannot use RTP-Proxy! \n " ) ;
}
}
}
/*
* process call from originator
*/
/* a new call is received */
static void orig_setup ( uint32_t callref , osmo_cc_msg_t * old_msg )
{
call_t * call ;
call_relation_t * relation ;
char sdp [ 65536 ] ;
uint8_t type , plan , present , screen ;
char number [ 256 ] ;
int rc ;
/* creating call instance, transparent until setup with hdlc */
call = call_create ( ) ;
if ( ! call ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " Cannot create calll instance. \n " ) ;
abort ( ) ;
}
relation = relation_create ( call ) ;
/* link with cc */
relation - > cc_callref = callref ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-SETUP-REQ from originator. \n " , relation_name ( relation ) ) ;
/* store setup message in call structure */
call - > setup_msg = osmo_cc_clone_msg ( old_msg ) ;
/* store SDP */
rc = osmo_cc_get_ie_sdp ( old_msg , 0 , sdp , sizeof ( sdp ) ) ;
if ( rc > = 0 ) {
free ( ( char * ) relation - > sdp ) ;
relation - > sdp = strdup ( sdp ) ;
}
/* store called number */
rc = osmo_cc_get_ie_called ( old_msg , 0 , & type , & plan , number , sizeof ( number ) ) ;
if ( rc > = 0 & & number [ 0 ] ) {
/* add number to current dial string */
if ( strlen ( number ) < sizeof ( call - > dialing_number ) )
strcpy ( call - > dialing_number , number ) ;
}
/* store keypad */
rc = osmo_cc_get_ie_keypad ( old_msg , 0 , number , sizeof ( number ) ) ;
if ( rc > = 0 & & number [ 0 ] ) {
/* add number to current dial string */
if ( strlen ( number ) < sizeof ( call - > dialing_keypad ) )
strcpy ( call - > dialing_keypad , number ) ;
}
/* store peer info for status */
rc = osmo_cc_get_ie_calling_interface ( old_msg , 0 , relation - > interface , sizeof ( relation - > interface ) ) ;
rc = osmo_cc_get_ie_calling ( old_msg , 0 , & type , & plan , & present , & screen , relation - > id , sizeof ( relation - > id ) ) ;
/* apply environment for routing */
routing_env_msg ( & call - > routing , old_msg ) ;
/* start routing script */
routing_start ( & call - > routing , routing_script , routing_shell ) ;
/* go into setup state and return */
new_state ( call , CALL_STATE_SETUP ) ;
/* refresh status: we have originating call */
status_needs_update = 1 ;
return ;
}
/* information (dialing) is received from originating side */
static void orig_info ( call_t * call , osmo_cc_msg_t * old_msg )
{
uint8_t type , plan ;
char number [ 256 ] ;
char keypad [ 256 ] ;
uint8_t duration_ms , pause_ms , dtmf_mode ;
char dtmf [ 256 ] ;
char command [ 512 ] ;
int rc ;
osmo_cc_msg_t * new_msg ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-INFO-REQ from originator. \n " , relation_name ( call - > relation_list ) ) ;
/* add called number to dial string */
rc = osmo_cc_get_ie_called ( old_msg , 0 , & type , & plan , number , sizeof ( number ) ) ;
if ( rc < 0 )
number [ 0 ] = ' \0 ' ;
if ( number [ 0 ] ) {
/* add number to current dial string */
if ( strlen ( number ) < sizeof ( call - > dialing_number ) - strlen ( call - > dialing_number ) )
strcat ( call - > dialing_number , number ) ;
}
/* add keypad to dial string */
rc = osmo_cc_get_ie_keypad ( old_msg , 0 , keypad , sizeof ( keypad ) ) ;
if ( rc < 0 )
keypad [ 0 ] = ' \0 ' ;
if ( keypad [ 0 ] ) {
/* add number to current dial string */
if ( strlen ( keypad ) < sizeof ( call - > dialing_keypad ) - strlen ( call - > dialing_keypad ) )
strcat ( call - > dialing_keypad , keypad ) ;
}
/* dtmf digits */
rc = osmo_cc_get_ie_dtmf ( old_msg , 0 , & duration_ms , & pause_ms , & dtmf_mode , dtmf , sizeof ( dtmf ) ) ;
if ( rc < 0 | | ( dtmf_mode ! = OSMO_CC_DTMF_MODE_ON & & dtmf_mode ! = OSMO_CC_DTMF_MODE_DIGITS ) )
dtmf [ 0 ] = ' \0 ' ;
/* if there is only one call relation, forward that message */
if ( call - > relation_list - > next & & ! call - > relation_list - > next - > next ) {
/* concat number to id of relation */
if ( strlen ( call - > relation_list - > next - > id ) + strlen ( number ) < sizeof ( call - > relation_list - > next - > id ) )
strcat ( call - > relation_list - > next - > id , number ) ;
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_INFO_IND ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > next - > cc_callref , new_msg ) ;
return ;
}
/* if there is no call in overlap state, perform routing */
if ( call - > state = = CALL_STATE_OVERLAP & & ! call - > relation_list - > next ) {
if ( ! call - > routing . routing ) {
/* restart routing with new dial string */
routing_env_dialing ( & call - > routing , call - > dialing_number , call - > dialing_keypad ) ;
routing_start ( & call - > routing , routing_script , routing_shell ) ;
} else {
/* send digits to routing */
if ( number [ 0 ] ) {
snprintf ( command , sizeof ( command ) - 1 , " dialing %s " , number ) ;
command [ sizeof ( command ) - 1 ] = ' \0 ' ;
routing_send ( & call - > routing , command ) ;
}
if ( keypad [ 0 ] ) {
snprintf ( command , sizeof ( command ) - 1 , " keypad %s " , keypad ) ;
command [ sizeof ( command ) - 1 ] = ' \0 ' ;
routing_send ( & call - > routing , command ) ;
}
}
}
/* send dtmf, to routing in all other states */
if ( ! call - > relation_list - > next ) {
if ( call - > routing . routing ) {
if ( dtmf [ 0 ] ) {
snprintf ( command , sizeof ( command ) - 1 , " dtmf %s " , dtmf ) ;
command [ sizeof ( command ) - 1 ] = ' \0 ' ;
routing_send ( & call - > routing , command ) ;
}
}
}
}
/* disconnect is received from originating side */
static void orig_disc ( call_t * call , osmo_cc_msg_t * old_msg )
{
osmo_cc_msg_t * new_msg ;
call_relation_t * relation ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-DISC-REQ from originator. \n " , relation_name ( call - > relation_list ) ) ;
/* stop routing, if originator hangs up */
if ( call - > routing . routing ) {
routing_stop ( & call - > routing ) ;
}
/* if there is no terminating call, release originator and destroy call */
if ( ! call - > relation_list - > next ) {
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_IND ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
/* destroy call */
call_destroy ( call ) ;
return ;
}
/* if there is one terminating call, forward the disc message */
if ( ! call - > relation_list - > next - > next ) {
/* update call state for status display */
call - > relation_list - > next - > state = CALL_STATE_DISC_FROM_TERM ;
status_needs_update = 1 ;
/* forward disc message */
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_DISC_IND ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > next - > cc_callref , new_msg ) ;
new_state ( call , CALL_STATE_DISC_FROM_ORIG ) ;
return ;
}
/* if there are multiple terminating calls, release them and originator and destroy call */
for ( relation = call - > relation_list - > next ; relation ; relation = relation - > next ) {
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_IND ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
}
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_IND ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
/* destroy call */
call_destroy ( call ) ;
}
/* release is received from originating side */
static void orig_rel ( call_t * call , osmo_cc_msg_t * old_msg )
{
osmo_cc_msg_t * new_msg ;
call_relation_t * relation ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-REL-REQ from originator. \n " , relation_name ( call - > relation_list ) ) ;
/* stop routing, if originator hangs up */
if ( call - > routing . routing ) {
routing_stop ( & call - > routing ) ;
}
/* release all terminating calls, if any and confirm originator and destroy call */
for ( relation = call - > relation_list - > next ; relation ; relation = relation - > next ) {
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_IND ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
}
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_CNF ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
/* destroy call */
call_destroy ( call ) ;
}
/* other message is received from originating side */
static void orig_other ( call_t * call , osmo_cc_msg_t * old_msg )
{
osmo_cc_msg_t * new_msg ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s Other CC message from originator. \n " , relation_name ( call - > relation_list ) ) ;
/* if there is one terminating call, forward the message */
if ( call - > relation_list - > next & & ! call - > forking ) {
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = old_msg - > type | 1 ; /* convert REQ->IND, RSP->CNF */
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > next - > cc_callref , new_msg ) ;
return ;
}
}
/*
* process call from terminator
*/
/* overlap dialing is received from terminating side */
static void term_progress ( call_t * call , call_relation_t * relation , osmo_cc_msg_t * old_msg )
{
osmo_cc_msg_t * new_msg ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-PROGRESS-REQ from terminator. \n " , relation_name ( relation ) ) ;
/* if single call exists, forward progress to originator */
if ( ! call - > forking ) {
/* forward message to originator */
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_PROGRESS_IND ;
/* send SDP answer */
proxy_send_sdp_answer ( call - > relation_list , new_msg ) ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
}
}
/* overlap dialing is received from terminating side */
static void term_overlap ( call_t * call , call_relation_t * relation , osmo_cc_msg_t * old_msg )
{
osmo_cc_msg_t * new_msg ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-SETUP-ACK-REQ from terminator. \n " , relation_name ( relation ) ) ;
/* update call state for status display */
relation - > state = CALL_STATE_OVERLAP ;
status_needs_update = 1 ;
/* notify routing */
if ( call - > routing . routing )
routing_send ( & call - > routing , " call-overlap " ) ;
/* if we already reached/passed that state, we ignore it */
if ( call - > state ! = CALL_STATE_SETUP )
return ;
/* change state */
new_state ( call , CALL_STATE_OVERLAP ) ;
if ( ! call - > forking ) {
/* forward message to originator */
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_SETUP_ACK_IND ;
} else {
/* send message to originator */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_SETUP_ACK_IND ) ;
}
/* send SDP answer */
proxy_send_sdp_answer ( call - > relation_list , new_msg ) ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
}
/* proceeding is received from terminating side */
static void term_proc ( call_t * call , call_relation_t * relation , osmo_cc_msg_t * old_msg )
{
osmo_cc_msg_t * new_msg ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-PROC-REQ from terminator. \n " , relation_name ( relation ) ) ;
/* update call state for status display */
relation - > state = CALL_STATE_PROCEEDING ;
status_needs_update = 1 ;
/* notify routing */
if ( call - > routing . routing )
routing_send ( & call - > routing , " call-proceeding " ) ;
/* if we already reached/passed that state, we ignore it */
if ( call - > state ! = CALL_STATE_SETUP
& & call - > state ! = CALL_STATE_OVERLAP )
return ;
/* change state */
new_state ( call , CALL_STATE_PROCEEDING ) ;
if ( ! call - > forking ) {
/* forward message to originator */
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_PROC_IND ;
} else {
/* send message to originator */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_PROC_IND ) ;
}
/* send SDP answer */
proxy_send_sdp_answer ( call - > relation_list , new_msg ) ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
}
/* alerting is received from terminating side */
static void term_alert ( call_t * call , call_relation_t * relation , osmo_cc_msg_t * old_msg )
{
osmo_cc_msg_t * new_msg ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-ALERT-REQ from terminator. \n " , relation_name ( relation ) ) ;
/* update call state for status display */
relation - > state = CALL_STATE_ALERTING ;
status_needs_update = 1 ;
/* notify routing */
if ( call - > routing . routing )
routing_send ( & call - > routing , " call-alerting " ) ;
/* if we already reached/passed that state, we ignore it */
if ( call - > state ! = CALL_STATE_SETUP
& & call - > state ! = CALL_STATE_OVERLAP
& & call - > state ! = CALL_STATE_PROCEEDING )
return ;
/* change state */
new_state ( call , CALL_STATE_ALERTING ) ;
if ( ! call - > forking ) {
/* forward message to originator */
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_ALERT_IND ;
} else {
/* send message to originator */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_ALERT_IND ) ;
}
/* send SDP answer */
proxy_send_sdp_answer ( call - > relation_list , new_msg ) ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
}
/* connect is received from terminating side */
static void term_connect ( call_t * call , call_relation_t * relation , osmo_cc_msg_t * old_msg )
{
osmo_cc_msg_t * new_msg ;
char sdp [ 65536 ] ;
int rc ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-SETUP-RSP (connect) from terminator. \n " , relation_name ( relation ) ) ;
/* update call state for status display */
relation - > state = CALL_STATE_CONNECT ;
status_needs_update = 1 ;
/* notify routing */
if ( call - > routing . routing )
routing_send ( & call - > routing , " call-answer " ) ;
/* if we already reached/passed that state, we ignore it */
if ( call - > state ! = CALL_STATE_SETUP
& & call - > state ! = CALL_STATE_OVERLAP
& & call - > state ! = CALL_STATE_PROCEEDING
& & call - > state ! = CALL_STATE_ALERTING ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " Connect message from terminating call now allowed in state '%s'. \n " , state_names [ call - > state ] ) ;
return ;
}
/* change state */
new_state ( call , CALL_STATE_CONNECT ) ;
/* release all other relations with "non-selected user clearing" */
while ( call - > relation_list - > next - > next ) {
call_relation_t * other ;
/* select other terminating call (not the one that answered) */
if ( call - > relation_list - > next = = relation )
other = call - > relation_list - > next - > next ;
else
other = call - > relation_list - > next ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " Sending 'non-selected user clearing' to other terminator. \n " ) ;
/* send release message */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
osmo_cc_add_ie_cause ( new_msg , cc_ep - > serving_location , OSMO_CC_ISDN_CAUSE_NONSE_USER_CLR , 0 , 0 ) ;
osmo_cc_ll_msg ( cc_ep , other - > cc_callref , new_msg ) ;
/* destroy terminating relation */
relation_destroy ( other ) ;
}
/* call is not forking anymore */
call - > forking = 0 ;
rc = osmo_cc_get_ie_sdp ( old_msg , 0 , sdp , sizeof ( sdp ) ) ;
if ( rc < 0 )
sdp [ 0 ] = ' \0 ' ;
/* forward message to originator */
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_SETUP_CNF ;
/* only if RTP-Proxy is used */
if ( call - > relation_list - > rtp_proxy ) {
/* send SDP answer */
proxy_send_sdp_answer ( call - > relation_list , new_msg ) ;
} else
/* use earlier SDP if not included */
if ( ! sdp [ 0 ] & & relation - > sdp )
osmo_cc_add_ie_sdp ( new_msg , relation - > sdp ) ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
}
/* disconnect is received from terminating side */
static void term_disc ( call_t * call , call_relation_t * relation , osmo_cc_msg_t * old_msg )
{
osmo_cc_msg_t * new_msg ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-DISC-REQ from terminator. \n " , relation_name ( relation ) ) ;
/* update call state for status display */
relation - > state = CALL_STATE_DISC_FROM_TERM ;
status_needs_update = 1 ;
/* if we have not yet connected a call */
if ( call - > state = = CALL_STATE_SETUP
| | call - > state = = CALL_STATE_OVERLAP
| | call - > state = = CALL_STATE_PROCEEDING
| | call - > state = = CALL_STATE_ALERTING ) {
uint8_t location , isdn_cause , socket_cause ;
uint16_t sip_cause ;
int rc ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " Got a disconnect before connect. \n " ) ;
/* if there is only one terminating call, forward that disconnect */
if ( ! call - > forking ) {
PDEBUG ( DROUTER , DEBUG_DEBUG , " Call is not forking, so we directly forward this disconnect. \n " ) ;
/* notify routing */
if ( call - > routing . routing )
routing_send ( & call - > routing , " call-disconnect " ) ;
/* change state */
new_state ( call , CALL_STATE_DISC_FROM_TERM ) ;
/* forward message to originator */
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_DISC_IND ;
/* send SDP answer */
proxy_send_sdp_answer ( call - > relation_list , new_msg ) ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
return ;
}
/* collect cause */
PDEBUG ( DROUTER , DEBUG_DEBUG , " Call is forking, so we collect ISDN cause and destroy relation. \n " ) ;
rc = osmo_cc_get_ie_cause ( old_msg , 0 , & location , & isdn_cause , & sip_cause , & socket_cause ) ;
if ( rc > = 0 )
call - > collect_cause = osmo_cc_collect_cause ( call - > collect_cause , isdn_cause ) ;
/* release the terminator */
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_IND ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
/* remove relation */
relation_destroy ( relation ) ;
/* if all terminating calls have been released, release the originator and destroy call */
if ( ! call - > relation_list - > next ) {
PDEBUG ( DROUTER , DEBUG_DEBUG , " All terminators have disconnected, so we forward a release with the collected cause. \n " ) ;
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
osmo_cc_add_ie_cause ( new_msg , cc_ep - > serving_location , call - > collect_cause , 0 , 0 ) ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
/* destroy call */
call_destroy ( call ) ;
}
return ;
}
/* this is connect or disconnect collision. the state implies that there is only one terminating call */
if ( call - > state = = CALL_STATE_CONNECT
| | call - > state = = CALL_STATE_DISC_FROM_ORIG ) {
PDEBUG ( DROUTER , DEBUG_DEBUG , " Got a disconnect after connect, so we directly forward this disconnect. \n " ) ;
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_IND ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_IND ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
/* destroy call */
call_destroy ( call ) ;
return ;
}
PDEBUG ( DROUTER , DEBUG_ERROR , " Disconnect message from terminating call now allowed in state '%s'. \n " , state_names [ call - > state ] ) ;
}
/* rel is received from terminating side */
static void term_rel ( call_t * call , call_relation_t * relation , osmo_cc_msg_t * old_msg )
{
osmo_cc_msg_t * new_msg ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-REL-REQ from terminator. \n " , relation_name ( relation ) ) ;
/* if we have not yet connected a call */
if ( call - > state = = CALL_STATE_SETUP
| | call - > state = = CALL_STATE_OVERLAP
| | call - > state = = CALL_STATE_PROCEEDING
| | call - > state = = CALL_STATE_ALERTING ) {
uint8_t location , isdn_cause , socket_cause ;
uint16_t sip_cause ;
int rc ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " Got a release before connect. \n " ) ;
/* if there is only one terminating call, forward that disconnect */
if ( ! call - > forking ) {
PDEBUG ( DROUTER , DEBUG_DEBUG , " Call is not forking, so we directly forward this release. \n " ) ;
/* forward message to originator */
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_IND ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
/* confirm to terminator */
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_CNF ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
/* destroy call */
call_destroy ( call ) ;
return ;
}
/* collect cause */
PDEBUG ( DROUTER , DEBUG_DEBUG , " Call is forking, so we collect ISDN cause and destroy relation. \n " ) ;
rc = osmo_cc_get_ie_cause ( old_msg , 0 , & location , & isdn_cause , & sip_cause , & socket_cause ) ;
if ( rc > = 0 )
call - > collect_cause = osmo_cc_collect_cause ( call - > collect_cause , isdn_cause ) ;
/* confirm the terminator */
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_CNF ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
/* remove relation */
relation_destroy ( relation ) ;
/* if all terminating calls have been released, release the originator and destroy call */
if ( ! call - > relation_list - > next ) {
PDEBUG ( DROUTER , DEBUG_DEBUG , " All terminators have released, so we forward it with the collected cause. \n " ) ;
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
osmo_cc_add_ie_cause ( new_msg , cc_ep - > serving_location , call - > collect_cause , 0 , 0 ) ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
/* destroy call */
call_destroy ( call ) ;
}
return ;
}
/* forward release to originator and confirm to terminator. the state implies that there is only one terminating call */
if ( call - > state = = CALL_STATE_CONNECT
| | call - > state = = CALL_STATE_DISC_FROM_ORIG ) {
PDEBUG ( DROUTER , DEBUG_DEBUG , " Got a release after connect, so we directly forward this release. \n " ) ;
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_IND ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_CNF ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
/* destroy call */
call_destroy ( call ) ;
return ;
}
PDEBUG ( DROUTER , DEBUG_ERROR , " Release message from terminating call now allowed in state '%s'. \n " , state_names [ call - > state ] ) ;
}
/* other message is received from terminating side */
static void term_other ( call_t * call , call_relation_t * relation , osmo_cc_msg_t * old_msg )
{
osmo_cc_msg_t * new_msg ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s Other CC message from terminator. \n " , relation_name ( relation ) ) ;
/* if there is one terminating call, forward the message */
if ( ! call - > relation_list - > next - > next ) {
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = old_msg - > type | 1 ; /* convert REQ->IND, RSP->CNF */
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
return ;
}
}
/* handle message from upper layer */
void cc_message ( osmo_cc_endpoint_t __attribute__ ( ( unused ) ) * ep , uint32_t callref , osmo_cc_msg_t * msg )
{
call_t * call ;
call_relation_t * relation ;
char sdp [ 65536 ] ;
int rc_sdp ;
/* hunt for callref */
call = call_list ;
while ( call ) {
relation = call - > relation_list ;
while ( relation ) {
if ( relation - > cc_callref = = callref )
break ;
relation = relation - > next ;
}
if ( relation )
break ;
call = call - > next ;
}
/* process SETUP (new call) */
if ( ! call ) {
if ( msg - > type ! = OSMO_CC_MSG_SETUP_REQ ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " Received message without call instance, please fix! \n " ) ;
return ;
}
orig_setup ( callref , msg ) ;
osmo_cc_free_msg ( msg ) ;
return ;
}
if ( call - > relation_list = = relation ) {
/* handle messages from caller */
switch ( msg - > type ) {
case OSMO_CC_MSG_INFO_REQ :
orig_info ( call , msg ) ;
break ;
case OSMO_CC_MSG_DISC_REQ :
orig_disc ( call , msg ) ;
break ;
case OSMO_CC_MSG_REL_REQ :
orig_rel ( call , msg ) ;
break ;
default :
orig_other ( call , msg ) ;
}
} else {
/* store sdp, if present */
rc_sdp = osmo_cc_get_ie_sdp ( msg , 0 , sdp , sizeof ( sdp ) ) ;
if ( rc_sdp > = 0 ) {
free ( ( char * ) relation - > sdp ) ;
relation - > sdp = strdup ( sdp ) ;
}
/* negotiate codec if RTP-Proxy is used and not already negotiated */
if ( call - > relation_list - > rtp_proxy & & relation - > cc_session & & ! relation - > codec_negotiated ) {
/* remove progress, since it will be added with the SDP answer */
osmo_cc_remove_ie ( msg , OSMO_CC_IE_PROGRESS , 0 ) ;
/* negotiate codec */
osmo_cc_helper_audio_negotiate ( msg , & relation - > cc_session , & relation - > codec ) ;
if ( relation - > codec )
relation - > codec_negotiated = 1 ;
}
/* remove SDP, if we do RTP-Proxy */
if ( rc_sdp > = 0 & & call - > relation_list - > rtp_proxy )
osmo_cc_remove_ie ( msg , OSMO_CC_IE_SDP , 0 ) ;
/* handle messages from called */
switch ( msg - > type ) {
case OSMO_CC_MSG_PROGRESS_IND :
term_progress ( call , relation , msg ) ;
break ;
case OSMO_CC_MSG_SETUP_ACK_REQ :
term_overlap ( call , relation , msg ) ;
break ;
case OSMO_CC_MSG_PROC_REQ :
term_proc ( call , relation , msg ) ;
break ;
case OSMO_CC_MSG_ALERT_REQ :
term_alert ( call , relation , msg ) ;
break ;
case OSMO_CC_MSG_SETUP_RSP :
term_connect ( call , relation , msg ) ;
break ;
case OSMO_CC_MSG_DISC_REQ :
term_disc ( call , relation , msg ) ;
break ;
case OSMO_CC_MSG_REL_REQ :
case OSMO_CC_MSG_REJ_REQ :
term_rel ( call , relation , msg ) ;
break ;
default :
term_other ( call , relation , msg ) ;
}
}
osmo_cc_free_msg ( msg ) ;
}
/*
* process message from routing
*/
2021-02-27 11:07:32 +00:00
static const char * value_of_param ( char * arg , char * param , char * * value_p )
2020-09-27 12:17:11 +00:00
{
if ( ! ! strncmp ( arg , param , strlen ( param ) ) )
return NULL ;
arg + = strlen ( param ) ;
if ( * arg = = ' \0 ' ) {
if ( value_p )
* value_p = " " ;
return " " ;
}
if ( * arg ! = ' = ' )
return NULL ;
arg + + ;
if ( value_p )
* value_p = arg ;
return arg ;
}
2021-02-27 11:07:32 +00:00
static void add_codecs_by_names ( struct osmo_cc_helper_audio_codecs * codecs , char * names )
{
int c = 0 , i ;
const char * name ;
while ( ( name = strsep ( & names , " , " ) ) ) {
for ( i = 0 ; codecs_def [ i ] . payload_name ; i + + ) {
if ( ! strcasecmp ( name , codecs_def [ i ] . payload_name ) ) {
if ( c = = MAX_CODECS ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " Maximum of %d codecs are reached. Ignoring codec '%s'. \n " , c , name ) ;
break ;
}
memcpy ( & codecs [ c ] , & codecs_def [ i ] , sizeof ( * codecs ) ) ;
PDEBUG ( DROUTER , DEBUG_INFO , " Adding given codec '%s'. \n " , codecs [ c ] . payload_name ) ;
c + + ;
break ;
}
}
if ( ! codecs_def [ i ] . payload_name ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " Given codec '%s' not supported! \n " , name ) ;
}
}
memset ( & codecs [ c ] , 0 , sizeof ( * codecs ) ) ;
}
2020-09-27 12:17:11 +00:00
/* routing orders us to activate rtp proxy */
2021-02-27 11:07:32 +00:00
static void routing_rtp_proxy ( call_t * call , int argc , char * argv [ ] )
2020-09-27 12:17:11 +00:00
{
call_relation_t * relation = call - > relation_list ;
2021-02-27 11:07:32 +00:00
int orig_given = 0 , term_given = 0 ;
int i ;
char * value ;
2020-09-27 12:17:11 +00:00
/* ignore, if already enabled */
if ( relation - > rtp_proxy ) {
PDEBUG ( DROUTER , DEBUG_NOTICE , " RTP proxy is already enabled!. \n " ) ;
return ;
}
2021-02-27 11:07:32 +00:00
/* loop through all arguments and stop if there is a ':' */
for ( i = 0 ; i < argc ; i + + ) {
if ( value_of_param ( argv [ i ] , " orig-codecs " , & value ) ) {
PDEBUG ( DROUTER , DEBUG_INFO , " Originating codecs given: '%s'. \n " , value ) ;
add_codecs_by_names ( relation - > orig_codecs , value ) ;
if ( relation - > orig_codecs [ 0 ] . payload_name )
orig_given = 1 ;
} else if ( value_of_param ( argv [ i ] , " term-codecs " , & value ) ) {
PDEBUG ( DROUTER , DEBUG_INFO , " Terminating codecs given: '%s'. \n " , value ) ;
add_codecs_by_names ( relation - > term_codecs , value ) ;
if ( relation - > term_codecs [ 0 ] . payload_name )
term_given = 1 ;
} else
PDEBUG ( DROUTER , DEBUG_ERROR , " Unknown 'rtp-proxy' parameter '%s' from routing. \n " , argv [ i ] ) ;
}
if ( ! orig_given ) {
memcpy ( & relation - > orig_codecs [ 0 ] , & codecs_def [ 0 ] , sizeof ( relation - > orig_codecs [ 0 ] ) ) ;
memcpy ( & relation - > orig_codecs [ 1 ] , & codecs_def [ 1 ] , sizeof ( relation - > orig_codecs [ 1 ] ) ) ;
memcpy ( & relation - > orig_codecs [ 2 ] , & codecs_def [ 2 ] , sizeof ( relation - > orig_codecs [ 2 ] ) ) ;
memset ( & relation - > orig_codecs [ 3 ] , 0 , sizeof ( relation - > orig_codecs [ 3 ] ) ) ;
PDEBUG ( DROUTER , DEBUG_INFO , " No originating codeds given, using default '%s' and '%s' and '%s'. \n " , relation - > orig_codecs [ 0 ] . payload_name , relation - > orig_codecs [ 1 ] . payload_name , relation - > orig_codecs [ 2 ] . payload_name ) ;
}
if ( ! term_given ) {
memcpy ( & relation - > term_codecs [ 0 ] , & codecs_def [ 0 ] , sizeof ( relation - > term_codecs [ 0 ] ) ) ;
memcpy ( & relation - > term_codecs [ 1 ] , & codecs_def [ 1 ] , sizeof ( relation - > term_codecs [ 1 ] ) ) ;
memset ( & relation - > term_codecs [ 2 ] , 0 , sizeof ( relation - > term_codecs [ 2 ] ) ) ;
PDEBUG ( DROUTER , DEBUG_INFO , " No terminating codeds given, using default '%s' and '%s'. \n " , relation - > term_codecs [ 0 ] . payload_name , relation - > term_codecs [ 1 ] . payload_name ) ;
}
2020-09-27 12:17:11 +00:00
/* error, if we already negotiated */
if ( call - > sdp_forwarded ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " RTP-Proxy cannot be enabled now, because we already forwarded a call. \n " ) ;
return ;
}
/* no SDP */
if ( ! relation - > sdp ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " Originator's setup message does not contain SDP, please fix!. \n " ) ;
return ;
}
relation - > rtp_proxy = 1 ;
}
/* routing orders us to play a wave file */
2021-02-27 11:07:32 +00:00
static void routing_play ( call_t * call , int argc , char * argv [ ] )
2020-09-27 12:17:11 +00:00
{
call_relation_t * relation = call - > relation_list ;
2021-02-27 11:07:32 +00:00
char * filename = NULL , * volume = " 1.0 " , * loop = NULL ;
2020-09-27 12:17:11 +00:00
int i ;
int samplerate = 8000 , channels = 0 ;
double deviation ;
int rc ;
wave_destroy_playback ( & relation - > play ) ;
if ( ! relation - > rtp_proxy ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " RTP-Proxy must be enabled to play a file!. \n " ) ;
return ;
}
/* loop through all arguments and stop if there is a ':' */
for ( i = 0 ; i < argc ; i + + ) {
if ( value_of_param ( argv [ i ] , " volume " , & volume ) ) ;
else if ( value_of_param ( argv [ i ] , " loop " , & loop ) ) ;
else {
if ( filename ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " 'play' command reqires only one file name, you specified '%s' and '%s'. \n " , filename , argv [ i ] ) ;
return ;
}
filename = argv [ i ] ;
}
}
if ( ! filename ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " 'play' command reqires a file name as parameter. \n " ) ;
return ;
}
deviation = 1.0 / SPEECH_LEVEL * atof ( volume ) ;
rc = wave_create_playback ( & relation - > play , filename , & samplerate , & channels , deviation ) ;
if ( rc < 0 )
return ;
strncpy ( relation - > play_filename , filename , sizeof ( relation - > play_filename ) - 1 ) ;
relation - > play_deviation = deviation ;
if ( channels ! = 1 & & channels ! = 2 ) {
wave_destroy_playback ( & relation - > play ) ;
PDEBUG ( DROUTER , DEBUG_ERROR , " 'play' command reqires a wave file that has 1 or 2 channels only. \n " ) ;
return ;
}
if ( loop )
relation - > play_loop = 1 ;
}
/* routing orders us stop playing a wave file */
static void routing_play_stop ( call_t * call )
{
call_relation_t * relation = call - > relation_list ;
wave_destroy_playback ( & relation - > play ) ;
}
/* routing orders us to record a wave file */
2021-02-27 11:07:32 +00:00
static void routing_record ( call_t * call , int argc , char * argv [ ] )
2020-09-27 12:17:11 +00:00
{
call_relation_t * relation = call - > relation_list ;
2021-02-27 11:07:32 +00:00
char * filename = NULL , * volume = " 1.0 " ;
2020-09-27 12:17:11 +00:00
int i ;
int samplerate = 8000 , channels = 2 ;
int rc ;
wave_destroy_record ( & relation - > rec ) ;
if ( ! relation - > rtp_proxy ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " RTP-Proxy must be enabled to record a file!. \n " ) ;
return ;
}
/* loop through all arguments and stop if there is a ':' */
for ( i = 0 ; i < argc ; i + + ) {
if ( value_of_param ( argv [ i ] , " volume " , & volume ) ) ;
else {
if ( filename ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " 'record' command reqires only one file name, you specified '%s' and '%s'. \n " , filename , argv [ i ] ) ;
return ;
}
filename = argv [ i ] ;
}
}
if ( ! filename ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " 'record' command reqires a file name as parameter. \n " ) ;
return ;
}
rc = wave_create_record ( & relation - > rec , filename , samplerate , channels , 1.0 / SPEECH_LEVEL / atof ( volume ) ) ;
if ( rc < 0 )
return ;
}
/* routing orders us stop recording a wave file */
static void routing_record_stop ( call_t * call )
{
call_relation_t * relation = call - > relation_list ;
wave_destroy_record ( & relation - > rec ) ;
}
/* routing orders us to set local gain */
2021-02-27 11:07:32 +00:00
static void routing_gain ( call_t * call , int argc , char * argv [ ] , int tx )
2020-09-27 12:17:11 +00:00
{
int i ;
2021-02-27 11:07:32 +00:00
char * gain = NULL ;
2020-09-27 12:17:11 +00:00
if ( ! call - > relation_list - > rtp_proxy ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " RTP-Proxy must be enabled to record a file! \n " ) ;
return ;
}
/* loop through all arguments and stop if there is a ':' */
for ( i = 0 ; i < argc ; i + + ) {
if ( gain ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " '%s-gain' command reqires only parameter, you specified '%s' and '%s'. \n " , ( tx ) ? " tx " : " rx " , gain , argv [ i ] ) ;
return ;
}
gain = argv [ i ] ;
}
if ( ! gain ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " '%s-gain' command reqires a gain value as parameter. \n " , ( tx ) ? " tx " : " rx " ) ;
return ;
}
if ( tx )
call - > tx_gain = atof ( gain ) ;
else
call - > rx_gain = atof ( gain ) ;
}
/* routing orders us to call remote end */
2021-02-27 11:07:32 +00:00
static void routing_call ( call_t * call , int argc , char * argv [ ] )
2020-09-27 12:17:11 +00:00
{
2021-02-27 11:07:32 +00:00
char * interface ;
char * bearer_coding , * bearer_capability , * bearer_mode ;
char * calling , * calling_type , * calling_plan , * calling_present , * calling_screen , * no_calling ;
char * calling2 , * calling2_type , * calling2_plan , * calling2_present , * calling2_screen , * no_calling2 ;
char * redirecting , * redirecting_type , * redirecting_plan , * redirecting_present , * redirecting_screen , * redirecting_reason , * no_redirecting ;
char * dialing , * dialing_type , * dialing_plan ;
char * keypad ;
2020-09-27 12:17:11 +00:00
uint8_t coding , capability , mode ;
uint8_t type , plan , present , screen , reason ;
char number [ 256 ] ;
int i , rc , calls = 0 ;
osmo_cc_msg_t * new_msg , * setup_msg ;
/* if we have a call, we don't add more terminators */
if ( call - > relation_list - > next ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " Multiple call commands from routing are not allowed. \n " ) ;
return ;
}
setup_msg = call - > setup_msg ;
next_call :
interface = NULL ;
bearer_coding = bearer_capability = bearer_mode = NULL ;
calling = calling_type = calling_plan = calling_present = calling_screen = no_calling = NULL ;
calling2 = calling2_type = calling2_plan = calling2_present = calling2_screen = no_calling2 = NULL ;
redirecting = redirecting_type = redirecting_plan = redirecting_present = redirecting_screen = redirecting_reason = no_redirecting = NULL ;
dialing = dialing_type = dialing_plan = NULL ;
keypad = NULL ;
/* loop through all arguments and stop if there is a ':' */
for ( i = 0 ; i < argc & & argv [ i ] [ 0 ] ! = ' : ' ; i + + ) {
if ( value_of_param ( argv [ i ] , " interface " , & interface ) ) ;
else if ( value_of_param ( argv [ i ] , " bearer-coding " , & bearer_coding ) ) ;
else if ( value_of_param ( argv [ i ] , " bearer-capability " , & bearer_capability ) ) ;
else if ( value_of_param ( argv [ i ] , " bearer-mode " , & bearer_mode ) ) ;
else if ( value_of_param ( argv [ i ] , " calling " , & calling ) ) ;
else if ( value_of_param ( argv [ i ] , " calling-type " , & calling_type ) ) ;
else if ( value_of_param ( argv [ i ] , " calling-plan " , & calling_plan ) ) ;
else if ( value_of_param ( argv [ i ] , " calling-present " , & calling_present ) ) ;
else if ( value_of_param ( argv [ i ] , " calling-screen " , & calling_screen ) ) ;
else if ( value_of_param ( argv [ i ] , " no-calling " , & no_calling ) ) ;
else if ( value_of_param ( argv [ i ] , " calling2 " , & calling2 ) ) ;
else if ( value_of_param ( argv [ i ] , " calling2-type " , & calling2_type ) ) ;
else if ( value_of_param ( argv [ i ] , " calling2-plan " , & calling2_plan ) ) ;
else if ( value_of_param ( argv [ i ] , " calling2-present " , & calling2_present ) ) ;
else if ( value_of_param ( argv [ i ] , " calling2-screen " , & calling2_screen ) ) ;
else if ( value_of_param ( argv [ i ] , " no-calling2 " , & no_calling2 ) ) ;
else if ( value_of_param ( argv [ i ] , " redirecting " , & redirecting ) ) ;
else if ( value_of_param ( argv [ i ] , " redirecting-type " , & redirecting_type ) ) ;
else if ( value_of_param ( argv [ i ] , " redirecting-plan " , & redirecting_plan ) ) ;
else if ( value_of_param ( argv [ i ] , " redirecting-present " , & redirecting_present ) ) ;
else if ( value_of_param ( argv [ i ] , " redirecting-screen " , & redirecting_screen ) ) ;
else if ( value_of_param ( argv [ i ] , " redirecting-reason " , & redirecting_reason ) ) ;
else if ( value_of_param ( argv [ i ] , " no-redirecting " , & no_redirecting ) ) ;
else if ( value_of_param ( argv [ i ] , " dialing " , & dialing ) ) ;
else if ( value_of_param ( argv [ i ] , " dialing-type " , & dialing_type ) ) ;
else if ( value_of_param ( argv [ i ] , " dialing-plan " , & dialing_plan ) ) ;
else if ( value_of_param ( argv [ i ] , " keypad " , & keypad ) ) ;
else
PDEBUG ( DROUTER , DEBUG_ERROR , " Unknown 'call' parameter '%s' from routing. \n " , argv [ i ] ) ;
}
/* if more calls, then forward arguments behind colon, otherwise set argc to 0 */
if ( i < argc ) {
argv + = i + 1 ;
argc - = i + 1 ;
} else
argc = 0 ;
if ( ! interface ) {
PDEBUG ( DROUTER , DEBUG_ERROR , " 'call' command without 'interface' parameter. \n " ) ;
goto try_next ;
}
calls + + ;
/* set call forking, if we have more than one terminating call */
if ( calls > 1 )
call - > forking = 1 ;
/* create endpoint */
osmo_cc_call_t * cc_call = osmo_cc_call_new ( cc_ep ) ;
call_relation_t * relation = relation_create ( call ) ;
/* link with cc */
relation - > cc_callref = cc_call - > callref ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-SETUP-IND to terminator. \n " , relation_name ( relation ) ) ;
/* create setup message */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_SETUP_IND ) ;
/* interface name */
osmo_cc_add_ie_called_interface ( new_msg , interface ) ;
/* bearer capability */
rc = osmo_cc_get_ie_bearer ( setup_msg , 0 , & coding , & capability , & mode ) ;
if ( rc > = 0 | | bearer_coding | | bearer_capability | | bearer_mode ) {
/* if not preset, use default values */
if ( rc < 0 ) {
coding = OSMO_CC_CODING_ITU_T ;
capability = OSMO_CC_CAPABILITY_AUDIO ;
mode = OSMO_CC_MODE_CIRCUIT ;
}
/* alter values */
if ( bearer_coding )
coding = atoi ( bearer_coding ) ;
if ( bearer_capability )
capability = atoi ( bearer_capability ) ;
if ( bearer_mode )
mode = atoi ( bearer_coding ) ;
osmo_cc_add_ie_bearer ( new_msg , coding , capability , mode ) ;
}
/* calling */
if ( ! no_calling ) {
rc = osmo_cc_get_ie_calling ( setup_msg , 0 , & type , & plan , & present , & screen , number , sizeof ( number ) ) ;
if ( rc > = 0 | | calling_type | | calling_plan | | calling_present | | calling_screen | | calling ) {
/* if not preset, use default values */
if ( rc < 0 ) {
type = OSMO_CC_TYPE_UNKNOWN ;
plan = OSMO_CC_PLAN_TELEPHONY ;
present = 0 ;
screen = 0 ;
number [ 0 ] = ' \0 ' ;
}
/* alter values */
if ( calling_type )
type = atoi ( calling_type ) ;
if ( calling_plan )
plan = atoi ( calling_plan ) ;
if ( calling_present )
present = atoi ( calling_present ) ;
if ( calling_screen )
screen = atoi ( calling_screen ) ;
if ( ! calling )
calling = number ;
osmo_cc_add_ie_calling ( new_msg , type , plan , present , screen , calling ) ;
}
}
if ( ! no_calling & & ! no_calling2 ) {
rc = osmo_cc_get_ie_calling ( setup_msg , 1 , & type , & plan , & present , & screen , number , sizeof ( number ) ) ;
if ( rc > = 0 | | calling2_type | | calling2_plan | | calling2_present | | calling2_screen | | calling2 ) {
/* if not preset, use default values */
if ( rc < 0 ) {
type = OSMO_CC_TYPE_UNKNOWN ;
plan = OSMO_CC_PLAN_TELEPHONY ;
present = 0 ;
screen = 0 ;
number [ 0 ] = ' \0 ' ;
}
/* alter values */
if ( calling2_type )
type = atoi ( calling2_type ) ;
if ( calling2_plan )
plan = atoi ( calling2_plan ) ;
if ( calling2_present )
present = atoi ( calling2_present ) ;
if ( calling2_screen )
screen = atoi ( calling2_screen ) ;
if ( ! calling2 )
calling2 = number ;
osmo_cc_add_ie_calling ( new_msg , type , plan , present , screen , calling2 ) ;
}
}
/* redirecting */
if ( ! no_redirecting ) {
rc = osmo_cc_get_ie_redir ( setup_msg , 0 , & type , & plan , & present , & screen , & reason , number , sizeof ( number ) ) ;
if ( rc > = 0 | | redirecting_type | | redirecting_plan | | redirecting_present | | redirecting_screen | | redirecting ) {
/* if not preset, use default values */
if ( rc < 0 ) {
type = OSMO_CC_TYPE_UNKNOWN ;
plan = OSMO_CC_PLAN_TELEPHONY ;
present = 0 ;
screen = 0 ;
reason = OSMO_CC_REDIR_REASON_UNKNOWN ;
number [ 0 ] = ' \0 ' ;
}
/* alter values */
if ( redirecting_type )
type = atoi ( redirecting_type ) ;
if ( redirecting_plan )
plan = atoi ( redirecting_plan ) ;
if ( redirecting_present )
present = atoi ( redirecting_present ) ;
if ( redirecting_screen )
screen = atoi ( redirecting_screen ) ;
if ( ! redirecting )
redirecting = number ;
osmo_cc_add_ie_redir ( new_msg , type , plan , present , screen , reason , redirecting ) ;
}
}
/* dialing */
rc = osmo_cc_get_ie_called ( setup_msg , 0 , & type , & plan , number , sizeof ( number ) ) ;
if ( rc > = 0 | | dialing_type | | dialing_plan | | dialing ) {
/* if not preset, use default values */
if ( rc < 0 ) {
type = OSMO_CC_TYPE_UNKNOWN ;
plan = OSMO_CC_PLAN_TELEPHONY ;
}
/* alter values */
if ( dialing_type )
type = atoi ( dialing_type ) ;
if ( dialing_plan )
plan = atoi ( dialing_plan ) ;
if ( ! dialing )
dialing = " " ;
osmo_cc_add_ie_called ( new_msg , type , plan , dialing ) ;
}
/* keypad */
if ( keypad ) {
if ( keypad [ 0 ] )
osmo_cc_add_ie_keypad ( new_msg , keypad ) ;
}
/* only if RTP-Proxy is used */
if ( call - > relation_list - > rtp_proxy ) {
PDEBUG ( DROUTER , DEBUG_DEBUG , " Sending our codecs to the terminator. \n " ) ;
2021-02-27 11:07:32 +00:00
relation - > cc_session = osmo_cc_helper_audio_offer ( relation , call - > relation_list - > term_codecs , receive_terminator , new_msg , 1 ) ;
2020-09-27 12:17:11 +00:00
} else
/* sdp from originator's setup message */
if ( call - > relation_list - > sdp )
osmo_cc_add_ie_sdp ( new_msg , call - > relation_list - > sdp ) ;
/* send message to osmo-cc */
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
/* remember this, since we cannot do RTP-Proxy after */
call - > sdp_forwarded = 1 ;
/* store peer info for status */
strncpy ( relation - > interface , interface , sizeof ( relation - > interface ) - 1 ) ;
strncpy ( relation - > id , dialing , sizeof ( relation - > id ) - 1 ) ;
/* update call state for status display */
relation - > state = CALL_STATE_SETUP ;
status_needs_update = 1 ;
try_next :
/* there is another call */
if ( argc )
goto next_call ;
}
/* routing orders us to hangup all terminating calls */
static void routing_call_stop ( call_t * call )
{
call_relation_t * relation ;
osmo_cc_msg_t * new_msg ;
/* send message to all terminators, if any */
while ( call - > relation_list - > next ) {
relation = call - > relation_list - > next ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-REL-IND to terminator. \n " , relation_name ( relation ) ) ;
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
osmo_cc_add_ie_cause ( new_msg , cc_ep - > serving_location , OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR , 0 , 0 ) ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
relation_destroy ( relation ) ;
}
}
/* routing orders us to set call into overlap state */
static void routing_overlap ( call_t * call )
{
call_relation_t * relation = call - > relation_list ;
osmo_cc_msg_t * new_msg ;
if ( call - > state ! = CALL_STATE_SETUP )
return ;
new_state ( call , CALL_STATE_OVERLAP ) ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-SETUP-ACK-IND to originator. \n " , relation_name ( relation ) ) ;
/* send message to originator */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_SETUP_ACK_IND ) ;
/* send SDP answer */
proxy_send_sdp_answer ( relation , new_msg ) ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
}
/* routing orders us to set call into proceeding state */
static void routing_proceeding ( call_t * call )
{
call_relation_t * relation = call - > relation_list ;
osmo_cc_msg_t * new_msg ;
if ( call - > state ! = CALL_STATE_SETUP
& & call - > state ! = CALL_STATE_OVERLAP )
return ;
new_state ( call , CALL_STATE_PROCEEDING ) ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-PROC-IND to originator. \n " , relation_name ( relation ) ) ;
/* send message to originator */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_PROC_IND ) ;
/* send SDP answer */
proxy_send_sdp_answer ( relation , new_msg ) ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
}
/* routing orders us to set call into alerting state */
static void routing_alerting ( call_t * call )
{
call_relation_t * relation = call - > relation_list ;
osmo_cc_msg_t * new_msg ;
if ( call - > state ! = CALL_STATE_SETUP
& & call - > state ! = CALL_STATE_OVERLAP
& & call - > state ! = CALL_STATE_PROCEEDING )
return ;
new_state ( call , CALL_STATE_ALERTING ) ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-ALERT-IND to originator. \n " , relation_name ( relation ) ) ;
/* send message to originator */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_ALERT_IND ) ;
/* send SDP answer */
proxy_send_sdp_answer ( relation , new_msg ) ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
}
/* routing orders us to set call into answer state */
static void routing_answer ( call_t * call )
{
call_relation_t * relation = call - > relation_list ;
osmo_cc_msg_t * new_msg ;
if ( call - > state ! = CALL_STATE_SETUP
& & call - > state ! = CALL_STATE_OVERLAP
& & call - > state ! = CALL_STATE_PROCEEDING
& & call - > state ! = CALL_STATE_ALERTING )
return ;
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-SETUP-CNF to originator. \n " , relation_name ( relation ) ) ;
new_state ( call , CALL_STATE_CONNECT ) ;
/* send message to originator */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_SETUP_CNF ) ;
/* send SDP answer */
proxy_send_sdp_answer ( relation , new_msg ) ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
}
/* routing orders us to dsiconnect/release the call */
2021-02-27 11:07:32 +00:00
static void routing_disc_rel ( call_t * call , int argc , char * argv [ ] , int disconnect )
2020-09-27 12:17:11 +00:00
{
call_relation_t * relation = call - > relation_list ;
2021-03-20 10:38:12 +00:00
char * cause_str ;
uint8_t cause = 16 ;
2020-09-27 12:17:11 +00:00
osmo_cc_msg_t * new_msg ;
int i ;
/* get cause, if any */
for ( i = 0 ; i < argc ; i + + ) {
2021-03-20 10:38:12 +00:00
if ( value_of_param ( argv [ i ] , " cause " , & cause_str ) )
cause = atoi ( cause_str ) ;
2020-09-27 12:17:11 +00:00
else
PDEBUG ( DROUTER , DEBUG_ERROR , " Unknown 'disconnect' / 'release' parameter '%s' from routing. \n " , argv [ i ] ) ;
}
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-%s-IND to originator. \n " , relation_name ( relation ) , ( disconnect ) ? " DISC " : " REL " ) ;
/* send message to originator */
new_msg = osmo_cc_new_msg ( ( disconnect ) ? OSMO_CC_MSG_DISC_IND : OSMO_CC_MSG_REL_IND ) ;
if ( disconnect ) {
/* send SDP answer */
proxy_send_sdp_answer ( relation , new_msg ) ;
}
/* add cause */
osmo_cc_add_ie_cause ( new_msg , cc_ep - > serving_location , cause , 0 , 0 ) ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
/* send message to all terminators, if any */
for ( relation = relation - > next ; relation ; relation = relation - > next ) {
PDEBUG ( DROUTER , DEBUG_DEBUG , " %s CC-REL-IND to terminator. \n " , relation_name ( relation ) ) ;
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
/* add cause */
osmo_cc_add_ie_cause ( new_msg , cc_ep - > serving_location , OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR , 0 , 0 ) ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
}
if ( ! disconnect ) {
/* destroy call */
call_destroy ( call ) ;
}
}
# define db2level(db) pow(10, (double)(db) / 20.0)
void recv_dtmf ( void * priv , char digit , dtmf_meas_t __attribute__ ( ( unused ) ) * meas )
{
call_relation_t * relation = ( call_relation_t * ) priv ;
char digit_string [ 7 ] = " dtmf x " ;
if ( ! relation - > call - > routing . routing )
return ;
digit_string [ 5 ] = digit ;
routing_send ( & relation - > call - > routing , digit_string ) ;
}
/* routing orders us to enable DTMF decoding */
static void routing_dtmf ( call_t * call )
{
call_relation_t * relation = call - > relation_list ;
dtmf_decode_exit ( & relation - > dtmf_dec ) ;
2021-01-01 21:14:42 +00:00
/* we add 13, because we working at speech level and not at 0dBm, which is -13 dBm */
2020-09-27 12:17:11 +00:00
dtmf_decode_init ( & relation - > dtmf_dec , relation , recv_dtmf , 8000 , db2level ( 6.0 + 13.0 ) , db2level ( - 30.0 + 13.0 ) ) ;
relation - > dtmf_dec_enable = 1 ;
}
/* routing orders us to disable DTMF decoding */
static void routing_dtmf_stop ( call_t * call )
{
call_relation_t * relation = call - > relation_list ;
dtmf_decode_exit ( & relation - > dtmf_dec ) ;
relation - > dtmf_dec_enable = 0 ;
}
/* routing failed, release the call */
2021-02-27 11:07:32 +00:00
static void routing_error ( call_t * call , char * error )
2020-09-27 12:17:11 +00:00
{
osmo_cc_msg_t * new_msg ;
call_relation_t * relation ;
PDEBUG ( DROUTER , DEBUG_ERROR , " Routing script error: '%s' \n " , error ) ;
/* send message to originator */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
osmo_cc_add_ie_cause ( new_msg , cc_ep - > serving_location , OSMO_CC_ISDN_CAUSE_NETWORK_OOO , 0 , 0 ) ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
/* send message to all terminators, if any */
for ( relation = call - > relation_list - > next ; relation ; relation = relation - > next ) {
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
osmo_cc_add_ie_cause ( new_msg , cc_ep - > serving_location , OSMO_CC_ISDN_CAUSE_NETWORK_OOO , 0 , 0 ) ;
osmo_cc_ll_msg ( cc_ep , relation - > cc_callref , new_msg ) ;
}
}
#if 0
/* routing script says something */
2021-02-27 11:07:32 +00:00
static void routing_say ( call_t * call , char * error )
2020-09-27 12:17:11 +00:00
{
char text [ 1024 ] = " " ;
fuer alle argv
fuege text hinzu
PDEBUG ( DROUTER , DEBUG_NOTICE , " Routing script says: %s \n " , text ) ;
das script anpassen
}
# endif
void routing_receive_stdout ( routing_t * routing , const char * string )
{
call_t * call = routing - > call ;
int argc = 0 ;
2021-02-27 11:07:32 +00:00
char * argv [ 256 ] , * token ;
2020-09-27 12:17:11 +00:00
/* convert string into tokens */
while ( ( token = osmo_cc_strtok_quotes ( & string ) ) )
argv [ argc + + ] = strdup ( token ) ;
if ( ! argc )
return ;
if ( ! strcasecmp ( argv [ 0 ] , " rtp-proxy " ) )
2021-02-27 11:07:32 +00:00
routing_rtp_proxy ( call , argc - 1 , argv + 1 ) ;
2020-09-27 12:17:11 +00:00
else
if ( ! strcasecmp ( argv [ 0 ] , " play " ) )
routing_play ( call , argc - 1 , argv + 1 ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " play-stop " ) )
routing_play_stop ( call ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " record " ) )
routing_record ( call , argc - 1 , argv + 1 ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " record-stop " ) )
routing_record_stop ( call ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " tx-gain " ) )
routing_gain ( call , argc - 1 , argv + 1 , 1 ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " rx-gain " ) )
routing_gain ( call , argc - 1 , argv + 1 , 0 ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " call " ) )
routing_call ( call , argc - 1 , argv + 1 ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " call-stop " ) )
routing_call_stop ( call ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " overlap " ) )
routing_overlap ( call ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " proceeding " ) )
routing_proceeding ( call ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " alerting " ) )
routing_alerting ( call ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " answer " ) )
routing_answer ( call ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " disconnect " ) )
routing_disc_rel ( call , argc - 1 , argv + 1 , 1 ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " release " ) )
routing_disc_rel ( call , argc - 1 , argv + 1 , 0 ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " dtmf " ) )
routing_dtmf ( call ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " dtmf-stop " ) )
routing_dtmf_stop ( call ) ;
else
if ( ! strcasecmp ( argv [ 0 ] , " error " ) )
routing_error ( call , ( argc > 1 ) ? argv [ 1 ] : " <unknown> " ) ;
else
#if 0
if ( ! strcasecmp ( argv [ 0 ] , " say " ) )
routing_say ( call , argc - 1 , argv + 1 , 0 ) ;
else
# endif
PDEBUG ( DROUTER , DEBUG_ERROR , " Unknown command '%s' from routing. \n " , argv [ 0 ] ) ;
while ( argc )
free ( ( char * ) argv [ - - argc ] ) ;
}
void routing_receive_stderr ( routing_t * routing , const char * string )
{
if ( routing - > call - > relation_list )
PDEBUG ( DSTDERR , DEBUG_NOTICE , " (call #%d) Routing STDERR: %s \n " , routing - > call - > num , string ) ;
else
PDEBUG ( DSTDERR , DEBUG_NOTICE , " Routing STDERR: %s \n " , string ) ;
}
void routing_close ( routing_t * routing )
{
call_t * call = routing - > call ;
osmo_cc_msg_t * new_msg ;
PDEBUG ( DROUTER , DEBUG_INFO , " (call #%d) Routing script exitted. \n " , call - > num ) ;
/* if we have a terminating call, it is fine to continue without routing process */
if ( call - > relation_list - > next )
return ;
/* in setup state we change to overlap state, so that routing can be restartet with dialing information added */
if ( call - > state = = CALL_STATE_SETUP ) {
/* change state */
new_state ( call , CALL_STATE_OVERLAP ) ;
/* forward message to originator */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_SETUP_ACK_IND ) ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
return ;
}
/* in overlap state we wait for more information to be added */
if ( call - > state = = CALL_STATE_OVERLAP ) {
return ;
}
/* there is no way to dial more digits, so we release the call */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
osmo_cc_add_ie_cause ( new_msg , cc_ep - > serving_location , OSMO_CC_ISDN_CAUSE_NO_ROUTE , 0 , 0 ) ;
osmo_cc_ll_msg ( cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
/* destroy call */
call_destroy ( call ) ;
}
2021-02-27 11:07:32 +00:00
void telephone_event ( call_relation_t * relation , struct telephone_event * te )
{
char digit_string [ 7 ] = " dtmf x " ;
if ( te - > event < 16 ) {
if ( ! relation - > te_started & & ! te - > e & & te - > volume < = 36 ) {
PDEBUG ( DROUTER , DEBUG_INFO , " Received start of Telephone-Event '%d' \n " , te - > event ) ;
relation - > te_started = 1 ;
digit_string [ 5 ] = " 0123456789*#ABCD " [ te - > event ] ;
}
if ( relation - > te_started & & te - > e ) {
PDEBUG ( DROUTER , DEBUG_INFO , " Received end of Telephone-Event '%d' \n " , te - > event ) ;
relation - > te_started = 0 ;
}
} else
PDEBUG ( DROUTER , DEBUG_INFO , " Received unsupported Telephone-Event '%d' \n " , te - > event ) ;
if ( ! relation - > call - > routing . routing )
return ;
if ( digit_string [ 5 ] ! = ' x ' )
routing_send ( & relation - > call - > routing , digit_string ) ;
}
2020-09-27 12:17:11 +00:00
# warning add progress, if terminating call sends sdp but call state already reached
# warning beim disc muss progress geprueft werden und damit entschieden ob wir audio mitsenden sollen