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>
2024-01-11 18:28:12 +00:00
# include "../liblogging/logging.h"
# include <osmocom/cc/g711.h>
2020-09-27 12:17:11 +00:00
# include "call.h"
# include "audio.h"
# include "display.h"
2022-11-24 18:26:19 +00:00
# ifdef HAVE_GSM
# include "gsm_codec.h"
# endif
2020-09-27 12:17:11 +00:00
call_t * call_list = NULL ;
2024-01-11 18:28:12 +00:00
struct osmo_timer_list status_timer ;
2021-08-25 13:35:09 +00:00
static osmo_cc_endpoint_t * cc_ep_list [ 3 ] ;
2020-09-27 12:17:11 +00:00
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 } ,
2022-11-24 18:26:19 +00:00
/* non-default (selectable) codecs */
2021-02-27 11:07:32 +00:00
{ " L16 " , 8000 , 1 , encode_l16 , decode_l16 } ,
2022-11-24 18:26:19 +00:00
# ifdef HAVE_GSM
{ " GSM " , 8000 , 1 , gsm_fr_encode , gsm_fr_decode } ,
# endif
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 ;
2021-08-25 13:35:09 +00:00
int e ;
2020-09-27 12:17:11 +00:00
display_status_start ( ) ;
2021-08-25 13:35:09 +00:00
for ( e = 0 ; cc_ep_list [ e ] ; e + + ) {
for ( cc_call = cc_ep_list [ e ] - > call_list ; cc_call ; cc_call = cc_call - > next ) {
if ( cc_call - > state ! = OSMO_CC_STATE_ATTACH_IN )
2020-09-27 12:17:11 +00:00
continue ;
2021-08-25 13:35:09 +00:00
if ( ! cc_call - > attached_name )
2020-09-27 12:17:11 +00:00
continue ;
2021-08-25 13:35:09 +00:00
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 + + ;
2020-09-27 12:17:11 +00:00
}
2021-08-25 13:35:09 +00:00
if ( ! from_count )
display_status_line ( cc_call - > attached_name , 0 , NULL , 0 , NULL , NULL , 0 ) ;
2020-09-27 12:17:11 +00:00
}
}
display_status_end ( ) ;
}
static int status_needs_update = 0 ;
2023-01-22 09:07:34 +00:00
static void status_timeout ( void __attribute__ ( ( unused ) ) * data )
2020-09-27 12:17:11 +00:00
{
static int last_interfaces = - 1 ;
osmo_cc_call_t * cc_call ;
int interfaces = 0 ;
2021-08-25 13:35:09 +00:00
int e ;
2020-09-27 12:17:11 +00:00
2021-08-25 13:35:09 +00:00
for ( e = 0 ; cc_ep_list [ e ] ; e + + ) {
for ( cc_call = cc_ep_list [ e ] - > call_list ; cc_call ; cc_call = cc_call - > next ) {
if ( cc_call - > state ! = OSMO_CC_STATE_ATTACH_IN )
continue ;
interfaces + + ;
}
2020-09-27 12:17:11 +00:00
}
if ( interfaces ! = last_interfaces ) {
last_interfaces = interfaces ;
status_needs_update = 1 ;
}
if ( status_needs_update ) {
refresh_status ( ) ;
status_needs_update = 0 ;
}
2024-01-11 18:28:12 +00:00
osmo_timer_schedule ( & status_timer , 0 , 100000 ) ;
2020-09-27 12:17:11 +00:00
}
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 )
{
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " (call #%d) Changing state %s -> %s. \n " , call - > num , state_names [ call - > state ] , state_names [ state ] ) ;
call - > state = state ;
2020-09-27 12:17:11 +00:00
}
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 ;
2022-09-22 13:09:59 +00:00
int rc ;
2020-09-27 12:17:11 +00:00
call = calloc ( 1 , sizeof ( * call ) ) ;
if ( ! call ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " No memory! \n " ) ;
2020-09-27 12:17:11 +00:00
abort ( ) ;
}
call - > num = + + call_num ;
call - > routing . call = call ;
2022-09-22 13:09:59 +00:00
/* allocate jitter buffer */
rc = jitter_create ( & call - > orig_dejitter , " tx " , 8000 , sizeof ( sample_t ) , JITTER_AUDIO ) ;
if ( rc < 0 )
abort ( ) ;
rc = jitter_create ( & call - > term_dejitter , " tx " , 8000 , sizeof ( sample_t ) , JITTER_AUDIO ) ;
if ( rc < 0 )
abort ( ) ;
2020-09-27 12:17:11 +00:00
/* append to list */
call_p = & call_list ;
while ( * call_p )
call_p = & ( ( * call_p ) - > next ) ;
* call_p = call ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " (call #%d) Created new call instance. \n " , call - > num ) ;
2020-09-27 12:17:11 +00:00
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 ) ;
2022-09-22 13:09:59 +00:00
/* playback and record */
wave_destroy_playback ( & call - > play ) ;
wave_destroy_record ( & call - > rec ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) ;
2022-09-22 13:09:59 +00:00
/* destroy jitter buffer */
jitter_destroy ( & call - > orig_dejitter ) ;
jitter_destroy ( & call - > term_dejitter ) ;
2020-09-27 12:17:11 +00:00
/* detach */
call_p = & call_list ;
while ( * call_p ) {
if ( * call_p = = call )
break ;
call_p = & ( ( * call_p ) - > next ) ;
}
* call_p = call - > next ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " (call #%d) Destroyed call instance. \n " , call - > num ) ;
2020-09-27 12:17:11 +00:00
free ( call ) ;
}
static call_relation_t * relation_create ( call_t * call )
{
call_relation_t * relation , * * relation_p ;
relation = calloc ( 1 , sizeof ( * relation ) ) ;
if ( ! relation ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " No memory! \n " ) ;
2020-09-27 12:17:11 +00:00
abort ( ) ;
}
relation - > call = call ;
/* 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 ;
2022-11-24 18:26:19 +00:00
# ifdef HAVE_GSM
gsm_fr_create ( relation ) ;
# endif
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s Created new endpoint relation instance. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
return relation ;
}
static void relation_destroy ( call_relation_t * relation )
{
call_relation_t * * relation_p ;
/* 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 ;
}
/* detach */
relation_p = & relation - > call - > relation_list ;
while ( * relation_p ) {
if ( * relation_p = = relation )
break ;
relation_p = & ( ( * relation_p ) - > next ) ;
}
* relation_p = relation - > next ;
2022-11-24 18:26:19 +00:00
# ifdef HAVE_GSM
if ( relation - > gsm_fr_encoder )
gsm_fr_destroy ( relation ) ;
# endif
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s Destroyed endpoint relation instance. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
free ( relation ) ;
/* refresh status: we lost a call (originating and/or terminating) */
status_needs_update = 1 ;
}
2021-08-25 13:35:09 +00:00
int call_init ( osmo_cc_endpoint_t * cc_ep1 , osmo_cc_endpoint_t * cc_ep2 , const char * _routing_script , const char * _routing_shell )
2020-09-27 12:17:11 +00:00
{
2021-08-25 13:35:09 +00:00
cc_ep_list [ 0 ] = cc_ep1 ;
cc_ep_list [ 1 ] = cc_ep2 ;
cc_ep_list [ 2 ] = NULL ;
2020-09-27 12:17:11 +00:00
routing_script = _routing_script ;
routing_shell = _routing_shell ;
2024-01-11 18:28:12 +00:00
osmo_timer_setup ( & status_timer , status_timeout , NULL ) ;
2023-01-22 09:07:34 +00:00
status_timeout ( NULL ) ;
2020-09-27 12:17:11 +00:00
return 0 ;
}
void call_exit ( void )
{
2024-01-11 18:28:12 +00:00
osmo_timer_del ( & status_timer ) ;
2020-09-27 12:17:11 +00:00
/* 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 )
{
2023-01-22 09:07:34 +00:00
int work = 0 ;
2020-09-27 12:17:11 +00:00
call_t * call ;
int status ;
pid_t pid ;
for ( call = call_list ; call ; call = call - > next ) {
/* must return, call may be destroyed */
2023-01-22 09:07:34 +00:00
work | = routing_handle ( & call - > routing ) ;
if ( work )
2020-09-27 12:17:11 +00:00
return 1 ;
}
/* eat zombies */
while ( ( pid = waitpid ( - 1 , & status , WNOHANG ) ) > 0 ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Script child %d terminated. \n " , pid ) ;
2020-09-27 12:17:11 +00:00
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 ;
2023-01-22 09:07:34 +00:00
work = 1 ;
2020-09-27 12:17:11 +00:00
}
}
}
2023-01-22 09:07:34 +00:00
return work ;
2020-09-27 12:17:11 +00:00
}
/*
* 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 */
2022-09-22 13:09:59 +00:00
if ( ! relation - > call - > rtp_proxy )
2020-09-27 12:17:11 +00:00
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 */
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Sending progress indicator 8 to allow audio. \n " ) ;
2020-09-27 12:17:11 +00:00
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 ) {
2023-11-12 16:45:29 +00:00
sdp = osmo_cc_helper_audio_accept_te ( & relation - > cc_ep - > session_config , relation , relation - > orig_codecs , receive_originator , relation - > call - > setup_msg , & relation - > cc_session , & relation - > codec , & relation - > telephone_event , 0 ) ;
2020-09-27 12:17:11 +00:00
if ( sdp ) {
relation - > codec_negotiated = 1 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Sending SDP answer to originator with supported codecs. \n " ) ;
2020-09-27 12:17:11 +00:00
/* SDP */
osmo_cc_add_ie_sdp ( msg , sdp ) ;
} else {
2022-09-22 13:09:59 +00:00
relation - > call - > rtp_proxy = 0 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " Originator's setup message does not contain a codec we support, cannot use RTP-Proxy! \n " ) ;
2020-09-27 12:17:11 +00:00
}
}
}
/*
* process call from originator
*/
/* a new call is received */
2021-08-25 13:35:09 +00:00
static void orig_setup ( osmo_cc_endpoint_t * cc_ep , uint32_t callref , osmo_cc_msg_t * old_msg )
2020-09-27 12:17:11 +00:00
{
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 ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " Cannot create calll instance. \n " ) ;
2020-09-27 12:17:11 +00:00
abort ( ) ;
}
relation = relation_create ( call ) ;
2021-08-25 13:35:09 +00:00
2020-09-27 12:17:11 +00:00
/* link with cc */
2021-08-25 13:35:09 +00:00
relation - > cc_ep = cc_ep ;
2020-09-27 12:17:11 +00:00
relation - > cc_callref = callref ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-SETUP-REQ from originator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) ) ;
2024-01-09 12:56:44 +00:00
/* init metering */
call - > metering_data_valid = 0 ;
2020-09-27 12:17:11 +00:00
/* 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 ] ;
2021-08-25 13:44:03 +00:00
int complete = 0 ;
2020-09-27 12:17:11 +00:00
uint8_t duration_ms , pause_ms , dtmf_mode ;
char dtmf [ 256 ] ;
char command [ 512 ] ;
int rc ;
osmo_cc_msg_t * new_msg ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-INFO-REQ from originator. \n " , relation_name ( call - > relation_list ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) ;
}
2021-08-25 13:44:03 +00:00
/* indicate complete number to environment */
rc = osmo_cc_get_ie_complete ( old_msg , 0 ) ;
if ( rc )
complete = 1 ;
2020-09-27 12:17:11 +00:00
/* 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 ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > next - > cc_ep , call - > relation_list - > next - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
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 */
2021-08-25 13:44:03 +00:00
routing_env_dialing ( & call - > routing , call - > dialing_number , call - > dialing_keypad , complete ) ;
2020-09-27 12:17:11 +00:00
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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-DISC-REQ from originator. \n " , relation_name ( call - > relation_list ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* 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 ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > next - > cc_ep , call - > relation_list - > next - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
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 ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
}
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_IND ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* 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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-REL-REQ from originator. \n " , relation_name ( call - > relation_list ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
}
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_CNF ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* 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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s Other CC message from originator. \n " , relation_name ( call - > relation_list ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 */
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > next - > cc_ep , call - > relation_list - > next - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-PROGRESS-REQ from terminator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
}
}
/* 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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-SETUP-ACK-REQ from terminator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
}
/* 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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-PROC-REQ from terminator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) ;
2024-01-09 12:56:44 +00:00
/* metering */
if ( call - > metering_data_valid ) {
2024-01-29 10:33:19 +00:00
osmo_cc_add_ie_metering ( new_msg , call - > metering_connect_units , & ( call - > metering_unit_period ) ) ;
2024-01-09 12:56:44 +00:00
call - > metering_data_valid = 0 ;
}
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
}
/* 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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-ALERT-REQ from terminator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) ;
2024-01-09 12:56:44 +00:00
/* metering */
if ( call - > metering_data_valid ) {
2024-01-29 10:33:19 +00:00
osmo_cc_add_ie_metering ( new_msg , call - > metering_connect_units , & ( call - > metering_unit_period ) ) ;
2024-01-09 12:56:44 +00:00
call - > metering_data_valid = 0 ;
}
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
}
/* 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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-SETUP-RSP (connect) from terminator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " Connect message from terminating call now allowed in state '%s'. \n " , state_names [ call - > state ] ) ;
2020-09-27 12:17:11 +00:00
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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Sending 'non-selected user clearing' to other terminator. \n " ) ;
2020-09-27 12:17:11 +00:00
/* send release message */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
2021-08-25 13:35:09 +00:00
osmo_cc_add_ie_cause ( new_msg , other - > cc_ep - > serving_location , OSMO_CC_ISDN_CAUSE_NONSE_USER_CLR , 0 , 0 ) ;
osmo_cc_ll_msg ( other - > cc_ep , other - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* 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 */
2022-09-22 13:09:59 +00:00
if ( call - > rtp_proxy ) {
2020-09-27 12:17:11 +00:00
/* 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 ) ;
2024-01-09 12:56:44 +00:00
/* metering */
if ( call - > metering_data_valid ) {
2024-01-29 10:33:19 +00:00
osmo_cc_add_ie_metering ( new_msg , call - > metering_connect_units , & ( call - > metering_unit_period ) ) ;
2024-01-09 12:56:44 +00:00
call - > metering_data_valid = 0 ;
}
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
}
/* 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 ;
2024-01-07 17:11:37 +00:00
int late_audio = 0 ;
uint8_t coding , location , progress ;
int rc ;
2020-09-27 12:17:11 +00:00
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-DISC-REQ from terminator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
/* update call state for status display */
relation - > state = CALL_STATE_DISC_FROM_TERM ;
status_needs_update = 1 ;
2024-01-07 17:11:37 +00:00
rc = osmo_cc_get_ie_progress ( old_msg , 0 , & coding , & location , & progress ) ;
if ( rc > = 0 & & coding = = OSMO_CC_CODING_ITU_T & & ( progress = = OSMO_CC_PROGRESS_NOT_END_TO_END_ISDN | | progress = = OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE ) )
late_audio = 1 ;
2020-09-27 12:17:11 +00:00
/* 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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Got a disconnect before connect. \n " ) ;
2020-09-27 12:17:11 +00:00
/* if there is only one terminating call, forward that disconnect */
if ( ! call - > forking ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Call is not forking, so we directly forward this disconnect. \n " ) ;
2020-09-27 12:17:11 +00:00
/* 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 ;
2024-01-07 17:11:37 +00:00
if ( late_audio ) {
/* send SDP answer */
proxy_send_sdp_answer ( call - > relation_list , new_msg ) ;
}
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
return ;
}
/* collect cause */
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Call is forking, so we collect ISDN cause and destroy relation. \n " ) ;
2020-09-27 12:17:11 +00:00
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 ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* remove relation */
relation_destroy ( relation ) ;
/* if all terminating calls have been released, release the originator and destroy call */
if ( ! call - > relation_list - > next ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " All terminators have disconnected, so we forward a release with the collected cause. \n " ) ;
2020-09-27 12:17:11 +00:00
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
2021-08-25 13:35:09 +00:00
osmo_cc_add_ie_cause ( new_msg , call - > relation_list - > cc_ep - > serving_location , call - > collect_cause , 0 , 0 ) ;
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Got a disconnect after connect, so we directly forward this disconnect. \n " ) ;
2020-09-27 12:17:11 +00:00
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_IND ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_IND ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* destroy call */
call_destroy ( call ) ;
return ;
}
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " Disconnect message from terminating call now allowed in state '%s'. \n " , state_names [ call - > state ] ) ;
2020-09-27 12:17:11 +00:00
}
/* 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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-REL-REQ from terminator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Got a release before connect. \n " ) ;
2020-09-27 12:17:11 +00:00
/* if there is only one terminating call, forward that disconnect */
if ( ! call - > forking ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Call is not forking, so we directly forward this release. \n " ) ;
2020-09-27 12:17:11 +00:00
/* forward message to originator */
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_IND ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* confirm to terminator */
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_CNF ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* destroy call */
call_destroy ( call ) ;
return ;
}
/* collect cause */
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Call is forking, so we collect ISDN cause and destroy relation. \n " ) ;
2020-09-27 12:17:11 +00:00
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 ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* remove relation */
relation_destroy ( relation ) ;
/* if all terminating calls have been released, release the originator and destroy call */
if ( ! call - > relation_list - > next ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " All terminators have released, so we forward it with the collected cause. \n " ) ;
2020-09-27 12:17:11 +00:00
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
2021-08-25 13:35:09 +00:00
osmo_cc_add_ie_cause ( new_msg , call - > relation_list - > cc_ep - > serving_location , call - > collect_cause , 0 , 0 ) ;
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Got a release after connect, so we directly forward this release. \n " ) ;
2020-09-27 12:17:11 +00:00
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_IND ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
new_msg = osmo_cc_clone_msg ( old_msg ) ;
new_msg - > type = OSMO_CC_MSG_REL_CNF ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* destroy call */
call_destroy ( call ) ;
return ;
}
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " Release message from terminating call now allowed in state '%s'. \n " , state_names [ call - > state ] ) ;
2020-09-27 12:17:11 +00:00
}
/* 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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s Other CC message from terminator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 */
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
return ;
}
}
/* handle message from upper layer */
2021-08-25 13:35:09 +00:00
void cc_message ( osmo_cc_endpoint_t * cc_ep , uint32_t callref , osmo_cc_msg_t * msg )
2020-09-27 12:17:11 +00:00
{
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 ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " Received message without call instance, please fix! \n " ) ;
2020-09-27 12:17:11 +00:00
return ;
}
2021-08-25 13:35:09 +00:00
orig_setup ( cc_ep , callref , msg ) ;
2020-09-27 12:17:11 +00:00
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 */
2022-09-22 13:09:59 +00:00
if ( call - > rtp_proxy & & relation - > cc_session & & ! relation - > codec_negotiated ) {
2020-09-27 12:17:11 +00:00
/* remove progress, since it will be added with the SDP answer */
osmo_cc_remove_ie ( msg , OSMO_CC_IE_PROGRESS , 0 ) ;
/* negotiate codec */
2023-11-12 16:45:29 +00:00
osmo_cc_helper_audio_negotiate_te ( msg , & relation - > cc_session , & relation - > codec , & relation - > telephone_event ) ;
2020-09-27 12:17:11 +00:00
if ( relation - > codec )
relation - > codec_negotiated = 1 ;
}
/* remove SDP, if we do RTP-Proxy */
2022-09-22 13:09:59 +00:00
if ( rc_sdp > = 0 & & call - > rtp_proxy )
2020-09-27 12:17:11 +00:00
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-08-25 13:44:03 +00:00
static struct param {
/* rtp-proxy */
struct osmo_cc_helper_audio_codecs orig_codecs [ MAX_CODECS + 1 ] ;
struct osmo_cc_helper_audio_codecs term_codecs [ MAX_CODECS + 1 ] ;
int orig_given , term_given ;
2020-09-27 12:17:11 +00:00
2021-08-25 13:44:03 +00:00
/* play */
char * filename , * volume , * loop ;
2020-09-27 12:17:11 +00:00
2021-08-25 13:44:03 +00:00
/* gain */
char * gain ;
2020-09-27 12:17:11 +00:00
2023-03-22 19:01:22 +00:00
/* maximum amplification (compressor) */
char * max ;
2024-02-08 22:16:23 +00:00
/* increase amplification in dB per second (compressor) */
char * increase ;
2023-03-22 19:01:22 +00:00
2021-08-25 13:44:03 +00:00
/* call */
char * interface ;
int bearer_coding , bearer_capability , bearer_mode ;
char * calling ;
int calling_type , calling_plan , calling_present , calling_screen , no_calling ;
char * calling2 ;
int calling2_type , calling2_plan , calling2_present , calling2_screen , no_calling2 ;
char * redirecting ;
int redirecting_type , redirecting_plan , redirecting_present , redirecting_screen , redirecting_reason , no_redirecting ;
char * dialing ;
int dialing_type , dialing_plan ;
char * keypad ;
2023-12-01 16:34:35 +00:00
char * sending_complete ;
2021-08-25 13:44:03 +00:00
2024-01-09 12:56:44 +00:00
/* metering */
int metering_connect_units ;
2024-01-29 10:33:19 +00:00
char * metering_unit_period ;
2024-01-09 12:56:44 +00:00
2021-08-25 13:44:03 +00:00
/* disc/rel */
int isdn_cause , sip_cause ;
2023-11-12 16:45:29 +00:00
/* telephone-event */
char * event ;
2021-08-25 13:44:03 +00:00
/* error */
char * error ;
} param ;
struct param_def {
const char * n ;
char * * s ;
int * i ;
struct osmo_cc_helper_audio_codecs * o , * t ;
const char * d ;
int mandatory ;
int no_value ;
const char * ( * value2name ) ( int value ) ;
int ( * name2value ) ( const char * name ) ;
int num ;
} ;
struct command_def {
const char * name ;
void ( * func ) ( call_t * call , int argc , char * argv [ ] , struct command_def * command_def ) ;
const char * description ;
struct param_def * param_list ;
} command_def [ ] ;
2020-09-27 12:17:11 +00:00
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 ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " Maximum of %d codecs are reached. Ignoring codec '%s'. \n " , c , name ) ;
2021-02-27 11:07:32 +00:00
break ;
}
memcpy ( & codecs [ c ] , & codecs_def [ i ] , sizeof ( * codecs ) ) ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_INFO , " Adding given codec '%s'. \n " , codecs [ c ] . payload_name ) ;
2021-02-27 11:07:32 +00:00
c + + ;
break ;
}
}
if ( ! codecs_def [ i ] . payload_name ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " Given codec '%s' not supported! \n " , name ) ;
2021-02-27 11:07:32 +00:00
}
}
memset ( & codecs [ c ] , 0 , sizeof ( * codecs ) ) ;
}
2021-08-25 13:44:03 +00:00
static int parse_params ( int argc , char * argv [ ] , struct command_def * command_def )
{
struct param_def * param_def = command_def - > param_list ;
2022-12-16 11:45:48 +00:00
int i , j , len ;
2021-08-25 13:44:03 +00:00
char * arg ;
/* clear parameter set */
memset ( & param , 0 , sizeof ( param ) ) ;
2023-01-22 08:01:53 +00:00
/* mark optional call parameters as beeing unset */
param . bearer_coding = param . bearer_capability = param . bearer_mode = - 1 ;
2023-01-23 17:44:06 +00:00
param . calling_type = param . calling_plan = param . calling_present = param . calling_screen = - 1 ;
param . calling2_type = param . calling2_plan = param . calling2_present = param . calling2_screen = - 1 ;
param . redirecting_type = param . redirecting_plan = param . redirecting_present = param . redirecting_screen = param . redirecting_reason = - 1 ;
2023-01-22 08:01:53 +00:00
param . dialing_type = param . dialing_plan = - 1 ;
2021-08-25 13:44:03 +00:00
/* no parameter list given */
if ( param_def = = NULL )
return - EINVAL ;
/* loop through all arguments and stop if there is a ':' */
for ( i = 0 ; i < argc & & argv [ i ] [ 0 ] ! = ' : ' ; i + + ) {
arg = argv [ i ] ;
/* loop through all possible parameter definitions */
for ( j = 0 ; param_def [ j ] . n ; j + + ) {
2022-12-16 11:45:48 +00:00
for ( len = 0 ; arg [ len ] & & arg [ len ] ! = ' = ' ; len + + )
;
2021-08-25 13:44:03 +00:00
/* stop if parameter has been found */
2022-12-16 11:45:48 +00:00
if ( ! strncasecmp ( arg , param_def [ j ] . n , len ) ) {
2021-08-25 13:44:03 +00:00
arg + = strlen ( param_def [ j ] . n ) ;
/* no value */
if ( * arg = = ' \0 ' ) {
arg = " " ;
break ;
}
/* has value */
if ( * arg = = ' = ' ) {
arg + + ;
break ;
}
/* continue, if more digits given */
}
}
if ( ! param_def [ j ] . n ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " Unknown '%s' parameter '%s' from routing. \n " , command_def - > name , argv [ i ] ) ;
2021-08-25 13:44:03 +00:00
continue ;
}
if ( arg [ 0 ] & & param_def [ j ] . no_value ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " '%s' parameter '%s' has no value, ignoring. \n " , command_def - > name , argv [ i ] ) ;
2021-08-25 13:44:03 +00:00
continue ;
}
if ( param_def [ j ] . no_value )
arg = " 1 " ;
if ( param_def [ j ] . i ) {
if ( param_def [ j ] . name2value ) {
* param_def [ j ] . i = param_def [ j ] . name2value ( arg ) ;
if ( * param_def [ j ] . i < 0 )
* param_def [ j ] . i = atoi ( arg ) ;
} else
* param_def [ j ] . i = atoi ( arg ) ;
}
if ( param_def [ j ] . s )
* param_def [ j ] . s = arg ;
if ( param_def [ j ] . o ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_INFO , " Originating codecs given: '%s'. \n " , arg ) ;
2021-08-25 13:44:03 +00:00
add_codecs_by_names ( param_def [ j ] . o , arg ) ;
param . orig_given = 1 ;
}
if ( param_def [ j ] . t ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_INFO , " Terminating codecs given: '%s'. \n " , arg ) ;
2021-08-25 13:44:03 +00:00
add_codecs_by_names ( param_def [ j ] . t , arg ) ;
param . term_given = 1 ;
}
}
return i ;
}
2020-09-27 12:17:11 +00:00
/* routing orders us to activate rtp proxy */
2021-08-25 13:44:03 +00:00
struct param_def param_rtp_proxy [ ] = {
2022-11-24 18:26:19 +00:00
{ . n = " orig-codecs " , . o = param . orig_codecs , . d = " Define allowed codecs to be accepted by originating endpoint. (codec1,[codec2[,...]]) " } ,
{ . n = " term-codecs " , . t = param . term_codecs , . d = " Define allowed codecs on terminating endpoint in order of preference. (codec1,[codec2[,...]]) " } ,
2021-08-25 13:44:03 +00:00
{ . n = NULL }
} ;
static void routing_rtp_proxy ( call_t * call , int argc , char * argv [ ] , struct command_def * command_def )
2020-09-27 12:17:11 +00:00
{
call_relation_t * relation = call - > relation_list ;
/* ignore, if already enabled */
2022-09-22 13:09:59 +00:00
if ( call - > rtp_proxy ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_NOTICE , " RTP proxy is already enabled!. \n " ) ;
2020-09-27 12:17:11 +00:00
return ;
}
2021-08-25 13:44:03 +00:00
parse_params ( argc , argv , command_def ) ;
if ( ! param . orig_given ) {
2021-02-27 11:07:32 +00:00
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 ] ) ) ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_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 ) ;
2021-08-25 13:44:03 +00:00
} else
memcpy ( relation - > orig_codecs , param . orig_codecs , sizeof ( param . orig_codecs ) ) ;
2021-02-27 11:07:32 +00:00
2021-08-25 13:44:03 +00:00
if ( ! param . term_given ) {
2021-02-27 11:07:32 +00:00
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 ] ) ) ;
2023-11-12 16:45:29 +00:00
memcpy ( & relation - > term_codecs [ 2 ] , & codecs_def [ 2 ] , sizeof ( relation - > term_codecs [ 2 ] ) ) ;
memset ( & relation - > term_codecs [ 3 ] , 0 , sizeof ( relation - > term_codecs [ 3 ] ) ) ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_INFO , " No terminating codeds given, using default '%s' and '%s' and '%s'. \n " , relation - > term_codecs [ 0 ] . payload_name , relation - > term_codecs [ 1 ] . payload_name , relation - > term_codecs [ 2 ] . payload_name ) ;
2021-08-25 13:44:03 +00:00
} else
memcpy ( relation - > term_codecs , param . term_codecs , sizeof ( param . term_codecs ) ) ;
2021-02-27 11:07:32 +00:00
2020-09-27 12:17:11 +00:00
/* error, if we already negotiated */
if ( call - > sdp_forwarded ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " RTP-Proxy cannot be enabled now, because we already forwarded a call. \n " ) ;
2020-09-27 12:17:11 +00:00
return ;
}
/* no SDP */
if ( ! relation - > sdp ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " Originator's setup message does not contain SDP, please fix!. \n " ) ;
2020-09-27 12:17:11 +00:00
return ;
}
2022-09-22 13:09:59 +00:00
call - > rtp_proxy = 1 ;
2020-09-27 12:17:11 +00:00
}
/* routing orders us to play a wave file */
2021-08-25 13:44:03 +00:00
struct param_def param_play [ ] = {
{ . n = " filename " , . s = & param . filename , . d = " file name of audio file " , . mandatory = 1 } ,
{ . n = " volume " , . s = & param . volume , . d = " speech level, which is '1.0' " } ,
{ . n = " loop " , . s = & param . loop , . d = " play in an endless loop " , . no_value = 1 } ,
{ . n = NULL }
} ;
static void routing_play ( call_t * call , int argc , char * argv [ ] , struct command_def * command_def )
2020-09-27 12:17:11 +00:00
{
int samplerate = 8000 , channels = 0 ;
double deviation ;
int rc ;
2022-09-22 13:09:59 +00:00
wave_destroy_playback ( & call - > play ) ;
2020-09-27 12:17:11 +00:00
2022-09-22 13:09:59 +00:00
if ( ! call - > rtp_proxy ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " RTP-Proxy must be enabled to play a file!. \n " ) ;
2020-09-27 12:17:11 +00:00
return ;
}
2021-08-25 13:44:03 +00:00
parse_params ( argc , argv , command_def ) ;
2020-09-27 12:17:11 +00:00
2021-08-25 13:44:03 +00:00
if ( ! param . filename ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " 'play' command reqires a file name as parameter. \n " ) ;
2020-09-27 12:17:11 +00:00
return ;
}
2021-08-25 13:44:03 +00:00
if ( ! param . volume )
param . volume = " 1.0 " ;
deviation = 1.0 / SPEECH_LEVEL * atof ( param . volume ) ;
2022-09-22 13:09:59 +00:00
rc = wave_create_playback ( & call - > play , param . filename , & samplerate , & channels , deviation ) ;
2020-09-27 12:17:11 +00:00
if ( rc < 0 )
return ;
2022-09-22 13:09:59 +00:00
strncpy ( call - > play_filename , param . filename , sizeof ( call - > play_filename ) - 1 ) ;
call - > play_deviation = deviation ;
2020-09-27 12:17:11 +00:00
if ( channels ! = 1 & & channels ! = 2 ) {
2022-09-22 13:09:59 +00:00
wave_destroy_playback ( & call - > play ) ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " 'play' command reqires a wave file that has 1 or 2 channels only. \n " ) ;
2020-09-27 12:17:11 +00:00
return ;
}
2021-08-25 13:44:03 +00:00
if ( param . loop )
2022-09-22 13:09:59 +00:00
call - > play_loop = 1 ;
2020-09-27 12:17:11 +00:00
}
/* routing orders us stop playing a wave file */
2021-08-25 13:44:03 +00:00
static void routing_play_stop ( call_t * call , int __attribute__ ( ( unused ) ) argc , char __attribute__ ( ( unused ) ) * argv [ ] , struct command_def __attribute__ ( ( unused ) ) * command_def )
2020-09-27 12:17:11 +00:00
{
2022-09-22 13:09:59 +00:00
wave_destroy_playback ( & call - > play ) ;
2020-09-27 12:17:11 +00:00
}
/* routing orders us to record a wave file */
2021-08-25 13:44:03 +00:00
struct param_def param_record [ ] = {
{ . n = " filename " , . s = & param . filename , . d = " file name of audio file " , . mandatory = 1 } ,
{ . n = " volume " , . s = & param . volume , . d = " speech level, which is '1.0' " } ,
{ . n = NULL }
} ;
static void routing_record ( call_t * call , int argc , char * argv [ ] , struct command_def * command_def )
2020-09-27 12:17:11 +00:00
{
int samplerate = 8000 , channels = 2 ;
int rc ;
2022-09-22 13:09:59 +00:00
wave_destroy_record ( & call - > rec ) ;
2020-09-27 12:17:11 +00:00
2022-09-22 13:09:59 +00:00
if ( ! call - > rtp_proxy ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " RTP-Proxy must be enabled to record a file!. \n " ) ;
2020-09-27 12:17:11 +00:00
return ;
}
2021-08-25 13:44:03 +00:00
parse_params ( argc , argv , command_def ) ;
2020-09-27 12:17:11 +00:00
2021-08-25 13:44:03 +00:00
if ( ! param . filename ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " 'record' command reqires a file name as parameter. \n " ) ;
2020-09-27 12:17:11 +00:00
return ;
}
2021-08-25 13:44:03 +00:00
if ( ! param . volume )
param . volume = " 1.0 " ;
2022-09-22 13:09:59 +00:00
rc = wave_create_record ( & call - > rec , param . filename , samplerate , channels , 1.0 / SPEECH_LEVEL / atof ( param . volume ) ) ;
2020-09-27 12:17:11 +00:00
if ( rc < 0 )
return ;
}
/* routing orders us stop recording a wave file */
2021-08-25 13:44:03 +00:00
static void routing_record_stop ( call_t * call , int __attribute__ ( ( unused ) ) argc , char __attribute__ ( ( unused ) ) * argv [ ] , struct command_def __attribute__ ( ( unused ) ) * command_def )
2020-09-27 12:17:11 +00:00
{
2022-09-22 13:09:59 +00:00
wave_destroy_record ( & call - > rec ) ;
2020-09-27 12:17:11 +00:00
}
/* routing orders us to set local gain */
2021-08-25 13:44:03 +00:00
struct param_def param_gain [ ] = {
{ . n = " gain " , . s = & param . gain , . d = " gain in dB " } ,
{ . n = NULL }
} ;
2020-09-27 12:17:11 +00:00
2021-08-25 13:44:03 +00:00
static void routing_tx_gain ( call_t * call , int argc , char * argv [ ] , struct command_def * command_def )
{
2022-09-22 13:09:59 +00:00
if ( ! call - > rtp_proxy ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " RTP-Proxy must be enabled to record a file! \n " ) ;
2020-09-27 12:17:11 +00:00
return ;
}
2021-08-25 13:44:03 +00:00
parse_params ( argc , argv , command_def ) ;
if ( ! param . gain ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " 'tx-gain' command reqires a gain value as parameter. \n " ) ;
2021-08-25 13:44:03 +00:00
return ;
2020-09-27 12:17:11 +00:00
}
2021-08-25 13:44:03 +00:00
call - > tx_gain = atof ( param . gain ) ;
}
static void routing_rx_gain ( call_t * call , int argc , char * argv [ ] , struct command_def * command_def )
{
2022-09-22 13:09:59 +00:00
if ( ! call - > rtp_proxy ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " RTP-Proxy must be enabled to record a file! \n " ) ;
2020-09-27 12:17:11 +00:00
return ;
}
2021-08-25 13:44:03 +00:00
parse_params ( argc , argv , command_def ) ;
if ( ! param . gain ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " 'rx-gain' command reqires a gain value as parameter. \n " ) ;
2021-08-25 13:44:03 +00:00
return ;
}
call - > rx_gain = atof ( param . gain ) ;
2020-09-27 12:17:11 +00:00
}
2023-03-22 19:01:22 +00:00
# define ABWAERTS_DBS (2.6 / 0.020) /* 2.6 dB in 20 ms */
# define AUFWAERTS_DBS 16.0 /* B-Netz uses 4.3, but this is more convenient */
# define MINIMUM_DB -16.0
# define MAXIMUM_DB 2.6
/* routing orders us to set levels */
struct param_def param_compress [ ] = {
{ . n = " max " , . s = & param . max , . d = " maximum amplification in dB (default 16) " } ,
2024-02-08 22:16:23 +00:00
{ . n = " increase " , . s = & param . increase , . d = " increase amplification in dB per second (default 16) " } ,
2023-03-22 19:01:22 +00:00
{ . n = NULL }
} ;
/* routing orders us to use compressor */
static void routing_tx_compress ( call_t * call , int __attribute__ ( ( unused ) ) argc , char __attribute__ ( ( unused ) ) * argv [ ] , struct command_def __attribute__ ( ( unused ) ) * command_def )
{
if ( ! call - > rtp_proxy ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " RTP-Proxy must be enabled to record a file! \n " ) ;
2023-03-22 19:01:22 +00:00
return ;
}
parse_params ( argc , argv , command_def ) ;
call - > tx_compress = 1 ;
2024-02-08 22:16:23 +00:00
init_sendevolumenregler ( & call - > tx_compressor , 8000 , ABWAERTS_DBS , ( param . increase ) ? atof ( param . increase ) : AUFWAERTS_DBS , MAXIMUM_DB , ( param . max ) ? - atof ( param . max ) : MINIMUM_DB , 1.0 ) ;
2023-03-22 19:01:22 +00:00
}
static void routing_rx_compress ( call_t * call , int __attribute__ ( ( unused ) ) argc , char __attribute__ ( ( unused ) ) * argv [ ] , struct command_def __attribute__ ( ( unused ) ) * command_def )
{
if ( ! call - > rtp_proxy ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " RTP-Proxy must be enabled to record a file! \n " ) ;
2023-03-22 19:01:22 +00:00
return ;
}
parse_params ( argc , argv , command_def ) ;
call - > rx_compress = 1 ;
2024-02-08 22:16:23 +00:00
init_sendevolumenregler ( & call - > rx_compressor , 8000 , ABWAERTS_DBS , ( param . increase ) ? atof ( param . increase ) : AUFWAERTS_DBS , MAXIMUM_DB , ( param . max ) ? - atof ( param . max ) : MINIMUM_DB , 1.0 ) ;
2023-03-22 19:01:22 +00:00
}
2020-09-27 12:17:11 +00:00
/* routing orders us to call remote end */
2021-08-25 13:44:03 +00:00
struct param_def param_call [ ] = {
{ . n = " interface " , . s = & param . interface , . d = " name of interface to route the call to " , . mandatory = 1 } ,
{ . n = " bearer-coding " , . i = & param . bearer_coding , . d = " coding of bearer capability " , . name2value = osmo_cc_coding_name2value , . value2name = osmo_cc_coding_value2name , . num = OSMO_CC_CODING_NUM } ,
{ . n = " bearer-capability " , . i = & param . bearer_capability , . d = " bearer capability value " , . name2value = osmo_cc_capability_name2value , . value2name = osmo_cc_capability_value2name , . num = OSMO_CC_CAPABILITY_NUM } ,
{ . n = " bearer-mode " , . i = & param . bearer_mode , . d = " bearer mode " , . name2value = osmo_cc_mode_name2value , . value2name = osmo_cc_mode_value2name , . num = OSMO_CC_MODE_NUM } ,
{ . n = " calling " , . s = & param . calling , . d = " calling party number " } ,
{ . n = " calling-type " , . i = & param . calling_type , . d = " type of calling party number " , . name2value = osmo_cc_type_name2value , . value2name = osmo_cc_type_value2name , . num = OSMO_CC_TYPE_NUM } ,
{ . n = " calling-plan " , . i = & param . calling_plan , . d = " numbering plan of calling party number " , . name2value = osmo_cc_plan_name2value , . value2name = osmo_cc_plan_value2name , . num = OSMO_CC_PLAN_NUM } ,
{ . n = " calling-present " , . i = & param . calling_present , . d = " presentation of calling party number " , . name2value = osmo_cc_present_name2value , . value2name = osmo_cc_present_value2name , . num = OSMO_CC_PRESENT_NUM } ,
{ . n = " calling-screen " , . i = & param . calling_screen , . d = " screening indicator of calling party number " , . name2value = osmo_cc_screen_name2value , . value2name = osmo_cc_screen_value2name , . num = OSMO_CC_SCREEN_NUM } ,
{ . n = " no-calling " , . i = & param . no_calling , . d = " disable calling party number " , . no_value = 1 } ,
{ . n = " calling2 " , . s = & param . calling2 , . d = " second calling party number " } ,
{ . n = " calling2-type " , . i = & param . calling2_type , . d = " type of second calling party number " , . name2value = osmo_cc_type_name2value , . value2name = osmo_cc_type_value2name , . num = OSMO_CC_TYPE_NUM } ,
{ . n = " calling2-plan " , . i = & param . calling2_plan , . d = " numbering plan of second calling party number " , . name2value = osmo_cc_plan_name2value , . value2name = osmo_cc_plan_value2name , . num = OSMO_CC_PLAN_NUM } ,
{ . n = " calling2-present " , . i = & param . calling2_present , . d = " presentation of second calling party number " , . name2value = osmo_cc_present_name2value , . value2name = osmo_cc_present_value2name , . num = OSMO_CC_PRESENT_NUM } ,
{ . n = " calling2-screen " , . i = & param . calling2_screen , . d = " screening indicator of second calling party number " , . name2value = osmo_cc_screen_name2value , . value2name = osmo_cc_screen_value2name , . num = OSMO_CC_SCREEN_NUM } ,
{ . n = " no-calling2 " , . i = & param . no_calling2 , . d = " disable seconds calling party number " , . no_value = 1 } ,
{ . n = " redirecting " , . s = & param . redirecting , . d = " redirecting number " } ,
{ . n = " redirecting-type " , . i = & param . redirecting_type , . d = " type of redirecting number " , . name2value = osmo_cc_type_name2value , . value2name = osmo_cc_type_value2name , . num = OSMO_CC_TYPE_NUM } ,
{ . n = " redirecting-plan " , . i = & param . redirecting_plan , . d = " numbering plan of redirecting number " , . name2value = osmo_cc_plan_name2value , . value2name = osmo_cc_plan_value2name , . num = OSMO_CC_PLAN_NUM } ,
{ . n = " redirecting-present " , . i = & param . redirecting_present , . d = " presentation of redirecting number " , . name2value = osmo_cc_present_name2value , . value2name = osmo_cc_present_value2name , . num = OSMO_CC_PRESENT_NUM } ,
{ . n = " redirecting-screen " , . i = & param . redirecting_screen , . d = " screening indicator of redirecting number " , . name2value = osmo_cc_screen_name2value , . value2name = osmo_cc_screen_value2name , . num = OSMO_CC_SCREEN_NUM } ,
{ . n = " redirecting-reason " , . i = & param . redirecting_reason , . d = " reason for redirecting the call " , . name2value = osmo_cc_redir_reason_name2value , . value2name = osmo_cc_redir_reason_value2name , . num = OSMO_CC_REDIR_REASON_NUM } ,
{ . n = " no-redirecting " , . i = & param . no_redirecting , . d = " disable redirecting number " , . no_value = 1 } ,
{ . n = " dialing " , . s = & param . dialing , . d = " alter dialed number " } ,
{ . n = " dialing-type " , . i = & param . dialing_type , . d = " type of dialed number " , . name2value = osmo_cc_type_name2value , . value2name = osmo_cc_type_value2name , . num = OSMO_CC_TYPE_NUM } ,
{ . n = " dialing-plan " , . i = & param . dialing_plan , . d = " numbering plan of dialed number " , . name2value = osmo_cc_plan_name2value , . value2name = osmo_cc_plan_value2name , . num = OSMO_CC_PLAN_NUM } ,
{ . n = " keypad " , . s = & param . keypad , . d = " keypulse digit or digits (might be required by some ISDN applications) " } ,
2023-12-01 16:34:35 +00:00
{ . n = " sending-complete " , . s = & param . sending_complete , . d = " Tell the called interface that the dialing is complete. " , . no_value = 1 } ,
2021-08-25 13:44:03 +00:00
{ . n = NULL }
} ;
static void routing_call ( call_t * call , int argc , char * argv [ ] , struct command_def * command_def )
2020-09-27 12:17:11 +00:00
{
2021-08-25 13:44:03 +00:00
osmo_cc_call_t * att = NULL ;
2020-09-27 12:17:11 +00:00
uint8_t coding , capability , mode ;
uint8_t type , plan , present , screen , reason ;
char number [ 256 ] ;
2021-08-25 13:44:03 +00:00
int e , i , rc , calls = 0 ;
2020-09-27 12:17:11 +00:00
osmo_cc_msg_t * new_msg , * setup_msg ;
/* if we have a call, we don't add more terminators */
if ( call - > relation_list - > next ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " Multiple call commands from routing are not allowed. \n " ) ;
2020-09-27 12:17:11 +00:00
return ;
}
setup_msg = call - > setup_msg ;
next_call :
2021-08-25 13:44:03 +00:00
i = parse_params ( argc , argv , command_def ) ;
2020-09-27 12:17:11 +00:00
/* if more calls, then forward arguments behind colon, otherwise set argc to 0 */
2021-08-25 13:44:03 +00:00
if ( i > = 0 & & i < argc ) {
2020-09-27 12:17:11 +00:00
argv + = i + 1 ;
argc - = i + 1 ;
} else
argc = 0 ;
2021-08-25 13:44:03 +00:00
if ( ! param . interface ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " 'call' command without 'interface' parameter. \n " ) ;
2023-12-10 13:43:00 +00:00
call - > collect_cause = osmo_cc_collect_cause ( call - > collect_cause , OSMO_CC_ISDN_CAUSE_NETWORK_OOO ) ;
2020-09-27 12:17:11 +00:00
goto try_next ;
}
2021-08-25 13:44:03 +00:00
for ( e = 0 ; cc_ep_list [ e ] ; e + + ) {
att = osmo_cc_get_attached_interface ( cc_ep_list [ e ] , param . interface ) ;
if ( att )
break ;
}
if ( ! att ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " 'call' command with 'interface' parameter '%s' which is not attached. \n " , param . interface ) ;
2023-12-10 13:43:00 +00:00
call - > collect_cause = osmo_cc_collect_cause ( call - > collect_cause , OSMO_CC_ISDN_CAUSE_DEST_OOO ) ;
2021-08-25 13:44:03 +00:00
goto try_next ;
}
2020-09-27 12:17:11 +00:00
calls + + ;
/* set call forking, if we have more than one terminating call */
if ( calls > 1 )
call - > forking = 1 ;
2021-08-25 13:35:09 +00:00
2020-09-27 12:17:11 +00:00
/* create endpoint */
2021-08-25 13:35:09 +00:00
osmo_cc_call_t * cc_call = osmo_cc_call_new ( att - > ep ) ;
2020-09-27 12:17:11 +00:00
call_relation_t * relation = relation_create ( call ) ;
/* link with cc */
2021-08-25 13:35:09 +00:00
relation - > cc_ep = att - > ep ;
2020-09-27 12:17:11 +00:00
relation - > cc_callref = cc_call - > callref ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-SETUP-IND to terminator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
/* create setup message */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_SETUP_IND ) ;
/* interface name */
2021-08-25 13:44:03 +00:00
osmo_cc_add_ie_called_interface ( new_msg , param . interface ) ;
2020-09-27 12:17:11 +00:00
/* bearer capability */
rc = osmo_cc_get_ie_bearer ( setup_msg , 0 , & coding , & capability , & mode ) ;
2023-01-22 08:01:53 +00:00
if ( rc > = 0 | | param . bearer_coding > = 0 | | param . bearer_capability > = 0 | | param . bearer_mode > = 0 ) {
2020-09-27 12:17:11 +00:00
/* 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 */
2023-01-22 08:01:53 +00:00
if ( param . bearer_coding > = 0 )
2021-08-25 13:44:03 +00:00
coding = param . bearer_coding ;
2023-01-22 08:01:53 +00:00
if ( param . bearer_capability > = 0 )
2021-08-25 13:44:03 +00:00
capability = param . bearer_capability ;
2023-01-22 08:01:53 +00:00
if ( param . bearer_mode > = 0 )
2021-08-25 13:44:03 +00:00
mode = param . bearer_coding ;
2020-09-27 12:17:11 +00:00
osmo_cc_add_ie_bearer ( new_msg , coding , capability , mode ) ;
}
/* calling */
2021-08-25 13:44:03 +00:00
if ( ! param . no_calling ) {
2020-09-27 12:17:11 +00:00
rc = osmo_cc_get_ie_calling ( setup_msg , 0 , & type , & plan , & present , & screen , number , sizeof ( number ) ) ;
2023-01-22 08:01:53 +00:00
if ( rc > = 0 | | param . calling_type > = 0 | | param . calling_plan > = 0 | | param . calling_present > = 0 | | param . calling_screen > = 0 | | param . calling ) {
2020-09-27 12:17:11 +00:00
/* 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 */
2023-01-22 08:01:53 +00:00
if ( param . calling_type > = 0 )
2021-08-25 13:44:03 +00:00
type = param . calling_type ;
2023-01-22 08:01:53 +00:00
if ( param . calling_plan > = 0 )
2021-08-25 13:44:03 +00:00
plan = param . calling_plan ;
2023-01-22 08:01:53 +00:00
if ( param . calling_present > = 0 )
2021-08-25 13:44:03 +00:00
present = param . calling_present ;
2023-01-22 08:01:53 +00:00
if ( param . calling_screen > = 0 )
2021-08-25 13:44:03 +00:00
screen = param . calling_screen ;
if ( ! param . calling )
param . calling = number ;
osmo_cc_add_ie_calling ( new_msg , type , plan , present , screen , param . calling ) ;
2020-09-27 12:17:11 +00:00
}
}
2021-08-25 13:44:03 +00:00
if ( ! param . no_calling & & ! param . no_calling2 ) {
2020-09-27 12:17:11 +00:00
rc = osmo_cc_get_ie_calling ( setup_msg , 1 , & type , & plan , & present , & screen , number , sizeof ( number ) ) ;
2023-01-22 08:01:53 +00:00
if ( rc > = 0 | | param . calling2_type > = 0 | | param . calling2_plan > = 0 | | param . calling2_present > = 0 | | param . calling2_screen > = 0 | | param . calling2 ) {
2020-09-27 12:17:11 +00:00
/* 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 */
2023-01-22 08:01:53 +00:00
if ( param . calling2_type > = 0 )
2021-08-25 13:44:03 +00:00
type = param . calling2_type ;
2023-01-22 08:01:53 +00:00
if ( param . calling2_plan > = 0 )
2021-08-25 13:44:03 +00:00
plan = param . calling2_plan ;
2023-01-22 08:01:53 +00:00
if ( param . calling2_present > = 0 )
2021-08-25 13:44:03 +00:00
present = param . calling2_present ;
2023-01-22 08:01:53 +00:00
if ( param . calling2_screen > = 0 )
2021-08-25 13:44:03 +00:00
screen = param . calling2_screen ;
if ( ! param . calling2 )
param . calling2 = number ;
osmo_cc_add_ie_calling ( new_msg , type , plan , present , screen , param . calling2 ) ;
2020-09-27 12:17:11 +00:00
}
}
/* redirecting */
2021-08-25 13:44:03 +00:00
if ( ! param . no_redirecting ) {
2020-09-27 12:17:11 +00:00
rc = osmo_cc_get_ie_redir ( setup_msg , 0 , & type , & plan , & present , & screen , & reason , number , sizeof ( number ) ) ;
2023-01-22 08:01:53 +00:00
if ( rc > = 0 | | param . redirecting_type > = 0 | | param . redirecting_plan > = 0 | | param . redirecting_present > = 0 | | param . redirecting_screen > = 0 | | param . redirecting ) {
2020-09-27 12:17:11 +00:00
/* 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 */
2023-01-22 08:01:53 +00:00
if ( param . redirecting_type > = 0 )
2021-08-25 13:44:03 +00:00
type = param . redirecting_type ;
2023-01-22 08:01:53 +00:00
if ( param . redirecting_plan > = 0 )
2021-08-25 13:44:03 +00:00
plan = param . redirecting_plan ;
2023-01-22 08:01:53 +00:00
if ( param . redirecting_present > = 0 )
2021-08-25 13:44:03 +00:00
present = param . redirecting_present ;
2023-01-22 08:01:53 +00:00
if ( param . redirecting_screen > = 0 )
2021-08-25 13:44:03 +00:00
screen = param . redirecting_screen ;
if ( ! param . redirecting )
param . redirecting = number ;
osmo_cc_add_ie_redir ( new_msg , type , plan , present , screen , reason , param . redirecting ) ;
2020-09-27 12:17:11 +00:00
}
}
/* dialing */
rc = osmo_cc_get_ie_called ( setup_msg , 0 , & type , & plan , number , sizeof ( number ) ) ;
2023-01-22 08:01:53 +00:00
if ( rc > = 0 | | param . dialing_type > = 0 | | param . dialing_plan > = 0 | | param . dialing ) {
2020-09-27 12:17:11 +00:00
/* if not preset, use default values */
if ( rc < 0 ) {
type = OSMO_CC_TYPE_UNKNOWN ;
plan = OSMO_CC_PLAN_TELEPHONY ;
}
/* alter values */
2023-01-22 08:01:53 +00:00
if ( param . dialing_type > = 0 )
2021-08-25 13:44:03 +00:00
type = param . dialing_type ;
2023-01-22 08:01:53 +00:00
if ( param . dialing_plan > = 0 )
2021-08-25 13:44:03 +00:00
plan = param . dialing_plan ;
if ( ! param . dialing )
param . dialing = " " ;
osmo_cc_add_ie_called ( new_msg , type , plan , param . dialing ) ;
2023-12-01 16:34:35 +00:00
if ( param . sending_complete )
osmo_cc_add_ie_complete ( new_msg ) ;
2020-09-27 12:17:11 +00:00
}
/* keypad */
2021-08-25 13:44:03 +00:00
if ( param . keypad ) {
if ( param . keypad [ 0 ] )
osmo_cc_add_ie_keypad ( new_msg , param . keypad ) ;
2020-09-27 12:17:11 +00:00
}
/* only if RTP-Proxy is used */
2022-09-22 13:09:59 +00:00
if ( call - > rtp_proxy ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Sending our codecs to the terminator. \n " ) ;
2021-08-25 13:44:03 +00:00
relation - > cc_session = osmo_cc_helper_audio_offer ( & relation - > cc_ep - > session_config , 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 */
2021-08-25 13:44:03 +00:00
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* remember this, since we cannot do RTP-Proxy after */
call - > sdp_forwarded = 1 ;
/* store peer info for status */
2021-08-25 13:44:03 +00:00
strncpy ( relation - > interface , ( param . interface ) ? : " " , sizeof ( relation - > interface ) - 1 ) ;
strncpy ( relation - > id , ( param . dialing ) ? : " " , sizeof ( relation - > id ) - 1 ) ;
2020-09-27 12:17:11 +00:00
/* 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 ;
2023-12-10 13:43:00 +00:00
/* if there is no terminator (interface not attached), release the originator and destroy call */
if ( ! call - > relation_list - > next ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_NOTICE , " No interace to be called to, so we forward a release with the collected cause. \n " ) ;
2023-12-10 13:43:00 +00:00
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
osmo_cc_add_ie_cause ( new_msg , call - > relation_list - > cc_ep - > serving_location , call - > collect_cause , 0 , 0 ) ;
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
/* destroy call */
call_destroy ( call ) ;
}
2020-09-27 12:17:11 +00:00
}
/* routing orders us to hangup all terminating calls */
2021-08-25 13:44:03 +00:00
static void routing_call_stop ( call_t * call , int __attribute__ ( ( unused ) ) argc , char __attribute__ ( ( unused ) ) * argv [ ] , struct command_def __attribute__ ( ( unused ) ) * command_def )
2020-09-27 12:17:11 +00:00
{
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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-REL-IND to terminator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
2021-08-25 13:44:03 +00:00
osmo_cc_add_ie_cause ( new_msg , relation - > cc_ep - > serving_location , OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR , 0 , 0 ) ;
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
relation_destroy ( relation ) ;
}
}
/* routing orders us to set call into overlap state */
2021-08-25 13:44:03 +00:00
static void routing_overlap ( call_t * call , int __attribute__ ( ( unused ) ) argc , char __attribute__ ( ( unused ) ) * argv [ ] , struct command_def __attribute__ ( ( unused ) ) * command_def )
2020-09-27 12:17:11 +00:00
{
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 ) ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-SETUP-ACK-IND to originator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) ;
2021-08-25 13:44:03 +00:00
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
}
2024-01-09 12:56:44 +00:00
struct param_def param_metering [ ] = {
{ . n = " connect_units " , . i = & param . metering_connect_units , . d = " initial metering units at call connect " } ,
2024-01-29 10:33:19 +00:00
{ . n = " period " , . s = & param . metering_unit_period , . d = " metering unit period in seconds (float) " } ,
2024-01-09 12:56:44 +00:00
{ . n = NULL }
} ;
/* routing orders us to set call into proceeding state */
static void routing_metering ( call_t * call , int __attribute__ ( ( unused ) ) argc , char __attribute__ ( ( unused ) ) * argv [ ] , struct command_def __attribute__ ( ( unused ) ) * command_def )
{
2024-01-29 10:33:19 +00:00
float sec_float ;
uint16_t sec ;
uint16_t msec ;
2024-01-09 12:56:44 +00:00
if ( call - > state ! = CALL_STATE_SETUP
& & call - > state ! = CALL_STATE_OVERLAP
& & call - > state ! = CALL_STATE_PROCEEDING
& & call - > state ! = CALL_STATE_ALERTING )
return ;
parse_params ( argc , argv , command_def ) ;
2024-01-29 10:33:19 +00:00
if ( ! param . metering_unit_period ) {
sec = 0 ;
msec = 0 ;
}
else {
sec_float = atof ( param . metering_unit_period ) ;
sec = ( int ) sec_float ;
msec = ( int ) ( ( ( float ) sec_float - ( float ) sec + ( float ) 0.0005 ) * ( float ) 1000 ) ;
}
2024-01-09 12:56:44 +00:00
call - > metering_connect_units = param . metering_connect_units ;
2024-01-29 10:33:19 +00:00
call - > metering_unit_period . tv_sec = sec ;
call - > metering_unit_period . tv_usec = ( uint32_t ) msec * 1000 ;
2024-01-09 12:56:44 +00:00
call - > metering_data_valid = 1 ;
}
2020-09-27 12:17:11 +00:00
/* routing orders us to set call into proceeding state */
2021-08-25 13:44:03 +00:00
static void routing_proceeding ( call_t * call , int __attribute__ ( ( unused ) ) argc , char __attribute__ ( ( unused ) ) * argv [ ] , struct command_def __attribute__ ( ( unused ) ) * command_def )
2020-09-27 12:17:11 +00:00
{
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 ) ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-PROC-IND to originator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) ;
2024-01-09 12:56:44 +00:00
/* metering */
if ( call - > metering_data_valid ) {
2024-01-29 10:33:19 +00:00
osmo_cc_add_ie_metering ( new_msg , call - > metering_connect_units , & ( call - > metering_unit_period ) ) ;
2024-01-09 12:56:44 +00:00
call - > metering_data_valid = 0 ;
}
2021-08-25 13:44:03 +00:00
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
}
/* routing orders us to set call into alerting state */
2021-08-25 13:44:03 +00:00
static void routing_alerting ( call_t * call , int __attribute__ ( ( unused ) ) argc , char __attribute__ ( ( unused ) ) * argv [ ] , struct command_def __attribute__ ( ( unused ) ) * command_def )
2020-09-27 12:17:11 +00:00
{
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 ) ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-ALERT-IND to originator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) ;
2024-01-09 12:56:44 +00:00
/* metering */
if ( call - > metering_data_valid ) {
2024-01-29 10:33:19 +00:00
osmo_cc_add_ie_metering ( new_msg , call - > metering_connect_units , & ( call - > metering_unit_period ) ) ;
2024-01-09 12:56:44 +00:00
call - > metering_data_valid = 0 ;
}
2021-08-25 13:44:03 +00:00
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
}
/* routing orders us to set call into answer state */
2021-08-25 13:44:03 +00:00
static void routing_answer ( call_t * call , int __attribute__ ( ( unused ) ) argc , char __attribute__ ( ( unused ) ) * argv [ ] , struct command_def __attribute__ ( ( unused ) ) * command_def )
2020-09-27 12:17:11 +00:00
{
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 ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-SETUP-CNF to originator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
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 ) ;
2024-01-09 12:56:44 +00:00
/* metering */
if ( call - > metering_data_valid ) {
2024-01-29 10:33:19 +00:00
osmo_cc_add_ie_metering ( new_msg , call - > metering_connect_units , & ( call - > metering_unit_period ) ) ;
2024-01-09 12:56:44 +00:00
call - > metering_data_valid = 0 ;
}
2021-08-25 13:44:03 +00:00
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
}
/* routing orders us to dsiconnect/release the call */
2021-08-25 13:44:03 +00:00
struct param_def param_disc_rel [ ] = {
{ . n = " isdn-cause " , . i = & param . isdn_cause , . d = " ISDN cause number (Will be generated, if omitted.) " } ,
{ . n = " sip-cause " , . i = & param . sip_cause , . d = " SIP cause number (Will be generated, if omitted.) " } ,
{ . n = NULL }
} ;
static void routing_disconnect ( call_t * call , int argc , char * argv [ ] , struct command_def * command_def )
2020-09-27 12:17:11 +00:00
{
call_relation_t * relation = call - > relation_list ;
osmo_cc_msg_t * new_msg ;
2021-08-25 13:44:03 +00:00
parse_params ( argc , argv , command_def ) ;
2020-09-27 12:17:11 +00:00
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-DISC-IND to originator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
/* send message to originator */
2021-08-25 13:44:03 +00:00
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_DISC_IND ) ;
/* send SDP answer */
proxy_send_sdp_answer ( relation , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* add cause */
2021-08-25 13:44:03 +00:00
osmo_cc_add_ie_cause ( new_msg , relation - > cc_ep - > serving_location , param . isdn_cause , param . sip_cause , 0 ) ;
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* send message to all terminators, if any */
for ( relation = relation - > next ; relation ; relation = relation - > next ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-REL-IND to terminator. \n " , relation_name ( relation ) ) ;
2020-09-27 12:17:11 +00:00
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
/* add cause */
2021-08-25 13:44:03 +00:00
osmo_cc_add_ie_cause ( new_msg , relation - > cc_ep - > serving_location , OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR , 0 , 0 ) ;
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
}
2021-08-25 13:44:03 +00:00
}
2020-09-27 12:17:11 +00:00
2021-08-25 13:44:03 +00:00
static void routing_release ( call_t * call , int argc , char * argv [ ] , struct command_def * command_def )
{
call_relation_t * relation = call - > relation_list ;
osmo_cc_msg_t * new_msg ;
parse_params ( argc , argv , command_def ) ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-REL-IND to originator. \n " , relation_name ( relation ) ) ;
2021-08-25 13:44:03 +00:00
/* send message to originator */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
/* add cause */
osmo_cc_add_ie_cause ( new_msg , relation - > cc_ep - > serving_location , param . isdn_cause , param . sip_cause , 0 ) ;
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
/* send message to all terminators, if any */
for ( relation = relation - > next ; relation ; relation = relation - > next ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " %s CC-REL-IND to terminator. \n " , relation_name ( relation ) ) ;
2021-08-25 13:44:03 +00:00
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
/* add cause */
osmo_cc_add_ie_cause ( new_msg , relation - > cc_ep - > serving_location , OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR , 0 , 0 ) ;
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
}
2021-08-25 13:44:03 +00:00
/* destroy call */
call_destroy ( call ) ;
2020-09-27 12:17:11 +00:00
}
# 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 ;
2023-11-12 16:45:29 +00:00
char digit_string [ 7 + 6 + 1 ] = " called-dtmf x " ;
2020-09-27 12:17:11 +00:00
if ( ! relation - > call - > routing . routing )
return ;
2023-11-12 16:45:29 +00:00
digit_string [ 7 + 5 ] = digit ;
if ( relation = = relation - > call - > relation_list )
routing_send ( & relation - > call - > routing , digit_string + 7 ) ;
else
routing_send ( & relation - > call - > routing , digit_string ) ;
2020-09-27 12:17:11 +00:00
}
/* routing orders us to enable DTMF decoding */
2021-08-25 13:44:03 +00:00
static void routing_dtmf ( call_t * call , int __attribute__ ( ( unused ) ) argc , char __attribute__ ( ( unused ) ) * argv [ ] , struct command_def __attribute__ ( ( unused ) ) * command_def )
2020-09-27 12:17:11 +00:00
{
call_relation_t * relation = call - > relation_list ;
2023-11-12 16:45:29 +00:00
if ( ! strcmp ( command_def - > name , " called-dtmf " ) ) {
relation = relation - > next ;
if ( ! relation | | relation - > next ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_NOTICE , " Cannot enable DTMF detection on the called side, because there is no single terminating callee. \n " ) ;
2023-11-12 16:45:29 +00:00
return ;
}
}
2020-09-27 12:17:11 +00:00
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 */
2021-08-25 13:44:03 +00:00
static void routing_dtmf_stop ( call_t * call , int __attribute__ ( ( unused ) ) argc , char __attribute__ ( ( unused ) ) * argv [ ] , struct command_def __attribute__ ( ( unused ) ) * command_def )
2020-09-27 12:17:11 +00:00
{
call_relation_t * relation = call - > relation_list ;
2023-11-12 16:45:29 +00:00
if ( ! strcmp ( command_def - > name , " called-dtmf-stop " ) ) {
relation = relation - > next ;
if ( ! relation | | relation - > next )
return ;
}
2020-09-27 12:17:11 +00:00
dtmf_decode_exit ( & relation - > dtmf_dec ) ;
relation - > dtmf_dec_enable = 0 ;
}
2023-11-12 16:45:29 +00:00
/* routing orders us to send a telephone event */
struct param_def param_event [ ] = {
{ . n = " event " , . s = & param . event , . d = " 0..9|*|#|A..D " , . mandatory = 1 } ,
{ . n = NULL }
} ;
static void routing_te ( call_t * call , int __attribute__ ( ( unused ) ) argc , char __attribute__ ( ( unused ) ) * argv [ ] , struct command_def __attribute__ ( ( unused ) ) * command_def )
{
call_relation_t * relation = call - > relation_list ;
struct telephone_event te ;
uint8_t marker ;
uint8_t event ;
if ( ! strcmp ( command_def - > name , " called-telephone-event " ) ) {
relation = relation - > next ;
if ( ! relation | | relation - > next ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_NOTICE , " Cannot send telephone event, because there is no single terminating callee. \n " ) ;
2023-11-12 16:45:29 +00:00
return ;
}
}
if ( ! relation - > telephone_event ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_NOTICE , " Cannot send telephone event, because the codec was not negotiated. \n " ) ;
2023-11-12 16:45:29 +00:00
return ;
}
parse_params ( argc , argv , command_def ) ;
if ( ! param . event | | ! param . event [ 0 ] | | param . event [ 1 ] ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " 'telephone-event' command reqires an event character as parameter. \n " ) ;
2023-11-12 16:45:29 +00:00
return ;
}
switch ( param . event [ 0 ] ) {
case ' 0 ' :
case ' 1 ' :
case ' 2 ' :
case ' 3 ' :
case ' 4 ' :
case ' 5 ' :
case ' 6 ' :
case ' 7 ' :
case ' 8 ' :
case ' 9 ' :
event = param . event [ 0 ] - ' 0 ' ;
break ;
case ' * ' :
event = 10 ;
break ;
case ' # ' :
event = 11 ;
break ;
case ' a ' :
case ' b ' :
case ' c ' :
case ' d ' :
event = param . event [ 0 ] - ' a ' + 12 ;
break ;
case ' A ' :
case ' B ' :
case ' C ' :
case ' D ' :
event = param . event [ 0 ] - ' A ' + 12 ;
break ;
default :
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " 'telephone-event' with illegal event character '%c'. \n " , param . event [ 0 ] ) ;
2023-11-12 16:45:29 +00:00
return ;
}
memset ( & te , 0 , sizeof ( te ) ) ;
marker = 1 ;
te . event = event ;
te . e = 1 ;
te . volume = 0 ;
te . duration = 800 ; /* 100 ms */
tx_telephone_event ( relation , marker , & te ) ;
}
2020-09-27 12:17:11 +00:00
/* routing failed, release the call */
2021-08-25 13:44:03 +00:00
struct param_def param_error [ ] = {
{ . n = " string " , . s = & param . error , . d = " error string to be displayed " , . mandatory = 1 } ,
{ . n = NULL }
} ;
static void routing_error ( call_t * call , int __attribute__ ( ( unused ) ) argc , char __attribute__ ( ( unused ) ) * argv [ ] , struct command_def __attribute__ ( ( unused ) ) * command_def )
2020-09-27 12:17:11 +00:00
{
osmo_cc_msg_t * new_msg ;
call_relation_t * relation ;
2021-08-25 13:44:03 +00:00
parse_params ( argc , argv , command_def ) ;
if ( ! param . error ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " 'error' command reqires an error string as parameter. \n " ) ;
2021-08-25 13:44:03 +00:00
return ;
}
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " Routing script error: '%s' \n " , param . error ) ;
2020-09-27 12:17:11 +00:00
/* send message to originator */
new_msg = osmo_cc_new_msg ( OSMO_CC_MSG_REL_IND ) ;
2021-08-25 13:44:03 +00:00
osmo_cc_add_ie_cause ( new_msg , call - > relation_list - > cc_ep - > serving_location , OSMO_CC_ISDN_CAUSE_NETWORK_OOO , 0 , 0 ) ;
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) ;
2021-08-25 13:44:03 +00:00
osmo_cc_add_ie_cause ( new_msg , relation - > cc_ep - > serving_location , OSMO_CC_ISDN_CAUSE_NETWORK_OOO , 0 , 0 ) ;
osmo_cc_ll_msg ( relation - > cc_ep , relation - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
}
}
2021-08-25 13:44:03 +00:00
struct command_def command_def [ ] = {
{ " rtp-proxy " , routing_rtp_proxy , " Turn on RTP proxy, so that audio processing is possible. " , param_rtp_proxy } ,
{ " play " , routing_play , " Play given audio file. " , param_play } ,
{ " play-stop " , routing_play_stop , " Stop playing audio file. " , NULL } ,
{ " record " , routing_record , " Record to given audio file. " , param_record } ,
{ " record-stop " , routing_record_stop , " Stop recording audio file. " , NULL } ,
2022-06-25 11:03:09 +00:00
{ " tx-gain " , routing_tx_gain , " Set gain of audio sent to originating interface. " , param_gain } ,
{ " rx-gain " , routing_rx_gain , " Set gain of audio received from originating interface. " , param_gain } ,
2023-03-22 19:01:22 +00:00
{ " tx-compress " , routing_tx_compress , " Enables compressor on TX side. (Compress to speech level) " , param_compress } ,
{ " rx-compress " , routing_rx_compress , " Enables compressor on Rx side. (Compress to speech level) " , param_compress } ,
2021-08-25 13:44:03 +00:00
{ " call " , routing_call , " Make one or mulriple calls to given interface. " , param_call } ,
{ " call-stop " , routing_call_stop , " Stop outgoing call(s). " , NULL } ,
{ " overlap " , routing_overlap , " Set caller into overlap state, digit may be dialed. " , NULL } ,
2024-01-09 12:56:44 +00:00
{ " metering " , routing_metering , " Set metering information for the caller. " , param_metering } ,
2021-08-25 13:44:03 +00:00
{ " proceeding " , routing_proceeding , " Set caller into proceeding state, no digits may be dialed. " , NULL } ,
{ " alerting " , routing_alerting , " Set caller into alerting state. " , NULL } ,
{ " answer " , routing_answer , " Answer call from caller. " , NULL } ,
{ " disconnect " , routing_disconnect , " Disconnect call towards caller and release callee, if any. " , param_disc_rel } ,
{ " release " , routing_release , " Release call towards caller and release callee, if any. " , param_disc_rel } ,
{ " dtmf " , routing_dtmf , " Turn on DTMF detection. " , NULL } ,
{ " dtmf-stop " , routing_dtmf_stop , " Turn off DTMF detection. " , NULL } ,
2023-11-12 16:45:29 +00:00
{ " called-dtmf " , routing_dtmf , " Turn on DTMF detection at callee. " , NULL } ,
{ " called-dtmf-stop " , routing_dtmf_stop , " Turn off DTMF detection at callee. " , NULL } ,
{ " telephone-event " , routing_te , " Send digit as telephone event to caller. " , param_event } ,
{ " called-telephone-event " , routing_te , " Send digit as telephone event to callee. " , param_event } ,
2021-08-25 13:44:03 +00:00
{ " error " , routing_error , " Display error message. " , param_error } ,
{ NULL , NULL , NULL , NULL }
} ;
2020-09-27 12:17:11 +00:00
2024-01-11 18:28:12 +00:00
static char * strtok_quotes ( const char * * text_p )
{
static char token [ 1024 ] ;
const char * text = * text_p ;
int i , quote ;
/* skip spaces */
while ( * text ) {
if ( * text > 32 )
break ;
text + + ;
}
/* if eol, return NULL */
if ( ! ( * text ) )
return NULL ;
i = 0 ;
quote = 0 ;
while ( * text ) {
/* escape allows all following characters */
if ( * text = = ' \\ ' ) {
text + + ;
if ( * text )
token [ i + + ] = * text + + ;
continue ;
}
/* no quote, check for them or break on white space */
if ( quote = = 0 ) {
if ( * text = = ' \' ' ) {
quote = 1 ;
text + + ;
continue ;
}
if ( * text = = ' \" ' ) {
quote = 2 ;
text + + ;
continue ;
}
if ( * text < = ' ' )
break ;
}
/* single quote, check for unquote */
if ( quote = = 1 & & * text = = ' \' ' ) {
quote = 0 ;
text + + ;
continue ;
}
/* double quote, check for unquote */
if ( quote = = 2 & & * text = = ' \" ' ) {
quote = 0 ;
text + + ;
continue ;
}
/* copy character */
token [ i + + ] = * text + + ;
}
token [ i ] = ' \0 ' ;
* text_p = text ;
return token ;
}
2020-09-27 12:17:11 +00:00
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 ;
2021-08-25 13:44:03 +00:00
int i ;
2020-09-27 12:17:11 +00:00
2023-11-12 16:45:29 +00:00
if ( routing - > call - > relation_list )
2024-01-11 18:28:12 +00:00
LOGP ( DSTDERR , LOGL_INFO , " (call #%d) Command from routing: '%s' \n " , routing - > call - > num , string ) ;
2023-11-12 16:45:29 +00:00
else
2024-01-11 18:28:12 +00:00
LOGP ( DSTDERR , LOGL_INFO , " Command from routing: '%s' \n " , string ) ;
2023-11-12 16:45:29 +00:00
2020-09-27 12:17:11 +00:00
/* convert string into tokens */
2024-01-11 18:28:12 +00:00
while ( ( token = strtok_quotes ( & string ) ) )
2020-09-27 12:17:11 +00:00
argv [ argc + + ] = strdup ( token ) ;
if ( ! argc )
return ;
2021-08-25 13:44:03 +00:00
for ( i = 0 ; command_def [ i ] . name ; i + + ) {
if ( ! strcasecmp ( argv [ 0 ] , command_def [ i ] . name ) ) {
command_def [ i ] . func ( call , argc - 1 , argv + 1 , & command_def [ i ] ) ;
break ;
}
}
if ( ! command_def [ i ] . name )
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_ERROR , " Unknown command '%s' from routing. \n " , argv [ 0 ] ) ;
2020-09-27 12:17:11 +00:00
while ( argc )
free ( ( char * ) argv [ - - argc ] ) ;
}
void routing_receive_stderr ( routing_t * routing , const char * string )
{
if ( routing - > call - > relation_list )
2024-01-11 18:28:12 +00:00
LOGP ( DSTDERR , LOGL_NOTICE , " (call #%d) Routing STDERR: %s \n " , routing - > call - > num , string ) ;
2020-09-27 12:17:11 +00:00
else
2024-01-11 18:28:12 +00:00
LOGP ( DSTDERR , LOGL_NOTICE , " Routing STDERR: %s \n " , string ) ;
2020-09-27 12:17:11 +00:00
}
void routing_close ( routing_t * routing )
{
call_t * call = routing - > call ;
osmo_cc_msg_t * new_msg ;
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_INFO , " (call #%d) Routing script exitted. \n " , call - > num ) ;
2020-09-27 12:17:11 +00:00
/* 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 ) ;
2021-08-25 13:35:09 +00:00
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
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 ) ;
2021-08-25 13:35:09 +00:00
osmo_cc_add_ie_cause ( new_msg , call - > relation_list - > cc_ep - > serving_location , OSMO_CC_ISDN_CAUSE_NO_ROUTE , 0 , 0 ) ;
osmo_cc_ll_msg ( call - > relation_list - > cc_ep , call - > relation_list - > cc_callref , new_msg ) ;
2020-09-27 12:17:11 +00:00
/* destroy call */
call_destroy ( call ) ;
}
2023-11-12 16:45:29 +00:00
void rx_telephone_event ( call_relation_t * relation , uint8_t marker , struct telephone_event * te )
2021-02-27 11:07:32 +00:00
{
char digit_string [ 7 ] = " dtmf x " ;
if ( te - > event < 16 ) {
2022-10-23 15:01:58 +00:00
if ( marker & & te - > volume < = 36 ) {
if ( ! te - > e ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_INFO , " Received start of Telephone-Event '%d' duration=%d ms (marker set) \n " , te - > event , te - > duration / 8 ) ;
2022-10-23 15:01:58 +00:00
relation - > te_started = 1 ;
} else
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_INFO , " Received start and end of Telephone-Event '%d' duration=%d ms (marker set) \n " , te - > event , te - > duration / 8 ) ;
2022-10-23 15:01:58 +00:00
digit_string [ 5 ] = " 0123456789*#ABCD " [ te - > event ] ;
} else if ( ! relation - > te_started & & ! te - > e & & te - > volume < = 36 ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_INFO , " Received start of Telephone-Event '%d' duration=%d ms (marker not set, this is wrong!) \n " , te - > event , te - > duration / 8 ) ;
2021-02-27 11:07:32 +00:00
relation - > te_started = 1 ;
digit_string [ 5 ] = " 0123456789*#ABCD " [ te - > event ] ;
2022-10-23 15:01:58 +00:00
} else if ( relation - > te_started & & ! te - > e & & te - > volume < = 36 ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Received subsequent Telephone-Event '%d' duration=%d ms \n " , te - > event , te - > duration / 8 ) ;
2022-10-23 15:01:58 +00:00
} else if ( relation - > te_started & & te - > e ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_INFO , " Received end of Telephone-Event '%d' \n " , te - > event ) ;
2021-02-27 11:07:32 +00:00
relation - > te_started = 0 ;
2022-10-23 15:01:58 +00:00
} else if ( ! relation - > te_started & & te - > e ) {
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_DEBUG , " Received subsequent end of Telephone-Event '%d' \n " , te - > event ) ;
2021-02-27 11:07:32 +00:00
}
} else
2024-01-11 18:28:12 +00:00
LOGP ( DROUTER , LOGL_INFO , " Received unsupported Telephone-Event '%d' \n " , te - > event ) ;
2021-02-27 11:07:32 +00:00
if ( ! relation - > call - > routing . routing )
return ;
if ( digit_string [ 5 ] ! = ' x ' )
routing_send ( & relation - > call - > routing , digit_string ) ;
}
2022-10-14 19:47:31 +00:00
void routing_help ( void )
2021-08-25 13:44:03 +00:00
{
int i , j , k ;
2022-10-14 19:47:31 +00:00
printf ( " Available routing commands: \n \n " ) ;
2021-08-25 13:44:03 +00:00
for ( i = 0 ; command_def [ i ] . name ; i + + ) {
2022-10-14 19:47:31 +00:00
struct param_def * param_def = command_def [ i ] . param_list ;
2021-08-25 13:44:03 +00:00
2022-10-14 19:47:31 +00:00
printf ( " Command: %s \n " , command_def [ i ] . name ) ;
printf ( " Description: %s \n " , command_def [ i ] . description ) ;
2021-08-25 13:44:03 +00:00
2022-10-14 19:47:31 +00:00
if ( ! param_def ) {
printf ( " This command does not have any parameters. \n " ) ;
continue ;
}
2021-08-25 13:44:03 +00:00
2022-10-14 19:47:31 +00:00
for ( j = 0 ; param_def [ j ] . n ; j + + ) {
printf ( " Parameter: %s%s \n " , param_def [ j ] . n , ( param_def [ j ] . mandatory ) ? " (manatory) " : " " ) ;
printf ( " Description: %s \n " , param_def [ j ] . d ) ;
if ( param_def [ j ] . o | | param_def [ j ] . t ) {
for ( k = 0 ; codecs_def [ k ] . payload_name ; k + + ) {
printf ( " Value: '%s' \n " , codecs_def [ k ] . payload_name ) ;
}
} else if ( param_def [ j ] . no_value ) {
printf ( " No value \n " ) ;
} else if ( param_def [ j ] . i & & param_def [ j ] . value2name ) {
for ( k = 0 ; k < param_def [ j ] . num ; k + + ) {
const char * name = param_def [ j ] . value2name ( k ) ;
if ( name & & name [ 0 ] ! = ' < ' )
printf ( " Value: '%d' or '%s' \n " , k , name ) ;
}
2021-08-25 13:44:03 +00:00
}
}
}
}
2020-09-27 12:17:11 +00:00
# warning add progress, if terminating call sends sdp but call state already reached