[Asterisk-code-review] res pjsip: Failover when server is not available (asterisk[master])

Kevin Harwell asteriskteam at digium.com
Tue Jun 30 17:03:03 CDT 2015


Kevin Harwell has uploaded a new change for review.

  https://gerrit.asterisk.org/750

Change subject: res_pjsip: Failover when server is not available
......................................................................

res_pjsip: Failover when server is not available

Previously Asterisk did not properly failover to the next resolved DNS
address when a endpoint could not be reached. With this patch, and while
using res_pjsip, SIP requests (both in/out of dialog) now attempt to use
the next address in the list of resolved addresses until a proper response
is received or no more addresses are left.

ASTERISK-25076 #close
Reported by: Joshua Colp

Change-Id: Ief14f4ebd82474881f72f4538f4577f30af2a764
---
M include/asterisk/res_pjsip.h
M res/res_pjsip.c
M res/res_pjsip_session.c
3 files changed, 151 insertions(+), 57 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/50/750/1

diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 9475d6d..5e18a69 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -2044,4 +2044,14 @@
 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 Prepare a new request that uses the next value in the list of resolved addresses.
+ *
+ * \param old_data the tx data from the original request
+ * \param new_data the new request
+ * \retval 0 No more addresses to try
+ * \retval 1 The new request was successfully intialized
+ */
+int ast_sip_create_failover_request(pjsip_tx_data *old_data, pjsip_tx_data **new_data);
+
 #endif /* _RES_PJSIP_H */
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index bb5bc03..3e50f7e 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -3135,18 +3135,81 @@
 	return ret_val;
 }
 
