diff options
-rw-r--r-- | channels/chan_sip.c | 2 | ||||
-rw-r--r-- | include/asterisk/res_pjsip.h | 9 | ||||
-rw-r--r-- | res/res_pjsip.c | 92 | ||||
-rw-r--r-- | res/res_pjsip_session.c | 36 | ||||
-rw-r--r-- | res/res_rtp_asterisk.c | 207 | ||||
-rw-r--r-- | res/res_sorcery_memory_cache.c | 5 |
6 files changed, 254 insertions, 97 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 96609d218..412a25764 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -24926,10 +24926,12 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, if (ast_bridge_impart(bridge, c, replaces_chan, NULL, AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) { ast_hangup(c); + ast_channel_unref(c); } } else { ast_channel_move(replaces_chan, c); ast_hangup(c); + ast_channel_unref(c); } sip_pvt_lock(p); return 0; diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 9475d6d22..f199b8fef 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -2044,4 +2044,13 @@ unsigned int ast_sip_get_max_initial_qualify_time(void); const char *ast_sip_get_contact_status_label(const enum ast_sip_contact_status_type status); const char *ast_sip_get_contact_short_status_label(const enum ast_sip_contact_status_type status); +/*! + * \brief Set a request to use the next value in the list of resolved addresses. + * + * \param tdata the tx data from the original request + * \retval 0 No more addresses to try + * \retval 1 The request was successfully re-intialized + */ +int ast_sip_failover_request(pjsip_tx_data *tdata); + #endif /* _RES_PJSIP_H */ diff --git a/res/res_pjsip.c b/res/res_pjsip.c index bb5bc0390..6d7e4f739 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -3135,19 +3135,88 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint, return ret_val; } +int ast_sip_failover_request(pjsip_tx_data *tdata) +{ + pjsip_via_hdr *via; + + if (tdata->dest_info.cur_addr == tdata->dest_info.addr.count - 1) { + /* No more addresses to try */ + return 0; + } + + /* Try next address */ + ++tdata->dest_info.cur_addr; + + via = (pjsip_via_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); + via->branch_param.slen = 0; + + pjsip_tx_data_invalidate_msg(tdata); + + return 1; +} + +static void send_request_cb(void *token, pjsip_event *e); + +static int check_request_status(struct send_request_data *req_data, pjsip_event *e) +{ + struct ast_sip_endpoint *endpoint; + pjsip_transaction *tsx; + pjsip_tx_data *tdata; + int res = 0; + + if (!(endpoint = ao2_bump(req_data->endpoint))) { + return 0; + } + + tsx = e->body.tsx_state.tsx; + + switch (tsx->status_code) { + case 401: + case 407: + /* Resend the request with a challenge response if we are challenged. */ + res = ++req_data->challenge_count < MAX_RX_CHALLENGES /* Not in a challenge loop */ + && !ast_sip_create_request_with_auth(&endpoint->outbound_auths, + e->body.tsx_state.src.rdata, tsx->last_tx, &tdata); + break; + case 408: + case 503: + if ((res = ast_sip_failover_request(tsx->last_tx))) { + tdata = tsx->last_tx; + /* + * Bump the ref since it will be on a new transaction and + * we don't want it to go away along with the old transaction. + */ + pjsip_tx_data_add_ref(tdata); + } + break; + } + + if (res) { + res = endpt_send_request(endpoint, tdata, -1, + req_data, send_request_cb) == PJ_SUCCESS; + } + + ao2_ref(endpoint, -1); + return res; +} + static void send_request_cb(void *token, pjsip_event *e) { struct send_request_data *req_data = token; - pjsip_transaction *tsx; pjsip_rx_data *challenge; - pjsip_tx_data *tdata; struct ast_sip_supplement *supplement; - struct ast_sip_endpoint *endpoint; - int res; switch(e->body.tsx_state.type) { case PJSIP_EVENT_TRANSPORT_ERROR: case PJSIP_EVENT_TIMER: + /* + * Check the request status on transport error or timeout. A transport + * error can occur when a TCP socket closes and that can be the result + * of a 503. Also we may need to failover on a timeout (408). + */ + if (check_request_status(req_data, e)) { + return; + } break; case PJSIP_EVENT_RX_MSG: challenge = e->body.tsx_state.src.rdata; @@ -3166,20 +3235,9 @@ static void send_request_cb(void *token, pjsip_event *e) } AST_RWLIST_UNLOCK(&supplements); - /* Resend the request with a challenge response if we are challenged. */ - tsx = e->body.tsx_state.tsx; - endpoint = ao2_bump(req_data->endpoint); - res = (tsx->status_code == 401 || tsx->status_code == 407) - && endpoint - && ++req_data->challenge_count < MAX_RX_CHALLENGES /* Not in a challenge loop */ - && !ast_sip_create_request_with_auth(&endpoint->outbound_auths, - challenge, tsx->last_tx, &tdata) - && endpt_send_request(endpoint, tdata, -1, req_data, send_request_cb) - == PJ_SUCCESS; - ao2_cleanup(endpoint); - if (res) { + if (check_request_status(req_data, e)) { /* - * Request with challenge response sent. + * Request with challenge response or failover sent. * Passed our req_data ref to the new request. */ return; diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index bbd74eeb5..c72959400 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -2267,6 +2267,29 @@ static int session_end(struct ast_sip_session *session) return 0; } +static int check_request_status(pjsip_inv_session *inv, pjsip_event *e) +{ + struct ast_sip_session *session = inv->mod_data[session_module.id]; + pjsip_transaction *tsx = e->body.tsx_state.tsx; + + if (tsx->status_code != 503 && tsx->status_code != 408) { + return 0; + } + + if (!ast_sip_failover_request(tsx->last_tx)) { + return 0; + } + + pjsip_inv_uac_restart(inv, PJ_FALSE); + /* + * Bump the ref since it will be on a new transaction and + * we don't want it to go away along with the old transaction. + */ + pjsip_tx_data_add_ref(tsx->last_tx); + ast_sip_session_send_request(session, tsx->last_tx); + return 1; +} + static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) { struct ast_sip_session *session = inv->mod_data[session_module.id]; @@ -2299,11 +2322,20 @@ static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) handle_outgoing(session, e->body.tsx_state.src.tdata); break; case PJSIP_EVENT_RX_MSG: - handle_incoming(session, e->body.tsx_state.src.rdata, type, - AST_SIP_SESSION_BEFORE_MEDIA); + if (!check_request_status(inv, e)) { + handle_incoming(session, e->body.tsx_state.src.rdata, type, + AST_SIP_SESSION_BEFORE_MEDIA); + } break; case PJSIP_EVENT_TRANSPORT_ERROR: case PJSIP_EVENT_TIMER: + /* + * Check the request status on transport error or timeout. A transport + * error can occur when a TCP socket closes and that can be the result + * of a 503. Also we may need to failover on a timeout (408). + */ + check_request_status(inv, e); + break; case PJSIP_EVENT_USER: case PJSIP_EVENT_UNKNOWN: case PJSIP_EVENT_TSX_STATE: diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 2e9d7e35e..c9270ed18 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -204,11 +204,13 @@ struct rtp_learning_info { #ifdef HAVE_OPENSSL_SRTP struct dtls_details { + ast_mutex_t lock; /*!< Lock for timeout timer synchronization */ SSL *ssl; /*!< SSL session */ BIO *read_bio; /*!< Memory buffer for reading */ BIO *write_bio; /*!< Memory buffer for writing */ enum ast_rtp_dtls_setup dtls_setup; /*!< Current setup state */ enum ast_rtp_dtls_connection connection; /*!< Whether this is a new or existing connection */ + int timeout_timer; /*!< Scheduler id for timeout timer */ }; #endif @@ -317,7 +319,6 @@ struct ast_rtp { #ifdef HAVE_OPENSSL_SRTP SSL_CTX *ssl_ctx; /*!< SSL context */ - ast_mutex_t dtls_timer_lock; /*!< Lock for synchronization purposes */ enum ast_rtp_dtls_verify dtls_verify; /*!< What to verify */ enum ast_srtp_suite suite; /*!< SRTP crypto suite */ enum ast_rtp_dtls_hash local_hash; /*!< Local hash used for the fingerprint */ @@ -326,7 +327,6 @@ struct ast_rtp { unsigned char remote_fingerprint[EVP_MAX_MD_SIZE]; /*!< Fingerprint of the peer certificate */ unsigned int rekey; /*!< Interval at which to renegotiate and rekey */ int rekeyid; /*!< Scheduled item id for rekeying */ - int dtlstimerid; /*!< Scheduled item id for DTLS retransmission for RTP */ struct dtls_details dtls; /*!< DTLS state information */ #endif }; @@ -444,6 +444,8 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level); #ifdef HAVE_OPENSSL_SRTP static int ast_rtp_activate(struct ast_rtp_instance *instance); static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp); +static void dtls_srtp_start_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp); +static void dtls_srtp_stop_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp); #endif static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp); @@ -1229,6 +1231,9 @@ static int dtls_details_initialize(struct dtls_details *dtls, SSL_CTX *ssl_ctx, } dtls->connection = AST_RTP_DTLS_CONNECTION_NEW; + ast_mutex_init(&dtls->lock); + dtls->timeout_timer = -1; + return 0; error: @@ -1397,6 +1402,8 @@ static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + dtls_srtp_stop_timeout_timer(instance, rtp, 0); + if (rtp->ssl_ctx) { SSL_CTX_free(rtp->ssl_ctx); rtp->ssl_ctx = NULL; @@ -1405,11 +1412,17 @@ static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance) if (rtp->dtls.ssl) { SSL_free(rtp->dtls.ssl); rtp->dtls.ssl = NULL; + ast_mutex_destroy(&rtp->dtls.lock); } - if (rtp->rtcp && rtp->rtcp->dtls.ssl) { - SSL_free(rtp->rtcp->dtls.ssl); - rtp->rtcp->dtls.ssl = NULL; + if (rtp->rtcp) { + dtls_srtp_stop_timeout_timer(instance, rtp, 1); + + if (rtp->rtcp->dtls.ssl) { + SSL_free(rtp->rtcp->dtls.ssl); + rtp->rtcp->dtls.ssl = NULL; + ast_mutex_destroy(&rtp->rtcp->dtls.lock); + } } } @@ -1586,21 +1599,25 @@ static void dtls_perform_handshake(struct ast_rtp_instance *instance, struct dtl { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - if (!dtls->ssl) { + /* If we are not acting as a client connecting to the remote side then + * don't start the handshake as it will accomplish nothing and would conflict + * with the handshake we receive from the remote side. + */ + if (!dtls->ssl || (dtls->dtls_setup != AST_RTP_DTLS_SETUP_ACTIVE)) { return; } - if (SSL_is_init_finished(dtls->ssl)) { - SSL_clear(dtls->ssl); - if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) { - SSL_set_accept_state(dtls->ssl); - } else { - SSL_set_connect_state(dtls->ssl); - } - dtls->connection = AST_RTP_DTLS_CONNECTION_NEW; - } SSL_do_handshake(dtls->ssl); + + /* Since the handshake is started in a thread outside of the channel thread it's possible + * for the response to be handled in the channel thread before we start the timeout timer. + * To ensure this doesn't actually happen we hold the DTLS lock. The channel thread will + * block until we're done at which point the timeout timer will be immediately stopped. + */ + ast_mutex_lock(&dtls->lock); dtls_srtp_check_pending(instance, rtp, rtcp); + dtls_srtp_start_timeout_timer(instance, rtp, rtcp); + ast_mutex_unlock(&dtls->lock); } #endif @@ -1754,48 +1771,83 @@ static inline int rtcp_debug_test_addr(struct ast_sockaddr *addr) } #ifdef HAVE_OPENSSL_SRTP - -static int dtls_srtp_handle_timeout(const void *data) +static int dtls_srtp_handle_timeout(struct ast_rtp_instance *instance, int rtcp) { - struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data; struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls; + struct timeval dtls_timeout; + + DTLSv1_handle_timeout(dtls->ssl); + dtls_srtp_check_pending(instance, rtp, rtcp); - if (!rtp) - { + /* If a timeout can't be retrieved then this recurring scheduled item must stop */ + if (!DTLSv1_get_timeout(dtls->ssl, &dtls_timeout)) { + dtls->timeout_timer = -1; return 0; } - ast_mutex_lock(&rtp->dtls_timer_lock); - if (rtp->dtlstimerid == -1) - { - ast_mutex_unlock(&rtp->dtls_timer_lock); + return dtls_timeout.tv_sec * 1000 + dtls_timeout.tv_usec / 1000; +} + +static int dtls_srtp_handle_rtp_timeout(const void *data) +{ + struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data; + int reschedule; + + reschedule = dtls_srtp_handle_timeout(instance, 0); + + if (!reschedule) { ao2_ref(instance, -1); - return 0; } - rtp->dtlstimerid = -1; - ast_mutex_unlock(&rtp->dtls_timer_lock); + return reschedule; +} + +static int dtls_srtp_handle_rtcp_timeout(const void *data) +{ + struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data; + int reschedule; + + reschedule = dtls_srtp_handle_timeout(instance, 1); - if (rtp->dtls.ssl && !SSL_is_init_finished(rtp->dtls.ssl)) { - DTLSv1_handle_timeout(rtp->dtls.ssl); + if (!reschedule) { + ao2_ref(instance, -1); } - dtls_srtp_check_pending(instance, rtp, 0); - if (rtp->rtcp && rtp->rtcp->dtls.ssl && !SSL_is_init_finished(rtp->rtcp->dtls.ssl)) { - DTLSv1_handle_timeout(rtp->rtcp->dtls.ssl); + return reschedule; +} + +static void dtls_srtp_start_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp) +{ + struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls; + struct timeval dtls_timeout; + + if (DTLSv1_get_timeout(dtls->ssl, &dtls_timeout)) { + int timeout = dtls_timeout.tv_sec * 1000 + dtls_timeout.tv_usec / 1000; + + ast_assert(dtls->timeout_timer == -1); + + ao2_ref(instance, +1); + if ((dtls->timeout_timer = ast_sched_add(rtp->sched, timeout, + !rtcp ? dtls_srtp_handle_rtp_timeout : dtls_srtp_handle_rtcp_timeout, instance)) < 0) { + ao2_ref(instance, -1); + ast_log(LOG_WARNING, "Scheduling '%s' DTLS retransmission for RTP instance [%p] failed.\n", + !rtcp ? "RTP" : "RTCP", instance); + } } - dtls_srtp_check_pending(instance, rtp, 1); +} - ao2_ref(instance, -1); +static void dtls_srtp_stop_timeout_timer(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp) +{ + struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls; - return 0; + AST_SCHED_DEL_UNREF(rtp->sched, dtls->timeout_timer, ao2_ref(instance, -1)); } static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp, int rtcp) { struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls; size_t pending; - struct timeval dtls_timeout; /* timeout on DTLS */ if (!dtls->ssl || !dtls->write_bio) { return; @@ -1821,24 +1873,6 @@ static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct as } out = BIO_read(dtls->write_bio, outgoing, sizeof(outgoing)); - - /* Stop existing DTLS timer if running */ - ast_mutex_lock(&rtp->dtls_timer_lock); - if (rtp->dtlstimerid > -1) { - AST_SCHED_DEL_UNREF(rtp->sched, rtp->dtlstimerid, ao2_ref(instance, -1)); - rtp->dtlstimerid = -1; - } - - if (DTLSv1_get_timeout(dtls->ssl, &dtls_timeout)) { - int timeout = dtls_timeout.tv_sec * 1000 + dtls_timeout.tv_usec / 1000; - ao2_ref(instance, +1); - if ((rtp->dtlstimerid = ast_sched_add(rtp->sched, timeout, dtls_srtp_handle_timeout, instance)) < 0) { - ao2_ref(instance, -1); - ast_log(LOG_WARNING, "scheduling DTLS retransmission for RTP instance [%p] failed.\n", instance); - } - } - ast_mutex_unlock(&rtp->dtls_timer_lock); - __rtp_sendto(instance, outgoing, out, 0, &remote_address, rtcp, &ice, 0); } } @@ -2014,8 +2048,6 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s } #ifdef HAVE_OPENSSL_SRTP - dtls_srtp_check_pending(instance, rtp, rtcp); - /* If this is an SSL packet pass it to OpenSSL for processing. RFC section for first byte value: * https://tools.ietf.org/html/rfc5764#section-5.1.2 */ if ((*in >= 20) && (*in <= 63)) { @@ -2029,6 +2061,15 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s return -1; } + /* This mutex is locked so that this thread blocks until the dtls_perform_handshake function + * completes. + */ + ast_mutex_lock(&dtls->lock); + ast_mutex_unlock(&dtls->lock); + + /* Before we feed data into OpenSSL ensure that the timeout timer is either stopped or completed */ + dtls_srtp_stop_timeout_timer(instance, rtp, rtcp); + /* If we don't yet know if we are active or passive and we receive a packet... we are obviously passive */ if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) { dtls->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE; @@ -2057,6 +2098,9 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s /* Use the keying material to set up key/salt information */ res = dtls_srtp_setup(rtp, srtp, instance); } + } else { + /* Since we've sent additional traffic start the timeout timer for retransmission */ + dtls_srtp_start_timeout_timer(instance, rtp, rtcp); } return res; @@ -2479,7 +2523,6 @@ static int ast_rtp_new(struct ast_rtp_instance *instance, #ifdef HAVE_OPENSSL_SRTP rtp->rekeyid = -1; - rtp->dtlstimerid = -1; #endif rtp->f.subclass.format = ao2_bump(ast_format_none); @@ -2497,6 +2540,10 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance) struct timespec ts = { .tv_sec = wait.tv_sec, .tv_nsec = wait.tv_usec * 1000, }; #endif +#ifdef HAVE_OPENSSL_SRTP + ast_rtp_dtls_stop(instance); +#endif + /* Destroy the smoother that was smoothing out audio if present */ if (rtp->smoother) { ast_smoother_free(rtp->smoother); @@ -2515,11 +2562,6 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance) * RTP instance while it's active. */ close(rtp->rtcp->s); -#ifdef HAVE_OPENSSL_SRTP - if (rtp->rtcp->dtls.ssl) { - SSL_free(rtp->rtcp->dtls.ssl); - } -#endif ast_free(rtp->rtcp); } @@ -2571,18 +2613,6 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance) } #endif -#ifdef HAVE_OPENSSL_SRTP - /* Destroy the SSL context if present */ - if (rtp->ssl_ctx) { - SSL_CTX_free(rtp->ssl_ctx); - } - - /* Destroy the SSL session if present */ - if (rtp->dtls.ssl) { - SSL_free(rtp->dtls.ssl); - } -#endif - ao2_cleanup(rtp->lasttxformat); ao2_cleanup(rtp->lastrxformat); ao2_cleanup(rtp->f.subclass.format); @@ -4909,9 +4939,11 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance) #ifdef HAVE_OPENSSL_SRTP AST_SCHED_DEL_UNREF(rtp->sched, rtp->rekeyid, ao2_ref(instance, -1)); - ast_mutex_lock(&rtp->dtls_timer_lock); - AST_SCHED_DEL_UNREF(rtp->sched, rtp->dtlstimerid, ao2_ref(instance, -1)); - ast_mutex_unlock(&rtp->dtls_timer_lock); + + dtls_srtp_stop_timeout_timer(instance, rtp, 0); + if (rtp->rtcp) { + dtls_srtp_stop_timeout_timer(instance, rtp, 1); + } #endif if (rtp->rtcp && rtp->rtcp->schedid > 0) { @@ -4993,10 +5025,31 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level) } #ifdef HAVE_OPENSSL_SRTP +static void dtls_perform_setup(struct dtls_details *dtls) +{ + if (!dtls->ssl || !SSL_is_init_finished(dtls->ssl)) { + return; + } + + SSL_clear(dtls->ssl); + if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) { + SSL_set_accept_state(dtls->ssl); + } else { + SSL_set_connect_state(dtls->ssl); + } + dtls->connection = AST_RTP_DTLS_CONNECTION_NEW; +} + static int ast_rtp_activate(struct ast_rtp_instance *instance) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + dtls_perform_setup(&rtp->dtls); + + if (rtp->rtcp) { + dtls_perform_setup(&rtp->rtcp->dtls); + } + /* If ICE negotiation is enabled the DTLS Handshake will be performed upon completion of it */ #ifdef HAVE_PJPROJECT if (rtp->ice) { diff --git a/res/res_sorcery_memory_cache.c b/res/res_sorcery_memory_cache.c index 7a07d14f0..e486a6400 100644 --- a/res/res_sorcery_memory_cache.c +++ b/res/res_sorcery_memory_cache.c @@ -2551,6 +2551,10 @@ static int load_module(void) return AST_MODULE_LOAD_DECLINE; } + /* This causes the stale unit test to execute last, so if a sorcery instance persists + * longer than expected subsequent unit tests don't fail when setting it up. + */ + AST_TEST_REGISTER(stale); AST_TEST_REGISTER(open_with_valid_options); AST_TEST_REGISTER(open_with_invalid_options); AST_TEST_REGISTER(create_and_retrieve); @@ -2558,7 +2562,6 @@ static int load_module(void) AST_TEST_REGISTER(delete); AST_TEST_REGISTER(maximum_objects); AST_TEST_REGISTER(expiration); - AST_TEST_REGISTER(stale); return AST_MODULE_LOAD_SUCCESS; } |