From bfce50ca66e811fa21aec4385ce14ea46ce1f84e Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Tue, 30 Mar 2010 11:13:59 +0000 Subject: Ticket #1044: - Added initial version of automatic re-registration after registration failure and automatic call disconnection after re-registration attempt fails. - Published auto re-registration setting to pjsua app. - Updated pjsip_regc_send() to retrieve the transport earlier (was only in tsx_callback()). - Fixed TCP and TLS transport to prevent transport deletion in transport disconnection callback. - Fixed wrong keep-alive settings used by TLS transport (was using TCP keep-alive settings). git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3128 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/include/pjsip-ua/sip_regc.h | 1 + pjsip/include/pjsua-lib/pjsua.h | 30 ++++++ pjsip/include/pjsua-lib/pjsua_internal.h | 7 ++ pjsip/src/pjsip-ua/sip_reg.c | 22 +++++ pjsip/src/pjsip/sip_transport_tcp.c | 9 ++ pjsip/src/pjsip/sip_transport_tls.c | 32 +++++-- pjsip/src/pjsua-lib/pjsua_acc.c | 157 +++++++++++++++++++++++++++++++ pjsip/src/pjsua-lib/pjsua_core.c | 6 ++ 8 files changed, 255 insertions(+), 9 deletions(-) (limited to 'pjsip') diff --git a/pjsip/include/pjsip-ua/sip_regc.h b/pjsip/include/pjsip-ua/sip_regc.h index eb8b33c0..a003641a 100644 --- a/pjsip/include/pjsip-ua/sip_regc.h +++ b/pjsip/include/pjsip-ua/sip_regc.h @@ -94,6 +94,7 @@ struct pjsip_regc_info pj_bool_t auto_reg; /**< Will register automatically? */ int interval; /**< Registration interval (seconds). */ int next_reg; /**< Time until next registration (seconds). */ + pjsip_transport *transport; /**< Last transport used. */ }; /** diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 451b8c9c..5d228083 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -1864,6 +1864,18 @@ PJ_DECL(pj_status_t) pjsua_transport_close( pjsua_transport_id id, #endif +/** + * Default auto retry re-registration interval, in seconds. Set to 0 + * to disable this. Application can set the timer on per account basis + * by setting the pjsua_acc_config.reg_retry_interval field instead. + * + * Default: 300 (5 minutes) + */ +#ifndef PJSUA_REG_RETRY_INTERVAL +# define PJSUA_REG_RETRY_INTERVAL 300 +#endif + + /** * This structure describes account configuration to be specified when * adding a new account with #pjsua_acc_add(). Application MUST initialize @@ -2116,6 +2128,24 @@ typedef struct pjsua_acc_config int srtp_secure_signaling; #endif + /** + * Specify interval of auto registration retry upon registration failure + * (including caused by transport problem), in second. Set to 0 to + * disable auto re-registration. + * + * Default: #PJSUA_REG_RETRY_INTERVAL + */ + unsigned reg_retry_interval; + + /** + * Specify whether calls of the configured account should be dropped + * after registration failure and an attempt of re-registration has + * also failed. + * + * Default: PJ_FALSE (disabled) + */ + pj_bool_t drop_calls_on_reg_fail; + } pjsua_acc_config; diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index 527b53ae..647adc06 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -128,6 +128,13 @@ typedef struct pjsua_acc pj_status_t reg_last_err; /**< Last registration error. */ int reg_last_code; /**< Last status last register. */ + struct { + pj_bool_t active; /**< Flag of reregister status. */ + pj_timer_entry timer; /**< Timer for reregistration. */ + void *reg_tp; /**< Transport for registration. */ + unsigned attempt_cnt; /**< Attempt counter. */ + } auto_rereg; /**< Reregister/reconnect data. */ + pj_timer_entry ka_timer; /**< Keep-alive timer for UDP. */ pjsip_transport *ka_transport; /**< Transport for keep-alive. */ pj_sockaddr ka_target; /**< Destination address for K-A */ diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c index 3975028f..5190bb0f 100644 --- a/pjsip/src/pjsip-ua/sip_reg.c +++ b/pjsip/src/pjsip-ua/sip_reg.c @@ -203,6 +203,7 @@ PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc, info->is_busy = (pj_atomic_get(regc->busy_ctr) || regc->has_tsx); info->auto_reg = regc->auto_reg; info->interval = regc->expires; + info->transport = regc->last_transport; if (regc->has_tsx) info->next_reg = 0; @@ -1202,12 +1203,33 @@ PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata) else regc->current_op = REGC_REGISTERING; + /* Prevent deletion of tdata, e.g: when something wrong in sending, + * we need tdata to retrieve the transport. + */ + pjsip_tx_data_add_ref(tdata); + status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT, regc, &tsx_callback); if (status!=PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status)); } + /* Get last transport used and add reference to it */ + if (tdata->tp_info.transport != regc->last_transport) { + if (regc->last_transport) { + pjsip_transport_dec_ref(regc->last_transport); + regc->last_transport = NULL; + } + + if (tdata->tp_info.transport) { + regc->last_transport = tdata->tp_info.transport; + pjsip_transport_add_ref(regc->last_transport); + } + } + + /* Release tdata */ + pjsip_tx_data_dec_ref(tdata); + pj_lock_release(regc->lock); /* Delete the record if user destroy regc during the callback. */ diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c index d8f74a9d..fa03f143 100644 --- a/pjsip/src/pjsip/sip_transport_tcp.c +++ b/pjsip/src/pjsip/sip_transport_tcp.c @@ -177,6 +177,12 @@ static void tcp_init_shutdown(struct tcp_transport *tcp, pj_status_t status) if (tcp->base.is_shutdown) return; + /* Prevent immediate transport destroy by application, as transport + * state notification callback may be stacked and transport instance + * must remain valid at any point in the callback. + */ + pjsip_transport_add_ref(&tcp->base); + /* Notify application of transport disconnected state */ state_cb = pjsip_tpmgr_get_status_cb(tcp->base.tpmgr); if (state_cb) { @@ -193,6 +199,9 @@ static void tcp_init_shutdown(struct tcp_transport *tcp, pj_status_t status) * procedure for this transport. */ pjsip_transport_shutdown(&tcp->base); + + /* Now, it is ok to destroy the transport. */ + pjsip_transport_dec_ref(&tcp->base); } diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c index 1fd5c7bd..fa5cd2b1 100644 --- a/pjsip/src/pjsip/sip_transport_tls.c +++ b/pjsip/src/pjsip/sip_transport_tls.c @@ -184,6 +184,12 @@ static void tls_init_shutdown(struct tls_transport *tls, pj_status_t status) if (tls->base.is_shutdown) return; + /* Prevent immediate transport destroy by application, as transport + * state notification callback may be stacked and transport instance + * must remain valid at any point in the callback. + */ + pjsip_transport_add_ref(&tls->base); + /* Notify application of transport disconnected state */ state_cb = pjsip_tpmgr_get_status_cb(tls->base.tpmgr); if (state_cb) { @@ -200,6 +206,9 @@ static void tls_init_shutdown(struct tls_transport *tls, pj_status_t status) * procedure for this transport. */ pjsip_transport_shutdown(&tls->base); + + /* Now, it is ok to destroy the transport. */ + pjsip_transport_dec_ref(&tls->base); } @@ -517,7 +526,7 @@ static pj_status_t tls_create( struct tls_listener *listener, struct tls_transport **p_tls) { struct tls_transport *tls; - const pj_str_t ka_pkt = PJSIP_TCP_KEEP_ALIVE_DATA; + const pj_str_t ka_pkt = PJSIP_TLS_KEEP_ALIVE_DATA; pj_status_t status; @@ -578,7 +587,12 @@ static pj_status_t tls_create( struct tls_listener *listener, sockaddr_to_host_port(pool, &tls->base.local_name, (pj_sockaddr_in*)&tls->base.local_addr); - sockaddr_to_host_port(pool, &tls->base.remote_name, remote); + if (tls->remote_name.slen) { + tls->base.remote_name.host = tls->remote_name; + tls->base.remote_name.port = pj_sockaddr_in_get_port(remote); + } else { + sockaddr_to_host_port(pool, &tls->base.remote_name, remote); + } tls->base.endpt = listener->endpt; tls->base.tpmgr = listener->tpmgr; @@ -1075,8 +1089,8 @@ static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock, tls_destroy(&tls->base, status); } else { /* Start keep-alive timer */ - if (PJSIP_TCP_KEEP_ALIVE_INTERVAL) { - pj_time_val delay = {PJSIP_TCP_KEEP_ALIVE_INTERVAL, 0}; + if (PJSIP_TLS_KEEP_ALIVE_INTERVAL) { + pj_time_val delay = {PJSIP_TLS_KEEP_ALIVE_INTERVAL, 0}; pjsip_endpt_schedule_timer(listener->endpt, &tls->ka_timer, &delay); @@ -1507,8 +1521,8 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock, tls_flush_pending_tx(tls); /* Start keep-alive timer */ - if (PJSIP_TCP_KEEP_ALIVE_INTERVAL) { - pj_time_val delay = { PJSIP_TCP_KEEP_ALIVE_INTERVAL, 0 }; + if (PJSIP_TLS_KEEP_ALIVE_INTERVAL) { + pj_time_val delay = { PJSIP_TLS_KEEP_ALIVE_INTERVAL, 0 }; pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer, &delay); tls->ka_timer.id = PJ_TRUE; @@ -1540,9 +1554,9 @@ static void tls_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e) pj_gettimeofday(&now); PJ_TIME_VAL_SUB(now, tls->last_activity); - if (now.sec > 0 && now.sec < PJSIP_TCP_KEEP_ALIVE_INTERVAL) { + if (now.sec > 0 && now.sec < PJSIP_TLS_KEEP_ALIVE_INTERVAL) { /* There has been activity, so don't send keep-alive */ - delay.sec = PJSIP_TCP_KEEP_ALIVE_INTERVAL - now.sec; + delay.sec = PJSIP_TLS_KEEP_ALIVE_INTERVAL - now.sec; delay.msec = 0; pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer, @@ -1570,7 +1584,7 @@ static void tls_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e) } /* Register next keep-alive */ - delay.sec = PJSIP_TCP_KEEP_ALIVE_INTERVAL; + delay.sec = PJSIP_TLS_KEEP_ALIVE_INTERVAL; delay.msec = 0; pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer, diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 18096629..8c2b942e 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -24,6 +24,8 @@ #define THIS_FILE "pjsua_acc.c" +static void schedule_reregistration(pjsua_acc *acc); + /* * Get number of current accounts. */ @@ -442,6 +444,9 @@ PJ_DEF(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_id) PJSUA_LOCK(); + /* Cancel any re-registration timer */ + pjsua_cancel_timer(&pjsua_var.acc[acc_id].auto_rereg.timer); + /* Delete registration */ if (pjsua_var.acc[acc_id].regc != NULL) { pjsua_acc_set_registration(acc_id, PJ_FALSE); @@ -1031,6 +1036,10 @@ static void regc_cb(struct pjsip_regc_cbparam *param) } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) { + /* Update auto registration flag */ + acc->auto_rereg.active = PJ_FALSE; + acc->auto_rereg.attempt_cnt = 0; + if (param->expiration < 1) { pjsip_regc_destroy(acc->regc); acc->regc = NULL; @@ -1082,6 +1091,21 @@ static void regc_cb(struct pjsip_regc_cbparam *param) if (pjsua_var.ua_cfg.cb.on_reg_state) (*pjsua_var.ua_cfg.cb.on_reg_state)(acc->index); + /* Check if we need to auto retry registration. Basically, registration + * failure codes triggering auto-retry are those of temporal failures + * considered to be recoverable in relatively short term. + */ + if (acc->cfg.reg_retry_interval && + (param->code == PJSIP_SC_REQUEST_TIMEOUT || + param->code == PJSIP_SC_INTERNAL_SERVER_ERROR || + param->code == PJSIP_SC_BAD_GATEWAY || + param->code == PJSIP_SC_SERVICE_UNAVAILABLE || + param->code == PJSIP_SC_SERVER_TIMEOUT || + PJSIP_IS_STATUS_IN_CLASS(param->code, 600))) /* Global failure */ + { + schedule_reregistration(acc); + } + PJSUA_UNLOCK(); } @@ -1220,6 +1244,12 @@ PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id, PJSUA_LOCK(); + /* Cancel any re-registration timer */ + pjsua_cancel_timer(&pjsua_var.acc[acc_id].auto_rereg.timer); + + /* Reset pointer to registration transport */ + pjsua_var.acc[acc_id].auto_rereg.reg_tp = NULL; + if (renew) { if (pjsua_var.acc[acc_id].regc == NULL) { status = pjsua_regc_init(acc_id); @@ -1275,6 +1305,14 @@ PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id, status = pjsip_regc_send( pjsua_var.acc[acc_id].regc, tdata ); } + /* Update pointer to registration transport */ + if (status == PJ_SUCCESS) { + pjsip_regc_info reg_info; + + pjsip_regc_get_info(pjsua_var.acc[acc_id].regc, ®_info); + pjsua_var.acc[acc_id].auto_rereg.reg_tp = reg_info.transport; + } + if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create/send REGISTER", status); @@ -1925,3 +1963,122 @@ PJ_DEF(pj_status_t) pjsua_acc_set_transport( pjsua_acc_id acc_id, return PJ_SUCCESS; } + +/* Auto re-registration timeout callback */ +static void auto_rereg_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te) +{ + pjsua_acc *acc; + pj_status_t status; + + PJ_UNUSED_ARG(th); + acc = (pjsua_acc*) te->user_data; + pj_assert(acc); + + PJSUA_LOCK(); + + if (!acc->valid || !acc->auto_rereg.active) + goto on_return; + + /* Start re-registration */ + acc->auto_rereg.attempt_cnt++; + status = pjsua_acc_set_registration(acc->index, PJ_TRUE); + if (status != PJ_SUCCESS) + schedule_reregistration(acc); + + /* If configured, disconnect calls of this account after the first + * reregistration attempt failed. + */ + if (acc->cfg.drop_calls_on_reg_fail && acc->auto_rereg.attempt_cnt > 1) + { + unsigned i, cnt; + + for (i = 0, cnt = 0; i < pjsua_var.ua_cfg.max_calls; ++i) { + if (pjsua_var.calls[i].acc_id == acc->index) { + pjsua_call_hangup(i, 0, NULL, NULL); + ++cnt; + } + } + + if (cnt) { + PJ_LOG(3, (THIS_FILE, "Disconnecting %d call(s) of account #%d " + "after reregistration attempt failed", + cnt, acc->index)); + } + } + +on_return: + + PJSUA_UNLOCK(); +} + + +/* Schedule reregistration for specified account. Note that the first + * re-registration after a registration failure will be done immediately. + * Also note that this function should be called within PJSUA mutex. + */ +static void schedule_reregistration(pjsua_acc *acc) +{ + pj_time_val delay; + + pj_assert(acc && acc->valid && acc->cfg.reg_retry_interval); + + /* Cancel any re-registration timer */ + pjsua_cancel_timer(&acc->auto_rereg.timer); + + /* Update re-registration flag */ + acc->auto_rereg.active = PJ_TRUE; + + /* Set up timer for reregistration */ + acc->auto_rereg.timer.cb = &auto_rereg_timer_cb; + acc->auto_rereg.timer.user_data = acc; + + /* Reregistration attempt. The first attempt will be done immediately. */ + delay.sec = acc->auto_rereg.attempt_cnt? acc->cfg.reg_retry_interval : 0; + delay.msec = 0; + pjsua_schedule_timer(&acc->auto_rereg.timer, &delay); +} + + +/* Internal function to perform auto-reregistration on transport + * connection/disconnection events. + */ +void pjsua_acc_on_tp_state_changed(pjsip_transport *tp, + pjsip_transport_state state, + const pjsip_transport_state_info *info) +{ + unsigned i; + + PJ_UNUSED_ARG(info); + + /* Only care for transport disconnection events */ + if (state != PJSIP_TP_STATE_DISCONNECTED) + return; + + /* Shutdown this transport, to make sure that the transport manager + * will create a new transport for reconnection. + */ + pjsip_transport_shutdown(tp); + + PJSUA_LOCK(); + + /* Enumerate accounts using this transport and perform actions + * based on the transport state. + */ + for (i = 0; i < PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { + pjsua_acc *acc = &pjsua_var.acc[i]; + + /* Skip if this account is not valid OR auto re-registration + * feature is disabled OR this transport is not used by this account. + */ + if (!acc->valid || !acc->cfg.reg_retry_interval || + tp != acc->auto_rereg.reg_tp) + { + continue; + } + + /* Schedule reregistration for this account */ + schedule_reregistration(acc); + } + + PJSUA_UNLOCK(); +} diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 41eda871..f25d2e51 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -176,6 +176,7 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg) cfg->use_srtp = pjsua_var.ua_cfg.use_srtp; cfg->srtp_secure_signaling = pjsua_var.ua_cfg.srtp_secure_signaling; #endif + cfg->reg_retry_interval = PJSUA_REG_RETRY_INTERVAL; } PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg) @@ -1525,6 +1526,10 @@ static const char *addr_string(const pj_sockaddr_t *addr) return str; } +void pjsua_acc_on_tp_state_changed(pjsip_transport *tp, + pjsip_transport_state state, + const pjsip_transport_state_info *info); + /* Callback to receive transport state notifications */ static void on_tp_state_callback(pjsip_transport *tp, pjsip_transport_state state, @@ -1536,6 +1541,7 @@ static void on_tp_state_callback(pjsip_transport *tp, if (pjsua_var.old_tp_cb) { (*pjsua_var.old_tp_cb)(tp, state, info); } + pjsua_acc_on_tp_state_changed(tp, state, info); } /* -- cgit v1.2.3