+int ast_sip_create_failover_request(pjsip_tx_data *old_data, pjsip_tx_data **new_data)
+{
+	pjsip_via_hdr *via;
+
+	if (old_data->dest_info.cur_addr == old_data->dest_info.addr.count - 1) {
+		/* No more addresses to try */
+		return 0;
+	}
+
+	/* Try next address */
+	++old_data->dest_info.cur_addr;
+
+	via = (pjsip_via_hdr*)pjsip_msg_find_hdr(old_data->msg, PJSIP_H_VIA, NULL);
+	via->branch_param.slen = 0;
+
+	pjsip_tx_data_invalidate_msg(old_data);
+	pjsip_tx_data_add_ref(old_data);
+
+	*new_data = old_data;
+	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 503:
+		res = ast_sip_create_failover_request(tsx->last_tx, &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:
+		/*
+		 * Check the request status on transport error. A transport error
+		 * can occur when a TCP socket closes and that can be the result
+		 * of a 503.
+		 */
+		if (check_request_status(req_data, e)) {
+			return;
+		}
 	case PJSIP_EVENT_TIMER:
 		break;
 	case PJSIP_EVENT_RX_MSG:
@@ -3166,20 +3229,9 @@
 		}
 		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 bbd74ee..abecd98 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -1420,7 +1420,8 @@
 
 /*!
  * \internal
- * \brief Handle initial INVITE challenge response message.
+ * \brief Possibly handle response to outbound requests (for instance,
+ *        initial INVITE challenge and failover).
  * \since 13.5.0
  *
  * \param rdata PJSIP receive response message data.
@@ -1428,82 +1429,89 @@
  * \retval PJ_FALSE Did not handle message.
  * \retval PJ_TRUE Handled message.
  */
-static pj_bool_t outbound_invite_auth(pjsip_rx_data *rdata)
+static pj_bool_t on_rx_outbound(pjsip_rx_data *rdata)
 {
 	pjsip_transaction *tsx;
 	pjsip_dialog *dlg;
 	pjsip_inv_session *inv;
 	pjsip_tx_data *tdata;
 	struct ast_sip_session *session;
+	int code = rdata->msg_info.msg->line.status.code;
 
-	if (rdata->msg_info.msg->line.status.code != 401
-		&& rdata->msg_info.msg->line.status.code != 407) {
+	if (code != 401 && code != 407 && code != 503) {
 		/* Doesn't pertain to us. Move on */
 		return PJ_FALSE;
 	}
 
 	tsx = pjsip_rdata_get_tsx(rdata);
 	dlg = pjsip_rdata_get_dlg(rdata);
+
 	if (!dlg || !tsx) {
 		return PJ_FALSE;
 	}
 
-	if (tsx->method.id != PJSIP_INVITE_METHOD) {
-		/* Not an INVITE that needs authentication */
-		return PJ_FALSE;
-	}
-
 	inv = pjsip_dlg_get_inv_session(dlg);
-	if (PJSIP_INV_STATE_CONFIRMED <= inv->state) {
-		/*
-		 * We cannot handle reINVITE authentication at this
-		 * time because the reINVITE transaction is still in
-		 * progress.
-		 */
-		ast_debug(1, "A reINVITE is being challenged.\n");
-		return PJ_FALSE;
-	}
-	ast_debug(1, "Initial INVITE is being challenged.\n");
-
 	session = inv->mod_data[session_module.id];
 
-	if (ast_sip_create_request_with_auth(&session->endpoint->outbound_auths, rdata,
-		tsx->last_tx, &tdata)) {
-		return PJ_FALSE;
+	switch (code) {
+	case 401:
+	case 407:
+		if (tsx->method.id != PJSIP_INVITE_METHOD) {
+			/* Not an INVITE that needs authentication */
+			return PJ_FALSE;
+		}
+
+		if (PJSIP_INV_STATE_CONFIRMED <= inv->state) {
+			/*
+			 * We cannot handle reINVITE authentication at this
+			 * time because the reINVITE transaction is still in
+			 * progress.
+			 */
+			ast_debug(1, "A reINVITE is being challenged.\n");
+			return PJ_FALSE;
+		}
+
+		ast_debug(1, "Initial INVITE is being challenged.\n");
+		if (ast_sip_create_request_with_auth(&session->endpoint->outbound_auths, rdata,
+						     tsx->last_tx, &tdata)) {
+			return PJ_FALSE;
+		}
+		break;
+	case 503:
+		if (!ast_sip_create_failover_request(tsx->last_tx, &tdata)) {
+			return PJ_FALSE;
+		}
+		break;
 	}
 
-	/*
-	 * Restart the outgoing initial INVITE transaction to deal
-	 * with authentication.
-	 */
 	pjsip_inv_uac_restart(inv, PJ_FALSE);
-
 	ast_sip_session_send_request(session, tdata);
 	return PJ_TRUE;
 }
 
-static pjsip_module outbound_invite_auth_module = {
-	.name = {"Outbound INVITE Auth", 20},
+static pjsip_module outbound_module = {
+	.name = {"Outbound", 8},
 	.priority = PJSIP_MOD_PRIORITY_DIALOG_USAGE,
-	.on_rx_response = outbound_invite_auth,
+	.on_rx_response = on_rx_outbound,
 };
 
 /*!
  * \internal
- * \brief Setup outbound initial INVITE authentication.
+ * \brief Setup outbound response handling for things like initial INVITE
+ *        authentication, failover, etc...
  * \since 13.5.0
  *
- * \param dlg PJSIP dialog to attach outbound authentication.
+ * \param dlg PJSIP dialog
  *
  * \retval 0 on success.
  * \retval -1 on error.
  */
-static int setup_outbound_invite_auth(pjsip_dialog *dlg)
+static int setup_outbound(pjsip_dialog *dlg)
 {
 	pj_status_t status;
 
 	++dlg->sess_count;
-	status = pjsip_dlg_add_usage(dlg, &outbound_invite_auth_module, NULL);
+	status = pjsip_dlg_add_usage(dlg, &outbound_module, NULL);
 	--dlg->sess_count;
 
 	return status != PJ_SUCCESS ? -1 : 0;
@@ -1544,7 +1552,7 @@
 		return NULL;
 	}
 
-	if (setup_outbound_invite_auth(dlg)) {
+	if (setup_outbound(dlg)) {
 		pjsip_dlg_terminate(dlg);
 		return NULL;
 	}
@@ -2267,6 +2275,22 @@
 	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;
+	pjsip_tx_data *tdata;
+
+	if (tsx->status_code != 503 ||
+	    !ast_sip_create_failover_request(tsx->last_tx, &tdata)) {
+		return 0;
+	}
+
+	pjsip_inv_uac_restart(inv, PJ_FALSE);
+	ast_sip_session_send_request(session, tdata);
+	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,10 +2323,18 @@
 			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:
+			/*
+			 * Check the request status on transport error. A transport error
+			 * can occur when a TCP socket closes and that can be the result
+			 * of a 503. If so attempt to failover.
+			 */
+			check_request_status(inv, e);
 		case PJSIP_EVENT_TIMER:
 		case PJSIP_EVENT_USER:
 		case PJSIP_EVENT_UNKNOWN:
@@ -2751,7 +2783,7 @@
 		return AST_MODULE_LOAD_DECLINE;
 	}
 	ast_sip_register_service(&session_reinvite_module);
-	ast_sip_register_service(&outbound_invite_auth_module);
+	ast_sip_register_service(&outbound_module);
 
 	ast_module_shutdown_ref(ast_module_info->self);
 
@@ -2760,7 +2792,7 @@
 
 static int unload_module(void)
 {
-	ast_sip_unregister_service(&outbound_invite_auth_module);
+	ast_sip_unregister_service(&outbound_module);
 	ast_sip_unregister_service(&session_reinvite_module);
 	ast_sip_unregister_service(&session_module);
 	ast_sorcery_delete(ast_sip_get_sorcery(), nat_hook);

-- 
To view, visit https://gerrit.asterisk.org/750
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ief14f4ebd82474881f72f4538f4577f30af2a764
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Kevin Harwell <kharwell at digium.com>



More information about the asterisk-code-review mailing list