2018-12-07 10:37:01 +00:00
/*
* mod_signalwire . c - - SignalWire module
*
* Copyright ( c ) 2018 SignalWire , Inc
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*/
# include <switch.h>
# include <switch_curl.h>
# include <switch_stun.h>
# include <signalwire-client-c/client.h>
# ifndef WIN32
# include <sys/utsname.h>
# endif
2018-12-26 23:17:31 +00:00
# ifdef WIN32
void sslLoadWindowsCACertificate ( ) ;
void sslUnLoadWindowsCACertificate ( ) ;
int sslContextFunction ( void * curl , void * sslctx , void * userdata ) ;
# endif
2018-12-07 10:37:01 +00:00
# define SW_KS_JSON_PRINT(_h, _j) do { \
char * _json = ks_json_print ( _j ) ; \
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ALERT , " --- %s --- \n %s \n --- \n " , _h , _json ) ; \
ks_json_free ( & _json ) ; \
} while ( 0 )
static int debug_level = 7 ;
static int signalwire_gateway_exists ( void ) ;
/* Prototypes */
SWITCH_MODULE_SHUTDOWN_FUNCTION ( mod_signalwire_shutdown ) ;
SWITCH_MODULE_LOAD_FUNCTION ( mod_signalwire_load ) ;
SWITCH_MODULE_RUNTIME_FUNCTION ( mod_signalwire_runtime ) ;
/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime)
* Defines a switch_loadable_module_function_table_t and a static const char [ ] modname
*/
SWITCH_MODULE_DEFINITION ( mod_signalwire , mod_signalwire_load , mod_signalwire_shutdown , mod_signalwire_runtime ) ;
typedef enum {
SW_STATE_ADOPTION ,
SW_STATE_OFFLINE ,
SW_STATE_ONLINE ,
SW_STATE_CONFIGURE ,
SW_STATE_START_PROFILE ,
SW_STATE_REGISTER ,
SW_STATE_READY ,
} sw_state_t ;
static struct {
int ssl_verify ;
ks_bool_t shutdown ;
ks_bool_t restarting ;
swclt_config_t * config ;
char blade_bootstrap [ 1024 ] ;
char adoption_service [ 1024 ] ;
char stun_server [ 1024 ] ;
char adoption_token [ 64 ] ;
2019-08-04 07:37:51 +00:00
char override_context [ 64 ] ;
2018-12-07 10:37:01 +00:00
ks_size_t adoption_backoff ;
ks_time_t adoption_next ;
char adoption_data_local_ip [ 256 ] ;
char adoption_data_external_ip [ 256 ] ;
char adoption_data_uname [ 1024 ] ;
char relay_connector_id [ 256 ] ;
swclt_sess_t signalwire_session ;
swclt_hmon_t signalwire_session_monitor ;
sw_state_t state ;
ks_bool_t profile_update ;
ks_bool_t profile_reload ;
ks_bool_t signalwire_reconnected ;
switch_xml_t signalwire_profile ;
char signalwire_profile_md5 [ SWITCH_MD5_DIGEST_STRING_SIZE ] ;
ks_bool_t kslog_on ;
switch_mutex_t * mutex ; // general mutex for this mod
char gateway_ip [ 80 ] ;
char gateway_port [ 10 ] ;
} globals ;
static void mod_signalwire_kslogger ( const char * file , const char * func , int line , int level , const char * fmt , . . . )
{
const char * fp ;
va_list ap ;
char buf [ 32768 ] ;
if ( level > debug_level ) return ;
fp = switch_cut_path ( file ) ;
va_start ( ap , fmt ) ;
vsnprintf ( buf , sizeof ( buf ) - 1 , fmt , ap ) ;
buf [ sizeof ( buf ) - 1 ] = ' \0 ' ;
va_end ( ap ) ;
switch_log_printf ( SWITCH_CHANNEL_ID_LOG , fp , func , line , NULL , level , " %s \n " , buf ) ;
}
static switch_status_t switch_find_available_port ( switch_port_t * port , const char * ip , int type )
{
switch_status_t ret = SWITCH_STATUS_SUCCESS ;
switch_memory_pool_t * pool = NULL ;
switch_sockaddr_t * addr = NULL ;
switch_socket_t * sock = NULL ;
switch_bool_t found = SWITCH_FALSE ;
if ( ( ret = switch_core_new_memory_pool ( & pool ) ) ! = SWITCH_STATUS_SUCCESS ) {
goto done ;
}
while ( ! found ) {
if ( ( ret = switch_sockaddr_info_get ( & addr , ip , SWITCH_UNSPEC , * port , 0 , pool ) ) ! = SWITCH_STATUS_SUCCESS ) {
goto done ;
}
if ( ( ret = switch_socket_create ( & sock , switch_sockaddr_get_family ( addr ) , type , 0 , pool ) ) ! = SWITCH_STATUS_SUCCESS ) {
goto done ;
}
if ( ! ( found = ( switch_socket_bind ( sock , addr ) = = SWITCH_STATUS_SUCCESS ) ) ) {
* port = * port + 1 ;
}
switch_socket_close ( sock ) ;
}
done :
if ( pool ) switch_core_destroy_memory_pool ( & pool ) ;
return ret ;
}
struct response_data {
char * data ;
size_t size ;
} ;
static size_t response_data_handler ( void * contents , size_t size , size_t nmemb , void * userp )
{
size_t received = size * nmemb ;
struct response_data * rd = ( struct response_data * ) userp ;
if ( ! rd - > data ) rd - > data = ks_pool_alloc ( NULL , received + 1 ) ;
else rd - > data = ks_pool_resize ( rd - > data , rd - > size + received + 1 ) ;
memcpy ( rd - > data + rd - > size , contents , received ) ;
rd - > size + = received ;
rd - > data [ rd - > size ] = 0 ;
return received ;
}
static void save_sip_config ( const char * config )
{
char confpath [ 1024 ] ;
FILE * fp = NULL ;
switch_snprintf ( confpath , sizeof ( confpath ) , " %s%s%s " , SWITCH_GLOBAL_dirs . storage_dir , SWITCH_PATH_SEPARATOR , " signalwire-conf.dat " ) ;
fp = fopen ( confpath , " w " ) ;
if ( ! fp ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to open %s to save SignalWire SIP configuration \n " , confpath ) ;
return ;
}
fputs ( config , fp ) ;
fclose ( fp ) ;
}
static void load_sip_config ( void )
{
char confpath [ 1024 ] ;
char data [ 32767 ] = { 0 } ;
FILE * fp = NULL ;
switch_snprintf ( confpath , sizeof ( confpath ) , " %s%s%s " , SWITCH_GLOBAL_dirs . storage_dir , SWITCH_PATH_SEPARATOR , " signalwire-conf.dat " ) ;
if ( ! ( fp = fopen ( confpath , " r " ) ) ) return ;
if ( ! fread ( data , 1 , sizeof ( data ) , fp ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to read SignalWire SIP configuration from %s \n " , confpath ) ;
}
fclose ( fp ) ;
if ( ! zstr_buf ( data ) ) {
switch_md5_string ( globals . signalwire_profile_md5 , ( void * ) data , strlen ( data ) ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " saved profile MD5 = \" %s \" \n " , globals . signalwire_profile_md5 ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " saved profile = \" %s \" \n " , ( char * ) data ) ;
globals . signalwire_profile = switch_xml_parse_str_dynamic ( ( char * ) data , SWITCH_TRUE ) ;
}
}
static ks_status_t load_credentials_from_json ( ks_json_t * json )
{
ks_status_t status = KS_STATUS_SUCCESS ;
ks_json_t * authentication = NULL ;
const char * authentication_str = NULL ;
const char * bootstrap = NULL ;
const char * relay_connector_id = NULL ;
if ( ( bootstrap = ks_json_get_object_cstr ( json , " bootstrap " ) ) = = NULL ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Unable to connect to SignalWire: missing bootstrap URL \n " ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
if ( ( relay_connector_id = ks_json_get_object_cstr ( json , " relay_connector_id " ) ) = = NULL ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Unable to connect to SignalWire: missing relay_connector_id \n " ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
if ( ( authentication = ks_json_get_object_item ( json , " authentication " ) ) = = NULL ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Unable to connect to SignalWire: missing authentication \n " ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
// update the internal connection target, which is normally assigned in swclt_sess_create()
if ( swclt_sess_target_set ( globals . signalwire_session , bootstrap ) ! = KS_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to connect to SignalWire at %s \n " , bootstrap ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
// update the relay_connector_id passed to profile configuration
strncpy ( globals . relay_connector_id , relay_connector_id , sizeof ( globals . relay_connector_id ) - 1 ) ;
strncpy ( globals . blade_bootstrap , bootstrap , sizeof ( globals . blade_bootstrap ) - 1 ) ;
// got adopted, update the client config authentication
authentication_str = ks_json_pprint_unformatted ( NULL , authentication ) ;
swclt_config_set_authentication ( globals . config , authentication_str ) ;
ks_pool_free ( & authentication_str ) ;
done :
return status ;
}
static ks_status_t mod_signalwire_adoption_post ( void )
{
ks_status_t status = KS_STATUS_SUCCESS ;
switch_memory_pool_t * pool = NULL ;
switch_CURL * curl = NULL ;
switch_curl_slist_t * headers = NULL ;
char url [ 1024 ] ;
char errbuf [ CURL_ERROR_SIZE ] ;
CURLcode res ;
long rescode ;
ks_json_t * json = ks_json_create_object ( ) ;
struct response_data rd = { 0 } ;
char * jsonstr = NULL ;
// Determine and cache adoption data values that are heavier to figure out
if ( ! globals . adoption_data_local_ip [ 0 ] ) {
switch_find_local_ip ( globals . adoption_data_local_ip , sizeof ( globals . adoption_data_local_ip ) , NULL , AF_INET ) ;
}
if ( ! globals . adoption_data_external_ip [ 0 ] ) {
switch_port_t local_port = 6050 ;
char * error = NULL ;
char * external_ip ;
switch_port_t external_port ;
if ( switch_core_new_memory_pool ( & pool ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " SignalWire adoption failed: could not allocate memory pool \n " ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
if ( switch_find_available_port ( & local_port , globals . adoption_data_local_ip , SOCK_STREAM ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " SignalWire adoption failed: could not get available local port \n " ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
external_ip = globals . adoption_data_local_ip ;
external_port = local_port ;
if ( switch_stun_lookup ( & external_ip , & external_port , globals . stun_server , SWITCH_STUN_DEFAULT_PORT , & error , pool ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " SignalWire adoption failed: stun [%s] lookup error: %s \n " , globals . stun_server , error ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
2019-06-19 19:53:35 +00:00
snprintf ( globals . adoption_data_external_ip , sizeof ( globals . adoption_data_external_ip ) , " %s " , external_ip ) ;
2018-12-07 10:37:01 +00:00
}
if ( ! globals . adoption_data_uname [ 0 ] ) {
# ifndef WIN32
struct utsname buf ;
if ( uname ( & buf ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " SignalWire adoption failed: could not get uname \n " ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
switch_snprintf ( globals . adoption_data_uname ,
sizeof ( globals . adoption_data_uname ) ,
" %s %s %s %s %s " ,
buf . sysname ,
buf . nodename ,
buf . release ,
buf . version ,
buf . machine ) ;
# else
// @todo set globals.adoption_data_uname from GetVersion Win32API
# endif
}
ks_json_add_string_to_object ( json , " client_uuid " , globals . adoption_token ) ;
ks_json_add_string_to_object ( json , " hostname " , switch_core_get_hostname ( ) ) ;
ks_json_add_string_to_object ( json , " ip " , globals . adoption_data_local_ip ) ;
ks_json_add_string_to_object ( json , " ext_ip " , globals . adoption_data_external_ip ) ;
ks_json_add_string_to_object ( json , " version " , switch_version_full ( ) ) ;
ks_json_add_string_to_object ( json , " uname " , globals . adoption_data_uname ) ;
jsonstr = ks_json_print_unformatted ( json ) ;
ks_json_delete ( & json ) ;
switch_snprintf ( url , sizeof ( url ) , " %s/%s " , globals . adoption_service , globals . adoption_token ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG10 , " Checking %s for SignalWire adoption of this FreeSWITCH \n " , url ) ;
curl = switch_curl_easy_init ( ) ;
headers = switch_curl_slist_append ( headers , " Accept: application/json " ) ;
headers = switch_curl_slist_append ( headers , " Accept-Charset: utf-8 " ) ;
headers = switch_curl_slist_append ( headers , " Content-Type: application/json " ) ;
switch_curl_easy_setopt ( curl , CURLOPT_CONNECTTIMEOUT , 5 ) ;
switch_curl_easy_setopt ( curl , CURLOPT_TIMEOUT , 5 ) ;
if ( ! strncasecmp ( url , " https " , 5 ) ) {
switch_curl_easy_setopt ( curl , CURLOPT_SSL_VERIFYPEER , globals . ssl_verify ) ;
switch_curl_easy_setopt ( curl , CURLOPT_SSL_VERIFYHOST , globals . ssl_verify ) ;
}
switch_curl_easy_setopt ( curl , CURLOPT_URL , url ) ;
switch_curl_easy_setopt ( curl , CURLOPT_HTTPHEADER , headers ) ;
switch_curl_easy_setopt ( curl , CURLOPT_USERAGENT , " mod_signalwire/1 " ) ;
switch_curl_easy_setopt ( curl , CURLOPT_POSTFIELDS , jsonstr ) ;
switch_curl_easy_setopt ( curl , CURLOPT_ERRORBUFFER , errbuf ) ;
switch_curl_easy_setopt ( curl , CURLOPT_WRITEDATA , ( void * ) & rd ) ;
switch_curl_easy_setopt ( curl , CURLOPT_WRITEFUNCTION , response_data_handler ) ;
2018-12-26 23:17:31 +00:00
# ifdef WIN32
curl_easy_setopt ( curl , CURLOPT_SSL_CTX_FUNCTION , sslContextFunction ) ;
# endif
2018-12-07 10:37:01 +00:00
if ( ( res = switch_curl_easy_perform ( curl ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Curl Result %d, Error: %s \n " , res , errbuf ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
switch_curl_easy_getinfo ( curl , CURLINFO_RESPONSE_CODE , & rescode ) ;
if ( rescode = = 404 ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_NOTICE ,
" Go to https://signalwire.com to set up your Connector now! Enter connection token %s \n " , globals . adoption_token ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
if ( rescode ! = 200 ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " SignalWire adoption failed with HTTP code %ld, %s \n " , rescode , rd . data ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
json = ks_json_parse ( rd . data ) ;
if ( ! json ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Received bad SignalWire adoption response \n %s \n " , rd . data ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
if ( ( status = load_credentials_from_json ( json ) ) ! = KS_STATUS_SUCCESS ) {
goto done ;
}
ks_json_delete ( & json ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " SignalWire adoption of this FreeSWITCH completed \n " ) ;
// write out the data to save it for reloading in the future
{
char authpath [ 1024 ] ;
FILE * fp = NULL ;
switch_snprintf ( authpath , sizeof ( authpath ) , " %s%s%s " , SWITCH_GLOBAL_dirs . storage_dir , SWITCH_PATH_SEPARATOR , " adoption-auth.dat " ) ;
fp = fopen ( authpath , " w " ) ;
if ( ! fp ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to open %s to save SignalWire creds \n " , authpath ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
fputs ( rd . data , fp ) ;
fclose ( fp ) ;
}
globals . state = SW_STATE_OFFLINE ;
swclt_sess_connect ( globals . signalwire_session ) ;
done :
if ( rd . data ) ks_pool_free ( & rd . data ) ;
if ( jsonstr ) ks_json_free_ex ( ( void * * ) & jsonstr ) ;
if ( json ) ks_json_delete ( & json ) ;
if ( curl ) {
curl_easy_cleanup ( curl ) ;
if ( headers ) curl_slist_free_all ( headers ) ;
}
if ( pool ) switch_core_destroy_memory_pool ( & pool ) ;
return status ;
}
# define SIGNALWIRE_SYNTAX "token | adoption | adopted | reload | update | debug <level> | kslog <on|off|logfile e.g. / tmp / ks.log>"
SWITCH_STANDARD_API ( mod_signalwire_api_function )
{
int argc = 0 ;
char * argv [ 2 ] = { 0 } ;
char * buf = NULL ;
if ( ! cmd | | ! ( buf = strdup ( cmd ) ) ) {
stream - > write_function ( stream , " -USAGE: signalwire %s \n " , SIGNALWIRE_SYNTAX ) ;
return SWITCH_STATUS_SUCCESS ;
}
if ( ( argc = switch_separate_string ( buf , ' ' , argv , ( sizeof ( argv ) / sizeof ( argv [ 0 ] ) ) ) ) ) {
if ( ! strcmp ( argv [ 0 ] , " token " ) ) {
if ( globals . adoption_token [ 0 ] ) {
stream - > write_function ( stream ,
" _____ _ ___ ___ \n "
" / ___/(_)___ _____ ____ _/ / | / (_)_______ \n "
" \\ __ \\ / / __ `/ __ \\ / __ `/ /| | /| / / / ___/ _ \\ \n "
" ___/ / / /_/ / / / / /_/ / / | |/ |/ / / / / __/ \n "
" /____/_/ \\ __, /_/ /_/ \\ __,_/_/ |__/|__/_/_/ \\ ___/ \n "
" /____/ \n "
" \n /===================================================================== \\ \n "
" | Connection Token: %s | \n "
" \\ =====================================================================/ \n "
" Go to https://signalwire.com to set up your Connector now! \n " , globals . adoption_token ) ;
} else {
stream - > write_function ( stream , " -ERR connection token not available \n " ) ;
}
goto done ;
}
else if ( ! strcmp ( argv [ 0 ] , " adoption " ) ) {
if ( globals . state = = SW_STATE_ADOPTION ) {
globals . adoption_next = ks_time_now ( ) ;
stream - > write_function ( stream , " +OK \n " ) ;
} else {
stream - > write_function ( stream , " -ERR adoption not currently pending \n " ) ;
}
goto done ;
}
else if ( ! strcmp ( argv [ 0 ] , " adopted " ) ) {
stream - > write_function ( stream , " +OK %s \n " , globals . state = = SW_STATE_ADOPTION ? " Not Adopted " : " Adopted " ) ;
goto done ;
}
else if ( ! strcmp ( argv [ 0 ] , " debug " ) ) {
if ( argv [ 1 ] ) {
debug_level = atoi ( argv [ 1 ] ) ;
}
stream - > write_function ( stream , " +OK debug %d \n " , debug_level ) ;
goto done ;
} else if ( ! strcmp ( argv [ 0 ] , " kslog " ) ) {
if ( argv [ 1 ] ) {
if ( ! strcmp ( argv [ 1 ] , " on " ) ) {
ks_global_set_logger ( mod_signalwire_kslogger ) ;
} else if ( ! strcmp ( argv [ 1 ] , " off " ) ) {
ks_global_set_logger ( NULL ) ;
}
}
stream - > write_function ( stream , " +OK %s \n " , argv [ 1 ] ) ;
goto done ;
} else if ( ! strcmp ( argv [ 0 ] , " reload " ) ) {
globals . profile_reload = KS_TRUE ;
stream - > write_function ( stream , " +OK \n " ) ;
goto done ;
} else if ( ! strcmp ( argv [ 0 ] , " update " ) ) {
globals . profile_update = KS_TRUE ;
stream - > write_function ( stream , " +OK \n " ) ;
goto done ;
}
}
stream - > write_function ( stream , " -USAGE: signalwire %s \n " , SIGNALWIRE_SYNTAX ) ;
done :
switch_safe_free ( buf ) ;
return SWITCH_STATUS_SUCCESS ;
}
static void mod_signalwire_session_state_handler ( swclt_sess_t sess , swclt_hstate_change_t * state_change_info , const char * cb_data )
{
SWCLT_HSTATE new_state = state_change_info - > new_state ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " SignalWire Session State Change: %s \n " , swclt_hstate_describe_change ( state_change_info ) ) ;
if ( new_state = = SWCLT_HSTATE_ONLINE ) {
// Connected with NEW or RESTORED session
globals . signalwire_reconnected = KS_TRUE ;
} else if ( new_state = = SWCLT_HSTATE_OFFLINE ) {
// Disconnected
}
}
static void __on_provisioning_events ( swclt_sess_t sess , blade_broadcast_rqu_t * rqu , void * cb_data )
{
if ( ! strcmp ( rqu - > event , " update " ) ) {
globals . profile_update = KS_TRUE ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " SignalWire SIP profile update requested \n " ) ;
}
}
static switch_xml_t xml_config_handler ( const char * section , const char * tag_name , const char * key_name , const char * key_value , switch_event_t * params ,
void * user_data )
{
char * profileName = NULL ;
char * reconfigValue = NULL ;
switch_xml_t signalwire_profile_dup = NULL ;
if ( ! section | | strcmp ( section , " configuration " ) ) return NULL ;
if ( ! key_name | | strcmp ( key_name , " name " ) ) return NULL ;
if ( ! key_value | | strcmp ( key_value , " sofia.conf " ) ) return NULL ;
if ( ! params ) return NULL ;
profileName = switch_event_get_header ( params , " profile " ) ;
if ( ! profileName | | strcmp ( profileName , " signalwire " ) ) return NULL ;
reconfigValue = switch_event_get_header ( params , " reconfig " ) ;
if ( ! reconfigValue | | strcmp ( reconfigValue , " true " ) ) return NULL ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Received XML lookup for SignalWire SIP profile \n " ) ;
if ( globals . signalwire_profile ) {
signalwire_profile_dup = switch_xml_dup ( globals . signalwire_profile ) ;
}
return signalwire_profile_dup ;
}
static switch_status_t mod_signalwire_load_or_generate_token ( void )
{
switch_status_t status = SWITCH_STATUS_SUCCESS ;
char tokenpath [ 1024 ] ;
switch_snprintf ( tokenpath , sizeof ( tokenpath ) , " %s%s%s " , SWITCH_GLOBAL_dirs . storage_dir , SWITCH_PATH_SEPARATOR , " adoption-token.dat " ) ;
if ( switch_file_exists ( tokenpath , NULL ) ! = SWITCH_STATUS_SUCCESS ) {
// generate first time uuid
ks_uuid_t uuid ;
const char * token ;
FILE * fp = fopen ( tokenpath , " w " ) ;
if ( ! fp ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to open %s to save SignalWire connection token \n " , tokenpath ) ;
status = SWITCH_STATUS_TERM ;
goto done ;
}
ks_uuid ( & uuid ) ;
token = ks_uuid_str ( NULL , & uuid ) ;
fputs ( token , fp ) ;
fclose ( fp ) ;
strncpy ( globals . adoption_token , token , sizeof ( globals . adoption_token ) - 1 ) ;
ks_pool_free ( & token ) ;
} else {
char token [ 64 ] ;
FILE * fp = fopen ( tokenpath , " r " ) ;
if ( ! fp ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to open %s to read SignalWire connection token \n " , tokenpath ) ;
status = SWITCH_STATUS_TERM ;
goto done ;
}
if ( ! fgets ( token , sizeof ( token ) , fp ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to read SignalWire connection token from %s \n " , tokenpath ) ;
fclose ( fp ) ;
status = SWITCH_STATUS_TERM ;
goto done ;
}
fclose ( fp ) ;
// trim newline markers in case they exist, only want the token
for ( size_t len = strlen ( token ) ; len > 0 & & ( token [ len - 1 ] = = ' \r ' | | token [ len - 1 ] = = ' \n ' ) ; - - len ) {
token [ len - 1 ] = ' \0 ' ;
}
2019-06-19 19:53:35 +00:00
snprintf ( globals . adoption_token , sizeof ( globals . adoption_token ) , " %s " , token ) ;
2018-12-07 10:37:01 +00:00
}
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO ,
" \n /===================================================================== \\ \n "
" | Connection Token: %s | \n "
" \\ =====================================================================/ \n "
" Go to https://signalwire.com to set up your Connector now! \n " , globals . adoption_token ) ;
done :
return status ;
}
static switch_status_t load_config ( )
{
char * cf = " signalwire.conf " ;
switch_xml_t cfg , xml ;
const char * data ;
globals . ssl_verify = 1 ;
switch_set_string ( globals . blade_bootstrap , " edge.<space>.signalwire.com/api/relay/wss " ) ;
switch_set_string ( globals . adoption_service , " https://adopt.signalwire.com/adoption " ) ;
switch_set_string ( globals . stun_server , " stun.freeswitch.org " ) ;
if ( ! ( xml = switch_xml_open_cfg ( cf , & cfg , NULL ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG1 , " open of %s failed \n " , cf ) ;
// don't need the config
} else {
switch_xml_t settings , param , tmp ;
if ( ( settings = switch_xml_child ( cfg , " settings " ) ) ) {
for ( param = switch_xml_child ( settings , " param " ) ; param ; param = param - > next ) {
char * var = ( char * ) switch_xml_attr_soft ( param , " name " ) ;
char * val = ( char * ) switch_xml_attr_soft ( param , " value " ) ;
if ( ! strcasecmp ( var , " kslog " ) & & ! ks_zstr ( val ) ) {
if ( ! strcmp ( val , " off " ) ) {
globals . kslog_on = KS_FALSE ;
} else if ( ! strcmp ( val , " on " ) ) {
globals . kslog_on = KS_TRUE ;
}
} else if ( ! strcasecmp ( var , " blade-bootstrap " ) & & ! ks_zstr ( val ) ) {
switch_set_string ( globals . blade_bootstrap , val ) ;
} else if ( ! strcasecmp ( var , " adoption-service " ) & & ! ks_zstr ( val ) ) {
switch_set_string ( globals . adoption_service , val ) ;
} else if ( ! strcasecmp ( var , " stun-server " ) & & ! ks_zstr ( val ) ) {
switch_set_string ( globals . stun_server , val ) ;
} else if ( ! strcasecmp ( var , " ssl-verify " ) ) {
globals . ssl_verify = switch_true ( val ) ? 1 : 0 ;
2019-08-04 07:37:51 +00:00
} else if ( ! strcasecmp ( var , " override-context " ) & & ! ks_zstr ( val ) ) {
switch_set_string ( globals . override_context , val ) ;
2018-12-07 10:37:01 +00:00
}
}
if ( ( tmp = switch_xml_child ( settings , " authentication " ) ) ) {
const char * txt = switch_xml_txt ( tmp ) ;
if ( ! ks_zstr ( txt ) ) {
swclt_config_set_authentication ( globals . config , txt ) ;
}
}
}
switch_xml_free ( xml ) ;
}
if ( ( data = getenv ( " SW_BLADE_BOOTSTRAP " ) ) ) {
switch_set_string ( globals . blade_bootstrap , data ) ;
}
if ( ( data = getenv ( " SW_ADOPTION_SERVICE " ) ) ) {
2019-06-19 19:53:35 +00:00
snprintf ( globals . adoption_service , sizeof ( globals . adoption_service ) , " %s " , data ) ;
2018-12-07 10:37:01 +00:00
}
swclt_config_load_from_env ( globals . config ) ;
return SWITCH_STATUS_SUCCESS ;
}
static ks_status_t load_credentials ( void )
{
ks_status_t status = KS_STATUS_SUCCESS ;
char authpath [ 1024 ] ;
char data [ 2048 ] ;
FILE * fp = NULL ;
ks_json_t * json = NULL ;
switch_snprintf ( authpath , sizeof ( authpath ) , " %s%s%s " , SWITCH_GLOBAL_dirs . storage_dir , SWITCH_PATH_SEPARATOR , " adoption-auth.dat " ) ;
if ( ! ( fp = fopen ( authpath , " r " ) ) ) goto done ;
if ( ! fgets ( data , sizeof ( data ) , fp ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to read SignalWire authentication data from %s \n " , authpath ) ;
fclose ( fp ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
fclose ( fp ) ;
json = ks_json_parse ( data ) ;
if ( ! json ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Unable to parse SignalWire authentication data from %s \n " , authpath ) ;
status = KS_STATUS_FAIL ;
goto done ;
}
status = load_credentials_from_json ( json ) ;
ks_json_delete ( & json ) ;
done :
return status ;
}
static void mod_signalwire_session_auth_failed_handler ( swclt_sess_t sess )
{
char path [ 1024 ] ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " SignalWire authentication failed \n " ) ;
switch_snprintf ( path , sizeof ( path ) , " %s%s%s " , SWITCH_GLOBAL_dirs . storage_dir , SWITCH_PATH_SEPARATOR , " adoption-auth.dat " ) ;
unlink ( path ) ;
switch_snprintf ( path , sizeof ( path ) , " %s%s%s " , SWITCH_GLOBAL_dirs . storage_dir , SWITCH_PATH_SEPARATOR , " signalwire-conf.dat " ) ;
unlink ( path ) ;
globals . restarting = KS_TRUE ;
globals . adoption_backoff = 0 ;
globals . adoption_next = 0 ;
globals . state = SW_STATE_ADOPTION ;
}
/* Dialplan INTERFACE */
SWITCH_STANDARD_DIALPLAN ( dialplan_hunt )
{
switch_caller_extension_t * extension = NULL ;
switch_channel_t * channel = switch_core_session_get_channel ( session ) ;
const char * network_ip = switch_channel_get_variable ( channel , " sip_network_ip " ) ;
const char * network_port = switch_channel_get_variable ( channel , " sip_network_port " ) ;
if ( ! caller_profile ) {
if ( ! ( caller_profile = switch_channel_get_caller_profile ( channel ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_ERROR , " Error obtaining caller profile! \n " ) ;
goto done ;
}
}
2019-08-04 07:37:51 +00:00
if ( globals . override_context [ 0 ] ! = ' \0 ' ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_INFO , " Overriding dialplan context from %s to %s \n " , caller_profile - > context , globals . override_context ) ;
caller_profile - > context = globals . override_context ;
}
2018-12-07 10:37:01 +00:00
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_INFO , " Processing %s <%s>->%s in context %s \n " ,
caller_profile - > caller_id_name , caller_profile - > caller_id_number , caller_profile - > destination_number , caller_profile - > context ) ;
if ( ( extension = switch_caller_extension_new ( session , " signalwire " , caller_profile - > destination_number ) ) = = NULL ) {
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_CRIT , " Memory Error! \n " ) ;
goto done ;
}
switch_log_printf ( SWITCH_CHANNEL_SESSION_LOG ( session ) , SWITCH_LOG_DEBUG , " call from %s:%s \n " , network_ip , network_port ) ;
switch_mutex_lock ( globals . mutex ) ;
if ( network_ip & &
! zstr_buf ( globals . gateway_ip ) & & ! strcmp ( globals . gateway_ip , network_ip ) ) {
// good to go
char transfer_to [ 1024 ] ;
switch_snprintf ( transfer_to , sizeof ( transfer_to ) , " %s %s %s " , caller_profile - > destination_number , " XML " , caller_profile - > context ) ;
switch_caller_extension_add_application ( session , extension , " transfer " , transfer_to ) ;
} else {
switch_caller_extension_add_application ( session , extension , " respond " , " 500 " ) ;
}
switch_mutex_unlock ( globals . mutex ) ;
done :
return extension ;
}
/**
* Module load or unload callback from core
* @ param event the event
*/
static void on_module_load_unload ( switch_event_t * event )
{
const char * type = switch_event_get_header ( event , " type " ) ;
const char * name = switch_event_get_header ( event , " name " ) ;
if ( ! zstr ( type ) & & ! zstr ( name ) & & ! strcmp ( type , " endpoint " ) & & ! strcmp ( name , " sofia " ) ) {
globals . profile_reload = KS_TRUE ;
}
}
/**
* Sofia sofia : : gateway_state change callback
* @ param event the event
*/
static void on_sofia_gateway_state ( switch_event_t * event )
{
const char * ip = switch_event_get_header ( event , " Register-Network-IP " ) ;
const char * port = switch_event_get_header ( event , " Register-Network-Port " ) ;
const char * state = switch_event_get_header ( event , " State " ) ;
const char * gateway = switch_event_get_header ( event , " Gateway " ) ;
if ( ! ip | | ! port | | ! state | | ! gateway ) {
return ;
}
if ( ! strcmp ( gateway , " signalwire " ) ) {
switch_mutex_lock ( globals . mutex ) ;
if ( ! strcmp ( state , " REGED " ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " SignalWire SIP Gateway registered to %s:%s \n " , ip , port ) ;
switch_set_string ( globals . gateway_ip , ip ) ;
switch_set_string ( globals . gateway_port , port ) ;
} else if ( ! strcmp ( state , " NOREG " ) ) {
globals . gateway_ip [ 0 ] = ' \0 ' ;
globals . gateway_port [ 0 ] = ' \0 ' ;
}
switch_mutex_unlock ( globals . mutex ) ;
}
}
/* Macro expands to: switch_status_t mod_signalwire_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
SWITCH_MODULE_LOAD_FUNCTION ( mod_signalwire_load )
{
switch_api_interface_t * api_interface = NULL ;
switch_dialplan_interface_t * dialplan_interface ;
const char * kslog_env = NULL ;
switch_status_t status = SWITCH_STATUS_SUCCESS ;
memset ( & globals , 0 , sizeof ( globals ) ) ;
kslog_env = getenv ( " KSLOG " ) ;
if ( kslog_env & & kslog_env [ 0 ] & & kslog_env [ 0 ] ! = ' 0 ' ) globals . kslog_on = KS_TRUE ;
/* connect my internal structure to the blank pointer passed to me */
* module_interface = switch_loadable_module_create_module_interface ( pool , modname ) ;
ks_global_set_logger ( mod_signalwire_kslogger ) ;
SWITCH_ADD_API ( api_interface , " signalwire " , " SignalWire API " , mod_signalwire_api_function , SIGNALWIRE_SYNTAX ) ;
switch_console_set_complete ( " add signalwire debug " ) ;
switch_console_set_complete ( " add signalwire debug 1 " ) ;
switch_console_set_complete ( " add signalwire debug 2 " ) ;
switch_console_set_complete ( " add signalwire debug 3 " ) ;
switch_console_set_complete ( " add signalwire debug 4 " ) ;
switch_console_set_complete ( " add signalwire debug 5 " ) ;
switch_console_set_complete ( " add signalwire debug 6 " ) ;
switch_console_set_complete ( " add signalwire debug 7 " ) ;
switch_console_set_complete ( " add signalwire kslog " ) ;
switch_console_set_complete ( " add signalwire kslog on " ) ;
switch_console_set_complete ( " add signalwire kslog off " ) ;
switch_console_set_complete ( " add signalwire token " ) ;
switch_console_set_complete ( " add signalwire adoption " ) ;
switch_console_set_complete ( " add signalwire adopted " ) ;
switch_console_set_complete ( " add signalwire update " ) ;
switch_console_set_complete ( " add signalwire reload " ) ;
switch_xml_bind_search_function ( xml_config_handler , SWITCH_XML_SECTION_CONFIG , NULL ) ;
ks_ssl_init_skip ( KS_TRUE ) ;
swclt_init ( KS_LOG_LEVEL_DEBUG ) ;
if ( globals . kslog_on = = KS_FALSE ) {
ks_global_set_logger ( NULL ) ;
} else {
ks_global_set_logger ( mod_signalwire_kslogger ) ;
}
2018-12-26 23:17:31 +00:00
# ifdef WIN32
sslLoadWindowsCACertificate ( ) ;
# endif
2018-12-07 10:37:01 +00:00
// Configuration
swclt_config_create ( & globals . config ) ;
load_config ( ) ;
switch_mutex_init ( & globals . mutex , SWITCH_MUTEX_NESTED , pool ) ;
switch_event_bind ( " mod_signalwire " , SWITCH_EVENT_MODULE_LOAD , NULL , on_module_load_unload , NULL ) ;
switch_event_bind ( " mod_signalwire " , SWITCH_EVENT_MODULE_UNLOAD , NULL , on_module_load_unload , NULL ) ;
switch_event_bind ( " mod_signalwire " , SWITCH_EVENT_CUSTOM , " sofia::gateway_state " , on_sofia_gateway_state , NULL ) ;
SWITCH_ADD_DIALPLAN ( dialplan_interface , " signalwire " , dialplan_hunt ) ;
// Load credentials if they exist from a prior adoption
load_credentials ( ) ;
// SignalWire
swclt_sess_create ( & globals . signalwire_session ,
globals . blade_bootstrap ,
globals . config ) ;
swclt_sess_set_auth_failed_cb ( globals . signalwire_session , mod_signalwire_session_auth_failed_handler ) ;
if ( ! globals . signalwire_session ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " signalwire_session create error \n " ) ;
switch_goto_status ( SWITCH_STATUS_TERM , err ) ;
}
swclt_hmon_register ( & globals . signalwire_session_monitor , globals . signalwire_session , mod_signalwire_session_state_handler , NULL ) ;
// @todo register nodestore callbacks here if needed
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CONSOLE , " Welcome to \n "
" _____ _ ___ ___ \n "
" / ___/(_)___ _____ ____ _/ / | / (_)_______ \n "
" \\ __ \\ / / __ `/ __ \\ / __ `/ /| | /| / / / ___/ _ \\ \n "
" ___/ / / /_/ / / / / /_/ / / | |/ |/ / / / / __/ \n "
" /____/_/ \\ __, /_/ /_/ \\ __,_/_/ |__/|__/_/_/ \\ ___/ \n "
" /____/ \n " ) ;
// storage_dir was missing in clean install
switch_dir_make_recursive ( SWITCH_GLOBAL_dirs . storage_dir , SWITCH_DEFAULT_DIR_PERMS , pool ) ;
if ( ( status = mod_signalwire_load_or_generate_token ( ) ) ! = SWITCH_STATUS_SUCCESS ) {
goto err ;
}
if ( swclt_sess_has_authentication ( globals . signalwire_session ) ) {
// Load cached profile if we already have one. We'll still connect to SignalWire and
// fetch a new profile in the background.
load_sip_config ( ) ;
if ( globals . signalwire_profile ) {
globals . state = SW_STATE_START_PROFILE ;
} else {
globals . state = SW_STATE_OFFLINE ;
}
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CONSOLE , " Connecting to SignalWire \n " ) ;
swclt_sess_connect ( globals . signalwire_session ) ;
} else {
globals . state = SW_STATE_ADOPTION ;
}
goto done ;
err :
if ( globals . signalwire_session ) ks_handle_destroy ( & globals . signalwire_session ) ;
swclt_config_destroy ( & globals . config ) ;
ks_global_set_logger ( NULL ) ;
done :
return status ;
}
/*
Called when the system shuts down
Macro expands to : switch_status_t mod_signalwire_shutdown ( ) */
SWITCH_MODULE_SHUTDOWN_FUNCTION ( mod_signalwire_shutdown )
{
/* Cleanup dynamically allocated config settings */
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Disconnecting from SignalWire \n " ) ;
switch_event_unbind_callback ( on_module_load_unload ) ;
switch_event_unbind_callback ( on_sofia_gateway_state ) ;
// stop things that might try to use blade or kafka while they are shutting down
globals . shutdown = KS_TRUE ;
swclt_sess_disconnect ( globals . signalwire_session ) ;
while ( swclt_hstate_current_get ( globals . signalwire_session ) = = SWCLT_HSTATE_ONLINE ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Sleeping for pending disconnect \n " ) ;
ks_sleep_ms ( 1000 ) ;
}
//signalwire_dialplan_shutdown();
// @todo signalwire profile unbinding and unloading
switch_xml_unbind_search_function_ptr ( xml_config_handler ) ;
// kill signalwire, so nothing more can come into the system
ks_handle_destroy ( & globals . signalwire_session ) ;
// cleanup config
swclt_config_destroy ( & globals . config ) ;
// shutdown libblade (but not libks?)
swclt_shutdown ( ) ;
2018-12-26 23:17:31 +00:00
# ifdef WIN32
// free certificate pointers previously loaded
sslUnLoadWindowsCACertificate ( ) ;
# endif
2018-12-07 10:37:01 +00:00
return SWITCH_STATUS_SUCCESS ;
}
static void mod_signalwire_state_adoption ( void )
{
// keep trying to check adoption token for authentication
if ( ks_time_now ( ) > = globals . adoption_next ) {
// Use a very very simple backoff algorithm, every time we try, backoff another minute
// so that after first try we wait 1 minute, after next try we wait 2 minutes, at third
// try we are waiting 3 minutes, upto a max backoff of 15 minutes between adoption checks
if ( globals . adoption_backoff < 15 ) globals . adoption_backoff + + ;
globals . adoption_next = ks_time_now ( ) + ( globals . adoption_backoff * 60 * KS_USEC_PER_SEC ) ;
if ( mod_signalwire_adoption_post ( ) ! = KS_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Next SignalWire adoption check in % " SWITCH_SIZE_T_FMT " minutes \n " , globals . adoption_backoff ) ;
}
}
if ( globals . signalwire_reconnected ) {
// OK to continue as is
globals . signalwire_reconnected = KS_FALSE ;
}
}
static void mod_signalwire_state_offline ( void )
{
if ( globals . signalwire_reconnected ) {
globals . signalwire_reconnected = KS_FALSE ;
globals . state = SW_STATE_ONLINE ;
}
}
static void mod_signalwire_state_online ( void )
{
globals . signalwire_reconnected = KS_FALSE ;
if ( ! swclt_sess_provisioning_setup ( globals . signalwire_session , __on_provisioning_events , NULL ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Connected to SignalWire \n " ) ;
globals . state = SW_STATE_CONFIGURE ;
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Failed to connect to SignalWire \n " ) ;
ks_sleep_ms ( 4000 ) ;
globals . state = SW_STATE_OFFLINE ;
globals . restarting = KS_TRUE ;
}
}
static void mod_signalwire_state_configure ( void )
{
switch_memory_pool_t * pool = NULL ;
2019-06-19 19:53:35 +00:00
char local_ip [ 64 ] ;
2018-12-07 10:37:01 +00:00
switch_port_t local_port = 6050 ;
char local_endpoint [ 256 ] ;
char * external_ip ;
switch_port_t external_port ;
char external_endpoint [ 256 ] ;
char * error = NULL ;
swclt_cmd_t cmd ;
if ( globals . signalwire_reconnected ) {
globals . signalwire_reconnected = KS_FALSE ;
globals . state = SW_STATE_ONLINE ;
}
// already restarting/updating...
globals . profile_reload = KS_FALSE ;
globals . profile_update = KS_FALSE ;
if ( switch_core_new_memory_pool ( & pool ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " SignalWire configure failed: could not allocate memory pool \n " ) ;
goto done ;
}
switch_find_local_ip ( local_ip , sizeof ( local_ip ) , NULL , AF_INET ) ;
if ( switch_find_available_port ( & local_port , local_ip , SOCK_STREAM ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " SignalWire configure failed: could not get available local port \n " ) ;
ks_sleep_ms ( 4000 ) ;
goto done ;
}
snprintf ( local_endpoint , sizeof ( local_endpoint ) , " %s:%u " , local_ip , local_port ) ;
external_ip = local_ip ;
external_port = local_port ;
if ( switch_stun_lookup ( & external_ip , & external_port , globals . stun_server , SWITCH_STUN_DEFAULT_PORT , & error , pool ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " SignalWire configure failed: stun [%s] lookup error: %s \n " , globals . stun_server , error ) ;
ks_sleep_ms ( 4000 ) ;
goto done ;
}
snprintf ( external_endpoint , sizeof ( external_endpoint ) , " %s:%u " , external_ip , external_port ) ;
if ( ! swclt_sess_provisioning_configure ( globals . signalwire_session , " freeswitch " , local_endpoint , external_endpoint , globals . relay_connector_id , & cmd ) ) {
SWCLT_CMD_TYPE cmd_type ;
swclt_cmd_type ( cmd , & cmd_type ) ;
if ( cmd_type = = SWCLT_CMD_TYPE_RESULT ) {
const ks_json_t * result ;
signalwire_provisioning_configure_response_t * configure_res ;
swclt_cmd_result ( cmd , & result ) ;
result = ks_json_get_object_item ( result , " result " ) ;
if ( ! SIGNALWIRE_PROVISIONING_CONFIGURE_RESPONSE_PARSE ( ks_handle_pool ( cmd ) , result , & configure_res ) ) {
const ks_json_t * configuration = configure_res - > configuration ;
const char * configuration_profile = ks_json_get_object_cstr ( configuration , " profile " ) ;
if ( globals . signalwire_profile ) {
switch_xml_free ( globals . signalwire_profile ) ;
globals . signalwire_profile = NULL ;
}
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " \" %s \" \n " , configuration_profile ) ;
globals . signalwire_profile = switch_xml_parse_str_dynamic ( ( char * ) configuration_profile , SWITCH_TRUE ) ;
if ( ! globals . signalwire_profile ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Failed to parse configuration profile \n " ) ;
} else {
char digest [ SWITCH_MD5_DIGEST_STRING_SIZE ] = { 0 } ;
switch_md5_string ( digest , ( void * ) configuration_profile , strlen ( configuration_profile ) ) ;
save_sip_config ( configuration_profile ) ;
if ( ! signalwire_gateway_exists ( ) | | zstr_buf ( globals . signalwire_profile_md5 ) | | strcmp ( globals . signalwire_profile_md5 , digest ) ) {
// not registered or new profile - update md5 and load it
strcpy ( globals . signalwire_profile_md5 , digest ) ;
globals . state = SW_STATE_START_PROFILE ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " profile MD5 = \" %s \" \n " , globals . signalwire_profile_md5 ) ;
} else {
// already registered
globals . state = SW_STATE_READY ;
}
}
}
}
}
ks_handle_destroy ( & cmd ) ;
if ( globals . state = = SW_STATE_CONFIGURE ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Failed to receive valid configuration from SignalWire \n " ) ;
ks_sleep_ms ( 4000 ) ;
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Received configuration from SignalWire \n " ) ;
}
done :
if ( pool ) switch_core_destroy_memory_pool ( & pool ) ;
}
static int signalwire_gateway_exists ( void )
{
int exists = 0 ;
switch_stream_handle_t stream = { 0 } ;
SWITCH_STANDARD_STREAM ( stream ) ;
if ( switch_api_execute ( " sofia " , " profile signalwire gwlist " , NULL , & stream ) = = SWITCH_STATUS_SUCCESS & & stream . data ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " gwlist = \" %s \" \n " , ( char * ) stream . data ) ;
exists = ( strstr ( ( char * ) stream . data , " Invalid Profile " ) = = NULL ) & &
( strstr ( ( char * ) stream . data , " signalwire " ) ! = NULL ) ;
}
switch_safe_free ( stream . data ) ;
return exists ;
}
static int signalwire_profile_is_started ( void )
{
int started = 0 ;
switch_stream_handle_t stream = { 0 } ;
SWITCH_STANDARD_STREAM ( stream ) ;
if ( switch_api_execute ( " sofia " , " status profile signalwire " , NULL , & stream ) = = SWITCH_STATUS_SUCCESS & & stream . data ) {
started = ( strstr ( ( char * ) stream . data , " Invalid Profile " ) = = NULL ) & &
( strstr ( ( char * ) stream . data , " signalwire " ) ! = NULL ) ;
}
switch_safe_free ( stream . data ) ;
return started ;
}
static int signalwire_profile_rescan ( void )
{
int success = 0 ;
switch_stream_handle_t stream = { 0 } ;
SWITCH_STANDARD_STREAM ( stream ) ;
if ( switch_api_execute ( " sofia " , " profile signalwire rescan " , NULL , & stream ) = = SWITCH_STATUS_SUCCESS ) {
success = signalwire_profile_is_started ( ) ;
}
switch_safe_free ( stream . data ) ;
return success ;
}
static int signalwire_profile_start ( void )
{
int success = 0 ;
switch_stream_handle_t stream = { 0 } ;
SWITCH_STANDARD_STREAM ( stream ) ;
if ( switch_api_execute ( " sofia " , " profile signalwire start " , NULL , & stream ) = = SWITCH_STATUS_SUCCESS ) {
success = signalwire_profile_is_started ( ) ;
}
switch_safe_free ( stream . data ) ;
return success ;
}
static void signalwire_profile_killgw ( void )
{
switch_stream_handle_t stream = { 0 } ;
SWITCH_STANDARD_STREAM ( stream ) ;
switch_api_execute ( " sofia " , " profile signalwire killgw signalwire " , NULL , & stream ) ;
switch_safe_free ( stream . data ) ;
}
static void mod_signalwire_state_start_profile ( void )
{
if ( globals . profile_update ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " SignalWire SIP profile update initiated \n " ) ;
globals . state = SW_STATE_CONFIGURE ;
globals . profile_update = KS_FALSE ;
return ;
}
globals . profile_reload = KS_FALSE ; // already here
// ignore SignalWire reconnections until register is attempted
if ( switch_loadable_module_exists ( " mod_sofia " ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Waiting for mod_sofia to load \n " ) ;
} else if ( signalwire_profile_is_started ( ) ) {
// kill gateway if already up and rescan the profile
if ( signalwire_gateway_exists ( ) ) {
signalwire_profile_killgw ( ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " SignalWire SIP gateway killed \n " ) ;
}
if ( signalwire_profile_rescan ( ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " SignalWire SIP profile rescanned \n " ) ;
globals . state = SW_STATE_REGISTER ;
}
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Starting SignalWire SIP profile \n " ) ;
signalwire_profile_start ( ) ; // assume success - it gets checked in next state
globals . state = SW_STATE_REGISTER ;
}
}
static void mod_signalwire_state_register ( void )
{
if ( globals . profile_update ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " SignalWire SIP profile update initiated \n " ) ;
globals . state = SW_STATE_CONFIGURE ;
globals . profile_update = KS_FALSE ;
return ;
} else if ( globals . profile_reload ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " SignalWire SIP profile reload initiated \n " ) ;
globals . state = SW_STATE_START_PROFILE ;
globals . profile_reload = KS_FALSE ;
return ;
}
// ignore SignalWire reconnections until register is attempted
if ( switch_loadable_module_exists ( " mod_sofia " ) ! = SWITCH_STATUS_SUCCESS ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Waiting for mod_sofia to load \n " ) ;
globals . state = SW_STATE_START_PROFILE ;
} else if ( signalwire_gateway_exists ( ) | | signalwire_profile_rescan ( ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " SignalWire SIP gateway started \n " ) ;
globals . state = SW_STATE_READY ;
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Failed to start SignalWire SIP gateway \n " ) ;
globals . state = SW_STATE_CONFIGURE ;
ks_sleep_ms ( 5000 ) ;
}
}
static void mod_signalwire_state_ready ( )
{
if ( globals . profile_update ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Signalwire SIP profile update initiated \n " ) ;
globals . state = SW_STATE_CONFIGURE ;
globals . profile_update = KS_FALSE ;
} else if ( globals . profile_reload ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " SignalWire SIP profile reload initiated \n " ) ;
globals . state = SW_STATE_START_PROFILE ;
globals . profile_reload = KS_FALSE ;
} else if ( globals . signalwire_reconnected ) {
globals . signalwire_reconnected = KS_FALSE ;
globals . state = SW_STATE_ONLINE ;
}
}
SWITCH_MODULE_RUNTIME_FUNCTION ( mod_signalwire_runtime )
{
while ( ! globals . shutdown ) {
if ( globals . restarting ) {
swclt_sess_disconnect ( globals . signalwire_session ) ;
while ( swclt_hstate_current_get ( globals . signalwire_session ) = = SWCLT_HSTATE_ONLINE ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " Sleeping for pending disconnect \n " ) ;
ks_sleep_ms ( 1000 ) ;
}
// kill signalwire, so nothing more can come into the system
ks_handle_destroy ( & globals . signalwire_session ) ;
// Create a new session and start over
swclt_sess_create ( & globals . signalwire_session ,
globals . blade_bootstrap ,
globals . config ) ;
swclt_sess_set_auth_failed_cb ( globals . signalwire_session , mod_signalwire_session_auth_failed_handler ) ;
swclt_hmon_register ( & globals . signalwire_session_monitor , globals . signalwire_session , mod_signalwire_session_state_handler , NULL ) ;
globals . restarting = KS_FALSE ;
continue ;
}
switch ( globals . state ) {
case SW_STATE_ADOPTION : // waiting for adoption to occur
mod_signalwire_state_adoption ( ) ;
break ;
case SW_STATE_OFFLINE : // waiting for session to go online
mod_signalwire_state_offline ( ) ;
break ;
case SW_STATE_ONLINE : // provisioning service setup
mod_signalwire_state_online ( ) ;
break ;
case SW_STATE_CONFIGURE : // provisioning configuration
mod_signalwire_state_configure ( ) ;
break ;
case SW_STATE_START_PROFILE :
mod_signalwire_state_start_profile ( ) ;
break ;
case SW_STATE_REGISTER :
mod_signalwire_state_register ( ) ;
break ;
case SW_STATE_READY : // ready for runtime
mod_signalwire_state_ready ( ) ;
break ;
default : break ;
}
ks_sleep_ms ( 1000 ) ;
}
return SWITCH_STATUS_TERM ;
}
/* For Emacs:
* Local Variables :
* mode : c
* indent - tabs - mode : t
* tab - width : 4
* c - basic - offset : 4
* End :
* For VIM :
* vim : set softtabstop = 4 shiftwidth = 4 tabstop = 4 noet
*/