diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c index a403224eda6f83184c3348d93be278a26c822099..55d9f5a99ed523c708bb4b096eb8c1a985496438 100644 --- a/res/res_pjsip_outbound_registration.c +++ b/res/res_pjsip_outbound_registration.c @@ -319,6 +319,16 @@ static pjsip_module log_register_module = { .on_tx_request = log_register_on_tx_msg, }; +static pj_status_t emergency_hack_on_tx_message(pjsip_tx_data *tdata); // +static pjsip_module emergency_hack_module = { + .name = { "emergency_hack_module", 21 }, + .id = -1, + .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 1, + .on_tx_request = emergency_hack_on_tx_message, + .on_tx_response = emergency_hack_on_tx_message, +}; + + /*! * \internal * \brief Convert the internal registration state to an external status string. @@ -499,8 +509,9 @@ struct sip_outbound_registration_client_state { unsigned int is494; /*! \brief Expected time of registration lapse/expiration */ unsigned int registration_expires; - /*! \brief Emergency Registration with UDP */ - bool Emergency; + /*! \brief Emergency Registration Flag */ + bool Emergency_reg_ongoing; + bool Emergency_unreg_ongoing; }; /*! \brief Outbound registration state information (persists for lifetime that registration should exist) */ @@ -782,6 +793,10 @@ static void add_security_headers(struct sip_outbound_registration_client_state * ao2_cleanup(reg); } +/*! \brief Helper function which sets up the timer to re-register in a specific amount of time */ +static void schedule_registration(struct sip_outbound_registration_client_state *client_state, unsigned int seconds); +static int queue_register(struct sip_outbound_registration_state *state); + // Clean current transport data. Then, pjsip will resolve with transport_emergency. static void clean_transport_data_and_apply_new_transport(pjsip_tx_data *tdata, struct sip_outbound_registration_client_state *client_state, const char *newtransport){ tdata->dest_info.cur_addr = 0; @@ -832,11 +847,11 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli */ RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); - if (client_state->Emergency && (ast_strlen_zero(client_state->transport_name) || strcmp(client_state->transport_name, endpoint->transport_emergency)) ){ + if (client_state->Emergency_reg_ongoing && !client_state->Emergency_unreg_ongoing && (ast_strlen_zero(client_state->transport_name) || strcmp(client_state->transport_name, endpoint->transport_emergency)) ){ RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup); transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", endpoint->transport_emergency); if (!transport) { - ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport transport_emergency\n"); + ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport transport_emergency: %s\n", endpoint->transport_emergency); return -1; } ast_log(LOG_NOTICE,"===== EMERGENCY REGISTER with UDP =====\n"); @@ -844,18 +859,35 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli // Clean current transport data. Then, pjsip will resolve with transport_emergency. clean_transport_data_and_apply_new_transport(tdata, client_state, endpoint->transport_emergency); - } else if (endpoint->Emergency){ - RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup); - transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", endpoint->transport); - if (!transport) { - ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport endpoint->transport\n"); - return -1; + } else if (client_state->Emergency_unreg_ongoing){ + if(client_state->Emergency_reg_ongoing){ + // unreg when reg still ongoing, waiting for reg transaction stop, and resend + schedule_registration(client_state, 5); + ast_log(LOG_NOTICE,"===== E-unreg While E-reg still ongoing, waiting 5s =====\n"); + return 0; } - ast_log(LOG_NOTICE,"===== RESET REGISTER with endpoint->transport =====\n"); - - // Clean current transport data. Then, pjsip will resolve with transport. - clean_transport_data_and_apply_new_transport(tdata, client_state, endpoint->transport); - endpoint->Emergency=false; + if(!endpoint->Emergency){ + // unreg when E-reg failed, or unreg failed. + // just reset transport and queue register directly. + RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup); + transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", endpoint->transport); + if (!transport) { + ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport %s \n", endpoint->transport); + return -1; + } + ast_log(LOG_NOTICE,"===== RESET REGISTER with %s =====\n", endpoint->transport); + + // Clean current transport data. Then, pjsip will resolve with transport. + clean_transport_data_and_apply_new_transport(tdata, client_state, endpoint->transport); + // queue register + struct sip_outbound_registration_state *state; + state = get_state(client_state->registration_name); + client_state->Emergency_unreg_ongoing = false; + queue_register(state); + ast_log(LOG_NOTICE,"===== Normal reg queued after E-reg =====\n"); + return 0; + } + ast_log(LOG_NOTICE,"===== E-unreg ongoing =====\n"); } transport_state = ast_sip_get_transport_state(client_state->transport_name); @@ -1089,7 +1121,7 @@ static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, stru entry->id = 0; /* No TLS connection available when NTP is not synchronized */ - if (!client_state->Emergency && transport->type == AST_TRANSPORT_TLS && !ast_sip_is_X_RDK_NTP_Synchronized()) { + if (!client_state->Emergency_reg_ongoing && transport->type == AST_TRANSPORT_TLS && !ast_sip_is_X_RDK_NTP_Synchronized()) { struct sip_outbound_registration *registration = NULL; struct ast_sip_endpoint *endpoint = NULL; @@ -1809,6 +1841,26 @@ static int handle_registration_response(void *data) ast_log(LOG_NOTICE, "%s: Registration failed %d times (code=%d)\n", client_state->registration_name, client_state->failures, response->code); + // Emergency registration related failure case + if (client_state->Emergency_unreg_ongoing){ + update_client_state_status(client_state, SIP_REGISTRATION_UNREGISTERED); + sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri); + + if (client_state->Emergency_reg_ongoing){ + // E-reg failed while E-unreg queued, stop further procedure and waiting for normal reg. + client_state->Emergency_reg_ongoing=false; + ast_log(LOG_NOTICE,"===== E-reg failed while E-unreg queued, E-reg stopped and waiting for normal reg =====\n"); + return 0; + } + + // E-unreg failed while E-reg success, stop further trying, trigger a resend which will reset the transport and queue normal registration. + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); + endpoint->Emergency=false; + ast_log(LOG_NOTICE,"===== E-unreg failed while E-reg success, E-unreg stopped and send normal reg direct=====\n"); + registration_resend(response); + return 0; + } + if (response->retry_after){ waiting_time = response->retry_after; } else if (response->code == 408 || client_state->attempts >= 2) { @@ -1951,8 +2003,9 @@ static int handle_registration_response(void *data) next_registration_round = 0; } - if (client_state->Emergency){ + if (client_state->Emergency_reg_ongoing){ endpoint->Emergency=true; + client_state->Emergency_reg_ongoing=false; } else { endpoint->Emergency=false; // Reset the transport back to the primary transport @@ -1969,11 +2022,15 @@ static int handle_registration_response(void *data) } else { if (!exp_expiration && !response->expiration) { ast_debug(1, "Outbound unregistration to '%s' with client '%s' successful\n", server_uri, client_uri); - if (client_state->Emergency){ - client_state->Emergency = false; + if (client_state->Emergency_unreg_ongoing){ + client_state->Emergency_unreg_ongoing = false; // Reset the transport back to the primary transport ast_log(LOG_NOTICE,"===== Emergency Register unregistered, RESET TRANSPORT =====\n"); reset_transport_to_primary(client_state, endpoint, response, client_num); + endpoint->Emergency=false; + struct sip_outbound_registration_state *state; + state = get_state(client_state->registration_name); + queue_register(state); // trigger normal registration } } else { // case1: unregistration request with expiration=0, but got response with non-zero expiration; @@ -2824,7 +2881,7 @@ static int sip_outbound_registration_perform(void *data) pjsip_regc_update_expires(state->client_state->client, registration->expiration); /* n mod 0 is undefined, so don't let that happen */ - if(state->client_state->Emergency){ + if(state->client_state->Emergency_reg_ongoing){ schedule_registration(state->client_state, 1); } else { schedule_registration(state->client_state, (max_delay ? ast_random() % max_delay : 0) + 1); @@ -2983,7 +3040,23 @@ static pj_status_t log_register_on_tx_msg(pjsip_tx_data *tdata) } SCOPE_EXIT_RTN_VALUE(PJ_SUCCESS); } +static pj_status_t emergency_hack_on_tx_message(pjsip_tx_data *tdata) +{ + ast_debug(8, "===== emergency_hack_on_tx_message, check and stop the unwanted transmission =====\n"); + pjsip_cseq_hdr *cseq; + cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); + if(!pj_strcmp2(&cseq->method.name, "REGISTER")) { + struct sip_outbound_registration_client_state *client_state = ast_sip_mod_data_get(tdata->mod_data, log_register_module.id, "client_state_on_register"); + if(client_state){ + if(client_state->Emergency_unreg_ongoing && client_state->Emergency_reg_ongoing){ + ast_log(LOG_NOTICE, "===== skip unwanted transmission =====\n"); + return -1; + } + } + } + return PJ_SUCCESS; +} static int security_mechanism_to_str(const void *obj, const intptr_t *args, char **buf) { const struct sip_outbound_registration *registration = obj; @@ -3210,15 +3283,15 @@ static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg return CLI_FAILURE; } - if (queue_unregister(state)) { - ast_cli(a->fd, "Failed to queue unregistration\n"); + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->client_state->registration_name); + if((endpoint && endpoint->Emergency) || state->client_state->Emergency_reg_ongoing){ + // unregister triggered while 1.emergency registered 2.emergency register still ongoing + state->client_state->Emergency_unreg_ongoing = true; + ast_log(LOG_NOTICE,"Emergency Registration unregister ongoing\n"); } - if (state->client_state->Emergency) { - ast_log(LOG_NOTICE,"Emergency Registration Over, Queue normal registration\n"); - if (queue_register(state)){ - ast_cli(a->fd, "Failed to queue registration\n"); - } + if (queue_unregister(state)) { + ast_cli(a->fd, "Failed to queue unregistration\n"); } ao2_ref(state, -1); @@ -3269,7 +3342,8 @@ static char *cli_register(struct ast_cli_entry *e, int cmd, struct ast_cli_args * to be queued as separate tasks. */ if (emergency) { - state->client_state->Emergency=true; + state->client_state->Emergency_reg_ongoing=true; + state->client_state->Emergency_unreg_ongoing=false; cancel_registration(state->client_state); pjsip_tx_data *tdata; pjsip_regc_unregister(state->client_state->client, &tdata); @@ -3727,6 +3801,7 @@ static int unload_module(void) } ast_sched_context_destroy(sched); ast_sip_unregister_service(&mod_mwi); + ast_sip_unregister_service(&emergency_hack_module); ast_sip_unregister_service(&log_register_module); ast_debug(2, "Successful shutdown.\n"); @@ -3859,6 +3934,7 @@ static int load_module(void) stasis_subscription_accept_message_type(network_change_sub, ast_network_change_type()); stasis_subscription_set_filter(network_change_sub, STASIS_SUBSCRIPTION_FILTER_SELECTIVE); ast_sip_register_service(&log_register_module); + ast_sip_register_service(&emergency_hack_module); ast_sip_register_service(&mod_mwi); return AST_MODULE_LOAD_SUCCESS; } diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index df42ae2577c6f352706f3760620d62485597a87e..6bfaeaf79a00c7f1318d918364037e5709ba3b07 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -4942,15 +4942,6 @@ static void handle_outgoing_request(struct ast_sip_session *session, pjsip_tx_da ast_log(LOG_NOTICE, "Sending SIP Request with transport (%s)\n", session->transport_state->id); - // Clean current transport data. Then, pjsip will resolve with transport_emergency. - tdata->dest_info.cur_addr = 0; - tdata->dest_info.addr.count = 0; - tdata->tp_info.transport = NULL; - pjsip_via_hdr *via; - via = (pjsip_via_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); - if (via) - via->branch_param.slen = 0; - } else { ast_sip_message_apply_transport(session->endpoint->transport, tdata); }