From ca017c8f73280d185f16813205a5bcb8ef3cf8e0 Mon Sep 17 00:00:00 2001 From: Aron Podrigal Date: Mon, 10 May 2021 18:27:22 -0500 Subject: [PATCH] When using multiple profiles and with the same gateway name on both, freeswitch mixes up the two gateways and is unable to restart any of them unless restarting the profile. This also effects when moving a gateway from one profile to the other. What this changes - Do not remove a gateway from the hash unless it matches the profile. - Fix `sofia profile killgw ` to delete the gateway by using the qualified name `ProfileName::GatewayName` so we only delete a gateway on profile `ProfileName`. - When loading/reloading a gateway, add it with the unqualified name to the hash if there is no other gateway with that name on another profile. (for example when `profileA` and `profileB` have a gateway `gw1`, `profileA` is first in the list, so we add `gw1` from `profileA` to the hash qualified and unqualified. for `profileB` it is added only qualified. Now, `sofia profile profileA killgw gw1` removes the gateway from `profileA`, so executing `sofia profile profileB startgw gw1` will now add `gw1` from `profileB` to the hash using the unqualified name). - Fix `sofia profile ProfileName register|unregister GatewayName` to be profile specific. - Ensure throughout sofia whenever we lookup a gateway related to an ongoing call, to also check the profile that it matches. --- src/mod/endpoints/mod_sofia/mod_sofia.c | 15 +-- src/mod/endpoints/mod_sofia/mod_sofia.h | 3 + src/mod/endpoints/mod_sofia/sofia.c | 26 +++-- src/mod/endpoints/mod_sofia/sofia_glue.c | 11 +- src/mod/endpoints/mod_sofia/sofia_presence.c | 2 +- src/mod/endpoints/mod_sofia/sofia_reg.c | 108 +++++++++++++------ 6 files changed, 112 insertions(+), 53 deletions(-) diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index c41ed9b2e3..25ec1276fe 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -422,16 +422,17 @@ switch_status_t sofia_on_hangup(switch_core_session_t *session) const char *gateway_name = NULL; sofia_gateway_t *gateway_ptr = NULL; - if ((gateway_name = switch_channel_get_variable(channel, "sip_gateway_name"))) { - gateway_ptr = sofia_reg_find_gateway(gateway_name); - } - if (!tech_pvt) { return SWITCH_STATUS_SUCCESS; } switch_mutex_lock(tech_pvt->sofia_mutex); + if (tech_pvt->gateway_name && tech_pvt->profile) { + gateway_name = tech_pvt->gateway_name; + gateway_ptr = sofia_reg_find_profile_gateway(tech_pvt->profile, gateway_name); + } + if (!switch_channel_test_flag(channel, CF_ANSWERED)) { if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { @@ -3567,7 +3568,7 @@ static switch_status_t cmd_profile(char **argv, int argc, switch_stream_handle_t sofia_glue_del_every_gateway(profile); stream->write_function(stream, "+OK every gateway marked for deletion.\n"); } else { - if ((gateway_ptr = sofia_reg_find_gateway(argv[2]))) { + if ((gateway_ptr = sofia_reg_find_profile_gateway(profile, argv[2]))) { sofia_glue_del_gateway(gateway_ptr); sofia_reg_release_gateway(gateway_ptr); stream->write_function(stream, "+OK gateway marked for deletion.\n"); @@ -3680,7 +3681,7 @@ static switch_status_t cmd_profile(char **argv, int argc, switch_stream_handle_t } } stream->write_function(stream, "+OK\n"); - } else if ((gateway_ptr = sofia_reg_find_gateway(gname))) { + } else if ((gateway_ptr = sofia_reg_find_profile_gateway(profile, gname))) { if (gateway_ptr->state != REG_STATE_NOREG) { gateway_ptr->retry = 0; gateway_ptr->state = REG_STATE_UNREGED; @@ -3713,7 +3714,7 @@ static switch_status_t cmd_profile(char **argv, int argc, switch_stream_handle_t } } stream->write_function(stream, "+OK\n"); - } else if ((gateway_ptr = sofia_reg_find_gateway(gname))) { + } else if ((gateway_ptr = sofia_reg_find_profile_gateway(profile, gname))) { if (gateway_ptr->state != REG_STATE_NOREG) { gateway_ptr->retry = 0; gateway_ptr->state = REG_STATE_UNREGISTER; diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index 8e2b1b483c..9121c53a55 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -1121,6 +1121,9 @@ switch_status_t sofia_reg_add_gateway(sofia_profile_t *profile, const char *key, sofia_gateway_t *sofia_reg_find_gateway__(const char *file, const char *func, int line, const char *key); #define sofia_reg_find_gateway(x) sofia_reg_find_gateway__(__FILE__, __SWITCH_FUNC__, __LINE__, x) +sofia_gateway_t *sofia_reg_find_profile_gateway__(const char *file, const char *func, int line, sofia_profile_t *profile, const char *key); +#define sofia_reg_find_profile_gateway(p, x) sofia_reg_find_profile_gateway__(__FILE__, __SWITCH_FUNC__, __LINE__, p, x) + sofia_gateway_t *sofia_reg_find_gateway_by_realm__(const char *file, const char *func, int line, const char *key); #define sofia_reg_find_gateway_by_realm(x) sofia_reg_find_gateway_by_realm__(__FILE__, __SWITCH_FUNC__, __LINE__, x) diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index a370b52442..c00d1aae1c 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -747,7 +747,7 @@ void sofia_handle_sip_i_notify(switch_core_session_t *session, int status, } - if (!(gateway = sofia_reg_find_gateway(sofia_private->gateway_name))) { + if (!(gateway = sofia_reg_find_profile_gateway(profile, sofia_private->gateway_name))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Gateway information missing\n"); goto error; } @@ -1491,7 +1491,7 @@ static void our_sofia_event_callback(nua_event_t event, if (sofia_private && sofia_private != &mod_sofia_globals.destroy_private && sofia_private != &mod_sofia_globals.keep_private) { if (!zstr(sofia_private->gateway_name)) { - if (!(gateway = sofia_reg_find_gateway(sofia_private->gateway_name))) { + if (!(gateway = sofia_reg_find_profile_gateway(profile,sofia_private->gateway_name))) { return; } } else if (!zstr(sofia_private->uuid)) { @@ -1520,7 +1520,7 @@ static void our_sofia_event_callback(nua_event_t event, } if (tech_pvt->gateway_name) { - gateway = sofia_reg_find_gateway(tech_pvt->gateway_name); + gateway = sofia_reg_find_profile_gateway(profile,tech_pvt->gateway_name); } if (channel && switch_channel_down(channel)) { @@ -2099,7 +2099,7 @@ static void our_sofia_event_callback(nua_event_t event, if (sofia_private && !zstr(sofia_private->gateway_name)) { sofia_gateway_t *gateway = NULL; - if ((gateway = sofia_reg_find_gateway(sofia_private->gateway_name))) { + if ((gateway = sofia_reg_find_profile_gateway(profile,sofia_private->gateway_name))) { gateway->state = REG_STATE_FAILED; gateway->failure_status = status; sofia_reg_release_gateway(gateway); @@ -3790,8 +3790,12 @@ static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag, } switch_mutex_lock(mod_sofia_globals.hash_mutex); - if (switch_core_hash_find(mod_sofia_globals.gateway_hash, name) && (gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, pkey)) && !gp->deleted) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate gateway '%s'\n", name); + if ((gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, pkey)) && !gp->deleted) { + if (sofia_reg_add_gateway(profile, name, gp) == SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Added gateway to hash '%s'\n", name); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate gateway '%s'\n", name); + } switch_mutex_unlock(mod_sofia_globals.hash_mutex); free(pkey); goto skip; @@ -4219,7 +4223,9 @@ static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag, parse_gateway_subscriptions(profile, gateway, gw_subs_tag); } - sofia_reg_add_gateway(profile, gateway->name, gateway); + if (sofia_reg_add_gateway(profile, gateway->name, gateway) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Skipped gateway '%s' on profile '%s'\n", gateway->name, profile->name); + } } @@ -6446,7 +6452,7 @@ static void sofia_handle_sip_r_options(switch_core_session_t *session, int statu switch_bool_t do_fire_gateway_state_event = SWITCH_FALSE; if (sofia_private && !zstr(sofia_private->gateway_name)) { - gateway = sofia_reg_find_gateway(sofia_private->gateway_name); + gateway = sofia_reg_find_profile_gateway(profile,sofia_private->gateway_name); sofia_private->destroy_me = 1; } @@ -11309,13 +11315,13 @@ void sofia_handle_sip_i_invite(switch_core_session_t *session, nua_t *nua, sofia sofia_gateway_t *gateway = NULL; char *extension = NULL; - if (gw_name && ((gateway = sofia_reg_find_gateway(gw_name)))) { + if (gw_name && ((gateway = sofia_reg_find_profile_gateway(profile, gw_name)))) { gw_param_name = NULL; extension = gateway->extension; } if (!gateway && gw_param_name) { - if ((gateway = sofia_reg_find_gateway(gw_param_name))) { + if ((gateway = sofia_reg_find_profile_gateway(profile, gw_param_name))) { extension = gateway->real_extension; } } diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index 1452a7cf0f..e3d287ddcd 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -2265,9 +2265,16 @@ void sofia_glue_del_profile(sofia_profile_t *profile) for (gp = profile->gateways; gp; gp = gp->next) { char *pkey = switch_mprintf("%s::%s", profile->name, gp->name); + // Only delete from hash if this gateway matches ours. + if (switch_core_hash_find(mod_sofia_globals.gateway_hash, gp->name) == gp) { + switch_core_hash_delete(mod_sofia_globals.gateway_hash, gp->name); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing gateway on profile %s from hash %s.\n", profile->name, gp->name); + } + if (switch_core_hash_find(mod_sofia_globals.gateway_hash, pkey) == gp) { + switch_core_hash_delete(mod_sofia_globals.gateway_hash, pkey); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing gateway on profile %s from hash %s.\n", profile->name, pkey); + } - switch_core_hash_delete(mod_sofia_globals.gateway_hash, gp->name); - switch_core_hash_delete(mod_sofia_globals.gateway_hash, pkey); switch_safe_free(pkey); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "deleted gateway %s from profile %s\n", gp->name, profile->name); } diff --git a/src/mod/endpoints/mod_sofia/sofia_presence.c b/src/mod/endpoints/mod_sofia/sofia_presence.c index 48ad579411..e567b615a9 100644 --- a/src/mod/endpoints/mod_sofia/sofia_presence.c +++ b/src/mod/endpoints/mod_sofia/sofia_presence.c @@ -4478,7 +4478,7 @@ void sofia_presence_handle_sip_r_subscribe(int status, } - if (!(gateway = sofia_reg_find_gateway(sofia_private->gateway_name))) { + if (!(gateway = sofia_reg_find_profile_gateway(profile,sofia_private->gateway_name))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Gateway information missing\n"); return; } diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c index d9e04528a8..5c5ee92218 100644 --- a/src/mod/endpoints/mod_sofia/sofia_reg.c +++ b/src/mod/endpoints/mod_sofia/sofia_reg.c @@ -322,13 +322,15 @@ void sofia_reg_check_gateway(sofia_profile_t *profile, time_t now) switch_mutex_lock(profile->gw_mutex); for (gateway_ptr = profile->gateways; gateway_ptr; gateway_ptr = gateway_ptr->next) { if (gateway_ptr->deleted) { + char *pkey = switch_mprintf("%s::%s", profile->name, gateway_ptr->name); + switch_assert(pkey); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing gateway on profile %s from hash %s.\n", profile->name, pkey); + switch_core_hash_delete(mod_sofia_globals.gateway_hash, pkey); + free(pkey); + if ((check = switch_core_hash_find(mod_sofia_globals.gateway_hash, gateway_ptr->name)) && check == gateway_ptr) { - char *pkey = switch_mprintf("%s::%s", profile->name, gateway_ptr->name); - switch_assert(pkey); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing gateway %s from hash.\n", pkey); - switch_core_hash_delete(mod_sofia_globals.gateway_hash, pkey); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing gateway on profile %s from hash %s.\n", profile->name, gateway_ptr->name); switch_core_hash_delete(mod_sofia_globals.gateway_hash, gateway_ptr->name); - free(pkey); } if (gateway_ptr->state == REG_STATE_NOREG || gateway_ptr->state == REG_STATE_DOWN) { @@ -2581,14 +2583,13 @@ void sofia_reg_handle_sip_r_register(int status, { sofia_gateway_t *gateway = NULL; - - if (!sofia_private) { + if (!profile || !sofia_private) { nua_handle_destroy(nh); return; } if (!zstr(sofia_private->gateway_name)) { - gateway = sofia_reg_find_gateway(sofia_private->gateway_name); + gateway = sofia_reg_find_profile_gateway(profile, sofia_private->gateway_name); } if (gateway) { @@ -2760,7 +2761,7 @@ void sofia_reg_handle_sip_r_challenge(int status, if (!gateway) { if (gw_name) { - var_gateway = sofia_reg_find_gateway((char *) gw_name); + var_gateway = sofia_reg_find_profile_gateway(profile,(char *) gw_name); } @@ -2774,13 +2775,17 @@ void sofia_reg_handle_sip_r_challenge(int status, if ((p = strchr(rb, '"'))) { *p = '\0'; } - if (!(var_gateway = sofia_reg_find_gateway(rb))) { + if (!(var_gateway = sofia_reg_find_profile_gateway(profile, rb))) { var_gateway = sofia_reg_find_gateway_by_realm(rb); + if(var_gateway && var_gateway->profile != profile) { + sofia_reg_release_gateway(var_gateway); + var_gateway = NULL; + } } } if (!var_gateway && sip && sip->sip_to) { - var_gateway = sofia_reg_find_gateway(sip->sip_to->a_url->url_host); + var_gateway = sofia_reg_find_profile_gateway(profile,sip->sip_to->a_url->url_host); } if (var_gateway) { @@ -3490,7 +3495,7 @@ auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile, name = "anonymous"; } - if ((gateway_ptr = sofia_reg_find_gateway(name))) { + if ((gateway_ptr = sofia_reg_find_profile_gateway(profile, name))) { reg_state_t ostate = gateway_ptr->state; gateway_ptr->retry = 0; if (exptime) { @@ -3516,7 +3521,7 @@ auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile, argc = switch_separate_string(mydata, ',', argv, (sizeof(argv) / sizeof(argv[0]))); for (x = 0; x < argc; x++) { - if ((gateway_ptr = sofia_reg_find_gateway((char *) argv[x]))) { + if ((gateway_ptr = sofia_reg_find_profile_gateway(profile,(char *) argv[x]))) { reg_state_t ostate = gateway_ptr->state; gateway_ptr->retry = 0; if (exptime) { @@ -3619,6 +3624,26 @@ sofia_gateway_t *sofia_reg_find_gateway__(const char *file, const char *func, in } +sofia_gateway_t *sofia_reg_find_profile_gateway__(const char *file, const char *func, int line, sofia_profile_t *profile, const char *key) { + char *pkey = NULL; + sofia_gateway_t *gw = NULL; + if (!strstr(key, "::")) { + pkey = switch_mprintf("%s::%s", profile->name, key); + key = pkey; + } + gw = sofia_reg_find_gateway__(file, func, line, key); + switch_safe_free(pkey); + if (gw) { + if (gw->profile != profile) { + sofia_reg_release_gateway__(file, func, line, gw); + gw = NULL; + } + } + + return gw; +} + + sofia_gateway_t *sofia_reg_find_gateway_by_realm__(const char *file, const char *func, int line, const char *key) { sofia_gateway_t *gateway = NULL; @@ -3684,42 +3709,59 @@ switch_status_t sofia_reg_add_gateway(sofia_profile_t *profile, const char *key, { switch_status_t status = SWITCH_STATUS_FALSE; char *pkey = switch_mprintf("%s::%s", profile->name, key); - sofia_gateway_t *gp; - - switch_mutex_lock(profile->gw_mutex); - - gateway->next = profile->gateways; - profile->gateways = gateway; - - switch_mutex_unlock(profile->gw_mutex); + sofia_gateway_t *unqualified_gw; + sofia_gateway_t *qualified_gw; switch_mutex_lock(mod_sofia_globals.hash_mutex); - if ((gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, key))) { - if (gp->deleted) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing deleted gateway from hash.\n"); - switch_core_hash_delete(mod_sofia_globals.gateway_hash, gp->name); - switch_core_hash_delete(mod_sofia_globals.gateway_hash, pkey); + if ((unqualified_gw = switch_core_hash_find(mod_sofia_globals.gateway_hash, key))) { + // regardless of the profile this belongs to, if it has been deleted, we remove it so we can add ours. + if (unqualified_gw->deleted) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing deleted gateway from hash %s.\n", key); switch_core_hash_delete(mod_sofia_globals.gateway_hash, key); + unqualified_gw = NULL; } } - if (!switch_core_hash_find(mod_sofia_globals.gateway_hash, key) && !switch_core_hash_find(mod_sofia_globals.gateway_hash, pkey)) { - status = switch_core_hash_insert(mod_sofia_globals.gateway_hash, key, gateway); - status |= switch_core_hash_insert(mod_sofia_globals.gateway_hash, pkey, gateway); - if (status != SWITCH_STATUS_SUCCESS) { - status = SWITCH_STATUS_FALSE; + if ((qualified_gw = switch_core_hash_find(mod_sofia_globals.gateway_hash, pkey))) { + if (qualified_gw->profile == profile) { + if (qualified_gw->deleted) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Re-adding gateway into hash %s.\n", pkey); + status = switch_core_hash_insert(mod_sofia_globals.gateway_hash, pkey, gateway); + } + } else { + qualified_gw = NULL; } } else { - status = SWITCH_STATUS_FALSE; + status = switch_core_hash_insert(mod_sofia_globals.gateway_hash, pkey, gateway); + qualified_gw = gateway; } + + if (status == SWITCH_STATUS_SUCCESS) { + switch_mutex_lock(profile->gw_mutex); + gateway->next = profile->gateways; + profile->gateways = gateway; + switch_mutex_unlock(profile->gw_mutex); + } + + // insert namespaced version profile::gateway + if (qualified_gw) { + // insert un-namespaced name if it does not exist + if (!unqualified_gw) { + status = switch_core_hash_insert(mod_sofia_globals.gateway_hash, key, gateway); + } else { + status = SWITCH_STATUS_INUSE; + } + } + switch_mutex_unlock(mod_sofia_globals.hash_mutex); free(pkey); if (status == SWITCH_STATUS_SUCCESS) { switch_event_t *s_event; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Added gateway '%s' to profile '%s'\n", gateway->name, gateway->profile->name); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Added gateway '%s' for profile '%s'\n", gateway->name, gateway->profile->name); if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_GATEWAY_ADD) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "Gateway", gateway->name); switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile-name", gateway->profile->name);