diff --git a/apps/app_dial.c b/apps/app_dial.c
index 8a3c70e0b7adc943fcecfbc56db86be72f9c3b13..0d30ebf4186be9686c3fbbd2caa57891827e0712 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -1587,6 +1587,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
 						if (single || (!single && !pa->sentringing)) {
 							ast_channel_codec_set(in, ast_channel_codec_get(c)); //for early media codec sync
 							ast_indicate(in, AST_CONTROL_PROGRESS);
+							pa->sentringing = 0;
 						}
 					}
 					if (!sent_progress) {
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index d29490cea32a86389e0c3954374d13636adcb1d0..edc5c0d265ecf7881b05989aa6189d9476058e7a 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -73,6 +73,7 @@
 #include "pjsip/include/dialplan_functions.h"
 #include "pjsip/include/cli_functions.h"
 
+#define EARLY_MEDIA_WAIT_MS 500  // 500 mseconds waiting time for incoming RTP of early media
 AST_THREADSTORAGE(uniqueid_threadbuf);
 #define UNIQUEID_BUFSIZE 256
 
@@ -690,7 +691,15 @@ static int answer(void *data)
 
 	pjsip_dlg_inc_lock(session->inv_session->dlg);
 	if (session->inv_session->invite_tsx) {
-		status = pjsip_inv_answer(session->inv_session, 200, NULL, NULL, &packet);
+		/*
+		 * Now the session has been answered and we know the capability of the local channel. Recalculate the joint
+		 * codecs as if a re-INVITE is received within an established session.
+		 */
+		if (ast_sip_session_sdp_answer(session) == 0) {
+			status = pjsip_inv_answer(session->inv_session, 200, NULL, NULL, &packet);
+		} else {
+			status = -1;
+		}
 	} else {
 		ast_log(LOG_ERROR,"Cannot answer '%s' because there is no associated SIP transaction\n",
 			ast_channel_name(session->channel));
@@ -824,6 +833,14 @@ static int is_compatible_format(struct ast_sip_session *session, struct ast_fram
 	return ast_format_cap_iscompatible_format(cap, f->subclass.format) != AST_FORMAT_CMP_NOT_EQUAL;
 }
 
+static void incoming_rtp_stop_timer(struct ast_sip_session *session)
+{
+	if (pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
+		&session->incoming_rtp_check_timer, 0)) {
+		ao2_ref(session, -1);
+	}
+}
+
 /*!
  * \brief Function called by core to read any waiting frames
  *
@@ -849,6 +866,14 @@ static struct ast_frame *chan_pjsip_read_stream(struct ast_channel *ast)
 		return f;
 	}
 
+	// Do not process incoming early media packets if the received P-Early-Media header is inactive/recvonly
+	enum early_media_direction early_media_dir = ast_channel_early_media_get(ast);
+	if (ast_channel_state(ast) != AST_STATE_UP && f->frametype == AST_FRAME_VOICE) {
+		incoming_rtp_stop_timer(session);
+		if (early_media_dir == EARLY_MEDIA_INACTIVE || early_media_dir == EARLY_MEDIA_RECVONLY)
+			return &ast_null_frame;
+	}
+
 	for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
 		if (cur->frametype == AST_FRAME_VOICE) {
 			break;
@@ -947,6 +972,12 @@ static int chan_pjsip_write_stream(struct ast_channel *ast, int stream_num, stru
 	struct ast_sip_session_media *media = NULL;
 	int res = 0;
 
+	// Do not process outgoing early media packets if the received P-Early-Media header is inactive/sendonly
+	enum early_media_direction early_media_dir = ast_channel_early_media_get(ast);
+	if (ast_channel_state(ast) != AST_STATE_UP && frame->frametype == AST_FRAME_VOICE &&
+	   (early_media_dir == EARLY_MEDIA_INACTIVE || early_media_dir == EARLY_MEDIA_SENDONLY))
+		return 0;
+
 	/* The core provides a guarantee that the stream will exist when we are called if stream_num is provided */
 	if (stream_num >= 0) {
 		/* What is not guaranteed is that a media session will exist */
@@ -2160,9 +2191,10 @@ static void sip_build_replaces_param(const struct ast_channel *ast, const char *
 
 			// Create URI encoded Replaces parameter
 			// ?Replaces=<callid>;to-tag=<totag>;from-tag=<fromtag>
-			snprintf(replaces, len - 1, "%s;to-tag=%s;from-tag=%s", callid, totag, fromtag);
+			// snprintf(replaces, len - 1, "%s;to-tag=%s;from-tag=%s", callid, totag, fromtag);
+			snprintf(replaces, len - 1, "%s", callid);
 			ast_uri_encode(replaces, tmp, sizeof(tmp), ast_uri_http);
-			snprintf(replaces, len - 1, "?Replaces=%s", tmp);
+			snprintf(replaces, len - 1, "?Require=replaces&Replaces=%s", tmp);
 
         } else {
                 ast_log(LOG_NOTICE, "Asked to replace a call on channel type %s\n", type);
@@ -2221,6 +2253,7 @@ static void transfer_refer(struct ast_sip_session *session, const char *target)
 			strncpy(method, "tel", sizeof(method) - 1);
 		else {
 			ast_log(LOG_ERROR, "Unsupported URI for Refer-To header. Refer will not be sent!\n");
+			ast_log_dt(LOG_EVENT_CODE_SV109);
 			goto failure;
 		}
 
@@ -2229,10 +2262,12 @@ static void transfer_refer(struct ast_sip_session *session, const char *target)
 		strncpy(method, strtok(contact_uri, ":"), sizeof(method) - 1);
 	} else {
 		ast_log(LOG_ERROR, "Unsupported URI for Refer-To header. Refer will not be sent!\n");
+		ast_log_dt(LOG_EVENT_CODE_SV109);
 		goto failure;
 	}
 
-	snprintf(referto, sizeof(referto), "<%s:%s@%s%s>", method, target, session->endpoint->fromdomain, replaces);
+	//snprintf(referto, sizeof(referto), "<%s:%s@%s%s>", method, target, session->endpoint->fromdomain, replaces);
+	snprintf(referto, sizeof(referto), "<%s:%s@%s;user=phone%s>", method, target, session->endpoint->fromdomain, replaces);
 	ast_debug(1, "Refer-To: %s\n", referto);
 
 	if (pjsip_xfer_initiate(sub, pj_cstr(&tmp, referto), &packet) != PJ_SUCCESS) {
@@ -2333,6 +2368,11 @@ static int chan_pjsip_digit_begin(struct ast_channel *chan, char digit)
 	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
 	struct ast_sip_session_media *media;
 
+	// Do not process outgoing dtmf packets if the received P-Early-Media header is inactive/sendonly
+	enum early_media_direction early_media_dir = ast_channel_early_media_get(chan);
+	if (early_media_dir == EARLY_MEDIA_INACTIVE || early_media_dir == EARLY_MEDIA_SENDONLY)
+		return 0;
+
 	media = channel->session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO];
 
 	switch (channel->session->dtmf) {
@@ -2451,6 +2491,11 @@ static int chan_pjsip_digit_end(struct ast_channel *ast, char digit, unsigned in
 		return -1;
 	}
 
+	// Do not process outgoing dtmf packets if the received P-Early-Media header is inactive/sendonly
+	enum early_media_direction early_media_dir = ast_channel_early_media_get(ast);
+	if (early_media_dir == EARLY_MEDIA_INACTIVE || early_media_dir == EARLY_MEDIA_SENDONLY)
+		return 0;
+
 	media = channel->session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO];
 
 	switch (channel->session->dtmf) {
@@ -2617,6 +2662,8 @@ static int hangup_cause2sip(int cause)
 		return 488;
 	case AST_CAUSE_INTERWORKING:    /* Unspecified Interworking issues */
 		return 500;
+	case AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE:
+		return 504;  /* could contains xml with init register request */
 	case AST_CAUSE_NOTDEFINED:
 	default:
 		ast_debug(1, "AST hangup cause %d (no match found in PJSIP)\n", cause);
@@ -3125,6 +3172,39 @@ static void set_sipdomain_variable(struct ast_sip_session *session)
 	return;
 }
 
+static int find_contact(void *obj, void *arg, int flags)
+{
+	struct ast_sip_contact *contact = obj;
+	char *contact_url = arg;
+
+	return !!strstr(contact->uri, contact_url);
+}
+
+static struct ast_sip_contact *contact_user_find_by_addr(char *contact_user, const pj_sockaddr *src_addr)
+{
+	RAII_VAR(struct ast_sip_aor *, aor_obj, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
+	struct ast_sorcery *pjsip_sorcery;
+	struct ao2_container *contacts;
+	char contact_url[PJ_INET6_ADDRSTRLEN];
+
+	pjsip_sorcery = ast_sip_get_sorcery();
+	if (!pjsip_sorcery)
+		return 0;
+
+	aor_obj = ast_sorcery_retrieve_by_id(pjsip_sorcery, "aor", contact_user);
+	if (!aor_obj)
+		return 0;
+
+	contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor_obj);
+	if (!contacts)
+		return 0;
+
+	pj_sockaddr_print(src_addr, contact_url, sizeof(contact_url), 3);
+	return ao2_callback(contacts, OBJ_UNLINK, find_contact, contact_url);
+}
+
+
 /*! \brief Function called when a request is received on the session */
 static int chan_pjsip_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 {
@@ -3137,29 +3217,30 @@ static int chan_pjsip_incoming_request(struct ast_sip_session *session, struct p
 		SCOPE_EXIT_RTN_VALUE(0, "%s: No channel\n", ast_sip_session_get_name(session));
 	}
 
-	if (ast_sip_get_accept_proxy_req_only() &&
+	if (ast_sip_get_accept_proxy_req_only() && session->inv_session &&
 	    session->inv_session->dlg && session->inv_session->dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
-		char buf[256];
-		struct ast_sockaddr resolved_addr;
+		char buf[PJ_INET6_ADDRSTRLEN];
+		pj_str_t proxy_config_val;
+		pj_sockaddr proxy_address;
 
 		pj_sockaddr_print(&rdata->pkt_info.src_addr, buf, sizeof(buf), 3);
-		ast_log(LOG_NOTICE, "Request received from address: <%s>\n", buf);
+		ast_log(LOG_DEBUG, "Request received from address: <%s>\n", buf);
 
-		resolved_addr.ss.ss_family = AST_AF_UNSPEC;
-		if (ast_get_ip(&resolved_addr, session->endpoint->fromdomain)) {
-			ast_log(LOG_ERROR, "Could not resolve the domain name: %s\n", session->endpoint->fromdomain);
+		pj_cstr(&proxy_config_val, session->endpoint->fromdomain);
+		if (contact_user_find_by_addr(session->endpoint->contact_user, &rdata->pkt_info.src_addr)) {
+			    ast_log(LOG_DEBUG, "Request from known contact %s\n", ast_sip_session_get_name(session));
+		}
+		// 'fromdomain' is not an IPv4 or IPv6 address
+		else if (pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &proxy_config_val, &proxy_address) != PJ_SUCCESS) {
+			if (!ast_sip_is_address_in_dns_records(&rdata->pkt_info.src_addr)) {
+				SCOPE_EXIT_RTN_VALUE(-1, "%s: Request not from DNS resolutions\n", ast_sip_session_get_name(session));
+			}
 		} else {
-			pj_sockaddr proxy_addr;
-
-			ast_sockaddr_to_pj_sockaddr(&resolved_addr, &proxy_addr);
-			pj_sockaddr_set_port(&proxy_addr, rdata->pkt_info.src_port);
-
-			pj_sockaddr_print(&proxy_addr, buf, sizeof(buf), 3);
-			ast_log(LOG_NOTICE, "Outgoing proxy address: <%s>\n", buf);
-
-			if (pj_sockaddr_cmp(&rdata->pkt_info.src_addr, &proxy_addr)) {
-				ast_sip_session_terminate(session, 400);
-				SCOPE_EXIT_RTN_VALUE(-1, "%s: Request not from Outgoing SIP proxy. Terminating session\n", ast_sip_session_get_name(session));
+			pj_sockaddr_set_port(&proxy_address, rdata->pkt_info.src_port);
+			pj_sockaddr_print(&proxy_address, buf, sizeof(buf), 3);
+			ast_log(LOG_DEBUG, "Outgoing proxy address: <%s>\n", buf);
+			if (pj_sockaddr_cmp(&rdata->pkt_info.src_addr, &proxy_address)) {
+				SCOPE_EXIT_RTN_VALUE(-1, "%s: Request not from configured SIP proxy\n", ast_sip_session_get_name(session));
 			}
 		}
 	}
@@ -3307,6 +3388,8 @@ static void chan_pjsip_incoming_response_update_cause(struct ast_sip_session *se
 	struct pjsip_status_line status = rdata->msg_info.msg->line.status;
 	struct ast_control_pvt_cause_code *cause_code;
 	int data_size = sizeof(*cause_code);
+	char * client_num = NULL;
+	char * saveptr = NULL;
 	SCOPE_ENTER(3, "%s: Status: %d\n", ast_sip_session_get_name(session), status.code);
 
 	if (!session->channel) {
@@ -3324,6 +3407,22 @@ static void chan_pjsip_incoming_response_update_cause(struct ast_sip_session *se
 	snprintf(cause_code->code, data_size - sizeof(*cause_code) + 1, "SIP %d %.*s", status.code,
 	(int) pj_strlen(&status.reason), pj_strbuf(&status.reason));
 
+	/* V003 & V006 event log*/
+	if (session && session->contact) {
+		char *contact_uri = ast_strdupa(session->contact->uri);
+		strtok_r(contact_uri, ":", &saveptr);
+		client_num = strtok_r(NULL, "@", &saveptr);
+		if(status.code == 488)
+		{
+			/* V003 */
+			ast_log_dt(LOG_EVENT_CODE_V003);
+		}
+		else if( (status.code >= 400 && status.code <= 600) && (client_num != NULL) ){
+			/* V006  4xx & 5xx call return error code */
+			ast_log_dt(LOG_EVENT_CODE_V006, client_num, (int) pj_strlen(&status.reason), pj_strbuf(&status.reason));
+		}
+	}
+
 	cause_code->ast_cause = ast_sip_hangup_sip2cause(status.code);
 	ast_queue_control_data(session->channel, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size);
 	ast_channel_hangupcause_hash_set(session->channel, cause_code, data_size);
@@ -3331,6 +3430,21 @@ static void chan_pjsip_incoming_response_update_cause(struct ast_sip_session *se
 	SCOPE_EXIT_RTN("%s\n", ast_sip_session_get_name(session));
 }
 
+static int handle_incoming_rtp_check_timeout (pj_timer_heap_t *timer, pj_timer_entry *entry)
+{
+	struct ast_sip_session *session = entry->user_data;
+
+	ast_queue_control(session->channel, AST_CONTROL_RINGING);
+	ast_channel_lock(session->channel);
+	if (ast_channel_state(session->channel) != AST_STATE_UP)
+		ast_setstate(session->channel, AST_STATE_RINGING);
+	ast_channel_unlock(session->channel);
+	// change early media direction to EARLY_MEDIA_RECVONLY to prevent from playing both local ringtone
+	// and backword early media (in case if incoming RTP from remote will occur later than EARLY_MEDIA_WAIT_MS
+	// we started the local ringtone here)
+	ast_channel_early_media_set(session->channel, EARLY_MEDIA_RECVONLY);
+}
+
 /*! \brief Function called when a response is received on the session */
 static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 {
@@ -3342,18 +3456,83 @@ static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct
 	}
 
 	switch (status.code) {
-	case 180: {
+	case 180:
+	case 181:
+	case 182:
+	case 183: {
 		pjsip_rdata_sdp_info *sdp = pjsip_rdata_get_sdp_info(rdata);
-		if (sdp && sdp->body.ptr) {
-			/* Always play backward (from UAS to UAC) early media if any in an outgoing call regardless the configuration
-			 * of early media  */
-			ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session));
-			ast_queue_control(session->channel, AST_CONTROL_PROGRESS);
-		} else {
-			ast_trace(-1, "%s: Queueing RINGING\n", ast_sip_session_get_name(session));
-			ast_queue_control(session->channel, AST_CONTROL_RINGING);
+		static const pj_str_t headerName = { "P-Early-Media", 13 };
+		pjsip_generic_string_hdr *earlyMediaHdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &headerName, NULL);
+		enum early_media_direction early_media_dir = EARLY_MEDIA_NONE;
+
+		if(earlyMediaHdr) {
+			if (!pj_stricmp2(&earlyMediaHdr->hvalue, "inactive"))
+				early_media_dir = EARLY_MEDIA_INACTIVE;
+			else if (!pj_stricmp2(&earlyMediaHdr->hvalue, "recvonly"))
+				early_media_dir = EARLY_MEDIA_RECVONLY;
+			else if (!pj_stricmp2(&earlyMediaHdr->hvalue, "sendonly"))
+				early_media_dir = EARLY_MEDIA_SENDONLY;
+			else if (!pj_stricmp2(&earlyMediaHdr->hvalue, "sendrecv"))
+				early_media_dir = EARLY_MEDIA_SENDRECV;
+			else
+				early_media_dir = EARLY_MEDIA_INACTIVE;
+		} else if (sdp && sdp->body.ptr) {
+			// receipt of SDP w/o P-Early-Media: P-Early-Media is implicitly treated as sendonly
+			early_media_dir = EARLY_MEDIA_SENDONLY;
+		}
+		ast_channel_early_media_set(session->channel, early_media_dir);
+
+		if (status.code == 183) {
+			if (session->endpoint->ignore_183_without_sdp) {
+				if (sdp && sdp->body.ptr) {
+					ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session));
+					ast_trace(1, "%s Method: %.*s Status: %d  Queueing PROGRESS with SDP\n", ast_sip_session_get_name(session),
+						(int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr, status.code);
+					ast_queue_control(session->channel, AST_CONTROL_PROGRESS);
+				}
+			} else {
+				ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session));
+				ast_trace(1, "%s Method: %.*s Status: %d  Queueing PROGRESS\n", ast_sip_session_get_name(session),
+					(int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr, status.code);
+				/* Always play backward (from UAS to UAC) early media if any in an outgoing call regardless the configuration
+				* of early media  */
+				ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session));
+				ast_queue_control(session->channel, AST_CONTROL_PROGRESS);
+			}
+		} else if (status.code == 180) {
+			if (early_media_dir != EARLY_MEDIA_SENDONLY && early_media_dir != EARLY_MEDIA_SENDRECV) {
+				ast_trace(-1, "%s: Queueing RINGING\n", ast_sip_session_get_name(session));
+				ast_queue_control(session->channel, AST_CONTROL_SRCUPDATE);
+				ast_queue_control(session->channel, AST_CONTROL_RINGING);
+				ast_channel_lock(session->channel);
+				if (ast_channel_state(session->channel) != AST_STATE_UP) {
+					ast_setstate(session->channel, AST_STATE_RINGING);
+				}
+				ast_channel_unlock(session->channel);
+			} else {
+				// immediately with receipt of P-Early-Media=sendonly/sendrecv any media received from
+				// the network shall be rendered.  If within 500 ms no RTP is received then device should
+				// play "local ringtone"
+				pj_time_val tv;
+				tv.sec = 0;
+				tv.msec = EARLY_MEDIA_WAIT_MS;
+				pj_timer_entry_init(&session->incoming_rtp_check_timer, 0, session, handle_incoming_rtp_check_timeout);
+
+				ao2_ref(session, +1);
+				if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(),
+					&session->incoming_rtp_check_timer, &tv) != PJ_SUCCESS) {
+					ao2_ref(session, -1);
+					SCOPE_EXIT_RTN("%s: Couldn't schedule incoming_rtp_check_timer\n", ast_sip_session_get_name(session));
+				}
+			}
 		}
-
+		break;
+	}
+	case 199: {
+		enum early_media_direction early_media_dir = EARLY_MEDIA_NONE;
+		ast_channel_early_media_set(session->channel, early_media_dir);
+		ast_queue_control(session->channel, AST_CONTROL_SRCUPDATE);
+		ast_queue_control(session->channel, AST_CONTROL_RINGING);
 		ast_channel_lock(session->channel);
 		if (ast_channel_state(session->channel) != AST_STATE_UP) {
 			ast_setstate(session->channel, AST_STATE_RINGING);
@@ -3361,25 +3540,6 @@ static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct
 		ast_channel_unlock(session->channel);
 		break;
 	}
-	case 183:
-		if (session->endpoint->ignore_183_without_sdp) {
-			pjsip_rdata_sdp_info *sdp = pjsip_rdata_get_sdp_info(rdata);
-			if (sdp && sdp->body.ptr) {
-				ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session));
-				ast_trace(1, "%s Method: %.*s Status: %d  Queueing PROGRESS with SDP\n", ast_sip_session_get_name(session),
-					(int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr, status.code);
-				ast_queue_control(session->channel, AST_CONTROL_PROGRESS);
-			}
-		} else {
-			ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session));
-			ast_trace(1, "%s Method: %.*s Status: %d  Queueing PROGRESS without SDP\n", ast_sip_session_get_name(session),
-				(int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr, status.code);
-			/* Always play backward (from UAS to UAC) early media if any in an outgoing call regardless the configuration
-			 * of early media  */
-			ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session));
-			ast_queue_control(session->channel, AST_CONTROL_PROGRESS);
-		}
-		break;
 	case 200:
 		ast_trace(-1, "%s: Queueing ANSWER\n", ast_sip_session_get_name(session));
 		ast_queue_control(session->channel, AST_CONTROL_ANSWER);
diff --git a/configure.ac b/configure.ac
index d3ded2799858991fe4f99b1d95dcb4a6c051134a..377d6c1ce6534ff29d5ce9cf83bc113017be1195 100644
--- a/configure.ac
+++ b/configure.ac
@@ -740,16 +740,6 @@ fi
 AST_EXT_LIB_CHECK([RT], [rt], [clock_gettime], [])
 
 AST_PKG_CONFIG_CHECK([LIBXML2], [libxml-2.0])
-AST_EXT_TOOL_CHECK([LIBXML2], [xml2-config], , ,
-        [#include <libxml/tree.h>
-        #include <libxml/parser.h>],
-        [LIBXML_TEST_VERSION])
-
-if test "${PBX_LIBXML2}" != 1; then
-	AC_MSG_NOTICE(*** The Asterisk menuselect tool requires the 'libxml2' development package.)
-	AC_MSG_NOTICE(*** Please install the 'libxml2' development package.)
-	exit 1
-fi
 
 AST_EXT_LIB_CHECK([URIPARSER], [uriparser], [uriParseUriA], [uriparser/Uri.h])
 
@@ -1616,6 +1606,8 @@ fi
 
 # do the package library checks now
 
+AST_PKG_CONFIG_CHECK([LIBXML2], [libxml-2.0])
+
 AST_EXT_LIB_CHECK([ALSA], [asound], [snd_pcm_open], [alsa/asoundlib.h])
 
 AST_EXT_LIB_CHECK([BFD], [bfd], [bfd_openr], [bfd.h])
@@ -2813,7 +2805,7 @@ AC_CHECK_HEADER([linux/compiler.h],
 AST_C_DEFINE_CHECK([MSG_NOSIGNAL], [MSG_NOSIGNAL], [sys/socket.h])
 AST_C_DEFINE_CHECK([SO_NOSIGPIPE], [SO_NOSIGPIPE], [sys/socket.h])
 
-AST_EXT_TOOL_CHECK([SDL], [sdl-config])
+AST_PKG_CONFIG_CHECK([SDL], [sdl])
 AST_EXT_LIB_CHECK([SDL_IMAGE], [SDL_image], [IMG_Load], [SDL_image.h], [${SDL_LIB}], [${SDL_INCLUDE}])
 AST_EXT_LIB_CHECK([FFMPEG], [avcodec], [sws_getContext], [ffmpeg/avcodec.h], [${PTHREAD_LIBS} -lz -lm], [${PTHREAD_CFLAGS}])
 
diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h
index 35ba85f3100ebf78996afcfe577797f01954e817..558ba66b270bcc5a8f9cebc86f3849b4030d10e6 100644
--- a/include/asterisk/cdr.h
+++ b/include/asterisk/cdr.h
@@ -260,6 +260,7 @@ enum ast_cdr_disposition {
 	AST_CDR_BUSY       = (1 << 2),
 	AST_CDR_ANSWERED   = (1 << 3),
 	AST_CDR_CONGESTION = (1 << 4),
+	AST_CDR_ANSWERED_ELSEWHERE = (1 << 5),
 };
 
 
@@ -359,7 +360,7 @@ struct ast_cdr {
 	/*! SessionId */
 	unsigned int sessionId;
 	/*! SIPSessionID */
-	char SIPSessionID[33];
+	char SIPSessionID[128];
 	/*! sipIpAddress */
 	char sipIpAddress[40];
 	/*! farEndIPAddress */
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index dcc191cf32f64a35fd6dff108dc675065eecad65..fa79a6901bedc328e7d3ea40cd9fdc2c4b880b97 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -203,6 +203,14 @@ extern "C" {
 #define AST_GENERATOR_FD	(AST_MAX_FDS-4)	/*!< used by generator */
 #define AST_JITTERBUFFER_FD	(AST_MAX_FDS-5)	/*!< used by generator */
 
+enum early_media_direction{
+	EARLY_MEDIA_NONE,
+	EARLY_MEDIA_INACTIVE,
+	EARLY_MEDIA_RECVONLY,
+	EARLY_MEDIA_SENDONLY,
+	EARLY_MEDIA_SENDRECV,
+};
+
 enum ast_bridge_result {
 	AST_BRIDGE_COMPLETE = 0,
 	AST_BRIDGE_FAILED = -1,
@@ -4339,6 +4347,10 @@ const char *ast_channel_farEndIPAddress(const struct ast_channel *chan);
 void ast_channel_farEndIPAddress_set(struct ast_channel *chan, const char *value, size_t size);
 unsigned int ast_channel_sipResponseCode(const struct ast_channel *chan);
 void ast_channel_sipResponseCode_set(struct ast_channel *chan, unsigned int value);
+int ast_channel_narrow_band_only(const struct ast_channel *chan);
+void ast_channel_narrow_band_only_set(struct ast_channel *chan, int value);
+struct ast_channel *ast_channel_bridged_chan(const struct ast_channel *chan);
+void ast_channel_bridged_chan_set(struct ast_channel *chan, struct ast_channel *value);
 const char *ast_channel_codec_get(const struct ast_channel *chan);
 void ast_channel_codec_set(struct ast_channel *chan, const char *value);
 struct ast_channel_snapshot *ast_channel_snapshot(const struct ast_channel *chan);
@@ -4354,7 +4366,8 @@ enum ast_sip_dtmf_mode ast_channel_dtmf_mode_get(const struct ast_channel *chan)
 void ast_channel_dtmf_mode_set(struct ast_channel *chan, enum ast_sip_dtmf_mode dtmf_mode);
 int ast_channel_dtmf_pt_get(const struct ast_channel *chan);
 void ast_channel_dtmf_pt_set(struct ast_channel *chan, int dtmf_pt);
-
+enum early_media_direction ast_channel_early_media_get(const struct ast_channel *chan);
+void ast_channel_early_media_set(struct ast_channel *chan, enum early_media_direction early_media_dir);
 
 /*!
  * \pre chan is locked
diff --git a/include/asterisk/dt_logger.h b/include/asterisk/dt_logger.h
new file mode 100644
index 0000000000000000000000000000000000000000..b85bf12550e45b5fdb617124c4c67b0e75df04ef
--- /dev/null
+++ b/include/asterisk/dt_logger.h
@@ -0,0 +1,71 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \author Grzegorz Sluja <grzegorz.sluja@genexis.eu>
+ *
+ * \brief Support for the DT syslog logging mechanism
+ */
+
+#ifndef _ASTERISK_DT_LOGGER_H
+#define _ASTERISK_DT_LOGGER_H
+
+// Define language support
+typedef enum {
+    LOG_LANGUAGE_DT_ENGLISH = 0,
+    LOG_LANGUAGE_DT_GERMAN,
+    // Add more languages here
+    LOG_LANGUAGE_DT_MAX
+} LOG_LANGUAGE_DT;
+
+// Define log event codes
+typedef enum {
+    LOG_EVENT_CODE_SPD001 = 1,
+    LOG_EVENT_CODE_SPD002,
+    LOG_EVENT_CODE_V001,
+    LOG_EVENT_CODE_V002,
+    LOG_EVENT_CODE_V003,
+    LOG_EVENT_CODE_V004,
+    LOG_EVENT_CODE_V005,
+    LOG_EVENT_CODE_V006,
+    LOG_EVENT_CODE_V100,
+    LOG_EVENT_CODE_V101,
+    LOG_EVENT_CODE_IPX001,
+    LOG_EVENT_CODE_IPX002,
+    LOG_EVENT_CODE_IPX003,
+    LOG_EVENT_CODE_IPX004,
+    LOG_EVENT_CODE_IPX100,
+    LOG_EVENT_CODE_IPX101,
+    LOG_EVENT_CODE_SV101,
+    LOG_EVENT_CODE_SV102,
+    LOG_EVENT_CODE_SV103,
+    LOG_EVENT_CODE_SV107,
+    LOG_EVENT_CODE_SV108,
+    LOG_EVENT_CODE_SV109,
+    LOG_EVENT_CODE_SV112,
+    LOG_EVENT_CODE_SV113,
+    LOG_EVENT_CODE_SV114,
+    LOG_EVENT_CODE_SV116,
+    // Add more event codes here
+} LOG_EVENT_CODE_DT;
+
+void dt_set_log_language(LOG_LANGUAGE_DT language);
+void ast_log_dt(LOG_EVENT_CODE_DT event_code, ...);
+
+#endif /* _ASTERISK_DT_LOGGER_H */
diff --git a/include/asterisk/logger.h b/include/asterisk/logger.h
index e2a21c7f14a47a8796c9aa145189ab3356b10233..05dde0f66e7b8c673c692cd8ee1fa6e7047b5236 100644
--- a/include/asterisk/logger.h
+++ b/include/asterisk/logger.h
@@ -27,6 +27,7 @@
 #define _ASTERISK_LOGGER_H
 
 #include "asterisk/options.h"	/* need option_debug */
+#include "asterisk/dt_logger.h"
 
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index d144157c460183c11d9b92d6a4ea58f0ee8942d9..d3c8884ceee44fefb378d826eefe751e4c9c61bd 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -23,6 +23,7 @@
 /* Needed for SUBSCRIBE, NOTIFY, and PUBLISH method definitions */
 #include <pjsip_simple.h>
 #include <pjsip/sip_transaction.h>
+#include <pjsip/sip_auth.h>
 #include <pj/timer.h>
 /* Needed for pj_sockaddr */
 #include <pjlib.h>
@@ -313,6 +314,8 @@ struct ast_sip_transport {
 	int symmetric_transport;
 	/*! This is a flow to another target */
 	int flow;
+	/*! Enable the transport */
+	int enable;
 };
 
 #define SIP_SORCERY_DOMAIN_ALIAS_TYPE "domain_alias"
@@ -759,6 +762,8 @@ struct ast_sip_endpoint_id_configuration {
 	unsigned int trust_outbound;
 	/*! Do we send P-Asserted-Identity headers to this endpoint? */
 	unsigned int send_pai;
+	/*! Do we send P-Preferred-Identity headers to this endpoint? */
+	unsigned int send_ppi;
 	/*! Do we send Remote-Party-ID headers to this endpoint? */
 	unsigned int send_rpid;
 	/*! Do we send messages for connected line updates for unanswered incoming calls immediately to this endpoint? */
@@ -954,8 +959,12 @@ struct ast_sip_endpoint {
 	AST_DECLARE_STRING_FIELDS(
 		/*! Context to send incoming calls to */
 		AST_STRING_FIELD(context);
-		/*! Name of an explicit transport to use */
+		/*! Name of an primary transport to use */
 		AST_STRING_FIELD(transport);
+		/*! Name of an secondary transport to use */
+		AST_STRING_FIELD(transport2);
+		/*! Name of an secondary transport to use */
+		AST_STRING_FIELD(transport_emergency);
 		/*! Outbound proxy to use */
 		AST_STRING_FIELD(outbound_proxy);
 		/*! Explicit AORs to dial if none are specified */
@@ -1073,6 +1082,27 @@ struct ast_sip_endpoint {
 	enum ast_sip_100rel_mode rel100;
 	/*! Send Advice-of-Charge messages */
 	unsigned int send_aoc;
+	bool Emergency;
+	/* Information of register destination */
+	struct pjsip_register_dest *register_dest;
+	unsigned int failover_reg_addr; // 0/1/2 -> none/504-register-ongoing/504-register-failed
+	int failover_reg_addr_timer_id; // timer for reset flag if register failed.
+	/* the transport name of latest registration */
+	char *register_transport;
+	unsigned int max_sessions;
+	char *realms;
+	bool cached_auth;
+	/* Authorization sessions. */
+	pjsip_auth_clt_sess auth_sess_reg;
+	pjsip_auth_clt_sess auth_sess_inv;
+	ast_mutex_t auth_sess_reg_lock;
+	ast_mutex_t auth_sess_inv_lock;
+};
+
+struct pjsip_register_dest {
+	char *host;
+	pjsip_server_addresses addr;
+	unsigned cur_addr;
 };
 
 /*! URI parameter for symmetric transport */
@@ -1086,6 +1116,7 @@ extern pjsip_media_type pjsip_media_type_application_pidf_xml;
 extern pjsip_media_type pjsip_media_type_application_xpidf_xml;
 extern pjsip_media_type pjsip_media_type_application_cpim_xpidf_xml;
 extern pjsip_media_type pjsip_media_type_application_rlmi_xml;
+extern pjsip_media_type pjsip_media_type_application_3gpp_ims_xml;
 extern pjsip_media_type pjsip_media_type_application_simple_message_summary;
 extern pjsip_media_type pjsip_media_type_application_sdp;
 extern pjsip_media_type pjsip_media_type_multipart_alternative;
@@ -3545,6 +3576,11 @@ struct ao2_container *ast_sip_get_transport_states(void);
  */
 int ast_sip_set_tpselector_from_transport(const struct ast_sip_transport *transport, pjsip_tpselector *selector);
 
+/*!
+ * \brief Check if the NTP has synchronized
+ */
+int ast_sip_is_X_RDK_NTP_Synchronized(void);
+
 /*!
  * \brief Sets pjsip_tpselector from ast_sip_transport
  * \since 13.8.0
@@ -4059,5 +4095,18 @@ unsigned int ast_sip_get_all_codecs_on_empty_reinvite(void);
  */
 const int ast_sip_hangup_sip2cause(int cause);
 
+/*!
+ * \brief Retrieve the global setting 'max_sessions_per_line'.
+ *
+ * \retval value of max_sessions_per_line
+ */
+unsigned int ast_sip_get_max_sessions_per_line(void);
+
+/*!
+ * \brief Retrieve the global setting 'max_sessions_total'.
+ *
+ * \retval value of max_sessions_total
+ */
+unsigned int ast_sip_get_max_sessions_total(void);
 
 #endif /* _RES_PJSIP_H */
diff --git a/include/asterisk/res_pjsip_outbound_registration.h b/include/asterisk/res_pjsip_outbound_registration.h
new file mode 100644
index 0000000000000000000000000000000000000000..0a47b416d048293baeff8c15be0617508fabbe35
--- /dev/null
+++ b/include/asterisk/res_pjsip_outbound_registration.h
@@ -0,0 +1,9 @@
+#ifndef _RES_PJSIP_OUTBOUND_REGISTRATION_H
+#define _RES_PJSIP_OUTBOUND_REGISTRATION_H
+
+void queue_registration_recovery_flow(const char *registration_name);
+void update_emergency_registration_ongoing_status(const char *registration_name, bool action);
+void check_and_update_nextnonce(pjsip_auth_clt_sess *sess, const char *realm, pjsip_rx_data *rdata);
+void sync_cached_auth_between_reg_inv(pjsip_auth_clt_sess *sess_from, pjsip_auth_clt_sess *sess_to, const pj_str_t *realm );
+
+#endif /* _RES_PJSIP_OUTBOUND_REGISTRATION_H */
diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h
index 1e2943090ae215044098f179da46632b9a4788ac..c7e8a3c33af92af9856edfaf625b642720137d3a 100644
--- a/include/asterisk/res_pjsip_session.h
+++ b/include/asterisk/res_pjsip_session.h
@@ -245,6 +245,9 @@ struct ast_sip_session {
 	int ani2;
 	int ring_cw;
 	int early_media;
+	/*! The working transport of the session */
+	struct ast_sip_transport_state *transport_state;
+	pj_timer_entry incoming_rtp_check_timer;
 };
 
 typedef int (*ast_sip_session_request_creation_cb)(struct ast_sip_session *session, pjsip_tx_data *tdata);
@@ -799,6 +802,19 @@ void ast_sip_session_send_request_with_cb(struct ast_sip_session *session, pjsip
  */
 struct ast_sip_session *ast_sip_dialog_get_session(pjsip_dialog *dlg);
 
+/*!
+ * \brief Recalculate the joint codecs as if a re-INVITE is received within an established session
+ *
+ * This function calculates the joint codecs between the answered channel's capability and the SDP
+ * offer from the remote.
+ *
+ * \param session The session which contains the SDP offer
+ *
+ * \retval  0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_session_sdp_answer(struct ast_sip_session *session);
+
 /*!
  * \brief Resumes processing of a deferred incoming re-invite
  *
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index f5533c31ffced6fb5882662cdf919c4a3db7a97a..5a017de14e55439fe6532799e7c2eeee68176b07 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -700,6 +700,8 @@ struct ast_rtp_engine {
 	int (*get_stat)(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat);
 	/*! Callback for setting QoS values */
 	int (*qos)(struct ast_rtp_instance *instance, int tos, int cos, const char *desc);
+	/*! Callback for setting RTCP QoS values */
+	void (*rtcp_qos)(struct ast_rtp_instance *instance, int tos, int cos, const char *desc);
 	/*! Callback for retrieving a file descriptor to poll on, not always required */
 	int (*fd)(struct ast_rtp_instance *instance, int rtcp);
 	/*! Callback for initializing RED support */
@@ -2021,7 +2023,7 @@ void ast_rtp_instance_change_source(struct ast_rtp_instance *instance);
  * \since 1.8
  */
 int ast_rtp_instance_set_qos(struct ast_rtp_instance *instance, int tos, int cos, const char *desc);
-
+void ast_rtp_instance_set_rtcp_qos(struct ast_rtp_instance *instance, int tos, int cos, const char *desc);
 /*!
  * \brief Stop an RTP instance
  *
diff --git a/include/asterisk/stasis_channels.h b/include/asterisk/stasis_channels.h
index 5a754f6715d532903c2966053a6bfb5ddc137384..0fbab12adb516e9633f668c118606cbb49f5a8b0 100644
--- a/include/asterisk/stasis_channels.h
+++ b/include/asterisk/stasis_channels.h
@@ -158,7 +158,7 @@ struct ast_channel_snapshot {
 	struct varshead *manager_vars;                    /*!< Variables to be appended to manager events */
 	struct varshead *ari_vars;                        /*!< Variables to be appended to ARI events */
 	unsigned int sessionId;                           /*!< SessionId */
-	char SIPSessionID[33];				  /*!< Session-ID */
+	char SIPSessionID[128];                           /*!< Session-ID */
 	char sipIpAddress[40];                            /*!< SIP IP Address */
 	char farEndIPAddress[40];                         /*!< Far End IP Address */
 	unsigned int sipResponseCode;                     /*!< SIP Response Code for Invite */
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h
index d46fed6fda529abbf686833afa8e392cdba8bc3b..55a5ad044ff8d251381ae087fe4160f00abd92e2 100644
--- a/include/asterisk/utils.h
+++ b/include/asterisk/utils.h
@@ -1137,4 +1137,6 @@ int ast_check_command_in_path(const char *cmd);
  */
 void ast_ubus_free_context(struct ubus_context *ctx);
 
+void ast_send_network_info_ubus_event(int enabled, char* protocol, char* addr_buf, int port);
+
 #endif /* _ASTERISK_UTILS_H */
diff --git a/main/Makefile b/main/Makefile
index 2e0b84f31628601d1dc8501c581755e5680b5d3b..b4c9b7c501fc9f285d16632aa8ff7a5d2603d6b0 100644
--- a/main/Makefile
+++ b/main/Makefile
@@ -17,7 +17,7 @@ all: asterisk
 
 include $(ASTTOPDIR)/Makefile.moddir_rules
 
-MOD_SRC:=cdr.c cel.c config.c ccss.c dnsmgr.c dsp.c enum.c features.c http.c indications.c logger.c manager.c named_acl.c plc.c sounds.c udptl.c
+MOD_SRC:=cdr.c cel.c config.c ccss.c dnsmgr.c dsp.c enum.c features.c http.c indications.c logger.c manager.c named_acl.c plc.c sounds.c udptl.c dt_logger.c
 # Allow deletion of built-in modules without needing to modify this source.
 MOD_SRC:=$(wildcard $(MOD_SRC))
 MOD_OBJS:=$(sort $(MOD_SRC:.c=.o))
diff --git a/main/asterisk.c b/main/asterisk.c
index dea849f10c1f0a6fdcd64ebcd9e82fcab0c43e30..454ec4a6c523a2d0bb9dcbddaa69b12c0f150362 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -246,6 +246,10 @@ int daemon(int, int);  /* defined in libresolv of all places */
 
 #include "../defaults.h"
 
+#ifdef INCLUDE_BREAKPAD
+#include "breakpad_wrapper.h"
+#endif
+
 /*** DOCUMENTATION
 	<managerEvent language="en_US" name="FullyBooted">
 		<managerEventInstance class="EVENT_FLAG_SYSTEM">
@@ -3862,7 +3866,8 @@ int main(int argc, char *argv[])
 	/* Must install this signal handler up here to ensure that if the canary
 	 * fails to execute that it doesn't kill the Asterisk process.
 	 */
-	sigaction(SIGCHLD, &child_handler, NULL);
+	if (ast_opt_high_priority)
+		sigaction(SIGCHLD, &child_handler, NULL);
 
 	/* It's common on some platforms to clear /var/run at boot.  Create the
 	 * socket file directory before we drop privileges. */
@@ -4200,6 +4205,10 @@ static void asterisk_daemon(int isroot, const char *runuser, const char *rungrou
 	check_init(ast_endpoint_stasis_init(), "Stasis Endpoint");
 
 	ast_makesocket();
+
+#ifdef INCLUDE_BREAKPAD
+	breakpad_ExceptionHandler();
+#endif
 	/* GCC 4.9 gives a bogus "right-hand operand of comma expression has
 	 * no effect" warning */
 	(void) sigemptyset(&sigs);
diff --git a/main/cdr.c b/main/cdr.c
index acae1fb60fdf8b5bbae3dc1ebbc31d7a90f3538b..964892fc7fbeab27a3077f54822122e3a227131b 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -1518,6 +1518,9 @@ static void cdr_object_set_disposition(struct cdr_object *cdr, int hangupcause)
 	case AST_CAUSE_NO_ANSWER:
 		cdr->disposition = AST_CDR_NOANSWER;
 		break;
+	case AST_CAUSE_ANSWERED_ELSEWHERE:
+		cdr->disposition = AST_CDR_ANSWERED_ELSEWHERE;
+		break;
 	default:
 		break;
 	}
@@ -1541,6 +1544,14 @@ static void cdr_object_finalize(struct cdr_object *cdr)
 	}
 	cdr->end = cdr->lastevent;
 
+	if (cdr->disposition == AST_CDR_NOANSWER) {
+		// Retrieve hangup cause and update disposition to answered_elsewhere when incoming cancel request with sip cause=200, rfc3326
+		if (cdr->party_a.snapshot->hangup->cause == AST_CAUSE_ANSWERED_ELSEWHERE
+			|| (cdr->party_b.snapshot && cdr->party_b.snapshot->hangup->cause == AST_CAUSE_ANSWERED_ELSEWHERE)) {
+
+			cdr_object_set_disposition(cdr, AST_CAUSE_ANSWERED_ELSEWHERE);
+		}
+	}
 	if (cdr->disposition == AST_CDR_NULL) {
 		if (!ast_tvzero(cdr->answer)) {
 			cdr->disposition = AST_CDR_ANSWERED;
@@ -3642,6 +3653,8 @@ const char *ast_cdr_disp2str(int disposition)
 		return "ANSWERED";
 	case AST_CDR_CONGESTION:
 		return "CONGESTION";
+	case AST_CDR_ANSWERED_ELSEWHERE:
+		return "ANSWERED ELSEWHERE";
 	}
 	return "UNKNOWN";
 }
diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c
index 128a9e70be2a072400d69954ced55175cdf7118a..f102ad674f68847352aabf0a2b1b5d7073982a25 100644
--- a/main/channel_internal_api.c
+++ b/main/channel_internal_api.c
@@ -225,7 +225,7 @@ struct ast_channel {
 	struct ast_channel_snapshot *snapshot; /*!< The current up to date snapshot of the channel */
 	struct ast_flags snapshot_segment_flags; /*!< Flags regarding the segments of the snapshot */
 	unsigned int sessionId; /*!< Session Id from SDP for channel */
-	char SIPSessionID[33]; /* SIPSessionID from  Session-ID header */
+	char SIPSessionID[128]; /* SIPSessionID from  Session-ID header */
 	char sipIpAddress[40];  /*!< local IP address that sip client binds to */
 	char farEndIPAddress[40];  /*!< Far End IP Address */
 	unsigned int sipResponseCode; /*!< SIP response Code */
@@ -235,6 +235,9 @@ struct ast_channel {
 	int ptime; /*!< Negotiated ptime */
 	enum ast_sip_dtmf_mode dtmf_mode;
 	int dtmf_pt;
+	enum early_media_direction early_media_dir;  /*!< The direction of the early media*/
+	int narrow_band_only;  /*!< Whether the channel supports narrow band only */
+	struct ast_channel *bridged_chan; /*!< Channel that will be bridged with */
 };
 
 /*! \brief The monotonically increasing integer counter for channel uniqueids */
@@ -869,6 +872,26 @@ void ast_channel_sipResponseCode_set(struct ast_channel *chan, unsigned int valu
 	chan->sipResponseCode = value;
 }
 
+int ast_channel_narrow_band_only(const struct ast_channel *chan)
+{
+	return chan->narrow_band_only;
+}
+
+void ast_channel_narrow_band_only_set(struct ast_channel *chan, int value)
+{
+	chan->narrow_band_only = value;
+}
+
+struct ast_channel *ast_channel_bridged_chan(const struct ast_channel *chan)
+{
+	return chan->bridged_chan;
+}
+
+void ast_channel_bridged_chan_set(struct ast_channel *chan, struct ast_channel *value)
+{
+	chan->bridged_chan = value;
+}
+
 const char *ast_channel_codec_get(const struct ast_channel *chan)
 {
 	return chan->codec;
@@ -918,11 +941,22 @@ int ast_channel_ptime_get(const struct ast_channel *chan)
 {
 	return chan->ptime;
 }
+
 void ast_channel_ptime_set(struct ast_channel *chan, int ptime)
 {
 	chan->ptime = ptime;
 }
 
+enum early_media_direction ast_channel_early_media_get(const struct ast_channel *chan)
+{
+	return chan->early_media_dir;
+}
+void ast_channel_early_media_set(struct ast_channel *chan, enum early_media_direction early_media_dir)
+{
+	chan->early_media_dir = early_media_dir;
+	ast_debug(3, "Set early_media_dir to %d for channel %s\n", chan->early_media_dir, ast_channel_name(chan));
+}
+
 void ast_channel_callid_set(struct ast_channel *chan, ast_callid callid)
 {
 	char call_identifier_from[AST_CALLID_BUFFER_LENGTH];
diff --git a/main/codec_builtin.c b/main/codec_builtin.c
index bd69d46be1f08c1490f616e768c06fad5554f5a3..dfd3b0a0fb8469f129ded15e9eeae4740523be6e 100644
--- a/main/codec_builtin.c
+++ b/main/codec_builtin.c
@@ -169,7 +169,8 @@ static struct ast_codec ulaw = {
 	.type = AST_MEDIA_TYPE_AUDIO,
 	.sample_rate = 8000,
 	.minimum_ms = 10,
-	.maximum_ms = 150,
+	.maximum_ms = 20, /* Changed according to DT requirements */
+	/*	.maximum_ms = 150, */
 	.default_ms = 20,
 	.minimum_bytes = 80,
 	.samples_count = ulaw_samples,
@@ -183,7 +184,8 @@ static struct ast_codec alaw = {
 	.type = AST_MEDIA_TYPE_AUDIO,
 	.sample_rate = 8000,
 	.minimum_ms = 10,
-	.maximum_ms = 150,
+	.maximum_ms = 20, /* Changed according to DT requirements */
+	/*	.maximum_ms = 150, */
 	.default_ms = 20,
 	.minimum_bytes = 80,
 	.samples_count = ulaw_samples,
@@ -649,7 +651,8 @@ static struct ast_codec g722 = {
 	.type = AST_MEDIA_TYPE_AUDIO,
 	.sample_rate = 16000,
 	.minimum_ms = 10,
-	.maximum_ms = 150,
+	.maximum_ms = 20, /* Changed according to DT requirements */
+	/*	.maximum_ms = 150,	*/
 	.default_ms = 20,
 	.minimum_bytes = 80,
 	.samples_count = g726_samples,
diff --git a/main/dns_naptr.c b/main/dns_naptr.c
index caeb6ac616d3f765990fb7a5fd514afae1848e9a..865a67c408ae0d80598f5a582af95850d20878e5 100644
--- a/main/dns_naptr.c
+++ b/main/dns_naptr.c
@@ -446,6 +446,7 @@ struct ast_dns_record *dns_naptr_alloc(struct ast_dns_query *query, const char *
 		replacement, sizeof(replacement) - 1);
 	if (replacement_size < 0) {
 		ast_log(LOG_ERROR, "Failed to expand domain name: %s\n", strerror(errno));
+		ast_log_dt(LOG_EVENT_CODE_SV108);
 		return NULL;
 	}
 
@@ -453,6 +454,7 @@ struct ast_dns_record *dns_naptr_alloc(struct ast_dns_query *query, const char *
 
 	if (ptr != end_of_record) {
 		ast_log(LOG_ERROR, "NAPTR record gave undersized string indications.\n");
+		ast_log_dt(LOG_EVENT_CODE_SV108);
 		return NULL;
 	}
 
@@ -462,16 +464,19 @@ struct ast_dns_record *dns_naptr_alloc(struct ast_dns_query *query, const char *
 	flags_res = interpret_flags(flags, flags_size);
 	if (flags_res == FLAGS_INVALID) {
 		ast_log(LOG_ERROR, "NAPTR Record contained invalid flags %.*s\n", flags_size, flags);
+		ast_log_dt(LOG_EVENT_CODE_SV108);
 		return NULL;
 	}
 
 	if (services_invalid(services, services_size)) {
 		ast_log(LOG_ERROR, "NAPTR record contained invalid services %.*s\n", services_size, services);
+		ast_log_dt(LOG_EVENT_CODE_SV108);
 		return NULL;
 	}
 
 	if (regexp_invalid(regexp, regexp_size)) {
 		ast_log(LOG_ERROR, "NAPTR record contained invalid regexp %.*s\n", regexp_size, regexp);
+		ast_log_dt(LOG_EVENT_CODE_SV108);
 		return NULL;
 	}
 
@@ -480,6 +485,7 @@ struct ast_dns_record *dns_naptr_alloc(struct ast_dns_query *query, const char *
 	 */
 	if (regexp_size && replacement_size > 1) {
 		ast_log(LOG_ERROR, "NAPTR record contained both a regexp and replacement\n");
+		ast_log_dt(LOG_EVENT_CODE_SV108);
 		return NULL;
 	}
 
diff --git a/main/dt_logger.c b/main/dt_logger.c
new file mode 100644
index 0000000000000000000000000000000000000000..c9ae9a2415efd2daeba3b9a94b61e2a5c0a12cfc
--- /dev/null
+++ b/main/dt_logger.c
@@ -0,0 +1,369 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \author Grzegorz Sluja <grzegorz.sluja@genexis.eu>
+ *
+ * \brief Support for the DT syslog logging mechanism
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <time.h>
+#include <pthread.h>
+#include <sys/file.h>
+#include "asterisk.h"
+#include "asterisk/dt_logger.h"
+
+#define LOG_FILE_PATH "/rdklogs/logs/VoIPlog.txt"
+
+// Define flags for extended syslog
+#define LOG_FLAG_EXTENDED_SYSLOG_ONLY 0x01
+
+// Current language setting
+static LOG_LANGUAGE_DT current_language = LOG_LANGUAGE_DT_ENGLISH;
+
+// Structure to store log messages in different languages and log options (flags)
+struct ast_log_message_table_dt {
+    LOG_EVENT_CODE_DT code;
+    const char *messages[LOG_LANGUAGE_DT_MAX];
+    uint32_t category; // priority of syslog message:
+                       // (LOG_EMERG|LOG_ALERT|LOG_CRIT|LOG_ERR|LOG_WARNING|LOG_NOTICE|LOG_INFO|LOG_DEBUG)
+    uint32_t flags;    // Flags, not used for now
+};
+
+// Log message table with messages in different languages and flags
+struct ast_log_message_table_dt log_message_table_dt[] = {
+    {
+        .code = LOG_EVENT_CODE_SPD001,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "The Speeddial memory is erased by entering the keycode **#93# in a connected telefon.",
+            [LOG_LANGUAGE_DT_GERMAN] = "Der Nummernspeicher wurde durch die Eingabe des Tastencodes **#93# in ein angeschlossen Telefon gelöscht."
+        },
+        .category = LOG_INFO,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_SPD002,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "The Speeddial memory is erased via the graphical user interface of the router.",
+            [LOG_LANGUAGE_DT_GERMAN] = "Der Nummernspeicher wurde über das grafische Benutzermenü gelöscht."
+        },
+        .category = LOG_INFO,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_V001,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "Registering with VoIP number ( %s ) failed: Reason: DNS error.",
+            [LOG_LANGUAGE_DT_GERMAN] = "Anmeldung der Internet-Telefonnummer ( %s ) war nicht erfolgreich. Ursache: DNS-Fehler."
+        },
+        .category = LOG_ERR,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_V002,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "Registering with VoIP number ( %s ) failed: Reason: %s",
+            [LOG_LANGUAGE_DT_GERMAN] = "Anmeldung der Internet-Telefonnummer ( %s ) war nicht erfolgreich. Gegenstelle meldet Ursache: %s"
+        },
+        .category = LOG_ERR,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_V003,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "A general error occurred during Internet telephony: This action is not allowed (SIP Error code 488)",
+            [LOG_LANGUAGE_DT_GERMAN] = "Es ist ein allgemeiner Fehler bei der Internet-Telefonie aufgetreten: Diese Aktion ist nicht erlaubt (SIP Fehlercode 488)"
+        },
+        .category = LOG_ERR,
+        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY  // Included in extended syslog only
+    },
+    {
+        .code = LOG_EVENT_CODE_V004,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "Registration of the Internet telephone number (%s) was not successful. Cause: Remote station does not respond: Timeout.",
+            [LOG_LANGUAGE_DT_GERMAN] = "Anmeldung der Internet-Telefonnummer (%s) war nicht erfolgreich. Ursache: Gegenstelle antwortet nicht: Zeitüberschreitung."
+        },
+        .category = LOG_ERR,
+        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY  // Included in extended syslog only
+    },
+    {
+        .code = LOG_EVENT_CODE_V005,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "Deregistration of the Internet telephone number %s was not successful. The other party reports the reason: %.*s",
+            [LOG_LANGUAGE_DT_GERMAN] = "Abmeldung der Internet-Telefonnummer %s war nicht erfolgreich. Gegenstelle meldet Ursache: %.*s"
+        },
+        .category = LOG_ERR,
+        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY  // Included in extended syslog only
+    },
+    {
+        .code = LOG_EVENT_CODE_V006,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "The Internet voice connection with %s was not successful. Reason: %.*s",
+            [LOG_LANGUAGE_DT_GERMAN] = "Die Internet-Sprachverbindung mit %s war nicht erfolgreich. Ursache: %.*s"
+        },
+        .category = LOG_ERR,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_V100,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "The Speeddial memory was emptied by the user.",
+            [LOG_LANGUAGE_DT_GERMAN] = "Der Speeddial Speicher wurde durch den Nutzer geleert."
+        },
+        .category = LOG_ERR,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_V101,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "The Internet telephony connection (registration) for %s was successfully established: IP address (registrar): %s",
+            [LOG_LANGUAGE_DT_GERMAN] = "Die Internet Telefonie Verbindung (Registrierung) für %s wurde erfolgreich hergestellt: IP-Adresse (Registrar): %s"
+        },
+        .category = LOG_INFO,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_IPX001,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "The IP phone %.*s with IP %.*s has registered successfully.",
+            [LOG_LANGUAGE_DT_GERMAN] = "Das IP-Telefon %.*s mit IP %.*s hat sich erfolgreich registriert."
+        },
+        .category = LOG_INFO,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_IPX002,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "The IP phone %.*s with IP %.*s has deregistered.",
+            [LOG_LANGUAGE_DT_GERMAN] = "Das IP-Telefon %.*s mit IP %.*s hat sich deregistriert."
+        },
+        .category = LOG_INFO,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_IPX003,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "The IP phone %.*s with IP %.*s did not register successfully due to missing authorization.",
+            [LOG_LANGUAGE_DT_GERMAN] = "Das IP-Telefon %.*s  mit IP %.*s hat sich aufgrund fehlender Autorisierung nicht erfolgreich registriert."
+        },
+        .category = LOG_INFO,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_IPX004,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "The IP phone %s with IP %s could not register successfully due to a timeout.",
+            [LOG_LANGUAGE_DT_GERMAN] = "Das IP-Telefon %s mit IP %s konnte sich aufgrund eines Timeouts nicht erfolgreich registrieren."
+        },
+        .category = LOG_INFO,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_IPX100,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "An error %d occurred with IP phone %.*s.",
+            [LOG_LANGUAGE_DT_GERMAN] = "Es ist ein Fehler %d mit IP-Telefon %.*s aufgetreten"
+        },
+        .category = LOG_INFO,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_IPX101,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "The registration of the local IP phone %.*s with the MAC address <mac address of registering client> failed more than five times in a row due to an authentication error %d. The telephony function of the IP phone %.*s has been deactivated.",
+            [LOG_LANGUAGE_DT_GERMAN] = "Die Registrierung des lokalen IP-Telefons %.*s  mit der MAC-Adresse <mac address of registering client> ist mehr als fünfmal in Folge aufgrund eines Authentifizierungsfehler %d fehlgeschlagen. Die Telephonie Funktion des IP-Telefons %.*s wurde deaktiviert."
+        },
+        .category = LOG_INFO,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_SV101,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "Security setting set to ‘Off’",
+            [LOG_LANGUAGE_DT_GERMAN] = "Sicherheitseinstellung auf ‚Aus’ gestellt"
+        },
+        .category = LOG_INFO,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_SV102,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "Security setting set to ‘Level 1’",
+            [LOG_LANGUAGE_DT_GERMAN] = "Sicherheitseinstellung auf ‚Stufe 1’ gestellt"
+        },
+        .category = LOG_INFO,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_SV103,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "Security setting set to ‘Level 2’",
+            [LOG_LANGUAGE_DT_GERMAN] = "Sicherheitseinstellung auf ‚Stufe 2’ gestellt"
+        },
+        .category = LOG_INFO,
+        .flags = 0  // Not extended syslog
+    },
+    {
+        .code = LOG_EVENT_CODE_SV107,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "Exceeding TLS",
+            [LOG_LANGUAGE_DT_GERMAN] = "TLS Verbindung erfolglos"
+        },
+        .category = LOG_ERR,
+        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY  // Included in extended syslog only
+    },
+    {
+        .code = LOG_EVENT_CODE_SV108,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "Invalid NAPTR entry for SIPS",
+            [LOG_LANGUAGE_DT_GERMAN] = "Ungültiger NAPTR Eintrag für SIPS"
+        },
+        .category = LOG_ERR,
+        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY  // Included in extended syslog only
+    },
+    {
+        .code = LOG_EVENT_CODE_SV109,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "Invalid request URI like e.g. „sips:%s“ used",
+            [LOG_LANGUAGE_DT_GERMAN] = "Invalid request URI like e.g. „sips:%s“ used"
+        },
+        .category = LOG_ERR,
+        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY  // Included in extended syslog only
+    },
+    {
+        .code = LOG_EVENT_CODE_SV112,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "Registration error in position ‘Level 1’",
+            [LOG_LANGUAGE_DT_GERMAN] = "Registrierungsfehler in Stellung ‚Stufe 1’"
+        },
+        .category = LOG_ERR,
+        .flags = 0
+    },
+    {
+        .code = LOG_EVENT_CODE_SV113,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "Registration error in position ‘Level 2’",
+            [LOG_LANGUAGE_DT_GERMAN] = "Registrierungsfehler in Stellung ‚Stufe 2’"
+        },
+        .category = LOG_ERR,
+        .flags = 0
+    },
+    {
+        .code = LOG_EVENT_CODE_SV114,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "Relapse to ‘Stage 1’",
+            [LOG_LANGUAGE_DT_GERMAN] = "Rückfall zu ‚Stufe 1’"
+        },
+        .category = LOG_INFO,
+        .flags = 0
+    },
+    {
+        .code = LOG_EVENT_CODE_SV116,
+        .messages = {
+            [LOG_LANGUAGE_DT_ENGLISH] = "The secure Internet telephony connection (registration) for <%s> was successfully established: IP address (registrar): <%s>",
+            [LOG_LANGUAGE_DT_GERMAN] = "Die sichere Internet Telefonie Verbindung (Registrierung) für <%s>) wurde erfolgreich hergestellt: IP-Adresse (Registrar): <%s>"
+        },
+        .category = LOG_INFO,
+        .flags = 0
+    }
+    // More log entries can be added here...
+};
+
+static void get_current_timestamp(char *buffer, size_t size) {
+    time_t now = time(NULL);
+    struct tm *tstruct = localtime(&now);
+    strftime(buffer, size, "%Y-%m-%dT%H:%M:%S", tstruct);
+}
+
+static const char *translate_syslog_priority(int priority) {
+    switch (priority) {
+        case LOG_EMERG:
+            return "EMERG";
+        case LOG_ALERT:
+            return "ALERT";
+        case LOG_CRIT:
+            return "CRIT";
+        case LOG_ERR:
+            return "ERROR";
+        case LOG_WARNING:
+            return "WARN";
+        case LOG_NOTICE:
+            return "NOTICE";
+        case LOG_INFO:
+            return "INFO";
+        case LOG_DEBUG:
+            return "DEBUG";
+        default:
+            return "UNKNOWN";
+    }
+}
+
+// Function to set language
+void dt_set_log_language(LOG_LANGUAGE_DT language) {
+    if (language >= 0 && language < LOG_LANGUAGE_DT_MAX) {
+        current_language = language;
+    }
+}
+
+// Function to log a message based on the event code
+void ast_log_dt(LOG_EVENT_CODE_DT event_code, ...) {
+    const char *message = NULL;
+    uint32_t category = 0;
+    int flags = 0;
+    char full_message[1024];
+    char log_message[1280];
+    va_list args;
+
+    // Find the log message for the event code in the correct language
+    for (size_t i = 0; i < sizeof(log_message_table_dt) / sizeof(log_message_table_dt[0]); i++) {
+        if (log_message_table_dt[i].code == event_code) {
+            message = log_message_table_dt[i].messages[current_language];
+            category = log_message_table_dt[i].category;
+            flags = log_message_table_dt[i].flags;
+            break;
+        }
+    }
+
+    if (!message)
+        return;
+
+    // Format the log message with variable arguments
+    va_start(args, event_code);
+    vsnprintf(full_message, sizeof(full_message), message, args);
+    va_end(args);
+
+    // get additional data for the final log message
+    char timestamp[64];
+    get_current_timestamp(timestamp, sizeof(timestamp));
+    const char *log_level = translate_syslog_priority(category);
+    unsigned long tid = gettid();
+
+    snprintf(log_message, sizeof(log_message), "%s telekom: %s.%s [tid=%lu] %s",
+       timestamp, "VOIP", log_level, tid, full_message);
+
+    // Write the log message to a file
+    FILE *log_file = fopen(LOG_FILE_PATH, "a");
+    if (log_file) {
+        fprintf(log_file, "%s\n", log_message);
+        fclose(log_file);
+    }
+}
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 0a18a02282b0d294818ac04cd122523223c2f1db..37ccd1f3aa1a59d6260c4dc0c8ecfad0c4fde67d 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -2193,6 +2193,15 @@ int ast_rtp_instance_set_qos(struct ast_rtp_instance *instance, int tos, int cos
 	return res;
 }
 
+void ast_rtp_instance_set_rtcp_qos(struct ast_rtp_instance *instance, int tos, int cos, const char *desc)
+{
+	if (instance->engine->rtcp_qos) {
+		ao2_lock(instance);
+		instance->engine->rtcp_qos(instance, tos, cos, desc);
+		ao2_unlock(instance);
+	}
+}
+
 void ast_rtp_instance_stop(struct ast_rtp_instance *instance)
 {
 	if (instance->engine->stop) {
diff --git a/main/utils.c b/main/utils.c
index 1e5b8abdf575264d0277106a55f7395ecc886aaf..b29bb01361d7f86beb62a7cdbb671d45f5e28fc9 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -81,6 +81,7 @@ static char base64[64];
 static char base64url[64];
 static char b2a[256];
 static char b2a_url[256];
+static const char broadcast_path[] = "voice.sip.data";
 
 AST_THREADSTORAGE(inet_ntoa_buf);
 
@@ -3266,3 +3267,33 @@ void ast_ubus_free_context(struct ubus_context *ctx)
 	free(ctx->msgbuf.data);
 	free(ctx);
 }
+
+void ast_send_network_info_ubus_event(int enabled, char* protocol, char* addr_buf, int port)
+{
+	struct blob_buf blob;
+	struct ubus_context *ubusContext = NULL;
+
+	ubusContext = ubus_connect(NULL);
+	if(!ubusContext){
+		return;
+	}
+
+	memset(&blob, 0, sizeof(blob));
+	if(blob_buf_init(&blob, 0)) {
+		ast_ubus_free_context(ubusContext);
+		return;
+	}
+
+	blobmsg_add_u8(&blob, "enabled", enabled);
+	blobmsg_add_string(&blob, "protocol", protocol);
+	blobmsg_add_string(&blob, "ip", addr_buf);
+	blobmsg_add_u32(&blob, "port", port);
+
+	if(ubus_send_event(ubusContext, broadcast_path, blob.head) != UBUS_STATUS_OK)
+		ast_log(LOG_ERROR,"Error sending ubus message\n");
+
+	ast_ubus_free_context(ubusContext);
+	blob_buf_free(&blob);
+
+	return;
+}
diff --git a/menuselect/configure.ac b/menuselect/configure.ac
index 589f0828e07f79136bbeae5f581f93a65f2f29ae..096d42fec64074268fb98fdf428fbce8af95697d 100644
--- a/menuselect/configure.ac
+++ b/menuselect/configure.ac
@@ -92,14 +92,6 @@ else
 fi
 
 AST_PKG_CONFIG_CHECK([LIBXML2], [libxml-2.0])
-AST_EXT_TOOL_CHECK([LIBXML2], [xml2-config], , ,
-        [#include <libxml/tree.h>
-        #include <libxml/parser.h>],
-        [LIBXML_TEST_VERSION])
-
-if test "${PBX_LIBXML2}" != 1; then
-  AC_MSG_ERROR([Could not find required 'Libxml2' development package])
-fi
 
 AST_PKG_CONFIG_CHECK([GTK2], [gtk+-2.0])
 AC_SUBST(PBX_GTK2)
diff --git a/res/Makefile b/res/Makefile
index b8756b17538bc8ab9b1d8e503de625b65c588c95..36b55a1721540e7242b5154d52e6b3c779a149ed 100644
--- a/res/Makefile
+++ b/res/Makefile
@@ -19,6 +19,12 @@ all: _all
 
 include $(ASTTOPDIR)/Makefile.moddir_rules
 
+LIBS += ../main/dt_logger.o
+
+ifneq ($(DATAMODEL_UCI_PATH),)
+_ASTCFLAGS += -DUCI_CONFIG_DIR=\"$(DATAMODEL_UCI_PATH)\"
+endif
+
 ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
   # cygwin has some dependencies among res_ things.
   # We use order-only dependencies, and then add the libraries as required.
@@ -28,6 +34,7 @@ endif
 
 res_pjsip_pubsub.so: LIBS+=-lubus -lubox -lpicoevent
 res_pjsip_outbound_registration.so: LIBS+=-lubus -lubox -lpicoevent
+res_pjsip.so: LIBS+=-luci
 
 res_config_ldap.o: _ASTCFLAGS+=-DLDAP_DEPRECATED
 
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 9d786bde024ccb0b9499e4b0c58cb5af38c8322d..8c71b70bd663315b9fb9a2a9f873ad494e64a335 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -591,6 +591,7 @@ int ast_sip_dlg_set_transport(const struct ast_sip_endpoint *endpoint, pjsip_dia
 	pjsip_tpselector *selector)
 {
 	pjsip_sip_uri *uri;
+	struct ast_sip_transport_state *transport2 = NULL;
 	pjsip_tpselector sel = { .type = PJSIP_TPSELECTOR_NONE, };
 
 	uri = pjsip_uri_get_uri(dlg->target);
@@ -598,7 +599,31 @@ int ast_sip_dlg_set_transport(const struct ast_sip_endpoint *endpoint, pjsip_dia
 		selector = &sel;
 	}
 
-	ast_sip_set_tpselector_from_ep_or_uri(endpoint, uri, selector);
+	if (endpoint->register_transport &&
+	    !ast_sip_set_tpselector_from_transport_name(endpoint->register_transport, selector)) {
+
+		transport2 = ast_sip_get_transport_state(endpoint->register_transport);
+		ast_debug(5, "%s: transport '%s' is used\n", ast_sorcery_object_get_id(endpoint), endpoint->register_transport);
+
+	} else if (!ast_sip_set_tpselector_from_ep_or_uri(endpoint, uri, selector)) {
+		ast_debug(5, "%s: transport '%s' is used\n", ast_sorcery_object_get_id(endpoint), endpoint->transport);
+
+	} else {
+		ast_log(LOG_ERROR, "%s: Failed to set tpselector for transport '%s'\n", ast_sorcery_object_get_id(endpoint), endpoint->transport);
+
+		if (ast_strlen_zero(endpoint->transport2) ||
+		    ast_sip_set_tpselector_from_transport_name(endpoint->transport2, selector)) {
+			ast_log(LOG_ERROR, "%s: Failed to set tpselector for transport2\n", ast_sorcery_object_get_id(endpoint));
+			return -1;
+		}
+
+		ast_log(LOG_NOTICE, "%s: Switched to transport2 '%s'\n", ast_sorcery_object_get_id(endpoint), endpoint->transport2);
+
+		transport2 = ast_sip_get_transport_state(endpoint->transport2);
+	}
+
+	if (transport2)
+		uri->port = pj_sockaddr_get_port(&transport2->host);
 
 	pjsip_dlg_set_transport(dlg, selector);
 
@@ -703,6 +728,16 @@ static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *u
 	return 0;
 }
 
+int ast_sip_is_X_RDK_NTP_Synchronized(void)
+{
+	int sync = 0;
+
+	if (access("/tmp/ntpd_synchronized", F_OK) == 0)
+		sync = 1;
+
+	return sync;
+}
+
 int ast_sip_set_tpselector_from_transport(const struct ast_sip_transport *transport, pjsip_tpselector *selector)
 {
 	int res = 0;
@@ -776,9 +811,8 @@ int ast_sip_set_tpselector_from_ep_or_uri(const struct ast_sip_endpoint *endpoin
 {
 	char transport_name[128];
 
-	if (ast_sip_get_transport_name(endpoint, sip_uri, transport_name, sizeof(transport_name))) {
-		return 0;
-	}
+	if (ast_sip_get_transport_name(endpoint, sip_uri, transport_name, sizeof(transport_name)))
+		return -1;
 
 	return ast_sip_set_tpselector_from_transport_name(transport_name, selector);
 }
@@ -989,9 +1023,25 @@ static pjsip_dialog *create_dialog_uas(const struct ast_sip_endpoint *endpoint,
 	ast_assert(status != NULL);
 
 	contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
-	if (!contact_hdr || ast_sip_set_tpselector_from_ep_or_uri(endpoint, pjsip_uri_get_uri(contact_hdr->uri),
-		&selector)) {
+	if (!contact_hdr)
 		return NULL;
+
+	if (endpoint->register_transport &&
+	    !ast_sip_set_tpselector_from_transport_name(endpoint->register_transport, &selector)) {
+		ast_debug(5, "%s: transport '%s' is used\n", ast_sorcery_object_get_id(endpoint), endpoint->register_transport);
+
+	} else if (!ast_sip_set_tpselector_from_ep_or_uri(endpoint, pjsip_uri_get_uri(contact_hdr->uri), &selector)) {
+		ast_debug(5, "%s: transport '%s' is used\n", ast_sorcery_object_get_id(endpoint), endpoint->transport);
+	} else {
+		ast_log(LOG_ERROR, "%s: Failed to set tpselector for transport '%s'\n", ast_sorcery_object_get_id(endpoint), endpoint->transport);
+
+		if (ast_strlen_zero(endpoint->transport2) ||
+		    ast_sip_set_tpselector_from_transport_name(endpoint->transport2, &selector)) {
+			ast_log(LOG_ERROR, "%s: Failed to set tpselector for transport2\n", ast_sorcery_object_get_id(endpoint));
+			return NULL;
+		}
+
+		ast_log(LOG_NOTICE, "%s: Switched to transport2 '%s'\n", ast_sorcery_object_get_id(endpoint), endpoint->transport2);
 	}
 
 	transport = rdata->tp_info.transport;
@@ -1227,7 +1277,25 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
 		return -1;
 	}
 
-	ast_sip_set_tpselector_from_ep_or_uri(endpoint, pjsip_uri_get_uri(sip_uri), &selector);
+	if (endpoint->register_transport &&
+	    !ast_sip_set_tpselector_from_transport_name(endpoint->register_transport, &selector)) {
+		ast_debug(5, "%s: transport '%s' is used\n", ast_sorcery_object_get_id(endpoint), endpoint->register_transport);
+
+	} else if (!ast_sip_set_tpselector_from_ep_or_uri(endpoint, pjsip_uri_get_uri(sip_uri), &selector)) {
+		ast_debug(5, "%s: transport '%s' is used\n", ast_sorcery_object_get_id(endpoint), endpoint->transport);
+
+	} else {
+		ast_log(LOG_ERROR, "%s: Failed to set tpselector for transport '%s'\n", ast_sorcery_object_get_id(endpoint), endpoint->transport);
+
+		if (ast_strlen_zero(endpoint->transport2) ||
+		    ast_sip_set_tpselector_from_transport_name(endpoint->transport2, &selector)) {
+			ast_log(LOG_ERROR, "%s: Failed to set tpselector for transport2\n", ast_sorcery_object_get_id(endpoint));
+			pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
+			return -1;
+		}
+
+		ast_log(LOG_NOTICE, "%s: Switched to transport2 '%s'\n", ast_sorcery_object_get_id(endpoint), endpoint->transport2);
+	}
 
 	fromuser = endpoint ? (!ast_strlen_zero(endpoint->fromuser) ? endpoint->fromuser : ast_sorcery_object_get_id(endpoint)) : NULL;
 	if (sip_dialog_create_from(pool, &from, fromuser,
@@ -1241,7 +1309,7 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
 	}
 
 	if (pjsip_endpt_create_request(ast_sip_get_pjsip_endpoint(), method, &remote_uri,
-			&from, &remote_uri, &from, NULL, -1, NULL, tdata) != PJ_SUCCESS) {
+			&from, &remote_uri, &from, NULL, NULL, -1, NULL, tdata) != PJ_SUCCESS) {
 		ast_log(LOG_ERROR, "Unable to create outbound %.*s request to endpoint %s\n",
 				(int) pj_strlen(&method->name), pj_strbuf(&method->name),
 				endpoint ? ast_sorcery_object_get_id(endpoint) : "<none>");
@@ -1722,14 +1790,14 @@ static int check_request_status(struct send_request_data *req_data, pjsip_event
 	case 408:
 	case 503:
 	case 500:
-		if ((res = ast_sip_failover_request(tsx->last_tx))) {
-			tdata = tsx->last_tx;
+		//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);
-		}
+		//	pjsip_tx_data_add_ref(tdata);
+		//}
 		break;
 	}
 
@@ -2423,7 +2491,7 @@ const char *ast_sip_call_codec_pref_to_str(struct ast_flags pref)
 
 	if (ast_sip_call_codec_pref_test(pref, LOCAL) &&  ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, ALL)) {
 		value = "local";
-	} else if (ast_sip_call_codec_pref_test(pref, LOCAL) &&  ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, ALL)) {
+	} else if (ast_sip_call_codec_pref_test(pref, LOCAL) &&  ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, ALL)) {
 		value = "local_merge";
 	} else if (ast_sip_call_codec_pref_test(pref, LOCAL) &&  ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, FIRST)) {
 		value = "local_first";
@@ -2431,7 +2499,7 @@ const char *ast_sip_call_codec_pref_to_str(struct ast_flags pref)
 		value = "remote";
 	} else if (ast_sip_call_codec_pref_test(pref, REMOTE) &&  ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, ALL)) {
 		value = "remote_merge";
-	} else if (ast_sip_call_codec_pref_test(pref, REMOTE) &&  ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, FIRST)) {
+	} else if (ast_sip_call_codec_pref_test(pref, REMOTE) &&  ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, FIRST)) {
 		value = "remote_first";
 	} else {
 		value = "unknown";
@@ -2447,7 +2515,7 @@ int ast_sip_call_codec_str_to_pref(struct ast_flags *pref, const char *pref_str,
 	if (strcmp(pref_str, "local") == 0) {
 		ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_ALL);
 	} else if (is_outgoing && strcmp(pref_str, "local_merge") == 0) {
-		ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_ALL);
+		ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_ALL);
 	} else if (strcmp(pref_str, "local_first") == 0) {
 		ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_FIRST);
 	} else if (strcmp(pref_str, "remote") == 0) {
@@ -2455,7 +2523,7 @@ int ast_sip_call_codec_str_to_pref(struct ast_flags *pref, const char *pref_str,
 	} else if (is_outgoing && strcmp(pref_str, "remote_merge") == 0) {
 		ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_ALL);
 	} else if (strcmp(pref_str, "remote_first") == 0) {
-		ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_FIRST);
+		ast_set_flag(pref, AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_FIRST);
 	} else {
 		return -1;
 	}
@@ -2830,6 +2898,8 @@ const int ast_sip_hangup_sip2cause(int cause)
 	/* Possible values taken from causes.h */
 
 	switch(cause) {
+	case 200:       /* Answered elsewhere, rfc3326 */
+		return AST_CAUSE_ANSWERED_ELSEWHERE;
 	case 401:       /* Unauthorized */
 		return AST_CAUSE_CALL_REJECTED;
 	case 403:       /* Not found */
@@ -2976,6 +3046,7 @@ static int reload_configuration_task(void *obj)
 {
 	ast_res_pjsip_reload_configuration();
 	ast_res_pjsip_init_options_handling(1);
+	ast_sip_initialize_resolver();
 	ast_sip_initialize_dns();
 	return 0;
 }
@@ -3097,6 +3168,7 @@ pjsip_media_type pjsip_media_type_application_pidf_xml;
 pjsip_media_type pjsip_media_type_application_xpidf_xml;
 pjsip_media_type pjsip_media_type_application_cpim_xpidf_xml;
 pjsip_media_type pjsip_media_type_application_rlmi_xml;
+pjsip_media_type pjsip_media_type_application_3gpp_ims_xml;
 pjsip_media_type pjsip_media_type_application_simple_message_summary;
 pjsip_media_type pjsip_media_type_application_sdp;
 pjsip_media_type pjsip_media_type_multipart_alternative;
@@ -3130,6 +3202,7 @@ static int load_module(void)
 	pjsip_media_type_init2(&pjsip_media_type_application_xpidf_xml, "application", "xpidf+xml");
 	pjsip_media_type_init2(&pjsip_media_type_application_cpim_xpidf_xml, "application", "cpim-xpidf+xml");
 	pjsip_media_type_init2(&pjsip_media_type_application_rlmi_xml, "application", "rlmi+xml");
+	pjsip_media_type_init2(&pjsip_media_type_application_3gpp_ims_xml, "application", "3gpp-ims+xml");
 	pjsip_media_type_init2(&pjsip_media_type_application_sdp, "application", "sdp");
 	pjsip_media_type_init2(&pjsip_media_type_application_simple_message_summary, "application",	"simple-message-summary");
 	pjsip_media_type_init2(&pjsip_media_type_multipart_alternative, "multipart", "alternative");
diff --git a/res/res_pjsip/config_global.c b/res/res_pjsip/config_global.c
index 0148116f7f41c4a2e69d2027d91cefe755590efa..394e2e1f8bcbf2857e578924ca1c49cd7cab40aa 100644
--- a/res/res_pjsip/config_global.c
+++ b/res/res_pjsip/config_global.c
@@ -56,6 +56,8 @@
 #define DEFAULT_TASKPROCESSOR_OVERLOAD_TRIGGER TASKPROCESSOR_OVERLOAD_TRIGGER_GLOBAL
 #define DEFAULT_NOREFERSUB 1
 #define DEFAULT_ALL_CODECS_ON_EMPTY_REINVITE 0
+#define DEFAULT_MAX_SESSIONS_PER_LINE 2
+#define DEFAULT_MAX_SESSIONS_TOTAL 4
 
 /*!
  * \brief Cached global config object
@@ -126,7 +128,8 @@ struct global_config {
 	unsigned int all_codecs_on_empty_reinvite;
 	/*! Allow hash ('#') to appear in outgoing URIs. Default: NO */
 	unsigned int allow_tx_hash_in_uri;
-
+	unsigned int max_sessions_per_line;
+	unsigned int max_sessions_total;
 };
 
 static void global_destructor(void *obj)
@@ -577,6 +580,36 @@ unsigned int ast_sip_get_all_codecs_on_empty_reinvite(void)
 	return all_codecs_on_empty_reinvite;
 }
 
+unsigned int ast_sip_get_max_sessions_per_line(void)
+{
+	unsigned int max_sessions_per_line;
+	struct global_config *cfg;
+
+	cfg = get_global_cfg();
+	if (!cfg) {
+		return DEFAULT_MAX_SESSIONS_PER_LINE;
+	}
+
+	max_sessions_per_line = cfg->max_sessions_per_line;
+	ao2_ref(cfg, -1);
+	return max_sessions_per_line;
+}
+
+unsigned int ast_sip_get_max_sessions_total(void)
+{
+	unsigned int max_sessions_total;
+	struct global_config *cfg;
+
+	cfg = get_global_cfg();
+	if (!cfg) {
+		return DEFAULT_MAX_SESSIONS_TOTAL;
+	}
+
+	max_sessions_total = cfg->max_sessions_total;
+	ao2_ref(cfg, -1);
+	return max_sessions_total;
+}
+
 static int overload_trigger_handler(const struct aco_option *opt,
 	struct ast_variable *var, void *obj)
 {
@@ -791,6 +824,10 @@ int ast_sip_initialize_sorcery_global(void)
 		OPT_BOOL_T, 1, FLDSET(struct global_config, all_codecs_on_empty_reinvite));
 	ast_sorcery_object_field_register(sorcery, "global", "allow_tx_hash_in_uri", "no",
 		OPT_YESNO_T, 1, FLDSET(struct global_config, allow_tx_hash_in_uri));
+	ast_sorcery_object_field_register(sorcery, "global", "max_sessions_per_line",
+		__stringify(DEFAULT_MAX_SESSIONS_PER_LINE), OPT_UINT_T, 0, FLDSET(struct global_config, max_sessions_per_line));
+	ast_sorcery_object_field_register(sorcery, "global", "max_sessions_total",
+		__stringify(DEFAULT_MAX_SESSIONS_TOTAL), OPT_UINT_T, 0, FLDSET(struct global_config, max_sessions_total));
 
 	if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {
 		return -1;
diff --git a/res/res_pjsip/config_system.c b/res/res_pjsip/config_system.c
index eb8fd12d125145a8a5cdb63cfa19661d5c11d82c..f4c522856f37c1139e4259a99d0698107a4014fd 100644
--- a/res/res_pjsip/config_system.c
+++ b/res/res_pjsip/config_system.c
@@ -30,12 +30,18 @@
 
 #define TIMER_T1_MIN 100
 #define DEFAULT_TIMER_T1 500
+#define DEFAULT_TIMER_T2 4000
+#define DEFAULT_TIMER_T4 5000
 #define DEFAULT_TIMER_B 32000
 
 struct system_config {
 	SORCERY_OBJECT(details);
 	/*! Transaction Timer T1 value */
 	unsigned int timert1;
+	/*! Transaction Timer T2 value */
+	unsigned int timert2;
+	/*! Transaction Timer T4 value */
+	unsigned int timert4;
 	/*! Transaction Timer B value */
 	unsigned int timerb;
 	/*! Should we use short forms for headers? */
@@ -104,6 +110,8 @@ static int system_apply(const struct ast_sorcery *sorcery, void *obj)
 
 	pjsip_cfg()->tsx.t1 = system->timert1;
 	pjsip_cfg()->tsx.td = system->timerb;
+	ast_log(LOG_NOTICE, "Timer Setting to T1:%d, T2:%d, T4:%d, TB/TD:%d\n", system->timert1, system->timert2, system->timert4,system->timerb);
+	pjsip_tsx_set_timers(system->timert1, system->timert2, system->timert4, system->timerb);
 
 	pjsip_cfg()->endpt.follow_early_media_fork = system->follow_early_media_fork;
 #ifdef HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS
@@ -195,6 +203,10 @@ int ast_sip_initialize_system(void)
 	ast_sorcery_object_field_register(system_sorcery, "system", "type", "", OPT_NOOP_T, 0, 0);
 	ast_sorcery_object_field_register(system_sorcery, "system", "timer_t1", __stringify(DEFAULT_TIMER_T1),
 			OPT_UINT_T, 0, FLDSET(struct system_config, timert1));
+	ast_sorcery_object_field_register(system_sorcery, "system", "timer_t2", __stringify(DEFAULT_TIMER_T2),
+			OPT_UINT_T, 0, FLDSET(struct system_config, timert2));
+	ast_sorcery_object_field_register(system_sorcery, "system", "timer_t4", __stringify(DEFAULT_TIMER_T4),
+			OPT_UINT_T, 0, FLDSET(struct system_config, timert4));
 	ast_sorcery_object_field_register(system_sorcery, "system", "timer_b", __stringify(DEFAULT_TIMER_B),
 			OPT_UINT_T, 0, FLDSET(struct system_config, timerb));
 	ast_sorcery_object_field_register(system_sorcery, "system", "compact_headers", "no",
diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c
index c8655c2eea62059cb181c852ba402a351212ef36..103015cfd288e12a0ea20f11d176a47e966b69ab 100644
--- a/res/res_pjsip/config_transport.c
+++ b/res/res_pjsip/config_transport.c
@@ -799,7 +799,9 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
 				usleep(BIND_DELAY_US);
 			}
 
-			if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
+			if (!transport->enable)
+				ast_log(LOG_WARNING, "UDP transport is disabled\n");
+			else if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
 				res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(),
 					&temp_state->state->host.ipv4, NULL, transport->async_operations,
 					&temp_state->state->transport);
@@ -847,8 +849,12 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
 				usleep(BIND_DELAY_US);
 			}
 
-			res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg,
-				&temp_state->state->factory);
+			if (!transport->enable) {
+				ast_log(LOG_WARNING, "TCP transport is disabled\n");
+			} else {
+				res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg,
+					&temp_state->state->factory);
+			}
 		}
 	} else if (transport->type == AST_TRANSPORT_TLS) {
 #if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0
@@ -877,9 +883,13 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
 				usleep(BIND_DELAY_US);
 			}
 
-			res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls,
-				&temp_state->state->host, NULL, transport->async_operations,
-				&temp_state->state->factory);
+			if (!transport->enable) {
+				ast_log(LOG_WARNING, "TLS transport is disabled\n");
+			} else {
+				res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls,
+					&temp_state->state->host, NULL, transport->async_operations,
+					&temp_state->state->factory);
+			}
 		}
 
 		if (res == PJ_SUCCESS) {
@@ -894,6 +904,8 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
 			 * later to look up the transport state.
 			 */
 			sprintf(temp_state->state->factory->info, "%s", transport_id);
+		}else{
+			ast_log_dt(LOG_EVENT_CODE_SV107);
 		}
 #else
 		ast_log(LOG_ERROR, "Transport: %s: PJSIP has not been compiled with TLS transport support, ensure OpenSSL development packages are installed\n",
@@ -1033,6 +1045,38 @@ static int privkey_file_to_str(const void *obj, const intptr_t *args, char **buf
 	return 0;
 }
 
+static int transport_tls_timeout_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+	const struct ast_sip_transport *transport = obj;
+	RAII_VAR(struct ast_sip_transport_state *, state, find_or_create_temporary_state(transport), ao2_cleanup);
+
+	if (transport->type == AST_TRANSPORT_TLS) {
+
+		if (!state)
+			return -1;
+
+		state->tls.timeout.sec = atoi(var->value);
+	}
+
+	return 0;
+}
+
+static int transport_tls_timeout_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+	const struct ast_sip_transport *transport = obj;
+	RAII_VAR(struct ast_sip_transport_state *, state, find_state_by_transport(transport), ao2_cleanup);
+
+	if (transport->type == AST_TRANSPORT_TLS) {
+
+		if (!state)
+			return -1;
+
+		ast_asprintf(buf, "%u", state->tls.timeout.sec);
+	}
+
+	return 0;
+}
+
 /*! \brief Custom handler for turning a string protocol into an enum */
 static int transport_protocol_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
 {
@@ -1226,6 +1270,8 @@ static int transport_tls_method_handler(const struct aco_option *opt, struct ast
 		state->tls.method = PJSIP_TLSV1_1_METHOD;
 	} else if (!strcasecmp(var->value, "tlsv1_2")) {
 		state->tls.method = PJSIP_TLSV1_2_METHOD;
+	} else if (!strcasecmp(var->value, "tlsv1_3")) {
+		state->tls.method = PJSIP_TLSV1_3_METHOD;
 #endif
 	} else if (!strcasecmp(var->value, "sslv2")) {
 		state->tls.method = PJSIP_SSLV2_METHOD;
@@ -1730,6 +1776,7 @@ int ast_sip_initialize_sorcery_transport(void)
 
 	/* Normally type is a OPT_NOOP_T but we're using it to make sure that state is created */
 	ast_sorcery_object_field_register_custom(sorcery, "transport", "type", "", transport_state_init, NULL, NULL, 0, 0);
+	ast_sorcery_object_field_register(sorcery, "transport", "enable", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_transport, enable));
 	ast_sorcery_object_field_register_custom(sorcery, "transport", "protocol", "udp", transport_protocol_handler, transport_protocol_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register_custom(sorcery, "transport", "bind", "", transport_bind_handler, transport_bind_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register(sorcery, "transport", "async_operations", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, async_operations));
@@ -1738,6 +1785,7 @@ int ast_sip_initialize_sorcery_transport(void)
 	ast_sorcery_object_field_register_custom(sorcery, "transport", "ca_list_path", "", transport_tls_file_handler, ca_list_path_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register_custom(sorcery, "transport", "cert_file", "", transport_tls_file_handler, cert_file_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register_custom(sorcery, "transport", "priv_key_file", "", transport_tls_file_handler, privkey_file_to_str, NULL, 0, 0);
+	ast_sorcery_object_field_register_custom(sorcery, "transport", "tls_timeout", "", transport_tls_timeout_handler, transport_tls_timeout_to_str, NULL, 0, 0);
 
 	ast_sorcery_object_field_register(sorcery, "transport", "password", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, password));
 	ast_sorcery_object_field_register(sorcery, "transport", "external_signaling_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, external_signaling_address));
diff --git a/res/res_pjsip/pjsip_config.xml b/res/res_pjsip/pjsip_config.xml
index b8f6775e298bb6f84cde5b582d61c4ec6bbdad03..4f19f56b03e0b11d7709eb47829d0ff9e872cd71 100644
--- a/res/res_pjsip/pjsip_config.xml
+++ b/res/res_pjsip/pjsip_config.xml
@@ -651,6 +651,9 @@
 				<configOption name="send_pai" default="no">
 					<synopsis>Send the P-Asserted-Identity header</synopsis>
 				</configOption>
+				<configOption name="send_ppi" default="no">
+					<synopsis>Send the P-Preffered-Identity header</synopsis>
+				</configOption>
 				<configOption name="send_rpid" default="no">
 					<synopsis>Send the Remote-Party-ID header</synopsis>
 				</configOption>
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index bae02465da79052509ac52ff93c04806c4f18c04..e9bba44565f789d6c8398e142473b28fd9ee3440 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -47,6 +47,8 @@ struct sip_persistent_endpoint {
 	struct ast_endpoint *endpoint;
 };
 
+#define DEFAULT_MAX_SESSIONS_PER_CLIENT 0
+
 /*! \brief Container for persistent endpoint information */
 static struct ao2_container *persistent_endpoints;
 
@@ -1625,6 +1627,15 @@ static int sip_endpoint_apply_handler(const struct ast_sorcery *sorcery, void *o
 		}
 	}
 
+	if (!ast_strlen_zero(endpoint->transport)) {
+		if (!ast_strlen_zero(endpoint->transport2)) {
+			ast_log_dt(LOG_EVENT_CODE_SV102);
+		}else if (strncmp(endpoint->transport, "transport-tls", 13)) {
+			ast_log_dt(LOG_EVENT_CODE_SV101);
+		} else {
+			ast_log_dt(LOG_EVENT_CODE_SV103);
+		}
+	}
 	return 0;
 }
 
@@ -2197,6 +2208,8 @@ int ast_res_pjsip_initialize_configuration(void)
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "force_rport", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, nat.force_rport));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rewrite_contact", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, nat.rewrite_contact));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, transport));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "transport2", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, transport2));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "transport_emergency", "transport-udp", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, transport_emergency));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, outbound_proxy));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "moh_suggest", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, mohsuggest));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "100rel", "yes", prack_handler, prack_to_str, NULL, 0, 0);
@@ -2223,6 +2236,7 @@ int ast_res_pjsip_initialize_configuration(void)
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "trust_id_inbound", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.trust_inbound));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "trust_id_outbound", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.trust_outbound));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_pai", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_pai));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_ppi", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_ppi));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_rpid", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_rpid));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rpid_immediate", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.rpid_immediate));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_diversion", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, id.send_diversion));
@@ -2341,6 +2355,8 @@ int ast_res_pjsip_initialize_configuration(void)
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "security_mechanisms", "", security_mechanism_handler, security_mechanism_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "security_negotiation", "no", security_negotiation_handler, security_negotiation_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_aoc", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, send_aoc));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "Emergency", "false", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, Emergency));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "max_sessions", __stringify(DEFAULT_MAX_SESSIONS_PER_CLIENT), OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, max_sessions));
 
 	if (ast_sip_initialize_sorcery_transport()) {
 		ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index 092e012a773b102bf4945f6ba2b6e6964e11b8fc..87549a6eb4ab9e4915b310f1d123668b6dc2f170 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -19,6 +19,7 @@
 #include "asterisk.h"
 
 #include <pjsip.h>
+#include <uci.h>
 
 #include "asterisk/res_pjsip.h"
 #include "asterisk/acl.h"
@@ -27,6 +28,11 @@
 #include "asterisk/threadpool.h"
 #include "asterisk/res_pjsip_cli.h"
 
+#ifndef UCI_CONFIG_DIR
+#define UCI_CONFIG_DIR "/etc/config/"
+#endif
+static const char uciStrConfig[] = "asterisk";
+
 static int distribute(void *data);
 static pj_bool_t distributor(pjsip_rx_data *rdata);
 static pj_status_t record_serializer(pjsip_tx_data *tdata);
@@ -877,11 +883,89 @@ static int apply_endpoint_contact_acl(pjsip_rx_data *rdata, struct ast_sip_endpo
 	return forbidden;
 }
 
+static void disable_registrar_uci(char* registrar)
+{
+	struct uci_context *context = NULL;
+	struct uci_package *package = NULL;
+	struct uci_section *section = NULL;
+	struct uci_element *element = NULL;
+	struct uci_ptr ptr = {0};
+	char option_enable[32];
+	const char *ext_number = NULL, *ext_provider = NULL;
+	int res;
+	FILE *fp = NULL;
+	char file_name[32] = "";
+	char package_path[32];
+
+	context = uci_alloc_context();
+	if (!context) {
+		ast_log(LOG_ERROR, "failed to alloc context %s\n");
+		return;
+	}
+
+	uci_set_confdir(context, UCI_CONFIG_DIR);
+	snprintf(package_path, sizeof(package_path), "%s%s", UCI_CONFIG_DIR, uciStrConfig);
+	res = uci_load(context, package_path, &package);
+	if (res != 0 || !package) {
+		ast_log(LOG_ERROR, "failed to load load uci, conf dir: %s, conf file: %s\n", UCI_CONFIG_DIR, uciStrConfig);
+	} else {
+		uci_foreach_element(&package->sections, element) {
+			section = uci_to_section(element);
+			if (strcasecmp(section->type, "extension") == 0) {
+				ext_number = uci_lookup_option_string(context, section, "extension_number");
+				if (ext_number && strcasecmp(ext_number, registrar) == 0) {
+					ext_provider = uci_lookup_option_string(context, section, "provider");
+					if (ext_provider) {
+						snprintf(option_enable, sizeof(option_enable), "asterisk.%s.enable", ext_provider);
+						if (uci_lookup_ptr(context, &ptr, option_enable, true)) {
+							ast_log(LOG_ERROR, "Failed to find section %s\n", option_enable);
+						} else {
+							ptr.value = "2";
+							if (uci_set(context, &ptr) != UCI_OK) {
+								ast_log(LOG_ERROR, "Failed to uci_set, package: %s\n", ptr.package);
+							} else {
+								if (uci_commit(context, &ptr.p, true) != UCI_OK) {
+									ast_log(LOG_ERROR, "Failed to uci_commit\n");
+								} else {
+									snprintf(file_name, sizeof(file_name), "/tmp/registrar.%s", ext_provider);
+									break;
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	// Free the memory
+	if (package)
+		uci_unload(context, package);
+	if (context)
+		uci_free_context(context);
+
+	// create /tmp/registrar.ext_provider file and reload asterisk
+	// reload will trigger config_asterisk.sh re-run
+	if (file_name[0] != '\0') {
+		fp = fopen(file_name, "w");
+		if (fp) {
+			fclose(fp);
+		}
+		system("systemctl reload asterisk");
+	}
+}
+
 static pj_bool_t authenticate(pjsip_rx_data *rdata)
 {
+	pjsip_sip_uri *client_uri = NULL;
+	pjsip_contact_hdr *contact_hdr = NULL;
 	RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
 	int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD;
 
+	contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
+	if(contact_hdr){
+		client_uri = pjsip_uri_get_uri(contact_hdr->uri);
+	}
+
 	ast_assert(endpoint != NULL);
 
 	if (is_ack) {
@@ -891,6 +975,7 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata)
 	if (ast_sip_requires_authentication(endpoint, rdata)) {
 		pjsip_tx_data *tdata;
 		struct unidentified_request *unid;
+		char client[24] = {0};
 
 		pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 401, NULL, &tdata);
 		switch (ast_sip_check_authentication(endpoint, rdata, tdata)) {
@@ -913,6 +998,13 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata)
 		case AST_SIP_AUTHENTICATION_FAILED:
 			log_failed_request(rdata, "Failed to authenticate", 0, 0);
 			ast_sip_report_auth_failed_challenge_response(endpoint, rdata);
+			struct pjsip_status_line status = tdata->msg->line.status;
+			/* IPX101 and disable registrar */
+			ast_log_dt(LOG_EVENT_CODE_IPX101, (int) client_uri->user.slen, client_uri->user.ptr, status.code, (int) client_uri->host.slen, client_uri->host.ptr);
+			ast_copy_pj_str(client, &client_uri->user, sizeof(client));
+			if (client[0] != '\0') {
+				disable_registrar_uci(client);
+			}
 			if (pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL) != PJ_SUCCESS) {
 				pjsip_tx_data_dec_ref(tdata);
 			}
diff --git a/res/res_pjsip/pjsip_message_ip_updater.c b/res/res_pjsip/pjsip_message_ip_updater.c
index 4a6bd0ea9be7a8373a73a387527715b6bc1b46d2..79b719afd204ab0aec4fc788f56a1fdcd59da2a1 100644
--- a/res/res_pjsip/pjsip_message_ip_updater.c
+++ b/res/res_pjsip/pjsip_message_ip_updater.c
@@ -327,6 +327,16 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata)
 		}
 	}
 
+	if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+		cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+		if(!pj_strcmp2(&cseq->method.name, "REGISTER")) {
+			pjsip_sip_uri *uri = (pjsip_sip_uri*) tdata->msg->line.req.uri;
+			uri->port = 0; // remove the un-wanted port to the REQUEST URI that been add from registration_client_send due to #15901
+			pjsip_tx_data_invalidate_msg(tdata);
+		}
+	}
+
+
 	/* If there's no body in the tdata we can just return here. */
 	if (!tdata->msg->body) {
 		return PJ_SUCCESS;
diff --git a/res/res_pjsip/pjsip_resolver.c b/res/res_pjsip/pjsip_resolver.c
index 5ea8d0dc7903c1505fc9c3c3fec398050745b31e..0a98b0435781378020f40951f2f6c128653de4b0 100644
--- a/res/res_pjsip/pjsip_resolver.c
+++ b/res/res_pjsip/pjsip_resolver.c
@@ -24,6 +24,7 @@
 #include <arpa/nameser.h>
 
 #include "asterisk/astobj2.h"
+#include "asterisk/cli.h"
 #include "asterisk/dns_core.h"
 #include "asterisk/dns_query_set.h"
 #include "asterisk/dns_srv.h"
@@ -32,6 +33,7 @@
 #include "include/res_pjsip_private.h"
 #include "asterisk/taskprocessor.h"
 #include "asterisk/threadpool.h"
+#include "asterisk/dns_internal.h"
 
 #ifdef HAVE_PJSIP_EXTERNAL_RESOLVER
 
@@ -41,48 +43,194 @@ struct sip_target {
 	pjsip_transport_type_e transport;
 	/*! \brief The port */
 	int port;
+	int priority; // SRV priority
+	int weight;   // SRV weight
 };
 
 /*! \brief The vector used for current targets */
 AST_VECTOR(targets, struct sip_target);
 
-/*! \brief Structure which contains latest resolved server addresses for tracking */
-struct host_track_entry {
-	struct {
-		char *host;
-		int port;
-		unsigned flag;
-		pjsip_transport_type_e type;
-	} target;
+/*! \brief Structure which keeps track of resolution */
+struct sip_resolve {
+	pjsip_host_info target;
+	/*! \brief Addresses currently being resolved, indexed based on index of queries in query set */
+	struct targets resolving;
+	/*! \brief Active queries */
+	struct ast_dns_query_set *queries;
+	/*! \brief Serializer to run async callback into pjlib. */
+	struct ast_taskprocessor *serializer;
+	/*! \brief Callback to invoke upon completion */
+	pjsip_resolver_callback *callback;
+	struct host_cache_entry *host_cache;
+	AST_LIST_ENTRY(sip_resolve) list;
+	/*! \brief User provided data */
+	void *token;
+};
+
+/*! \brief Our own defined transports, reduces the size of sip_available_transports */
+enum sip_resolver_transport {
+	SIP_RESOLVER_TRANSPORT_UDP,
+	SIP_RESOLVER_TRANSPORT_TCP,
+	SIP_RESOLVER_TRANSPORT_TLS,
+	SIP_RESOLVER_TRANSPORT_UDP6,
+	SIP_RESOLVER_TRANSPORT_TCP6,
+	SIP_RESOLVER_TRANSPORT_TLS6,
+};
+
+struct dns_cache_record {
+	char *name;
+	struct sip_target target;
+	time_t expiry;
+	struct ast_dns_record *dns_record;
+	AST_LIST_ENTRY(dns_cache_record) list;
+};
+
+struct host_cache_entry {
+	pjsip_host_info target;
+	int querying;
 	pjsip_server_addresses addresses;
 	unsigned cur_addr;
-	AST_LIST_ENTRY(host_track_entry) next;
+	/*! \brief list of cached DNS records */
+	AST_LIST_HEAD_NOLOCK(naptr_records, dns_cache_record) naptr_records;
+	AST_LIST_HEAD_NOLOCK(srv_records, dns_cache_record) srv_records;
+	AST_LIST_HEAD_NOLOCK(a_records, dns_cache_record) a_records;
+	AST_LIST_HEAD_NOLOCK(resolves, sip_resolve) resolves;
+	AST_LIST_ENTRY(host_cache_entry) list;
 };
 
-/*! \brief the list of all tracked hosts */
-AST_LIST_HEAD(, host_track_entry) sip_resolve_track;
+AST_LIST_HEAD(, host_cache_entry) dns_host_cache;
 
-/*! \brief compare the target host info with track record */
-static int host_track_target_cmp(pjsip_host_info *host, struct host_track_entry *hostc)
+/*! \brief compare the target host info with cache entry */
+static int host_target_cmp(const pjsip_host_info *host1, const struct pjsip_host_info *host2)
 {
-	if(!host || !hostc)
+	if (!host1 || !host2)
 		return -1;
 
-	if (host->type != hostc->target.type)
+	if (host1->type != host2->type)
 		return -1;
 
-	if (host->flag != hostc->target.flag)
+	if (host1->flag != host2->flag)
 		return -1;
 
-	if (host->addr.port != hostc->target.port)
+	if (host1->addr.port != host2->addr.port)
 		return -1;
 
-	if (pj_strcmp2(&host->addr.host, hostc->target.host))
+	if (pj_strcmp(&host1->addr.host, &host2->addr.host))
 		return -1;
 
 	return 0;
 }
 
+static struct host_cache_entry * get_host_cache_entry(const pjsip_host_info *target)
+{
+	struct host_cache_entry *host = NULL;
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&dns_host_cache, host, list) {
+		if (!host_target_cmp(target, &host->target)) {
+			return host;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	return NULL;
+}
+
+/* the host cache is valid when none of cached DNS records is expired */
+static int is_host_cache_valid(struct host_cache_entry *host)
+{
+	struct dns_cache_record *record;
+	time_t now = time(NULL);
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&host->naptr_records, record, list) {
+		if (record->expiry <= now) {
+			ast_debug(5, "NAPTR record '%s' (%s) is not valid after %s\n", record->name,
+				ast_dns_naptr_get_replacement(record->dns_record), ctime(&record->expiry));
+			return 0;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&host->srv_records, record, list) {
+		if (record->expiry <= now) {
+			ast_debug(5, "SRV record '%s' (%s) is not valid after %s\n", record->name,
+				ast_dns_srv_get_host(record->dns_record), ctime(&record->expiry));
+			return 0;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&host->a_records, record, list) {
+		if (record->expiry <= now) {
+			ast_debug(5, "A/AAAA record '%s' (0x%x) is not valid after %s\n", record->name,
+				*(unsigned *) ast_dns_record_get_data(record->dns_record), ctime(&record->expiry));
+			return 0;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	ast_debug(3, "host cache '%s:%s' is valid\n",
+			host->target.addr.host.ptr,
+			pjsip_transport_get_type_desc(host->target.type));
+
+	return 1;
+}
+
+/* the host cache is expired when all cached DNS records are expired */
+static int is_host_cache_expired(struct host_cache_entry *host)
+{
+	struct dns_cache_record *record;
+	time_t now = time(NULL);
+	int expired = 1;
+
+	if (host->querying) {
+		ast_debug(5, "host cache '%s' is updating\n", host->target.addr.host.ptr);
+		return 0;
+	}
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&host->naptr_records, record, list) {
+		if (record->expiry > now) {
+			ast_debug(5, "NAPTR record '%s' (%s) is not expired before %s\n", record->name,
+				ast_dns_naptr_get_replacement(record->dns_record), ctime(&record->expiry));
+			expired = 0;
+		} else {
+			ast_debug(5, "NAPTR record '%s' (%s) is expired at %s\n", record->name,
+				ast_dns_naptr_get_replacement(record->dns_record), ctime(&record->expiry));
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&host->srv_records, record, list) {
+		if (record->expiry > now) {
+			ast_debug(5, "SRV record '%s' (%s) is not expired before %s\n", record->name,
+				ast_dns_srv_get_host(record->dns_record), ctime(&record->expiry));
+			expired = 0;
+		} else {
+			ast_debug(5, "SRV record '%s' (%s) is expired at %s\n", record->name,
+				ast_dns_srv_get_host(record->dns_record), ctime(&record->expiry));
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&host->a_records, record, list) {
+		if (record->expiry > now) {
+			ast_debug(5, "A/AAAA record '%s' (0x%x) is not expired before %s\n", record->name,
+				*(unsigned *) ast_dns_record_get_data(record->dns_record), ctime(&record->expiry));
+			expired = 0;
+		} else {
+			ast_debug(5, "A/AAAA record '%s' (0x%x) is expired at %s\n", record->name,
+				*(unsigned *) ast_dns_record_get_data(record->dns_record), ctime(&record->expiry));
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	ast_debug(3, "host cache '%s:%s' is%s expired\n",
+			host->target.addr.host.ptr,
+			pjsip_transport_get_type_desc(host->target.type),
+			!expired ? " not" : "");
+
+	return expired;
+}
+
 /*! \brief compare 2 entries in 2 pjsip_server_addresses */
 static int pjsip_server_addresses_entry_cmp(
 	pjsip_server_addresses *addr1, int i1,
@@ -130,154 +278,180 @@ static int pjsip_server_addresses_cmp(pjsip_server_addresses *addr1, pjsip_serve
 	return 0;
 }
 
-/*! \brief update the tracked pjsip_server_addresses with new resolve
- *  Note: The tracked and new resolved server addresses must same, but maybe in different order.
- *  The order of resolved addresses may be round-shifted, to make the current using server address
- *  always be the first entry.
- */
-static void pjsip_server_addresses_track_update(struct host_track_entry *host, pjsip_server_addresses *addrs)
+static pjsip_server_addresses *get_cached_host_addrs(struct host_cache_entry *host)
 {
-	int c, i;
-	char buffer[512];
+	if (host->querying || !is_host_cache_valid(host))
+		return NULL;
+
+	// current addr must be the head of addresses
+	if (host->cur_addr != 0) {
+		pjsip_server_addresses addrs;
+		int c = host->cur_addr;
+		int i;
+
+		pj_memcpy(&addrs, &host->addresses, sizeof(addrs));
+		for (i = 0; i < addrs.count; i++) {
+			pj_memcpy(&host->addresses.entry[i], &addrs.entry[c], sizeof(addrs.entry[0]));
+			if (++c >= addrs.count)
+				c = 0;
+		}
 
-	// Get the index of current server address in the new address table
-	for (c = 0; c < addrs->count; c++) {
-		if (!pjsip_server_addresses_entry_cmp(&host->addresses, host->cur_addr, addrs, c))
-			break;
+		host->cur_addr = 0;
 	}
 
-	// The current server address is the first entry in the new table
-	if (c == 0) {
-		pj_memcpy(&host->addresses, addrs, sizeof(*addrs));
-		host->cur_addr = 0;
-		pj_sockaddr_print(&host->addresses.entry[0].addr, buffer, sizeof(buffer), 3);
+	return &host->addresses;
+}
 
-		for (i = 0; i < addrs->count; i++) {
-			pj_sockaddr_print(&host->addresses.entry[i].addr, buffer, sizeof(buffer), 3);
-			ast_debug(5, "host track '%s': address[%d] = '%s'\n", host->target.host, i, buffer);
-		}
-		return;
+static void host_cache_record_add(struct host_cache_entry *host,
+	struct ast_dns_query *query,
+	struct ast_dns_record *dns_record,
+	struct sip_target *target)
+{
+	struct dns_cache_record *record;
+
+	record = ast_calloc(1, sizeof(*record));
+
+	record->expiry = time(NULL) + ast_dns_record_get_ttl(dns_record);
+	record->name = ast_strdup(ast_dns_query_get_name(query));
+	record->dns_record = dns_record;
+
+	if (target) {
+		record->target.transport = target->transport;
+		record->target.port = target->port;
+		record->target.priority = target->priority;
+		record->target.weight = target->weight;
 	}
 
-	// Re-order the new table, to make sure the current server address is the first entry
-	for (i = 0; i < addrs->count; i++) {
-		pj_memcpy(&host->addresses.entry[i], &addrs->entry[c], sizeof(addrs->entry[0]));
-		if (++c >= addrs->count)
-			c = 0;
+	switch(ast_dns_record_get_rr_type(dns_record)) {
+		case T_A:
+		case T_AAAA:
+			AST_LIST_INSERT_TAIL(&host->a_records, record, list);
+			ast_debug(5, "A record %s is add to host cache '%s'\n", record->name, host->target.addr.host.ptr);
+			break;
+		case T_SRV:
+			ast_debug(5, "SRV record %s is add to host cache '%s'\n", record->name, host->target.addr.host.ptr);
+			AST_LIST_INSERT_TAIL(&host->srv_records, record, list);
+			break;
+		case T_NAPTR:
+			ast_debug(5, "NAPTR record %s is add to host cache '%s'\n", record->name, host->target.addr.host.ptr);
+			AST_LIST_INSERT_TAIL(&host->naptr_records, record, list);
+			break;
+	}
+}
+
+static struct host_cache_entry * host_cache_add(const pjsip_host_info *target)
+{
+	struct host_cache_entry *host;
+	char buf[NI_MAXHOST];
+
+	host = ast_calloc(1, sizeof(*host));
+	if (host) {
+		ast_copy_pj_str(buf, &target->addr.host, sizeof(buf));
+
+		host->target.addr.host.ptr = ast_strdup(buf);
+		host->target.addr.host.slen = strlen(buf);
+		host->target.addr.port = target->addr.port;
+		host->target.type = target->type;
+		host->target.flag = target->flag;
 
-		pj_sockaddr_print(&host->addresses.entry[i].addr, buffer, sizeof(buffer), 3);
-		ast_debug(5, "host track '%s': address[%d] = '%s'\n", host->target.host, i, buffer);
+		AST_LIST_HEAD_INIT_NOLOCK(&host->a_records);
+		AST_LIST_HEAD_INIT_NOLOCK(&host->srv_records);
+		AST_LIST_HEAD_INIT_NOLOCK(&host->naptr_records);
+		AST_LIST_HEAD_INIT_NOLOCK(&host->resolves);
+
+		AST_LIST_INSERT_HEAD(&dns_host_cache, host, list);
+
+		ast_debug(3, "Add target '%s:%s' to host cache\n",
+				host->target.addr.host.ptr,
+				pjsip_transport_get_type_desc(target->type));
 	}
-	pj_memcpy(addrs, &host->addresses, sizeof(*addrs));
-	host->cur_addr = 0;
+
+	return host;
 }
 
-/*! \brief make or update track record for the resolve target */
-static void sip_resolve_target_track(pjsip_host_info *target, pjsip_server_addresses *addresses)
+static void host_cache_add_resolve(struct host_cache_entry *host, struct sip_resolve *resolve)
 {
-	struct host_track_entry *host = NULL;
-	struct host_track_entry *track_host = NULL;
+	resolve->host_cache = host;
+	AST_LIST_INSERT_TAIL(&host->resolves, resolve, list);
+}
 
-	if (!target || !addresses)
-		return;
+static void host_cache_free(struct host_cache_entry *host)
+{
+	struct dns_cache_record *record;
 
-	AST_LIST_LOCK(&sip_resolve_track);
+	ast_debug(3, "Remove target '%s:%s' from host cache\n",
+			host->target.addr.host.ptr,
+			pjsip_transport_get_type_desc(host->target.type));
 
-	AST_LIST_TRAVERSE_SAFE_BEGIN(&sip_resolve_track, host, next) {
-		if (!host_track_target_cmp(target, host)) {
-			ast_debug(5, "host track '%s': record found\n", host->target.host);
+	ast_free(host->target.addr.host.ptr);
 
-			// Remove the track entry if the addresses are different
-			if (pjsip_server_addresses_cmp(&host->addresses, addresses)) {
-				ast_debug(5, "host track '%s': different in the new resolve. record removed\n", host->target.host);
-				// Free the track entry
-				AST_LIST_REMOVE_CURRENT(next);
-				ast_free(host->target.host);
-				ast_free(host);
-				break;
-			}
+	while ((record = AST_LIST_REMOVE_HEAD(&host->a_records, list))) {
+		ast_free(record->name);
+		ast_free(record->dns_record);
+		ast_free(record);
+	}
 
-			// The new addresses are same as tracked, but maybe in different order
-			pjsip_server_addresses_track_update(host, addresses);
+	while ((record = AST_LIST_REMOVE_HEAD(&host->srv_records, list))) {
+		ast_free(record->name);
+		ast_free(record->dns_record);
+		ast_free(record);
+	}
 
-			track_host = host;
-			break;
-		}
+	while ((record = AST_LIST_REMOVE_HEAD(&host->naptr_records, list))) {
+		ast_free(record->name);
+		ast_free(record->dns_record);
+		ast_free(record);
 	}
-	AST_LIST_TRAVERSE_SAFE_END;
+}
 
-	// Make a new track entry
-	if (!track_host) {
-		track_host = ast_calloc(1, sizeof(*track_host));
-		if (track_host) {
-			track_host->target.host = ast_strndup(pj_strbuf(&target->addr.host), pj_strlen(&target->addr.host));
-			track_host->target.port = target->addr.port;
-			track_host->target.type = target->type;
-			track_host->target.flag = target->flag;
+// clean up all expired cache entries
+static void host_cache_cleanup(void)
+{
+	struct host_cache_entry *host = NULL;
 
-			pj_memcpy(&track_host->addresses, addresses, sizeof(*addresses));
-			track_host->cur_addr = 0;
+	AST_LIST_LOCK(&dns_host_cache);
 
-			AST_LIST_INSERT_HEAD(&sip_resolve_track, track_host, next);
-			ast_debug(5, "host track '%s': record added\n", track_host->target.host);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&dns_host_cache, host, list) {
+		if (!host->querying && is_host_cache_expired(host)) {
+			AST_LIST_REMOVE_CURRENT(list);
+			host_cache_free(host);
 		}
 	}
+	AST_LIST_TRAVERSE_SAFE_END;
 
-	AST_LIST_UNLOCK(&sip_resolve_track);
+	AST_LIST_UNLOCK(&dns_host_cache);
 }
 
 /*! \brief update current used address in the host track record */
 void ast_sip_resolve_track_cur_addr(pjsip_tx_data *tdata)
 {
-	struct host_track_entry *host = NULL;
+	struct host_cache_entry *host = NULL;
+	pjsip_transport_type_e transport_type;
 
 	if (!tdata || !pj_strlen(&tdata->dest_info.name))
 		return;
 
-	AST_LIST_LOCK(&sip_resolve_track);
+	transport_type = pjsip_transport_get_type_from_flag(tdata->tp_info.transport->flag);
+
+	AST_LIST_LOCK(&dns_host_cache);
 
-	AST_LIST_TRAVERSE(&sip_resolve_track, host, next) {
-		if (!pj_strcmp2(&tdata->dest_info.name, host->target.host) &&
+	AST_LIST_TRAVERSE(&dns_host_cache, host, list) {
+		if (!pj_strcmp(&tdata->dest_info.name, &host->target.addr.host) &&
+		    host->target.type == transport_type &&
 		    !pjsip_server_addresses_cmp(&host->addresses, &tdata->dest_info.addr)) {
 
 			if(host->cur_addr != tdata->dest_info.cur_addr) {
 				host->cur_addr = tdata->dest_info.cur_addr;
-				ast_debug(5, "host track '%s': current addr changed to '%d'.\n", host->target.host, host->cur_addr);
+				ast_debug(5, "host track %s: current addr changed to '%d'.\n",
+						host->target.addr.host.ptr, host->cur_addr);
 			}
 			break;
 		}
 	}
 
-	AST_LIST_UNLOCK(&sip_resolve_track);
+	AST_LIST_UNLOCK(&dns_host_cache);
 }
 
-/*! \brief Structure which keeps track of resolution */
-struct sip_resolve {
-	pjsip_host_info target;
-	/*! \brief Addresses currently being resolved, indexed based on index of queries in query set */
-	struct targets resolving;
-	/*! \brief Active queries */
-	struct ast_dns_query_set *queries;
-	/*! \brief Current viable server addresses */
-	pjsip_server_addresses addresses;
-	/*! \brief Serializer to run async callback into pjlib. */
-	struct ast_taskprocessor *serializer;
-	/*! \brief Callback to invoke upon completion */
-	pjsip_resolver_callback *callback;
-	/*! \brief User provided data */
-	void *token;
-};
-
-/*! \brief Our own defined transports, reduces the size of sip_available_transports */
-enum sip_resolver_transport {
-	SIP_RESOLVER_TRANSPORT_UDP,
-	SIP_RESOLVER_TRANSPORT_TCP,
-	SIP_RESOLVER_TRANSPORT_TLS,
-	SIP_RESOLVER_TRANSPORT_UDP6,
-	SIP_RESOLVER_TRANSPORT_TCP6,
-	SIP_RESOLVER_TRANSPORT_TLS6,
-};
-
 /*! \brief Available transports on the system */
 static int sip_available_transports[] = {
 	/* This is a list of transports with whether they are available as a valid transport
@@ -355,11 +529,20 @@ static int sip_transport_is_available(enum pjsip_transport_type_e transport)
  * \retval 0 success
  * \retval -1 failure
  */
-static int sip_resolve_add(struct sip_resolve *resolve, const char *name, int rr_type, int rr_class, pjsip_transport_type_e transport, int port)
+static int sip_resolve_add(struct sip_resolve *resolve,
+	const char *name,
+	int rr_type,
+	int rr_class,
+	pjsip_transport_type_e transport,
+	int port,
+	int priority,
+	int weight)
 {
 	struct sip_target target = {
 		.transport = transport,
 		.port = port,
+		.priority = priority,
+		.weight = weight,
 	};
 
 	if (!resolve->queries) {
@@ -383,6 +566,128 @@ static int sip_resolve_add(struct sip_resolve *resolve, const char *name, int rr
 	return ast_dns_query_set_add(resolve->queries, name, rr_type, rr_class);
 }
 
+static void host_cache_sort_a_records(struct host_cache_entry *host)
+{
+	struct a_records newlist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+	struct dns_cache_record *current;
+
+	if (DEBUG_ATLEAST(8)) {
+		AST_LIST_TRAVERSE(&host->a_records, current, list) {
+			ast_debug(8, "'%s': addr=0x%x, pri=%d, weight=%d\n", current->name,
+				*(unsigned *) ast_dns_record_get_data(current->dns_record),
+				current->target.priority, current->target.weight);
+		}
+	}
+
+	while (AST_LIST_FIRST(&host->a_records)) {
+		struct a_records temp_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+		int cur_pri = ((struct dns_cache_record *)(AST_LIST_FIRST(&host->a_records)))->target.priority;
+
+		// Get the lowest priority
+		if (cur_pri) {
+			AST_LIST_TRAVERSE(&host->a_records, current, list) {
+				if (current->target.priority < cur_pri)
+					cur_pri = current->target.priority;
+			}
+		}
+
+		/* Find all records which match this priority */
+		AST_LIST_TRAVERSE_SAFE_BEGIN(&host->a_records, current, list) {
+			if (current->target.priority != cur_pri)
+				continue;
+
+			AST_LIST_REMOVE_CURRENT(list);
+
+			/* Records with a weight of zero must always be at the head */
+			if (current->target.weight == 0) {
+				AST_LIST_INSERT_HEAD(&temp_list, current, list);
+			} else {
+				AST_LIST_INSERT_TAIL(&temp_list, current, list);
+			}
+		}
+		AST_LIST_TRAVERSE_SAFE_END;
+
+		while (AST_LIST_FIRST(&temp_list)) {
+			int cur_weight = ((struct dns_cache_record *)(AST_LIST_FIRST(&temp_list)))->target.weight;
+
+			AST_LIST_TRAVERSE(&temp_list, current, list) {
+				if (current->target.weight > cur_weight)
+					cur_weight = current->target.weight;
+			}
+
+			if (cur_weight == 0) {
+				AST_LIST_APPEND_LIST(&newlist, &temp_list, list);
+				break;
+			}
+
+			AST_LIST_TRAVERSE_SAFE_BEGIN(&temp_list, current, list) {
+				if (current->target.weight == cur_weight)
+					AST_LIST_MOVE_CURRENT(&newlist, list);
+			}
+			AST_LIST_TRAVERSE_SAFE_END;
+		}
+	}
+
+	AST_LIST_APPEND_LIST(&host->a_records, &newlist, list);
+
+	if (DEBUG_ATLEAST(8)) {
+		AST_LIST_TRAVERSE(&host->a_records, current, list) {
+			ast_debug(8, "'%s': addr=0x%x, pri=%d, weight=%d\n", current->name,
+				*(unsigned *) ast_dns_record_get_data(current->dns_record),
+				current->target.priority, current->target.weight);
+		}
+	}
+}
+
+static void host_cache_update_addresses(struct host_cache_entry *host)
+{
+	struct dns_cache_record *current;
+	int index = 0;
+
+	ast_debug(2, "Updating addresses in host cache for '%s:%s'\n",
+			host->target.addr.host.ptr,
+			pjsip_transport_get_type_desc(host->target.type));
+
+	host_cache_sort_a_records(host);
+
+	AST_LIST_TRAVERSE(&host->a_records, current, list) {
+		/* PJSIP has a fixed maximum number of addresses that can exist, so limit ourselves to that */
+		if (index == PJSIP_MAX_RESOLVED_ADDRESSES)
+			break;
+
+		host->addresses.entry[index].type = current->target.transport;
+		host->addresses.entry[index].priority = current->target.priority;
+		host->addresses.entry[index].weight = current->target.weight;
+
+		if (ast_dns_record_get_rr_type(current->dns_record) == T_A) {
+			host->addresses.entry[index].addr_len = sizeof(pj_sockaddr_in);
+			pj_sockaddr_init(pj_AF_INET(), &host->addresses.entry[index].addr, NULL, current->target.port);
+			host->addresses.entry[index].addr.ipv4.sin_addr = *(pj_in_addr *) ast_dns_record_get_data(current->dns_record);
+		} else {
+			host->addresses.entry[index].addr_len = sizeof(pj_sockaddr_in6);
+			pj_sockaddr_init(pj_AF_INET6(), &host->addresses.entry[index].addr, NULL, current->target.port);
+			pj_memcpy(&host->addresses.entry[index].addr.ipv6.sin6_addr, ast_dns_record_get_data(current->dns_record),
+					ast_dns_record_get_data_size(current->dns_record));
+		}
+
+		if (DEBUG_ATLEAST(2)) {
+			char addr[PJ_INET6_ADDRSTRLEN + 10];
+
+			pj_sockaddr_print(&host->addresses.entry[index].addr, addr, sizeof(addr), 3);
+			ast_log(LOG_DEBUG, "[%s] Address[%d]: %s, priority: %d, weight: %d, transport: %s\n",
+				host->target.addr.host.ptr, index, addr,
+				host->addresses.entry[index].priority,
+				host->addresses.entry[index].weight,
+				pjsip_transport_get_type_desc(host->addresses.entry[index].type));
+		}
+
+		index++;
+	}
+
+	host->addresses.count = index;
+	host->cur_addr = 0;
+}
+
 /*!
  * \internal
  * \brief Task used to invoke the user specific callback
@@ -394,26 +699,32 @@ static int sip_resolve_add(struct sip_resolve *resolve, const char *name, int rr
 static int sip_resolve_invoke_user_callback(void *data)
 {
 	struct sip_resolve *resolve = data;
+	struct host_cache_entry *host = resolve->host_cache;
+	char buf[NI_MAXHOST];
 
-	if (DEBUG_ATLEAST(2)) {
-		/* This includes space for the IP address, [, ], :, and the port */
-		char addr[PJ_INET6_ADDRSTRLEN + 10];
-		int idx;
+	ast_copy_pj_str(buf, &resolve->target.addr.host, sizeof(buf));
 
-		for (idx = 0; idx < resolve->addresses.count; ++idx) {
-			pj_sockaddr_print(&resolve->addresses.entry[idx].addr, addr, sizeof(addr), 3);
-			ast_log(LOG_DEBUG, "[%p] Address '%d' is %s with transport '%s'\n",
-				resolve, idx, addr,
-				pjsip_transport_get_type_desc(resolve->addresses.entry[idx].type));
-		}
-	}
+	ast_debug(2, "[%p] Invoking user callback %s\n", resolve, buf);
 
-	sip_resolve_target_track(&resolve->target, &resolve->addresses);
+	AST_LIST_LOCK(&dns_host_cache);
 
-	ast_debug(2, "[%p] Invoking user callback with '%d' addresses\n", resolve, resolve->addresses.count);
-	resolve->callback(resolve->addresses.count ? PJ_SUCCESS : PJLIB_UTIL_EDNSNOANSWERREC, resolve->token, &resolve->addresses);
+	host->querying = 0;
 
-	ao2_ref(resolve, -1);
+	host_cache_update_addresses(host);
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&host->resolves, resolve, list) {
+		AST_LIST_REMOVE_CURRENT(list);
+
+		ast_debug(2, "[%p] Invoking user callback with '%d' addresses\n", resolve, host->addresses.count);
+		/* Always return PJ_SUCCESS regardless of host->addresses.count.
+		   Let pjproject handle address count validation and related logic. */
+		resolve->callback(PJ_SUCCESS, resolve->token, &host->addresses);
+
+		ao2_ref(resolve, -1);
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	AST_LIST_UNLOCK(&dns_host_cache);
 
 	return 0;
 }
@@ -459,8 +770,7 @@ static int sip_resolve_handle_naptr(struct sip_resolve *resolve, const struct as
 		return -1;
 	}
 
-	return sip_resolve_add(resolve, ast_dns_naptr_get_replacement(record), T_SRV, C_IN,
-		transport, 0);
+	return sip_resolve_add(resolve, ast_dns_naptr_get_replacement(record), T_SRV, C_IN, transport, 0, 0, 0);
 }
 
 /*!
@@ -497,7 +807,7 @@ static void sip_resolve_callback(const struct ast_dns_query_set *query_set)
 		struct ast_dns_query *query = ast_dns_query_set_get(queries, idx);
 		struct ast_dns_result *result = ast_dns_query_get_result(query);
 		struct sip_target *target;
-		const struct ast_dns_record *record;
+		struct ast_dns_record *record;
 
 		if (!result) {
 			ast_debug(2, "[%p] No result information for target '%s' of type '%d'\n", resolve,
@@ -506,7 +816,8 @@ static void sip_resolve_callback(const struct ast_dns_query_set *query_set)
 		}
 
 		target = AST_VECTOR_GET_ADDR(&resolving, idx);
-		for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
+
+		AST_LIST_TRAVERSE_SAFE_BEGIN(&result->records, record, list) {
 
 			if (ast_dns_record_get_rr_type(record) == T_A ||
 				ast_dns_record_get_rr_type(record) == T_AAAA) {
@@ -518,30 +829,10 @@ static void sip_resolve_callback(const struct ast_dns_query_set *query_set)
 					continue;
 				}
 
-				/* PJSIP has a fixed maximum number of addresses that can exist, so limit ourselves to that */
-				if (address_count == PJSIP_MAX_RESOLVED_ADDRESSES) {
-					break;
-				}
-
-				resolve->addresses.entry[address_count].type = target->transport;
-
-				/* Populate address information for the new address entry */
-				if (ast_dns_record_get_rr_type(record) == T_A) {
-					ast_debug(2, "[%p] A record received on target '%s'\n", resolve, ast_dns_query_get_name(query));
-					resolve->addresses.entry[address_count].addr_len = sizeof(pj_sockaddr_in);
-					pj_sockaddr_init(pj_AF_INET(), &resolve->addresses.entry[address_count].addr, NULL,
-						target->port);
-					resolve->addresses.entry[address_count].addr.ipv4.sin_addr = *(pj_in_addr *) ast_dns_record_get_data(record);
-				} else {
-					ast_debug(2, "[%p] AAAA record received on target '%s'\n", resolve, ast_dns_query_get_name(query));
-					resolve->addresses.entry[address_count].addr_len = sizeof(pj_sockaddr_in6);
-					pj_sockaddr_init(pj_AF_INET6(), &resolve->addresses.entry[address_count].addr, NULL,
-						target->port);
-					pj_memcpy(&resolve->addresses.entry[address_count].addr.ipv6.sin6_addr, ast_dns_record_get_data(record),
-						ast_dns_record_get_data_size(record));
-				}
-
 				address_count++;
+
+				AST_LIST_REMOVE_CURRENT(list);
+				host_cache_record_add(resolve->host_cache, query, record, target);
 			} else if (ast_dns_record_get_rr_type(record) == T_SRV) {
 				if (have_naptr) {
 					ast_debug(2, "[%p] SRV record being skipped on target '%s' because NAPTR record exists\n",
@@ -556,23 +847,34 @@ static void sip_resolve_callback(const struct ast_dns_query_set *query_set)
 				if ((target->transport & PJSIP_TRANSPORT_IPV6) &&
 					sip_transport_is_available(target->transport)) {
 					sip_resolve_add(resolve, ast_dns_srv_get_host(record), T_AAAA, C_IN, target->transport,
-						ast_dns_srv_get_port(record));
+						ast_dns_srv_get_port(record),
+						ast_dns_srv_get_priority(record),
+						ast_dns_srv_get_weight(record));
 					have_srv = 1;
 				} else if (!(target->transport & PJSIP_TRANSPORT_IPV6) &&
 					sip_transport_is_available(target->transport | PJSIP_TRANSPORT_IPV6)) {
 					sip_resolve_add(resolve, ast_dns_srv_get_host(record), T_AAAA, C_IN, target->transport | PJSIP_TRANSPORT_IPV6,
-						ast_dns_srv_get_port(record));
+						ast_dns_srv_get_port(record),
+						ast_dns_srv_get_priority(record),
+						ast_dns_srv_get_weight(record));
 					have_srv = 1;
 				}
 
 				if (!(target->transport & PJSIP_TRANSPORT_IPV6) &&
 					sip_transport_is_available(target->transport)) {
 					sip_resolve_add(resolve, ast_dns_srv_get_host(record), T_A, C_IN, target->transport,
-						ast_dns_srv_get_port(record));
+						ast_dns_srv_get_port(record),
+						ast_dns_srv_get_priority(record),
+						ast_dns_srv_get_weight(record));
 					have_srv = 1;
 				}
+
+				AST_LIST_REMOVE_CURRENT(list);
+				host_cache_record_add(resolve->host_cache, query, record, NULL);
 			} else if (ast_dns_record_get_rr_type(record) == T_NAPTR) {
-				int added = -1;
+				int added_UDP = -1;
+				int added_TCP = -1;
+				int added_TLS = -1;
 
 				ast_debug(2, "[%p] NAPTR record received on target '%s'\n", resolve, ast_dns_query_get_name(query));
 
@@ -582,35 +884,52 @@ static void sip_resolve_callback(const struct ast_dns_query_set *query_set)
 					continue;
 				}
 
-				if (target->transport == PJSIP_TRANSPORT_UNSPECIFIED || target->transport == PJSIP_TRANSPORT_UDP ||
-					target->transport == PJSIP_TRANSPORT_UDP6) {
-					added = sip_resolve_handle_naptr(resolve, record, "sip+d2u",
-						target->transport == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP : target->transport);
-				}
-				if (target->transport == PJSIP_TRANSPORT_UNSPECIFIED || target->transport == PJSIP_TRANSPORT_TCP ||
-					target->transport == PJSIP_TRANSPORT_TCP6) {
-					added = sip_resolve_handle_naptr(resolve, record, "sip+d2t",
-						target->transport == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_TCP : target->transport);
-				}
-				if (target->transport == PJSIP_TRANSPORT_UNSPECIFIED || target->transport == PJSIP_TRANSPORT_TLS ||
-					target->transport == PJSIP_TRANSPORT_TLS6) {
-					added = sip_resolve_handle_naptr(resolve, record, "sips+d2t",
-						target->transport == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_TLS : target->transport);
+				switch(target->transport) {
+					case PJSIP_TRANSPORT_UNSPECIFIED:
+						sip_resolve_handle_naptr(resolve, record, "sip+d2u", PJSIP_TRANSPORT_UDP);
+						sip_resolve_handle_naptr(resolve, record, "sip+d2u", PJSIP_TRANSPORT_UDP6);
+
+						sip_resolve_handle_naptr(resolve, record, "sip+d2t", PJSIP_TRANSPORT_TCP);
+						sip_resolve_handle_naptr(resolve, record, "sip+d2t", PJSIP_TRANSPORT_TCP6);
+
+						sip_resolve_handle_naptr(resolve, record, "sips+d2t", PJSIP_TRANSPORT_TLS);
+						sip_resolve_handle_naptr(resolve, record, "sips+d2t", PJSIP_TRANSPORT_TLS6);
+						break;
+
+					case PJSIP_TRANSPORT_UDP:
+					case PJSIP_TRANSPORT_UDP6:
+						added_UDP = sip_resolve_handle_naptr(resolve, record, "sip+d2u", target->transport);
+						break;
+
+					case PJSIP_TRANSPORT_TCP:
+					case PJSIP_TRANSPORT_TCP6:
+						added_TCP = sip_resolve_handle_naptr(resolve, record, "sip+d2t", target->transport);
+						break;
+
+					case PJSIP_TRANSPORT_TLS:
+					case PJSIP_TRANSPORT_TLS6:
+						added_TLS = sip_resolve_handle_naptr(resolve, record, "sips+d2t", target->transport);
+						break;
+
+					default:
+						break;
 				}
 
 				/* If this record was successfully handled then we need to limit ourselves to this order */
-				if (!added) {
+				if (!added_UDP || !added_TCP || !added_TLS) {
 					have_naptr = 1;
 					strict_order = 1;
 					order = ast_dns_naptr_get_order(record);
+
+					host_cache_record_add(resolve->host_cache, query, record, NULL);
 				}
+
+				AST_LIST_REMOVE_CURRENT(list);
 			}
+			AST_LIST_TRAVERSE_SAFE_END;
 		}
 	}
 
-	/* Update the server addresses count, this is not limited as it can never exceed the max allowed */
-	resolve->addresses.count = address_count;
-
 	/* Free the vector we stole as we are responsible for it */
 	AST_VECTOR_FREE(&resolving);
 
@@ -622,7 +941,7 @@ static void sip_resolve_callback(const struct ast_dns_query_set *query_set)
 		return;
 	}
 
-	ast_debug(2, "[%p] Resolution completed - %d viable targets\n", resolve, resolve->addresses.count);
+	ast_debug(2, "[%p] Resolution completed - %d viable targets\n", resolve, address_count);
 
 	/* Push a task to invoke the callback, we do this so it is guaranteed to run in a PJSIP thread */
 	ao2_ref(resolve, +1);
@@ -659,6 +978,190 @@ static int sip_resolve_get_ip_addr_ver(const pj_str_t *host)
 	return 0;
 }
 
+static void dns_a_record_free(struct host_cache_entry *host, const char *host_name)
+{
+	struct dns_cache_record *record;
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&host->a_records, record, list) {
+		if (!strcmp(host_name, record->name)) {
+			AST_LIST_REMOVE_CURRENT(list);
+			ast_free(record->name);
+			ast_free(record->dns_record);
+			ast_free(record);
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	return;
+}
+
+static void dns_srv_record_free(struct host_cache_entry *host, const char *srv_name)
+{
+	struct dns_cache_record *record;
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&host->srv_records, record, list) {
+		if (!strcmp(srv_name, record->name)) {
+			AST_LIST_REMOVE_CURRENT(list);
+
+			dns_a_record_free(host, ast_dns_srv_get_host(record->dns_record));
+
+			ast_free(record->name);
+			ast_free(record->dns_record);
+			ast_free(record);
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	return;
+}
+
+static int dns_a_records_update(struct sip_resolve *resolve, struct host_cache_entry *host)
+{
+	struct dns_cache_record *record;
+	int update = 0;
+	time_t now = time(NULL);
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&host->a_records, record, list) {
+		if (record->expiry <= now) {
+			ast_debug(5, "%s: Updating A/AAAA record '%s'(0x%x,TTL=%d)\n", host->target.addr.host.ptr,
+					record->name,
+					*(unsigned *) ast_dns_record_get_data(record->dns_record),
+					ast_dns_record_get_ttl(record->dns_record));
+
+			sip_resolve_add(resolve, record->name, ast_dns_record_get_rr_type(record->dns_record),
+					C_IN, record->target.transport, record->target.port,
+					record->target.priority, record->target.weight);
+
+			AST_LIST_REMOVE_CURRENT(list);
+
+			ast_free(record->name);
+			ast_free(record->dns_record);
+			ast_free(record);
+
+			update = 1;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	return update;
+}
+
+static int dns_srv_records_update(struct sip_resolve *resolve, struct host_cache_entry *host)
+{
+	struct dns_cache_record *record;
+	int update = 0;
+	char *srv_name = NULL;
+	time_t now = time(NULL);
+
+	do {
+		// Find the name of expired SRV
+		AST_LIST_TRAVERSE_SAFE_BEGIN(&host->srv_records, record, list) {
+			if (record->expiry <= now) {
+				ast_debug(5, "%s: Updating SRV record '%s'(%s, TTL=%d)\n", host->target.addr.host.ptr,
+					record->name,
+					ast_dns_srv_get_host(record->dns_record),
+					ast_dns_record_get_ttl(record->dns_record));
+
+				sip_resolve_add(resolve, record->name, T_SRV, C_IN, host->target.type, 0, 0, 0);
+				srv_name = ast_strdup(record->name);
+
+				update = 1;
+				break;
+			}
+		}
+		AST_LIST_TRAVERSE_SAFE_END;
+
+		if (!srv_name)
+			break;
+
+		// One SRV may have more than one records, so clean them all.
+		AST_LIST_TRAVERSE_SAFE_BEGIN(&host->srv_records, record, list) {
+			if (!strcmp(record->name, srv_name)) {
+				ast_debug(5, "%s: Cleaning SRV record '%s'(%s, TTL=%d)\n", host->target.addr.host.ptr,
+					record->name,
+					ast_dns_srv_get_host(record->dns_record),
+					ast_dns_record_get_ttl(record->dns_record));
+
+				AST_LIST_REMOVE_CURRENT(list);
+
+				dns_a_record_free(host, ast_dns_srv_get_host(record->dns_record));
+
+				ast_free(record->name);
+				ast_free(record->dns_record);
+				ast_free(record);
+			}
+		}
+		AST_LIST_TRAVERSE_SAFE_END;
+
+		ast_free(srv_name);
+		srv_name = NULL;
+	} while (true);
+
+	return update;
+}
+
+static int dns_naptr_records_update(struct sip_resolve *resolve, struct host_cache_entry *host)
+{
+	struct dns_cache_record *record;
+	int update = 0;
+	time_t now = time(NULL);
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&host->naptr_records, record, list) {
+		if (record->expiry <= now) {
+
+			ast_debug(5, "%s: Updating NAPTR record '%s'(%s, TTL=%d)\n", host->target.addr.host.ptr,
+					record->name,
+					ast_dns_naptr_get_replacement(record->dns_record),
+					ast_dns_record_get_ttl(record->dns_record));
+
+			sip_resolve_add(resolve, record->name, T_NAPTR, C_IN, host->target.type, 0, 0, 0);
+
+			AST_LIST_REMOVE_CURRENT(list);
+
+			dns_srv_record_free(host, ast_dns_naptr_get_replacement(record->dns_record));
+
+			ast_free(record->name);
+			ast_free(record->dns_record);
+			ast_free(record);
+
+			update = 1;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	return update;
+}
+
+static void host_cache_update(struct sip_resolve *resolve)
+{
+	struct host_cache_entry *host = resolve->host_cache;
+	int update = 0;
+
+	ast_debug(2, "Updating DNS records in host cache for '%s:%s'\n",
+			host->target.addr.host.ptr,
+			pjsip_transport_get_type_desc(host->target.type));
+
+	update |= dns_naptr_records_update(resolve, host);
+
+	update |= dns_srv_records_update(resolve, host);
+
+	update |= dns_a_records_update(resolve, host);
+
+	if (!update) {
+		resolve->callback(PJ_SUCCESS, resolve->token, &host->addresses);
+	} else {
+		resolve->serializer = ao2_bump(ast_threadpool_serializer_get_current());
+
+		ast_debug(2, "[%p] Starting queries for updating target '%s'\n", resolve, host->target.addr.host.ptr);
+
+		ast_dns_query_set_resolve_async(resolve->queries, sip_resolve_callback, resolve);
+	}
+
+	ao2_ref(resolve, -1);
+
+	return;
+}
+
 /*!
  * \internal
  * \brief Perform SIP resolution of a host
@@ -672,6 +1175,7 @@ static int sip_resolve_get_ip_addr_ver(const pj_str_t *host)
 static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip_host_info *target,
 	void *token, pjsip_resolver_callback *cb)
 {
+	struct host_cache_entry *host_cache;
 	int ip_addr_ver;
 	pjsip_transport_type_e type = target->type;
 	struct sip_resolve *resolve;
@@ -680,7 +1184,10 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip
 
 	ast_copy_pj_str(host, &target->addr.host, sizeof(host));
 
-	ast_debug(2, "Performing SIP DNS resolution of target '%s'\n", host);
+	ast_debug(2, "Performing SIP DNS resolution of target '%s:%s'\n", host,
+		pjsip_transport_get_type_desc(type));
+
+	host_cache_cleanup();
 
 	/* If the provided target is already an address don't bother resolving */
 	ip_addr_ver = sip_resolve_get_ip_addr_ver(&target->addr.host);
@@ -735,9 +1242,30 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip
 		return;
 	}
 
+	AST_LIST_LOCK(&dns_host_cache);
+
+	host_cache = get_host_cache_entry(target);
+	if (host_cache) {
+		pjsip_server_addresses *cached_addr;
+
+		ast_debug(2, "Target '%s:%s' is found in host cache\n", host, pjsip_transport_get_type_desc(type));
+
+		// Use cached addresses, if all cache records are valid.
+		cached_addr = get_cached_host_addrs(host_cache);
+		if (cached_addr) {
+			ast_debug(2, "Target '%s' is valid in cache, skipping resolution\n", host);
+
+			cb(PJ_SUCCESS, token, cached_addr);
+
+			AST_LIST_UNLOCK(&dns_host_cache);
+			return;
+		}
+	}
+
 	resolve = ao2_alloc_options(sizeof(*resolve), sip_resolve_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
 	if (!resolve) {
 		cb(PJ_ENOMEM, token, NULL);
+		AST_LIST_UNLOCK(&dns_host_cache);
 		return;
 	}
 
@@ -748,9 +1276,40 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip
 	if (AST_VECTOR_INIT(&resolve->resolving, 4)) {
 		ao2_ref(resolve, -1);
 		cb(PJ_ENOMEM, token, NULL);
+		AST_LIST_UNLOCK(&dns_host_cache);
 		return;
 	}
 
+	if (host_cache) {
+		host_cache_add_resolve(host_cache, resolve);
+
+		ast_debug(2, "Target '%s:%s' is being updated in host cache\n", host, pjsip_transport_get_type_desc(type));
+
+		// Try to re-query expired records
+		if (!host_cache->querying) {
+			host_cache->querying = 1;
+			host_cache_update(resolve);
+		}
+		AST_LIST_UNLOCK(&dns_host_cache);
+		return;
+	}
+
+	ast_debug(2, "Target '%s:%s' is not found in host cache\n", host, pjsip_transport_get_type_desc(type));
+
+	// Create a cache entry for the target
+	host_cache = host_cache_add(target);
+	if (!host_cache) {
+		ao2_ref(resolve, -1);
+		cb(PJ_ENOMEM, token, NULL);
+		AST_LIST_UNLOCK(&dns_host_cache);
+		return;
+	}
+
+	host_cache_add_resolve(host_cache, resolve);
+	host_cache->querying = 1;
+
+	AST_LIST_UNLOCK(&dns_host_cache);
+
 	ast_debug(2, "[%p] Created resolution tracking for target '%s'\n", resolve, host);
 
 	/* NAPTR + SRV */
@@ -767,14 +1326,14 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip
 	 * explicitly and only looks for IPv6 records.
 	 */
 
-	res |= sip_resolve_add(resolve, host, T_NAPTR, C_IN, type, 0);
+	res |= sip_resolve_add(resolve, host, T_NAPTR, C_IN, type, 0, 0, 0);
 
 	if (type == PJSIP_TRANSPORT_UNSPECIFIED ||
 		(type == PJSIP_TRANSPORT_TLS && sip_transport_is_available(PJSIP_TRANSPORT_TLS)) ||
 		(type == PJSIP_TRANSPORT_TLS6 && sip_transport_is_available(PJSIP_TRANSPORT_TLS6))) {
 		if (snprintf(srv, sizeof(srv), "_sips._tcp.%s", host) < NI_MAXHOST) {
 			res |= sip_resolve_add(resolve, srv, T_SRV, C_IN,
-				type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_TLS : type, 0);
+				type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_TLS : type, 0, 0, 0);
 		}
 	}
 	if (type == PJSIP_TRANSPORT_UNSPECIFIED ||
@@ -782,7 +1341,7 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip
 		(type == PJSIP_TRANSPORT_TCP6 && sip_transport_is_available(PJSIP_TRANSPORT_TCP6))) {
 		if (snprintf(srv, sizeof(srv), "_sip._tcp.%s", host) < NI_MAXHOST) {
 			res |= sip_resolve_add(resolve, srv, T_SRV, C_IN,
-				type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_TCP : type, 0);
+				type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_TCP : type, 0, 0, 0);
 		}
 	}
 	if (type == PJSIP_TRANSPORT_UNSPECIFIED ||
@@ -790,20 +1349,22 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip
 		(type == PJSIP_TRANSPORT_UDP6 && sip_transport_is_available(PJSIP_TRANSPORT_UDP6))) {
 		if (snprintf(srv, sizeof(srv), "_sip._udp.%s", host) < NI_MAXHOST) {
 			res |= sip_resolve_add(resolve, srv, T_SRV, C_IN,
-				type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP : type, 0);
+				type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP : type, 0, 0, 0);
 		}
 	}
 	/* AAAA + A */
 	if ((type == PJSIP_TRANSPORT_UNSPECIFIED && sip_transport_is_available(PJSIP_TRANSPORT_UDP6)) ||
 		((type & PJSIP_TRANSPORT_IPV6) && sip_transport_is_available(type))) {
-		res |= sip_resolve_add(resolve, host, T_AAAA, C_IN, (type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP6 : type), target->addr.port);
+		res |= sip_resolve_add(resolve, host, T_AAAA, C_IN, (type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP6 : type),
+				target->addr.port, 0, 0);
 	} else if (!(type & PJSIP_TRANSPORT_IPV6) && sip_transport_is_available(type | PJSIP_TRANSPORT_IPV6)) {
-		res |= sip_resolve_add(resolve, host, T_AAAA, C_IN, type | PJSIP_TRANSPORT_IPV6, target->addr.port);
+		res |= sip_resolve_add(resolve, host, T_AAAA, C_IN, type | PJSIP_TRANSPORT_IPV6, target->addr.port, 0, 0);
 	}
 
 	if ((type == PJSIP_TRANSPORT_UNSPECIFIED && sip_transport_is_available(PJSIP_TRANSPORT_UDP)) ||
 		(!(type & PJSIP_TRANSPORT_IPV6) && sip_transport_is_available(type))) {
-		res |= sip_resolve_add(resolve, host, T_A, C_IN, (type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP : type), target->addr.port);
+		res |= sip_resolve_add(resolve, host, T_A, C_IN, (type == PJSIP_TRANSPORT_UNSPECIFIED ? PJSIP_TRANSPORT_UDP : type),
+				target->addr.port, 0, 0);
 	}
 
 	if (res) {
@@ -869,6 +1430,145 @@ static void sip_check_transport(pj_pool_t *pool, pjsip_transport_type_e transpor
 	}
 }
 
+static char * get_host_cache_addr_expiry(char *buf, size_t buflen, struct host_cache_entry *host, pj_sockaddr *addr)
+{
+	char strbuf[32];
+	struct dns_cache_record *record;
+	int ttl = 0;
+	time_t expiry = LONG_MAX;
+	struct timeval tv = {
+		.tv_sec = 0,
+		.tv_usec = 0,
+	};
+	struct ast_tm tm;
+	pj_sockaddr addr_c;
+	char *hostname = NULL;
+	char *srvname = NULL;
+
+	if (host->querying) {
+		ast_debug(5, "host cache '%s' is updating\n", host->target.addr.host.ptr);
+		memset(buf, 0, buflen);
+		return buf;
+	}
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&host->a_records, record, list) {
+		if (addr->addr.sa_family == PJ_AF_INET && ast_dns_record_get_rr_type(record->dns_record) == T_A) {
+			pj_sockaddr_init(pj_AF_INET(), &addr_c, NULL, record->target.port);
+			addr_c.ipv4.sin_addr = *(pj_in_addr *) ast_dns_record_get_data(record->dns_record);
+
+		} else if (addr->addr.sa_family == PJ_AF_INET6 && ast_dns_record_get_rr_type(record->dns_record) == T_AAAA) {
+			pj_sockaddr_init(pj_AF_INET6(), &addr_c, NULL, record->target.port);
+			pj_memcpy(&addr_c.ipv6.sin6_addr, ast_dns_record_get_data(record->dns_record),
+					ast_dns_record_get_data_size(record->dns_record));
+		} else
+			continue;
+
+		if (!pj_sockaddr_cmp(addr, &addr_c)) {
+			hostname = record->name;
+			expiry = record->expiry;
+			ttl = ast_dns_record_get_ttl(record->dns_record);
+			break;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	if (hostname) {
+		AST_LIST_TRAVERSE_SAFE_BEGIN(&host->srv_records, record, list) {
+			if (!strcmp(hostname, ast_dns_srv_get_host(record->dns_record))) {
+				srvname = record->name;
+				if (record->expiry < expiry) {
+					expiry = record->expiry;
+					ttl = ast_dns_record_get_ttl(record->dns_record);
+				}
+				break;
+			}
+		}
+		AST_LIST_TRAVERSE_SAFE_END;
+	}
+
+	if (srvname) {
+		AST_LIST_TRAVERSE_SAFE_BEGIN(&host->naptr_records, record, list) {
+			if (!strcmp(srvname, ast_dns_naptr_get_replacement(record->dns_record))) {
+				if (record->expiry < expiry) {
+					expiry = record->expiry;
+					ttl = ast_dns_record_get_ttl(record->dns_record);
+				}
+			}
+		}
+		AST_LIST_TRAVERSE_SAFE_END;
+	}
+
+	tv.tv_sec = expiry;
+	ast_localtime(&tv, &tm, NULL);
+	ast_strftime(strbuf, sizeof(strbuf), "%F %T %Z", &tm);
+	snprintf(buf, buflen, "%s (TTL=%d)", strbuf, ttl);
+
+	return buf;
+}
+
+static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	char addr[PJ_INET6_ADDRSTRLEN + 10];
+	struct host_cache_entry *host = NULL;
+	struct dns_cache_record *current;
+	struct dns_cache_record *next;
+	int idx, cnt;
+	char tbuf[64];
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "pjsip show srv_lookups";
+		e->usage = "Usage: pjsip show SRV DNS lookup records\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc != 3) {
+		return CLI_SHOWUSAGE;
+	}
+
+	ast_cli(a->fd,
+		"<Host name.............................> "
+		"<Target name......................................> "
+		"<IP address.............> <Port> <Transport.....> "
+		"<Priority> <Weight> <Expired at>\n"
+		"============================================================="
+		"============================================================="
+		"============================================================="
+		"=====\n");
+
+	AST_LIST_LOCK(&dns_host_cache);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&dns_host_cache, host, list) {
+		if (host->querying) {
+			ast_cli(a->fd, "%-40.40s   Resolving...\n", host->target.addr.host.ptr);
+			continue;
+		}
+		if (host->target.type == PJSIP_TRANSPORT_UNSPECIFIED)
+			continue;
+		for (cnt = 0, current = host->a_records.first, next = current ? current->list.next : NULL; current;
+			cnt++, current = next, next = current ? current->list.next : NULL) {
+			ast_cli(a->fd, "%-40.40s %-40.40s %-25.25s %-6d %-15.15s %10d %8d %-24s\n",
+				host->target.addr.host.ptr,
+				current->name,
+				pj_sockaddr_print(&host->addresses.entry[cnt].addr, addr, sizeof(addr), 2),
+				pj_sockaddr_get_port(&host->addresses.entry[cnt].addr),
+				pjsip_transport_get_type_desc(host->target.type),
+				host->addresses.entry[cnt].priority,
+				host->addresses.entry[cnt].weight,
+				get_host_cache_addr_expiry(tbuf, sizeof(tbuf), host, &host->addresses.entry[cnt].addr));
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+	AST_LIST_UNLOCK(&dns_host_cache);
+
+	return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_commands[] = {
+	AST_CLI_DEFINE(cli_show_tasks, "Show pjsip SRV DNS lookup records"),
+};
+
 /*! \brief External resolver implementation for PJSIP */
 static pjsip_ext_resolver ext_resolver = {
 	.resolve = sip_resolve,
@@ -910,6 +1610,7 @@ void ast_sip_initialize_resolver(void)
 {
 	/* Replace the existing PJSIP resolver with our own implementation */
 	ast_sip_push_task_wait_servant(NULL, sip_replace_resolver, NULL);
+	ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
 }
 
 #else
@@ -921,3 +1622,50 @@ void ast_sip_initialize_resolver(void)
 }
 
 #endif
+
+int ast_sip_destroy_resolver(void)
+{
+	ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
+}
+
+/*!
+ * \brief Check if the given IP address matches any DNS-resolved address.
+ * \param src_addr The source address to be checked in DNS cache list
+ * \return 1 if the address matches a resolved DNS entry, 0 otherwise.
+ */
+int ast_sip_is_address_in_dns_records(const pj_sockaddr *src_addr)
+{
+	struct host_cache_entry *host = NULL;
+	struct dns_cache_record *current;
+	struct dns_cache_record *next;
+	int cnt;
+	char src_addr_buf[PJ_INET6_ADDRSTRLEN];
+	char resolved_addr_buf[PJ_INET6_ADDRSTRLEN];
+	int match_found = 0;
+
+	pj_sockaddr_print(src_addr, src_addr_buf, sizeof(src_addr_buf), 3);
+
+	AST_LIST_LOCK(&dns_host_cache);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&dns_host_cache, host, list) {
+		if (host->querying)
+			continue;
+		for (cnt = 0, current = host->a_records.first, next = current ? current->list.next : NULL; current;
+			cnt++, current = next, next = current ? current->list.next : NULL) {
+			if (!pj_sockaddr_cmp(src_addr, &host->addresses.entry[cnt].addr)) {
+				pj_sockaddr_print(&host->addresses.entry[cnt].addr, resolved_addr_buf, sizeof(resolved_addr_buf), 3);
+				ast_log(LOG_NOTICE, "Source address %s matches resolved DNS entry %s\n", src_addr_buf, resolved_addr_buf);
+				match_found = 1;
+				break;
+			}
+		}
+		if (match_found)
+			break;
+	}
+
+	AST_LIST_TRAVERSE_SAFE_END;
+	AST_LIST_UNLOCK(&dns_host_cache);
+	if (!match_found)
+		ast_log(LOG_NOTICE, "Source address %s did not match any resolved DNS entries\n", src_addr_buf);
+
+	return match_found;
+}
diff --git a/res/res_pjsip_authenticator_digest.c b/res/res_pjsip_authenticator_digest.c
index 2cdbe6eff5866193894523980bbf84bd908300b2..0a882ecb6a82ddd6e9850256ad8c463cd1bfb95e 100644
--- a/res/res_pjsip_authenticator_digest.c
+++ b/res/res_pjsip_authenticator_digest.c
@@ -485,6 +485,19 @@ static enum ast_sip_check_auth_result digest_check_auth(struct ast_sip_endpoint
 		if (verify_res[idx] == AUTH_FAIL) {
 			failures++;
 		}
+		if(verify_res[idx] == AUTH_NOAUTH) {
+			if (rdata && rdata->msg_info.msg) {
+				pjsip_contact_hdr *contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
+				if (contact_hdr && contact_hdr->uri) {
+					pjsip_sip_uri *client_uri = pjsip_uri_get_uri(contact_hdr->uri);
+					if (client_uri) {
+						/* lack of auth. IPX003 */
+						ast_log_dt(LOG_EVENT_CODE_IPX003, (int)client_uri->user.slen, client_uri->user.ptr,
+								(int)client_uri->host.slen, client_uri->host.ptr);
+					}
+				}
+			}
+		}
 	}
 
 	for (idx = 0; idx < auth_size; ++idx) {
diff --git a/res/res_pjsip_caller_id.c b/res/res_pjsip_caller_id.c
index b11e5908bfe26fb8d30bf76ba9206f2a0dbed057..665e3565f4a34117ad88e30d00f4a3209e09fc0c 100644
--- a/res/res_pjsip_caller_id.c
+++ b/res/res_pjsip_caller_id.c
@@ -370,6 +370,45 @@ static void add_pai_header(const struct ast_sip_session *session, pjsip_tx_data
 	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)pai_hdr);
 }
 
+/*!
+ * \internal
+ * \brief Add a P-Preffered-Identity header to an outbound message
+ * \param session The session on which communication is happening
+ * \param tdata The message to add the header to
+ */
+static void add_ppi_header(const struct ast_sip_session *session, pjsip_tx_data *tdata)
+{
+	static const pj_str_t pj_ppi_name = { "P-Preferred-Identity", 20 };
+	pjsip_fromto_hdr *old_ppi;
+	pjsip_fromto_hdr *base;
+	pjsip_fromto_hdr *ppi_hdr;
+
+	/* Since inv_session reuses responses, we have to make sure there's not already
+	 * a P-Preffered-Identity present. If there is, we just skip adding a new one.
+	 */
+	old_ppi = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_ppi_name, NULL);
+	if (old_ppi) {
+		if (old_ppi->type == PJSIP_H_OTHER)
+			pj_list_erase(old_ppi);
+		else
+			return;
+	}
+
+	if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+		base = session->saved_from_hdr ? session->saved_from_hdr : PJSIP_MSG_FROM_HDR(tdata->msg);
+	} else {
+		base = PJSIP_MSG_TO_HDR(tdata->msg);
+	}
+
+	ppi_hdr = pjsip_from_hdr_create(tdata->pool);
+	ppi_hdr->type = PJSIP_H_OTHER;
+	ppi_hdr->sname = ppi_hdr->name = pj_ppi_name;
+
+	ppi_hdr->uri = (pjsip_uri *) pjsip_uri_clone(tdata->pool, base->uri);
+
+	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)ppi_hdr);
+}
+
 /*!
  * \internal
  * \brief Add party parameter to a Remote-Party-ID header.
@@ -529,6 +568,9 @@ static void add_id_headers(const struct ast_sip_session *session, pjsip_tx_data
 	if (session->endpoint->id.send_pai) {
 		add_pai_header(session, tdata, id);
 	}
+	if (session->endpoint->id.send_ppi) {
+		add_ppi_header(session, tdata);
+	}
 	if (session->endpoint->id.send_rpid) {
 		add_rpid_header(session, tdata, id);
 	}
diff --git a/res/res_pjsip_logger.c b/res/res_pjsip_logger.c
index 456bb224a94b48687a184b8627bdf34bb111d028..c282567f47e45d0eb0bf8aec577d59721e631c61 100644
--- a/res/res_pjsip_logger.c
+++ b/res/res_pjsip_logger.c
@@ -327,6 +327,11 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
 			(int) (tdata->buf.end - tdata->buf.start), tdata->buf.start);
 	}
 
+	if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+		char addr_buf[AST_SOCKADDR_BUFLEN] = {'\0'};
+		pj_sockaddr_print(&tdata->tp_info.dst_addr, addr_buf, sizeof(addr_buf), 0);
+	}
+
 	if (default_logger->log_to_pcap) {
 		pjsip_logger_write_to_pcap(default_logger, tdata->buf.start, (int) (tdata->buf.cur - tdata->buf.start),
 			NULL, &tdata->tp_info.dst_addr);
diff --git a/res/res_pjsip_outbound_authenticator_digest.c b/res/res_pjsip_outbound_authenticator_digest.c
index aee4afc90ec5a23b9b4090ce1c278eb9c5f24380..d3a6b825ba79b72212dc131098a6a587ed96b5e3 100644
--- a/res/res_pjsip_outbound_authenticator_digest.c
+++ b/res/res_pjsip_outbound_authenticator_digest.c
@@ -31,6 +31,7 @@
 #include "asterisk/module.h"
 #include "asterisk/strings.h"
 #include "asterisk/vector.h"
+#include "asterisk/res_pjsip_outbound_registration.h"
 
 pj_str_t supported_digest_algorithms[] = {
 	{ "MD5", 3}
@@ -478,16 +479,25 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut
 		goto cleanup;
 	}
 
-	if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(),
-				old_request->pool, 0) != PJ_SUCCESS) {
-		ast_log(LOG_ERROR, "%s: '%s': Failed to initialize client authentication session\n",
-			id_type, id);
-		res = -1;
-		goto cleanup;
+	pjsip_hdr_e search_type = get_auth_search_type(challenge);
+	struct ast_sip_auth *auth = NULL;
+	if (search_type != PJSIP_H_OTHER) {
+		pjsip_www_authenticate_hdr *auth_hdr = NULL;
+		while ((auth_hdr = pjsip_msg_find_hdr(challenge->msg_info.msg, search_type, auth_hdr ? auth_hdr->next : NULL))) {
+			for (int i = 0; i < auth_object_count; ++i) {
+				auth = AST_VECTOR_GET(&auth_objects_vector, i);
+
+				if (pj_stricmp2(&auth_hdr->challenge.digest.realm, auth->realm) == 0) {
+					ast_debug(3, "Found matching auth '%s' with realm '%s'\n", ast_sorcery_object_get_id(auth), auth->realm);
+					endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",  ast_sorcery_object_get_id(auth));
+					break;
+				}
+			}
+		}
 	}
 
 	/*
-	 * realms is used only for displaying good error messages.
+	 * realms is used for displaying good error messages, and for caching auth header
 	 */
 	realms = ast_str_create(32);
 	if (!realms) {
@@ -495,11 +505,25 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut
 		goto cleanup;
 	}
 
-	/*
-	 * Load pjproject with the valid credentials for the Authentication headers
-	 * received on the 401 or 407 response.
-	 */
-	status = set_outbound_authentication_credentials(&auth_sess, &auth_objects_vector, challenge, &realms);
+	if (endpoint && (pjsip_method_cmp(&old_request->msg->line.req.method, &pjsip_register_method) ==0 || pjsip_method_cmp(&old_request->msg->line.req.method, &pjsip_invite_method) ==0)) {
+		if(auth && auth->realm){
+			ast_str_append(&realms, 0, "%s ", auth->realm);
+		}
+		goto add_header; // auth_sess for REGISTER and INVITE has been inited during sip_outbound_registration_regc_alloc
+	} else if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(),
+				old_request->pool, 0) != PJ_SUCCESS) {
+		ast_log(LOG_ERROR, "%s: '%s': Failed to initialize client authentication session\n",
+			id_type, id);
+		res = -1;
+		goto cleanup;
+	} else {
+		/*
+		 * Load pjproject with the valid credentials for the Authentication headers
+		 * received on the 401 or 407 response.
+		 */
+		status = set_outbound_authentication_credentials(&auth_sess, &auth_objects_vector, challenge, &realms);
+	}
+
 	switch (status) {
 	case PJ_SUCCESS:
 		break;
@@ -515,13 +539,40 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut
 		goto cleanup;
 	}
 
+add_header:
 	/*
 	 * reinit_req actually creates the Authorization headers to send on
 	 * the next request.  If reinit_req already has a cached credential
 	 * from an earlier successful authorization, it'll use it. Otherwise
 	 * it'll create a new authorization and cache it.
 	 */
-	status = pjsip_auth_clt_reinit_req(&auth_sess, challenge, old_request, new_request);
+	if(endpoint &&pjsip_method_cmp(&old_request->msg->line.req.method, &pjsip_register_method) ==0 ){
+		ast_mutex_lock(&endpoint->auth_sess_reg_lock);
+		status = pjsip_auth_clt_reinit_req(&endpoint->auth_sess_reg, challenge, old_request, new_request);
+		//if success, sync cached info to inv
+		if (status == PJ_SUCCESS){
+			ast_debug(3, "sync cached auth info from auth_sess_reg to auth_sess_inv\n");
+			pj_str_t realms_pj_str = pj_str(endpoint->realms);
+			ast_mutex_lock(&endpoint->auth_sess_inv_lock);
+			sync_cached_auth_between_reg_inv(&endpoint->auth_sess_reg, &endpoint->auth_sess_inv, &realms_pj_str);
+			ast_mutex_unlock(&endpoint->auth_sess_inv_lock);
+		}
+		ast_mutex_unlock(&endpoint->auth_sess_reg_lock);
+	} else if (endpoint && pjsip_method_cmp(&old_request->msg->line.req.method, &pjsip_invite_method) ==0 ){
+		ast_mutex_lock(&endpoint->auth_sess_inv_lock);
+		status = pjsip_auth_clt_reinit_req(&endpoint->auth_sess_inv, challenge, old_request, new_request);
+		//if success, sync cached info to reg
+		if (status == PJ_SUCCESS){
+			ast_debug(3, "sync cached auth info from auth_sess_inv to auth_sess_reg\n");
+			pj_str_t realms_pj_str = pj_str(endpoint->realms);
+			ast_mutex_lock(&endpoint->auth_sess_reg_lock);
+			sync_cached_auth_between_reg_inv(&endpoint->auth_sess_inv, &endpoint->auth_sess_reg, &realms_pj_str);
+			ast_mutex_unlock(&endpoint->auth_sess_reg_lock);
+		}
+		ast_mutex_unlock(&endpoint->auth_sess_inv_lock);
+	} else {
+		status = pjsip_auth_clt_reinit_req(&auth_sess, challenge, old_request, new_request);
+	}
 
 	switch (status) {
 	case PJ_SUCCESS:
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index b7ef2b0e2b9da7fbefb07ac00b55c58e06a9e583..78244f54e9286f4fd165d6f34598f6f9f954ecdf 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -38,10 +38,9 @@
 #include "asterisk/threadstorage.h"
 #include "asterisk/threadpool.h"
 #include "asterisk/statsd.h"
-#include "res_pjsip/include/res_pjsip_private.h"
 #include "asterisk/vector.h"
 #include "asterisk/pbx.h"
-
+#include "asterisk/res_pjsip_outbound_registration.h"
 /*** DOCUMENTATION
 	<configInfo name="res_pjsip_outbound_registration" language="en_US">
 		<synopsis>SIP resource for outbound registrations</synopsis>
@@ -138,6 +137,9 @@
 				<configOption name="retry_interval" default="1">
 					<synopsis>Interval in seconds between retries if outbound registration is unsuccessful</synopsis>
 				</configOption>
+				<configOption name="retry_percent" default="90">
+					<synopsis>Percentage of Register Expire timer on which CPE will perform re-Register</synopsis>
+				</configOption>
 				<configOption name="forbidden_retry_interval" default="0">
 					<synopsis>Interval used when receiving a 403 Forbidden response.</synopsis>
 					<description><para>
@@ -173,13 +175,6 @@
 						the registrar, e.g. sip:sip.example.com.
 					</para></description>
 				</configOption>
-				<configOption name="transport">
-					<synopsis>Transport used for outbound authentication</synopsis>
-					<description>
-						<note><para>A <replaceable>transport</replaceable> configured in
-						<literal>pjsip.conf</literal>. As with other <literal>res_pjsip</literal> modules, this will use the first available transport of the appropriate type if unconfigured.</para></note>
-					</description>
-				</configOption>
 				<configOption name="line">
 					<synopsis>Whether to add a 'line' parameter to the Contact for inbound call matching</synopsis>
 					<description><para>
@@ -293,6 +288,9 @@ AST_THREADSTORAGE(register_callback_invoked);
 #define LINE_PARAMETER_SIZE 8
 static const char broadcast_path[] = "voice.sip.client";
 
+/*! \brief timer for REGISTER attempts between two addresses in the record list */
+static struct ast_sched_context *sched;
+
 /*! \brief Various states that an outbound registration may be in */
 enum sip_outbound_registration_status {
 	/*! \brief Currently unregistered */
@@ -313,6 +311,24 @@ static pjsip_module mod_mwi = {
 	.id = -1,
 };
 
+/* for counting register request that has been sent out before success response */
+static pj_status_t log_register_on_tx_msg(pjsip_tx_data *tdata); //
+static pjsip_module log_register_module = {
+	.name = { "log_register_module", 19 },
+	.priority = 0,
+	.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.
@@ -357,8 +373,6 @@ struct sip_outbound_registration {
 		AST_STRING_FIELD(contact_user);
 		/*! \brief Optional header parameters for contact */
 		AST_STRING_FIELD(contact_header_params);
-		/*! \brief Explicit transport to use for registration */
-		AST_STRING_FIELD(transport);
 		/*! \brief Outbound proxy to use */
 		AST_STRING_FIELD(outbound_proxy);
 		/*! \brief Endpoint to use for related incoming calls */
@@ -368,6 +382,8 @@ struct sip_outbound_registration {
 	unsigned int expiration;
 	/*! \brief Maximum random initial delay interval for initial registrations */
 	unsigned int max_random_initial_delay;
+	/*! \brief Percentage of Register Expire timer on which CPE will perform re-Register */
+	unsigned int retry_percent;
 	/*! \brief Interval at which retries should occur for temporal responses */
 	unsigned int retry_interval;
 	/*! \brief Interval at which retries should occur for permanent responses */
@@ -398,6 +414,14 @@ struct sip_outbound_registration {
 	unsigned int mediasec;
 	/*! \brief Whether Outbound support is enabled */
 	unsigned int support_outbound;
+	/*! \brief max_time (RFC 5626), default 1800 seconds */
+	unsigned int retry_max_time;
+	/*! \brief base_time_all_failed (RFC 5626), default 30 seconds */
+	unsigned int retry_base_time1;
+	/*! \brief base_time_not_all_failed (RFC 5626), default 90 seconds */
+	unsigned int retry_base_time2;
+	/*! \brief 1TR114, retry after time if no retry-after header included in response, default 15 seconds */
+	unsigned int retry_after;
 };
 
 /*! \brief Outbound registration client state information (persists for lifetime of regc) */
@@ -425,8 +449,14 @@ struct sip_outbound_registration_client_state {
 	char line[LINE_PARAMETER_SIZE];
 	/*! \brief Current number of retries */
 	unsigned int retries;
+	/*! \brief number of consecutive unsuccessful initial registrations to one P-CSCF address, 1TR114 */
+	unsigned int attempts;
+	/*! \brief number of consecutive unsuccessful initial registrations in total, 1TR114 */
+	unsigned int failures;
 	/*! \brief Maximum number of retries permitted */
 	unsigned int max_retries;
+	/*! \brief Percentage of Register Expire timer on which CPE will perform re-Register */
+	unsigned int retry_percent;
 	/*! \brief Interval at which retries should occur for temporal responses */
 	unsigned int retry_interval;
 	/*! \brief Interval at which retries should occur for permanent responses */
@@ -441,6 +471,10 @@ struct sip_outbound_registration_client_state {
 	unsigned int failover_retry_interval1;
 	/*! \brief Interval2 at which retries should occur for failover */
 	unsigned int failover_retry_interval2;
+	/*! \brief the upper-bound wait time, RFC 5626 section 4.5 */
+	int waiting_time_upper_bound;
+	/*! \brief control the time for REGISTER attempts between two addresses in the record list */
+	int registration_resend_timer_id;
 	/*! \brief Treat authentication challenges that we cannot handle as permanent failures */
 	unsigned int auth_rejection_permanent;
 	/*! \brief Determines whether SIP Path support should be advertised */
@@ -465,6 +499,8 @@ struct sip_outbound_registration_client_state {
 	unsigned int destroy:1;
 	/*! \brief Non-zero if we have attempted sending a REGISTER with authentication */
 	unsigned int auth_attempted:1;
+	/*! \brief Non-zero if we have sent a REGISTER successful with security_verify */
+	unsigned int cached_security_verify:1;
 	/*! \brief Status code of last response if we have tried to register before */
 	int last_status_code;
 	/*! \brief The name of the transport to be used for the registration */
@@ -475,6 +511,10 @@ struct sip_outbound_registration_client_state {
 	unsigned int is494;
 	/*! \brief Expected time of registration lapse/expiration */
 	unsigned int registration_expires;
+	/*! \brief Emergency Registration Flag */
+	bool Emergency_reg_ongoing;
+	bool Emergency_reg_hangup;
+	bool Emergency_call_ongoing;
 };
 
 /*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
@@ -485,6 +525,9 @@ struct sip_outbound_registration_state {
 	struct sip_outbound_registration_client_state *client_state;
 };
 
+/* RFC5626, Section 4.5: waiting randomly between 50-100% of upper_bound */
+#define RFC5626_RETRY_DELAY(time) (time ? ((ast_random() % 50 + 50) * time / 100) : 15)
+
 /*! Time needs to be long enough for a transaction to timeout if nothing replies. */
 #define MAX_UNLOAD_TIMEOUT_TIME		35	/* Seconds */
 
@@ -686,8 +729,8 @@ out:
 static void add_security_headers(struct sip_outbound_registration_client_state *client_state,
 	pjsip_tx_data *tdata)
 {
-	int add_require_header = 1;
-	int add_proxy_require_header = 1;
+	int add_require_header;
+	int add_proxy_require_header;
 	int add_sec_client_header = 0;
 	struct sip_outbound_registration *reg = NULL;
 	struct ast_sip_endpoint *endpt = NULL;
@@ -699,10 +742,6 @@ static void add_security_headers(struct sip_outbound_registration_client_state *
 	static const pj_str_t proxy_require = { "Proxy-Require", 13 };
 	static const pj_str_t require = { "Require", 7 };
 
-	if (client_state->security_negotiation != AST_SIP_SECURITY_NEG_MEDIASEC) {
-		return;
-	}
-
 	/* Get contact status through registration -> endpoint name -> aor -> contact (if set) */
 	if ((reg = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration", client_state->registration_name))
 		&& !ast_strlen_zero(reg->endpoint) && (endpt = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", reg->endpoint))
@@ -721,9 +760,15 @@ static void add_security_headers(struct sip_outbound_registration_client_state *
 	if (!contact_status && AST_VECTOR_SIZE(&client_state->server_security_mechanisms)) {
 		sec_mechs = &client_state->server_security_mechanisms;
 	}
-	if (client_state->status == SIP_REGISTRATION_REJECTED_TEMPORARY || client_state->auth_attempted) {
+	if (client_state->status == SIP_REGISTRATION_REJECTED_TEMPORARY || client_state->auth_attempted || client_state->cached_security_verify ) {
 		if (sec_mechs != NULL && pjsip_msg_find_hdr_by_name(tdata->msg, &security_verify, NULL) == NULL) {
 			ast_sip_add_security_headers(sec_mechs, "Security-Verify", 0, tdata);
+		} else if ((endpt = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name)) && !AST_LIST_EMPTY(&endpt->secur_mechanisms) && pjsip_msg_find_hdr_by_name(tdata->msg, &security_verify, NULL) == NULL) {
+			struct security_mechanism *sec_mechanism;
+			AST_LIST_TRAVERSE(&endpt->secur_mechanisms, sec_mechanism, entry) {
+				ast_debug(3, "Adding security header: %s\n", sec_mechanism->value);
+				ast_sip_add_header(tdata,"Security-Verify",sec_mechanism->value);
+			}
 		}
 		if (client_state->last_status_code == 494) {
 			ast_sip_remove_headers_by_name_and_value(tdata->msg, &security_client, NULL);
@@ -731,22 +776,21 @@ static void add_security_headers(struct sip_outbound_registration_client_state *
 			/* necessary if a retry occures */
 			add_sec_client_header = (pjsip_msg_find_hdr_by_name(tdata->msg, &security_client, NULL) == NULL) ? 1 : 0;
 		}
-		add_require_header =
-			(pjsip_msg_find_hdr_by_name(tdata->msg, &require, NULL) == NULL) ? 1 : 0;
-		add_proxy_require_header =
-			(pjsip_msg_find_hdr_by_name(tdata->msg, &proxy_require, NULL) == NULL) ? 1 : 0;
 	} else {
-		ast_sip_add_security_headers(&client_state->security_mechanisms, "Security-Client", 0, tdata);
+		add_sec_client_header = (pjsip_msg_find_hdr_by_name(tdata->msg, &security_client, NULL) == NULL) ? 1 : 0;
 	}
 
-	if (add_require_header) {
-		ast_sip_add_header(tdata, "Require", "mediasec");
+	add_require_header = (pjsip_msg_find_hdr_by_name(tdata->msg, &require, NULL) == NULL) ? 1 : 0;
+	add_proxy_require_header = (pjsip_msg_find_hdr_by_name(tdata->msg, &proxy_require, NULL) == NULL) ? 1 : 0;
+
+	if (add_sec_client_header) {
+		ast_sip_add_header(tdata,"Security-Client","sdes-srtp;mediasec");
 	}
 	if (add_proxy_require_header) {
 		ast_sip_add_header(tdata, "Proxy-Require", "mediasec");
 	}
-	if (add_sec_client_header) {
-		ast_sip_add_security_headers(&client_state->security_mechanisms, "Security-Client", 0, tdata);
+	if (add_require_header) {
+		ast_sip_add_header(tdata, "Require", "mediasec");
 	}
 
 	/* Cleanup */
@@ -758,6 +802,28 @@ 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 new transport.
+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;
+	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;
+
+	// Apply new transport
+	ast_free(client_state->transport_name);
+	client_state->transport_name = ast_strdup(newtransport);
+	ast_log_dt(LOG_EVENT_CODE_SV114);
+	ast_debug(2, "REGISTER %s: switch transport to %s\n",
+		client_state->registration_name, client_state->transport_name);
+}
+
 /*! \brief Helper function which sends a message and cleans up, if needed, on failure */
 static pj_status_t registration_client_send(struct sip_outbound_registration_client_state *client_state,
 	pjsip_tx_data *tdata)
@@ -765,6 +831,7 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli
 	pj_status_t status;
 	int *callback_invoked;
 	pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
+	struct ast_sip_transport_state *transport_state;
 
 	callback_invoked = ast_threadstorage_get(&register_callback_invoked, sizeof(int));
 	if (!callback_invoked) {
@@ -782,20 +849,135 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli
 	 */
 	pjsip_tx_data_add_ref(tdata);
 
-	/* Add Security-Verify or Security-Client headers */
-	add_security_headers(client_state, tdata);
-
 	/*
 	 * Set the transport in case transports were reloaded.
 	 * When pjproject removes the extraneous error messages produced,
 	 * we can check status and only set the transport and resend if there was an error
 	 */
-	ast_sip_set_tpselector_from_transport_name(client_state->transport_name, &selector);
+	struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name);
+	if (client_state->Emergency_reg_ongoing && !client_state->Emergency_reg_hangup && (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: %s\n", endpoint->transport_emergency);
+			return -1;
+		}
+		ast_log(LOG_NOTICE,"===== EMERGENCY REGISTER with UDP =====\n");
+
+		// 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 (client_state->Emergency_reg_hangup && client_state->Emergency_call_ongoing != true){
+			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-reg-hangup While E-reg still ongoing, waiting 5s =====\n");
+				return 0;
+			}
+			ast_log(LOG_NOTICE,"===== E-reg-hangup, new registration with primary transport =====\n");
+			if (strcmp(client_state->transport_name, endpoint->transport)) {
+				clean_transport_data_and_apply_new_transport(tdata, client_state, endpoint->transport);
+			}
+			client_state->Emergency_reg_hangup = false;
+			endpoint->Emergency = false;
+	}
+
+	transport_state = ast_sip_get_transport_state(client_state->transport_name);
+	if (transport_state->type == AST_TRANSPORT_TLS && !ast_sip_is_X_RDK_NTP_Synchronized()) {
+		if (ast_strlen_zero(endpoint->transport2) || !strcmp(client_state->transport_name, endpoint->transport2)) {
+			ast_log(LOG_ERROR, "%s: '%s' is unavailable, as NTP is not synchronized\n",
+				client_state->registration_name, client_state->transport_name);
+			return -1;
+		}
+
+		/* Fallback to transport2 */
+		ast_log(LOG_WARNING, "%s: NTP is not synchronized. Transport falls back from '%s' to '%s'\n",
+			client_state->registration_name, client_state->transport_name, endpoint->transport2);
+
+		ast_free(client_state->transport_name);
+		client_state->transport_name = ast_strdup(endpoint->transport2);
+
+		transport_state = ast_sip_get_transport_state(client_state->transport_name);
+	}
+
+	if (client_state->security_negotiation == AST_SIP_SECURITY_NEG_MEDIASEC) {
+		/* Add Security-Verify or Security-Client headers */
+		if (transport_state->type == AST_TRANSPORT_TLS)
+			add_security_headers(client_state, tdata);
+	}
+
+	if (ast_sip_set_tpselector_from_transport_name(client_state->transport_name, &selector)) {
+		ast_log(LOG_ERROR, "Failed to set tpselector for %s\n", client_state->transport_name);
+		return -1;
+	}
 	pjsip_regc_set_transport(client_state->client, &selector);
 	ast_sip_tpselector_unref(&selector);
 
+	ast_debug(2, "%s: Outbound REGISTER sending with transport '%s'\n",
+			client_state->registration_name, client_state->transport_name);
+
+	if (endpoint->register_dest && endpoint->failover_reg_addr == 1) {
+		char addr_buf[64];
+		pj_sockaddr_print(&endpoint->register_dest->addr.entry[endpoint->register_dest->cur_addr].addr, addr_buf, sizeof(addr_buf), 0);
+		ast_log(LOG_NOTICE,"Current Registraion addr: %s\n", addr_buf);
+		pj_strdup2(tdata->pool, &tdata->dest_info.name, endpoint->register_dest->host);
+		memcpy(&tdata->dest_info.addr, &endpoint->register_dest->addr, sizeof(pjsip_server_addresses));
+		tdata->dest_info.cur_addr = endpoint->register_dest->cur_addr;
+
+		if(!ast_sip_failover_request(tdata)){
+			// failover to the next address in the list or go back to the head of the list if current at the end.
+			tdata->dest_info.cur_addr = 0;
+		}
+		pj_sockaddr_print(&tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr, addr_buf, sizeof(addr_buf), 0);
+		ast_log(LOG_NOTICE,"Failover Registration request to addr: %s\n", addr_buf);
+
+	}
+
+	pjsip_sip_uri *sip_uri = pjsip_uri_get_uri(tdata->msg->line.req.uri);
+	sip_uri->port = pj_sockaddr_get_port(&transport_state->host);
+
+	/* Expires header is missing in an outgoing REGISTER request in some use scenarios and that causes the default value
+	 * from the registrar side being used. Here we add Expires header with the configured expiration value if the header
+	 * is absent */
+	if (pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL) == NULL) {
+		struct sip_outbound_registration *reg = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(),
+				"registration", client_state->registration_name);
+		if (reg) {
+			pjsip_expires_hdr *expires_hdr = pjsip_expires_hdr_create(tdata->pool, reg->expiration);
+			if (expires_hdr) {
+				pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr);
+			}
+			ao2_cleanup(reg);
+		}
+	}
+	//if cached_auth, no auth header, call pjsip_auth_clt_init_req
+	if (endpoint->cached_auth == true && (pjsip_msg_find_hdr(tdata->msg, PJSIP_H_AUTHORIZATION, NULL) == NULL)){
+		//check and sync info from auth_sess_inv, nc, nonce
+		pj_str_t realms_pj_str = pj_str(endpoint->realms);
+		ast_mutex_lock(&endpoint->auth_sess_reg_lock);
+		ast_mutex_lock(&endpoint->auth_sess_inv_lock);
+		sync_cached_auth_between_reg_inv(&endpoint->auth_sess_inv, &endpoint->auth_sess_reg, &realms_pj_str);
+		ast_mutex_unlock(&endpoint->auth_sess_inv_lock);
+		ast_debug(3, "Add cached auth header\n");
+		pjsip_auth_clt_init_req(&endpoint->auth_sess_reg, tdata);
+		ast_mutex_unlock(&endpoint->auth_sess_reg_lock);
+	}
+
+	client_state->attempts++;
+	ast_sip_mod_data_set(tdata->pool, tdata->mod_data, log_register_module.id,
+			     "client_state_on_register", client_state);
 	status = pjsip_regc_send(client_state->client, tdata);
 
+	if (tdata->dest_info.addr.count) {
+		int i = tdata->dest_info.cur_addr;
+		char addr[64];
+
+		pj_sockaddr_print(&tdata->dest_info.addr.entry[i].addr, addr, sizeof(addr), 3);
+		ast_debug(2, "%s: Outbound REGISTER attempt %u to address[%d] %s\n",
+			client_state->registration_name,
+			client_state->attempts, i, addr);
+	}
+
 	/*
 	 * If the attempt to send the message failed and the callback was not invoked we need to
 	 * drop the references we just added
@@ -926,12 +1108,6 @@ static int handle_client_registration(void *data)
 				}
 			}
 		}
-		else {
-			ast_debug(1, "Adding default security headers\n");
-			ast_sip_add_header(tdata,"Security-Client","sdes-srtp;mediasec");
-			ast_sip_add_header(tdata,"Proxy-Require","mediasec");
-			ast_sip_add_header(tdata,"Require","mediasec");
-		}
 	}
 
 	registration_client_send(client_state, tdata);
@@ -943,9 +1119,62 @@ static int handle_client_registration(void *data)
 static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
 {
 	struct sip_outbound_registration_client_state *client_state = entry->user_data;
+	struct ast_sip_transport_state *transport = ast_sip_get_transport_state(client_state->transport_name);
 
 	entry->id = 0;
 
+	/* No TLS connection available when NTP is not 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;
+
+		registration = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration", client_state->registration_name);
+		endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name);
+
+		/* If the client first time registers, wait NTP synchronization for some time */
+		if (client_state->status == SIP_REGISTRATION_UNREGISTERED) {
+			pj_time_val delay = { .sec = 0, .msec = 0};
+
+			client_state->failures++;
+
+			if (client_state->failures <= 10)
+				delay.sec = registration->retry_base_time1;  // 30s by default
+			else
+				delay.sec = registration->retry_base_time2;  // 90s by default
+
+			/* Connection retry logic:
+			 * 1) If transport2 exists: Try up to 3 times, then fall back to transport2
+			 * 2) If transport2 does not exist: Retry up to 10 times, then continue waiting with a longer delay
+			 */
+			if ((client_state->failures <= 3 ||
+			    ast_strlen_zero(endpoint->transport2) ||
+			    !strcmp(client_state->transport_name, endpoint->transport2)) &&
+			    client_state->failures <= 10) {
+
+				ast_log(LOG_NOTICE, "%s: NTP is not synchronized. Try TLS connection later after %ld seconds\n",
+						client_state->registration_name, delay.sec);
+				pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &client_state->timer, &delay);
+
+				return;
+			}
+
+			client_state->failures = 0;
+		}
+
+		if (ast_strlen_zero(endpoint->transport2) || !strcmp(client_state->transport_name, endpoint->transport2)) {
+			ast_log(LOG_ERROR, "%s: '%s' is unavailable, as NTP is not synchronized\n",
+				client_state->registration_name, client_state->transport_name);
+			return;
+		}
+
+		/* Fallback to transport2 */
+		ast_log(LOG_WARNING, "%s: NTP is not synchronized. Transport falls back from '%s' to '%s'\n",
+			client_state->registration_name, client_state->transport_name, endpoint->transport2);
+
+		ast_free(client_state->transport_name);
+		client_state->transport_name = ast_strdup(endpoint->transport2);
+	}
+
 	/*
 	 * Transfer client_state reference to serializer task so the
 	 * nominal path will not dec the client_state ref in this
@@ -966,7 +1195,8 @@ static void schedule_registration(struct sip_outbound_registration_client_state
 	cancel_registration(client_state);
 
 	pjsip_regc_get_info(client_state->client, &info);
-	ast_debug(1, "Scheduling outbound registration to server '%.*s' from client '%.*s' in %d seconds\n",
+	ast_debug(1, "Scheduling %s outbound registration to server '%.*s' from client '%.*s' in %d seconds\n",
+			client_state->registration_name,
 			(int) info.server_uri.slen, info.server_uri.ptr,
 			(int) info.client_uri.slen, info.client_uri.ptr,
 			seconds);
@@ -978,7 +1208,11 @@ static void schedule_registration(struct sip_outbound_registration_client_state
 				(int) info.client_uri.slen, info.client_uri.ptr);
 		ao2_ref(client_state, -1);
 	}
-	client_state->registration_expires = ((int) time(NULL)) + seconds;
+	struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name);
+	if (!(endpoint && endpoint->failover_reg_addr == 1)){
+		// do not update expires time for 2nd reg while 504 with xml, required while 2nd reg failed.
+		client_state->registration_expires = ((int) time(NULL)) + seconds;
+	}
 }
 
 static void update_client_state_status(struct sip_outbound_registration_client_state *client_state, enum sip_outbound_registration_status status)
@@ -1042,11 +1276,24 @@ static int handle_client_state_destruction(void *data)
 				(int) info.server_uri.slen, info.server_uri.ptr,
 				(int) info.client_uri.slen, info.client_uri.ptr);
 
+			struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
+                                                client_state->registration_name);
+			if (endpoint && endpoint->failover_reg_addr == 1){
+				// skip unregister if INVITE failed with 504 xml. request could have no response and will messed up with following re-register
+				return 0;
+			}
+
 			update_client_state_status(client_state, SIP_REGISTRATION_STOPPING);
 			client_state->destroy = 1;
 			if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS
 				&& add_configured_supported_headers(client_state, tdata)
 				&& registration_client_send(client_state, tdata) == PJ_SUCCESS) {
+				pjsip_uri *uri = pjsip_parse_uri(pjsip_regc_get_pool(client_state->client), info.client_uri.ptr, (int) info.client_uri.slen, 0);
+				pjsip_sip_uri *sip_uri = pjsip_uri_get_uri(uri);
+				if (sip_uri) {
+					ast_log_dt(LOG_EVENT_CODE_IPX002, (int) sip_uri->user.slen, sip_uri->user.ptr,
+													  (int) sip_uri->host.slen, sip_uri->host.ptr);
+				}
 				ao2_ref(client_state, -1);
 				return 0;
 			}
@@ -1061,7 +1308,25 @@ static int handle_client_state_destruction(void *data)
 		pjsip_regc_destroy(client_state->client);
 		client_state->client = NULL;
 	}
-
+	struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name);
+	if(endpoint){
+		if(endpoint->auth_sess_reg.endpt){
+			pjsip_auth_clt_deinit(&endpoint->auth_sess_reg);
+			if (endpoint->auth_sess_reg.pool) {
+				pj_pool_release(endpoint->auth_sess_reg.pool);
+				endpoint->auth_sess_reg.pool = NULL;
+			}
+			ast_mutex_destroy(&endpoint->auth_sess_reg_lock);
+		}
+		if(endpoint->auth_sess_inv.endpt){
+			pjsip_auth_clt_deinit(&endpoint->auth_sess_inv);
+			if (endpoint->auth_sess_inv.pool) {
+				pj_pool_release(endpoint->auth_sess_inv.pool);
+				endpoint->auth_sess_inv.pool = NULL;
+			}
+			ast_mutex_destroy(&endpoint->auth_sess_inv_lock);
+		}
+	}
 	update_client_state_status(client_state, SIP_REGISTRATION_STOPPED);
 	ast_sip_auth_vector_destroy(&client_state->outbound_auths);
 	ast_sip_security_mechanisms_vector_destroy(&client_state->security_mechanisms);
@@ -1096,13 +1361,16 @@ static void registration_response_destroy(void *obj)
 
 	if (response->rdata) {
 		pjsip_rx_data_free_cloned(response->rdata);
+		response->rdata = NULL;
 	}
 
 	if (response->old_request) {
 		pjsip_tx_data_dec_ref(response->old_request);
+		response->old_request = NULL;
 	}
 
 	ao2_cleanup(response->client_state);
+	response->client_state = NULL;
 }
 
 /*! \brief Helper function which determines if a response code is temporal or not */
@@ -1125,7 +1393,7 @@ static int sip_outbound_registration_is_temporal(unsigned int code,
 	}
 }
 
-static int sip_outbound_registration_send_ubus_event(char *ev_name,int time ,char *client)
+static int sip_outbound_registration_send_ubus_event(char *ev_name, int time, char *client)
 {
 	struct blob_buf blob;
 	int res = 0;
@@ -1159,7 +1427,7 @@ static int sip_outbound_registration_send_ubus_event(char *ev_name,int time ,cha
 }
 
 static void schedule_retry(struct registration_response *response, unsigned int interval,
-			   const char *server_uri, const char *client_uri)
+			   const char *server_uri, const char *client_uri, const char *client_num)
 {
 	update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
 	schedule_registration(response->client_state, interval);
@@ -1168,10 +1436,12 @@ static void schedule_retry(struct registration_response *response, unsigned int
 		ast_log(LOG_WARNING, "Temporal response '%d' received from '%s' on "
 			"registration attempt to '%s', retrying in '%u'\n",
 			response->code, server_uri, client_uri, interval);
+		ast_log_dt(LOG_EVENT_CODE_V002, client_num, "Temporal fail response received");
 	} else {
 		ast_log(LOG_WARNING, "No response received from '%s' on "
 			"registration attempt to '%s', retrying in '%u'\n",
 			server_uri, client_uri, interval);
+		ast_log_dt(LOG_EVENT_CODE_V002, client_num, "No response received");
 	}
 	sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri);
 }
@@ -1377,49 +1647,206 @@ static void mwi_send_subscribe(struct ast_sip_endpoint *endpoint, pj_uint32_t ex
 		ast_log(LOG_WARNING, "No contacts configured for endpoint %s. Unable to create MWI subsription\n",
 				ast_sorcery_object_get_id(endpoint));
 		ao2_cleanup(contact);
-		return NULL;
+		return;
 	}
 
 	dlg = ast_sip_create_dialog_uac(endpoint, contact->uri, NULL);
 	ao2_cleanup(contact);
 	if (!dlg) {
 		ast_log(LOG_WARNING, "Unable to create dialog for MWI subscription\n");
-		return NULL;
+		return;
 	}
 
 	pjsip_mwi_init_module(ast_sip_get_pjsip_endpoint(), pjsip_mwi_instance());
-	pjsip_mwi_create_uac(dlg, &mwi_cb, NULL, &evsub);
+	pjsip_mwi_create_uac(dlg, &mwi_cb, 0, &evsub);
 	pjsip_evsub_set_mod_data( evsub, mod_mwi.id, endpoint);
 	pjsip_mwi_initiate (evsub, expires, &tdata);
 	pjsip_mwi_send_request (evsub, tdata);
 }
 
+/* Check if any client has been registered in the same device */
+static bool has_registered(void)
+{
+	bool result = false;
+
+	struct sip_outbound_registration *registration;
+	struct ao2_container *registrations;
+	struct ao2_iterator i;
+	struct sip_outbound_registration_state *state;
+
+	registrations = get_registrations();
+	if (!registrations) {
+		return false;
+	}
+
+	i = ao2_iterator_init(registrations, 0);
+	while ((registration = ao2_iterator_next(&i))) {
+		state = get_state(ast_sorcery_object_get_id(registration));
+		if (state && state->client_state->status == SIP_REGISTRATION_REGISTERED) {
+			result = true;
+		}
+		ao2_ref(state, -1);
+		ao2_ref(registration, -1);
+		if (result) {
+			break;
+		}
+	}
+	ao2_iterator_destroy(&i);
+	ao2_ref(registrations, -1);
+	return result;
+}
+
+/* REGISTER attempts between two addresses in the record list */
+static ast_sched_cb registration_resend(struct registration_response *response)
+{
+	struct sip_outbound_registration_client_state *client_state = response->client_state;
+	pjsip_tx_data *tdata = response->old_request;
+	pjsip_via_hdr *via;
+
+	if (!client_state || client_state->registration_resend_timer_id == -1 || !tdata) {
+		ast_debug(2, "Expired registration request, skipping\n");
+		return 0;
+	}
+
+	ast_debug(2, "%s: resend registration on %s\n",
+			client_state->registration_name, client_state->transport_name);
+	client_state->registration_resend_timer_id = -1;
+	if (client_state->status == SIP_REGISTRATION_STOPPED) {
+		ao2_ref(response, -1);
+		return 0;
+	}
+
+	via = (pjsip_via_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+	if (via)
+		via->branch_param.slen = 0;
+
+	registration_client_send(client_state, tdata);
+	/* The tdata ref was stolen */
+	response->old_request = NULL;
+	ao2_ref(response, -1);
+	return 0;
+}
+
+/* switch to next server address if possible */
+static int sip_registration_failover(struct registration_response *response)
+{
+	pjsip_tx_data *tdata = response->old_request;
+
+	if (tdata && ast_sip_failover_request(tdata))
+		return 1;
+
+	ast_debug(2, "%s: No more address to try\n", response->client_state->registration_name);
+
+	return 0;
+}
+
+/* switch to transport2 if possible */
+static int sip_registration_transport2(struct registration_response *response)
+{
+	struct sip_outbound_registration_client_state *client_state = response->client_state;
+	pjsip_tx_data *tdata = response->old_request;
+	struct ast_sip_endpoint *endpoint = NULL;
+
+	endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name);
+	if (endpoint && !ast_strlen_zero(endpoint->transport2)) {
+		if (ast_strlen_zero(client_state->transport_name) || strcmp(client_state->transport_name, endpoint->transport2)) {
+
+			ast_log(LOG_NOTICE, "%s: Registration transport fallback to '%s'\n", client_state->registration_name, endpoint->transport2);
+
+			// Clean current transport data. Then, pjsip will resolve with transport2.
+			clean_transport_data_and_apply_new_transport(tdata, client_state, endpoint->transport2);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+// Reset the transport back to the primary transport
+static void reset_transport_to_primary(struct sip_outbound_registration_client_state *client_state, struct ast_sip_endpoint *endpoint, struct registration_response *response, const char *client_num){
+	if (strcmp(client_state->transport_name, endpoint->transport)) {
+		ast_free(client_state->transport_name);
+		client_state->transport_name = ast_strdup(endpoint->transport);
+
+	}else if (strncmp(client_state->transport_name, "transport-tls", 13) == 0){
+		if ( (NULL != response->old_request) && (NULL != response->old_request->msg) ){
+			pjsip_uri *uri = response->old_request->msg->line.req.uri;
+			if ( NULL != uri ){
+				pjsip_sip_uri *sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
+				if ( NULL != sip_uri ){
+					ast_log_dt(LOG_EVENT_CODE_SV116, client_num, sip_uri->host);
+				}
+			}
+	    }
+	}
+}
+static ast_sched_cb reset_504_flag_after_failed(struct ast_sip_endpoint *endpoint){
+	endpoint->failover_reg_addr = 0;
+	endpoint->failover_reg_addr_timer_id = -1;
+	return 0;
+}
 /*! \brief Callback function for handling a response to a registration attempt */
 static int handle_registration_response(void *data)
 {
 	struct registration_response *response = data;
+	struct sip_outbound_registration_client_state *client_state = response->client_state;
 	pjsip_regc_info info;
 	char server_uri[PJSIP_MAX_URL_SIZE];
 	char client_uri[PJSIP_MAX_URL_SIZE];
-
-	if (response->client_state->status == SIP_REGISTRATION_STOPPED) {
+	struct sip_outbound_registration *registration = NULL;
+	pjsip_tx_data *tdata = response->old_request;
+	char s_uri[PJSIP_MAX_URL_SIZE];
+	char c_uri[PJSIP_MAX_URL_SIZE];
+	char * client_addr = NULL;
+	char *client_num = NULL;
+	char * saveptr = NULL;
+
+	if (client_state->status == SIP_REGISTRATION_STOPPED) {
 		ao2_ref(response, -1);
 		return 0;
 	}
 
-	pjsip_regc_get_info(response->client_state->client, &info);
+	registration = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration", client_state->registration_name);
+	if (!registration) {
+		ast_log(LOG_ERROR, "No registration found for '%s'\n", client_state->registration_name);
+		return -1;
+	}
+
+	pjsip_regc_get_info(client_state->client, &info);
 	ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
 	ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
-	response->client_state->last_status_code = response->code;
+	client_state->last_status_code = response->code;
+
+	if (tdata && tdata->dest_info.addr.count){
+		char addr[AST_SOCKADDR_BUFLEN] = {'\0'};
+		pj_sockaddr_print(&tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr, addr, sizeof(addr), 3);
+		ast_debug(1, "Processing response %d from %s(%s) for %s(%s)\n",
+			response->code, addr, server_uri,
+			client_state->registration_name, client_uri);
+	} else {
+		ast_debug(1, "Processing response %d from %s for %s(%s)\n",
+			response->code, server_uri, client_state->registration_name, client_uri);
+		ast_debug(1, "No destination addresses available (tdata=%p, addr.count=%d)\n"
+			"Setting client's retry counter to max_retries(%d) immediately\n",
+			tdata, tdata ? tdata->dest_info.addr.count : 0, client_state->max_retries);
+		client_state->retries = client_state->max_retries;
+	}
+
+
+	strncpy(s_uri, server_uri, sizeof(s_uri)-1);
+	strtok_r(s_uri, ":", &saveptr);
+	client_addr = strtok_r(NULL, ":", &saveptr);
+	strncpy(c_uri, client_uri, sizeof(c_uri)-1);
+	saveptr  = NULL;
+	strtok_r(c_uri, ":", &saveptr);
+	client_num = strtok_r(NULL, "@", &saveptr);
 
-	ast_debug(1, "Processing REGISTER response %d from server '%s' for client '%s'\n",
-			response->code, server_uri, client_uri);
 
 	if (response->code == 401 || response->code == 407 || response->code == 494) {
 		/* Store MEDIASEC headers */
-		if (response->client_state->mediasec) {
+		if (client_state->mediasec) {
 			struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
-					response->client_state->registration_name);
+					client_state->registration_name);
 			static const pj_str_t headerName = { "Security-Server", 15 };
 			pjsip_generic_string_hdr *secSrv = NULL;
 
@@ -1431,7 +1858,7 @@ static int handle_registration_response(void *data)
 				secSrv = pjsip_msg_find_hdr_by_name(response->rdata->msg_info.msg, &headerName, NULL);
 				struct security_mechanism *sec_mechanism;
 				while (secSrv) {
-					response->client_state->is494=0;
+					client_state->is494=0;
 
 					sec_mechanism = ast_calloc(1, sizeof(*sec_mechanism) + pj_strlen(&secSrv->hvalue));
 					if (!sec_mechanism) {
@@ -1448,77 +1875,98 @@ static int handle_registration_response(void *data)
 		}
 	}
 
-	if (response->code == 408 || response->code == 503 || response->code == 500) {
-		if ((ast_sip_failover_request(response->old_request))) {
-			int res = registration_client_send(response->client_state, response->old_request);
-			/* The tdata ref was stolen */
-			response->old_request = NULL;
-			if (res == PJ_SUCCESS) {
-				ao2_ref(response, -1);
-				return 0;
+	if (response->code == 408 || response->code == 444 || response->code == 503 || response->code == 500 ||
+			response->code == 504 || response->code == 600) {
+		int retry = 1;
+		int waiting_time = registration->retry_after;
+
+		ast_log(LOG_NOTICE, "%s: Registration failed %d times (code=%d)\n",
+			client_state->registration_name, client_state->failures, response->code);
+		struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name);
+		// Emergency registration related failure case
+		if (client_state->Emergency_reg_hangup){
+			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-reg-hangup queued, stop further procedure and waiting for normal reg.
+				client_state->Emergency_reg_ongoing=false;
+				ast_log(LOG_NOTICE,"===== E-reg failed while E-reg-hangup queued =====\n");
 			}
+			ast_log(LOG_NOTICE,"===== E-reg-hangup and start normal reg =====\n");
+			client_state->registration_resend_timer_id = ast_sched_add(sched, 1, registration_resend, response);
+			return 0;
+		}
+		if((!response->retry_after && endpoint && endpoint->failover_reg_addr == 1) && (tdata && tdata->dest_info.addr.count)){
+			// 2nd registration on 504 with xml failed, back to the original registration schedule.
+			endpoint->failover_reg_addr = 2;
+
+			int next_round = client_state->registration_expires - ((int) time(NULL));
+			if(next_round>3){
+				// update the status back to the original
+				update_client_state_status(client_state, SIP_REGISTRATION_REGISTERED);
+			} else {
+				next_round=3; // wait 3s(2s for reseting the flag + 1s in case of delay) to make sure flag been reset before the new registration.
+			}
+			client_state->retries = 0;
+			client_state->failover_retries = 0;
+			client_state->waiting_time_upper_bound = 0;
+			client_state->failures = 0;
+			client_state->attempts = 0;
+			schedule_registration(client_state, next_round);
+			// reset flag after 2s, to make sure chan_voicemngr could retrieve the flag info.
+			endpoint->failover_reg_addr_timer_id = ast_sched_add(sched, 2000, reset_504_flag_after_failed, endpoint);
+			return 0;
+		}
+
+		if (response->retry_after){
+			waiting_time = response->retry_after;
+		} else if (response->code == 444 || client_state->attempts >= 2 || !tdata || !tdata->dest_info.addr.count ||  client_state->waiting_time_upper_bound) {
+			/* failover directly when not received any response(444 from pjsip), or received twice with other response, or under recovery-flow(second round),
+			   or no DNS records are found */
+			client_state->attempts = 0;
+			retry = sip_registration_failover(response);
+		}
+
+		if (retry) {
+			ast_log_dt(LOG_EVENT_CODE_V002, client_num, "Bad response received");
+			update_client_state_status(client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
+			sip_outbound_registration_send_ubus_event("UNREGISTERED", response->expiration, client_uri);
+
+			if (response->code == 444 || client_state->attempts == 0){
+				ast_debug(1, "%s: Registration re-try to the next address immediately.\n", client_state->registration_name);
+				client_state->registration_resend_timer_id = ast_sched_add(sched, 1, registration_resend, response);
+			} else {
+				ast_debug(1, "%s: Registration re-try after %d seconds.\n", client_state->registration_name, waiting_time);
+				client_state->registration_resend_timer_id = ast_sched_add(sched, waiting_time * 1000, registration_resend, response);
+			}
+			return 0;
+		} else if (client_state->Emergency_reg_ongoing) {
+			ast_log(LOG_NOTICE,"===== E-reg failed on all addr before E-reg-hangup being triggered, stop and send normal reg direct=====\n");
+			client_state->Emergency_reg_ongoing=false;
+			client_state->Emergency_reg_hangup=true;
+			update_client_state_status(client_state, SIP_REGISTRATION_UNREGISTERED);
+			sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri);
+			client_state->registration_resend_timer_id = ast_sched_add(sched, 1, registration_resend, response);
+			return 0;
 		}
-	// change the condition to use iopsys mediasec implementation instead of asterisk one
-	//} else if ((response->code == 401 || response->code == 407 || response->code == 494)
 	} else if ((response->code == 401 || response->code == 407)
-		&& (!response->client_state->auth_attempted
-			|| response->rdata->msg_info.cseq->cseq != response->client_state->auth_cseq)) {
+		&& (!client_state->auth_attempted
+			|| response->rdata->msg_info.cseq->cseq != client_state->auth_cseq)) {
 		int res;
 		pjsip_cseq_hdr *cseq_hdr;
 		pjsip_tx_data *tdata;
 
-		// comment out this code to use iopsys mediasec implementation instead of asterisk one
-		//if (response->client_state->security_negotiation == AST_SIP_SECURITY_NEG_MEDIASEC) {
-		//	struct sip_outbound_registration *reg = NULL;
-		//	struct ast_sip_endpoint *endpt = NULL;
-		//	struct ao2_container *contact_container = NULL;
-		//	pjsip_generic_string_hdr *header;
-		//	struct pjsip_generic_string_hdr_vector header_vector;
-		//	static const pj_str_t security_server = { "Security-Server", 15 };
-		//
-		//	if ((reg = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration",
-		//		response->client_state->registration_name)) && reg->endpoint &&
-		//		(endpt = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", reg->endpoint))) {
-		//		/* Retrieve all contacts associated with aors from this endpoint (if set). */
-		//		contact_container = ast_sip_location_retrieve_contacts_from_aor_list(endpt->aors);
-		//	}
-		//	/* Add server list of security mechanism to client_state and contact status if exists. */
-		//	AST_VECTOR_INIT(&header_vector, 1);
-		//	header = pjsip_msg_find_hdr_by_name(response->rdata->msg_info.msg, &security_server, NULL);
-		//	for (; header;
-		//		header = pjsip_msg_find_hdr_by_name(response->rdata->msg_info.msg, &security_server, header->next)) {
-		//		AST_VECTOR_APPEND(&header_vector, header);
-		//		ast_sip_header_to_security_mechanism(header, &response->client_state->server_security_mechanisms);
-		//	}
-		//	if (contact_container) {
-		//		/* Add server security mechanisms to contact status of all associated contacts to be able to send correct
-		//		 * Security-Verify headers on subsequent non-REGISTER requests through this outbound registration.
-		//		 */
-		//		ao2_callback(contact_container, OBJ_NODATA, contact_add_security_headers_to_status, &header_vector);
-		//		ao2_cleanup(contact_container);
-		//	}
-		//	AST_VECTOR_FREE(&header_vector);
-		//	ao2_cleanup(endpt);
-		//	ao2_cleanup(reg);
-		//}
-		//
-		//if (response->code == 494) {
-		//	update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
-		//	response->client_state->retries++;
-		//	schedule_registration(response->client_state, 0);
-		//	ao2_ref(response, -1);
-		//	return 0;
-		//} else if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths,
-		if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths,
+		if (!ast_sip_create_request_with_auth(&client_state->outbound_auths,
 				response->rdata, response->old_request, &tdata)) {
-			response->client_state->auth_attempted = 1;
+			client_state->auth_attempted = 1;
 			ast_debug(1, "Sending authenticated REGISTER to server '%s' from client '%s'\n",
 					server_uri, client_uri);
 
 			/* Add MEDIASEC headers */
-			if (response->client_state->mediasec) {
+			if (client_state->mediasec) {
 				struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
-						response->client_state->registration_name);
+						client_state->registration_name);
 
 				if (!endpoint) {
 					ast_log(LOG_ERROR, "No endpoint found to store/add mediasec headers\n");
@@ -1535,14 +1983,16 @@ static int handle_registration_response(void *data)
 
 			pjsip_tx_data_add_ref(tdata);
 
-			res = registration_client_send(response->client_state, tdata);
+			res = registration_client_send(client_state, tdata);
 
 			/* Save the cseq that actually got sent. */
 			cseq_hdr = (pjsip_cseq_hdr *) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ,
 				NULL);
-			response->client_state->auth_cseq = cseq_hdr->cseq;
+			client_state->auth_cseq = cseq_hdr->cseq;
 			pjsip_tx_data_dec_ref(tdata);
 			if (res == PJ_SUCCESS) {
+				client_state->failures--; // not count for auth re-send
+				client_state->attempts--; // not count for auth re-send
 				ao2_ref(response, -1);
 				return 0;
 			}
@@ -1556,9 +2006,27 @@ static int handle_registration_response(void *data)
 		ast_sip_resolve_track_cur_addr(response->old_request);
 	}
 
-	response->client_state->auth_attempted = 0;
+	client_state->auth_attempted = 0;
+	client_state->cached_security_verify = 0;
 
 	if (PJSIP_IS_STATUS_IN_CLASS(response->code, 200)) {
+		struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name);
+		static const pj_str_t security_verify = { "Security-Verify", 15 };
+
+		if(response->old_request && pjsip_msg_find_hdr(response->old_request->msg, PJSIP_H_AUTHORIZATION, NULL)){
+			if(endpoint){
+				endpoint->cached_auth = true;
+			}
+			ast_debug(8, "Outbound registration request success with Authorization.\n");
+			if(pjsip_msg_find_hdr_by_name(response->old_request->msg, &security_verify, NULL)){
+				client_state->cached_security_verify = 1;
+				ast_debug(8, "Outbound registration request success with Security-Verify.\n");
+			}
+		} else {
+			if(endpoint){
+				endpoint->cached_auth = false;
+			}
+		}
 		/* Check if this is in regards to registering or unregistering
 		 * Retrieve the requested expiration if possible,
 		 * and check it combined with the received expiration to identify if it is reg or un-reg
@@ -1568,27 +2036,71 @@ static int handle_registration_response(void *data)
 		if(response->old_request && (expires = pjsip_msg_find_hdr(response->old_request->msg, PJSIP_H_EXPIRES, NULL))){
 			exp_expiration = expires->ivalue;
 		}
+		// DNS resolve: stick to the server address
+		ast_sip_resolve_track_cur_addr(response->old_request);
+
+		save_response_fields_to_transport(response);
+
 		if (exp_expiration && response->expiration) {
 			int next_registration_round;
 
-			response->client_state->is494=0;
+			client_state->is494=0;
+
+			/* update register dest info */
+			if (endpoint->register_dest) {
+				if (endpoint->register_dest->host)
+					ast_free(endpoint->register_dest->host);
+
+				ast_free(endpoint->register_dest);
+			}
+
+			endpoint->register_dest = ast_malloc(sizeof(struct pjsip_register_dest));
+
+			endpoint->register_dest->cur_addr = tdata->dest_info.cur_addr;
+			memcpy(&endpoint->register_dest->addr, &tdata->dest_info.addr, sizeof(pjsip_server_addresses));
+
+			endpoint->register_dest->host = ast_calloc(1, pj_strlen(&tdata->dest_info.name) + 1);
+			ast_copy_pj_str(endpoint->register_dest->host, &tdata->dest_info.name, pj_strlen(&tdata->dest_info.name) + 1);
+			char addr_buf[AST_SOCKADDR_BUFLEN] = {'\0'};
+			pj_sockaddr_print(&endpoint->register_dest->addr.entry[endpoint->register_dest->cur_addr].addr, addr_buf, sizeof(addr_buf), 1);
 			/* If the registration went fine simply reschedule registration for the future */
-			ast_debug(1, "Outbound registration to '%s' with client '%s' successful\n", server_uri, client_uri);
-			update_client_state_status(response->client_state, SIP_REGISTRATION_REGISTERED);
+			ast_debug(1, "Outbound registration to '%s' with client '%s' successful to addr: %s\n", server_uri, client_uri, addr_buf);
+
+			update_client_state_status(client_state, SIP_REGISTRATION_REGISTERED);
+			if (endpoint->register_transport)
+				ast_free(endpoint->register_transport);
+			endpoint->register_transport = ast_strdup(client_state->transport_name);
+			ast_log_dt(LOG_EVENT_CODE_V101, client_num, client_addr);
 			sip_outbound_registration_send_ubus_event("REGISTERED",response->expiration,client_uri);
-			response->client_state->retries = 0;
-			response->client_state->failover_retries = 0;
-			next_registration_round = response->expiration - REREGISTER_BUFFER_TIME;
+			client_state->retries = 0;
+			client_state->failover_retries = 0;
+			client_state->waiting_time_upper_bound = 0;
+			client_state->failures = 0;
+			client_state->attempts = 0;
+			next_registration_round = (response->expiration * client_state->retry_percent) / 100;
+			ast_debug(1, "Setting next retry in %d seconds (%d percent of %d)\n",
+				next_registration_round, client_state->retry_percent, response->expiration);
+
 			if (next_registration_round < 0) {
 				/* Re-register immediately. */
 				next_registration_round = 0;
 			}
-			schedule_registration(response->client_state, next_registration_round);
+
+			if (client_state->Emergency_reg_ongoing){
+				endpoint->Emergency=true;
+				client_state->Emergency_reg_ongoing=false;
+			} else if (!endpoint->Emergency) {
+				// Reset the transport back to the primary transport
+				reset_transport_to_primary(client_state, endpoint, response, client_num);
+			}
+			endpoint->failover_reg_addr = 0;
+
+			schedule_registration(client_state, next_registration_round);
 
 			/* See if we should monitor for transport shutdown */
 			if (PJSIP_TRANSPORT_IS_RELIABLE(response->rdata->tp_info.transport)) {
 				registration_transport_monitor_setup(response->transport_key,
-					response->client_state->registration_name);
+					client_state->registration_name);
 			}
 		} else {
 			if (!exp_expiration && !response->expiration) {
@@ -1597,102 +2109,177 @@ static int handle_registration_response(void *data)
 				// case1: unregistration request with expiration=0, but got response with non-zero expiration;
 				// case2: registration request with non-zero expiration but got expiration=0 in response
 				ast_debug(1, "Outbound registration/unregistration to '%s' with client '%s' failed, expiration not valid\n", server_uri, client_uri);
+				ast_log_dt(LOG_EVENT_CODE_V002, client_num, "Expiration not valid");
 			}
-			response->client_state->is494=0;
-			update_client_state_status(response->client_state, SIP_REGISTRATION_UNREGISTERED);
+			client_state->is494=0;
+			update_client_state_status(client_state, SIP_REGISTRATION_UNREGISTERED);
 			sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri);
+
 			if (PJSIP_TRANSPORT_IS_RELIABLE(response->rdata->tp_info.transport)) {
 				ast_sip_transport_monitor_unregister_key(response->transport_key,
-					registration_transport_shutdown_cb, response->client_state->registration_name,
+					registration_transport_shutdown_cb, client_state->registration_name,
 					monitor_matcher);
 			}
 		}
 
-		save_response_fields_to_transport(response);
-
-		struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", response->client_state->registration_name);
 		if (strlen(endpoint->incoming_mwi_mailbox))
 			mwi_send_subscribe(endpoint, response->expiration);
 
-	} else if (response->client_state->destroy) {
+		if (endpoint){
+			if (endpoint->realms){
+				ast_mutex_lock(&endpoint->auth_sess_reg_lock);
+				check_and_update_nextnonce(&endpoint->auth_sess_reg, endpoint->realms, response->rdata);
+				ast_mutex_unlock(&endpoint->auth_sess_reg_lock);
+			}
+			if (endpoint->cached_auth){
+				//if cached, sync to auth_sess_inv, nc, nonce
+				pj_str_t realms_pj_str = pj_str(endpoint->realms);
+				ast_debug(3, "Sync cached auth sess from auth_sess_reg to auth_sess_inv\n");
+				ast_mutex_lock(&endpoint->auth_sess_reg_lock);
+				ast_mutex_lock(&endpoint->auth_sess_inv_lock);
+				sync_cached_auth_between_reg_inv(&endpoint->auth_sess_reg, &endpoint->auth_sess_inv, &realms_pj_str); // (from, to, realm)
+				ast_mutex_unlock(&endpoint->auth_sess_inv_lock);
+				ast_mutex_unlock(&endpoint->auth_sess_reg_lock);
+			}
+		}
+
+	} else if (client_state->destroy) {
 		/* We need to deal with the pending destruction instead. */
 	} else if (response->code == 494) {
-		if (response->client_state->is494) {
+		if (client_state->is494) {
 			ast_log(LOG_WARNING, "MEDIASEC registration to '%s' with client '%s' failed (494-loop detected), stopping registration attempt\n",
 				server_uri, client_uri);
+			ast_log_dt(LOG_EVENT_CODE_V002, client_num, "494-loop detected");
 			/* 494 loop detected! This is fatal! */
-			update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
+			update_client_state_status(client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
 			sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri);
+
 			/* reset is494 */
-			response->client_state->is494=0;
+			client_state->is494=0;
 		} else {
 			/* Try (initial) registration again - but now with additional headers */
-			response->client_state->is494=1;
-			ao2_ref(response->client_state, +1);
-			handle_client_registration(response->client_state);
+			client_state->is494=1;
+			ao2_ref(client_state, +1);
+			handle_client_registration(client_state);
 			ao2_ref(response, -1);
 			return 0;
 		}
-	} else if (response->retry_after) {
+	} else if (response->retry_after && tdata && tdata->dest_info.addr.count) {
 		/* If we have been instructed to retry after a period of time, schedule it as such */
-		schedule_retry(response, response->retry_after, server_uri, client_uri);
-	} else if (response->client_state->failover_max_retries &&
-			(response->code == 408 || response->code == 503 || response->code == 500)) {
-		if (response->client_state->failover_max_retries >= 0 &&
-				response->client_state->failover_retries >= response->client_state->failover_max_retries) {
-			update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
+		schedule_retry(response, response->retry_after, server_uri, client_uri, client_num);
+	} else if (client_state->failover_max_retries &&
+			(response->code == 408 || response->code == 444 || response->code == 503 || response->code == 500)) {
+		if (client_state->failover_max_retries >= 0 &&
+				client_state->failover_retries >= client_state->failover_max_retries) {
+			update_client_state_status(client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
 			sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri);
-			ast_log(LOG_WARNING, "Maximum failover retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n",
-				server_uri, client_uri);
+
+			ast_log(LOG_WARNING, "Maximum failover retries reached when attempting outbound registration to '%s' with "
+					"client '%s', stopping registration attempt\n", server_uri, client_uri);
+			ast_log_dt(LOG_EVENT_CODE_V002, client_num, "Maximum failover retries reached");
+			struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name);
+			if ((strncmp(endpoint->transport, "transport-tls", 13) == 0) && ast_strlen_zero(endpoint->transport2)){
+				ast_log_dt(LOG_EVENT_CODE_SV113);
+			} else if(!ast_strlen_zero(endpoint->transport2) && strncmp(client_state->transport_name, "transport-udp", 13)) {
+				ast_log_dt(LOG_EVENT_CODE_SV112);
+			}
 		} else {
 			unsigned int retry_interval;
 			// If tried all addresses for 3 times, then start to use retry interval2, otherwise interval1
-			++response->client_state->failover_retries;
-			if (!response->old_request || response->client_state->failover_retries % (3 * response->old_request->dest_info.addr.count))
-				retry_interval = response->client_state->failover_retry_interval1;
+			if (response->old_request && ++client_state->failover_retries % (3 * response->old_request->dest_info.addr.count))
+				retry_interval = client_state->failover_retry_interval1;
 			else
-				retry_interval = response->client_state->failover_retry_interval2;
+				retry_interval = client_state->failover_retry_interval2;
 
-			ast_log(LOG_NOTICE, "Schedule failover retry [%d] in %d seconds", response->client_state->failover_retries, retry_interval);
-			schedule_retry(response, retry_interval, server_uri, client_uri);
+			ast_log(LOG_NOTICE, "Schedule failover retry [%d] in %d seconds", client_state->failover_retries, retry_interval);
+			client_state->retries++;
+			schedule_retry(response, retry_interval, server_uri, client_uri, client_num);
+		}
+	} else if (client_state->retry_interval &&
+		   sip_outbound_registration_is_temporal(response->code, client_state)) {
+		if (!tdata || !tdata->dest_info.addr.count) {
+			client_state->retries = client_state->max_retries;
+			ast_log(LOG_ERROR, "No destination addresses available (tdata=%p, addr.count=%d)\n"
+				"Setting client's retry counter to max_retries(%d) immediately\n",
+				tdata, tdata ? tdata->dest_info.addr.count : 0, client_state->max_retries);
 		}
-	} else if (response->client_state->retry_interval
-		&& sip_outbound_registration_is_temporal(response->code, response->client_state)) {
-		if (response->client_state->retries == response->client_state->max_retries) {
+
+		if (client_state->retries == client_state->max_retries) {
 			/* If we received enough temporal responses to exceed our maximum give up permanently */
-			update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
+			update_client_state_status(client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
 			sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri);
+
 			ast_log(LOG_WARNING, "Maximum retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n",
 				server_uri, client_uri);
+			ast_log_dt(LOG_EVENT_CODE_V002, client_num, "Maximum retries reached");
+			struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name);
+			if ((strncmp(endpoint->transport, "transport-tls", 13) == 0) && ast_strlen_zero(endpoint->transport2)){
+				ast_log_dt(LOG_EVENT_CODE_SV113);
+			} else if((strncmp(client_state->transport_name, "transport-udp", 13) == 0) &&!ast_strlen_zero(endpoint->transport2)) {
+				ast_log_dt(LOG_EVENT_CODE_SV112);
+			}
 		} else {
 			/* On the other hand if we can still try some more do so */
-			response->client_state->retries++;
-			schedule_retry(response, response->client_state->retry_interval, server_uri, client_uri);
+			client_state->retries++;
+			schedule_retry(response, client_state->retry_interval, server_uri, client_uri, client_num);
+		}
+	} else if (response->code == 408 || response->code == 444 || response->code == 503 || response->code == 500 || response->code == 504 || response->code == 600) {
+		int waiting_time = registration->retry_after;
+
+		// All addresses had been tried, now try transport2 if possible
+		if ((client_state->failures >= 2 || !tdata || !tdata->dest_info.addr.count) && !sip_registration_transport2(response)) {
+			// if transport2 not available and failed twice or more, then wait as RFC5626 required
+			if (client_state->waiting_time_upper_bound < registration->retry_max_time){
+				int base_time = has_registered() ? registration->retry_base_time2 : registration->retry_base_time1;
+				client_state->waiting_time_upper_bound =
+					MIN(registration->retry_max_time, base_time * (int)pow(2.0, (double)client_state->failures));
+				ast_log(LOG_NOTICE, "The upper_bound wait time set to: '%d = min(%d, %d * 2 ^ %d)' seconds\n",
+					client_state->waiting_time_upper_bound, registration->retry_max_time, base_time, client_state->failures);
+			} else {
+				ast_log(LOG_NOTICE, "The upper_bound wait time was set to %d seconds\n", client_state->waiting_time_upper_bound);
+			}
+			ast_log(LOG_NOTICE, "Queue registration recovery flow for %s\n", response->client_state->registration_name);
+			queue_registration_recovery_flow(client_state->registration_name);
+		} else {
+			ast_debug(1, "%s: Registration re-try after %d seconds.\n", client_state->registration_name, waiting_time);
+			schedule_retry(response, waiting_time, server_uri, client_uri, client_num);
 		}
 	} else {
+		if (!tdata || !tdata->dest_info.addr.count) {
+			client_state->retries = client_state->max_retries;
+			ast_log(LOG_ERROR, "No destination addresses available (tdata=%p, addr.count=%d)\n"
+				"Setting client's retry counter to max_retries(%d) immediately\n",
+				tdata, tdata ? tdata->dest_info.addr.count : 0, client_state->max_retries);
+		}
 		if (response->code == 403
-			&& response->client_state->forbidden_retry_interval
-			&& response->client_state->retries < response->client_state->max_retries) {
+			&& client_state->forbidden_retry_interval
+			&& client_state->retries < client_state->max_retries) {
 			/* A forbidden response retry interval is configured and there are retries remaining */
-			update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
+			update_client_state_status(client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
 			sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri);
-			response->client_state->retries++;
-			schedule_registration(response->client_state, response->client_state->forbidden_retry_interval);
+			client_state->retries++;
+			schedule_registration(client_state, client_state->forbidden_retry_interval);
 			ast_log(LOG_WARNING, "403 Forbidden fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n",
-				server_uri, client_uri, response->client_state->forbidden_retry_interval);
-		} else if (response->client_state->fatal_retry_interval
-			   && response->client_state->retries < response->client_state->max_retries) {
+				server_uri, client_uri, client_state->forbidden_retry_interval);
+		} else if (client_state->fatal_retry_interval
+			   && client_state->retries < client_state->max_retries) {
 			/* Some kind of fatal failure response received, so retry according to configured interval */
-			update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
+			update_client_state_status(client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);
 			sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri);
-			response->client_state->retries++;
-			schedule_registration(response->client_state, response->client_state->fatal_retry_interval);
+			client_state->retries++;
+			schedule_registration(client_state, client_state->fatal_retry_interval);
 			ast_log(LOG_WARNING, "'%d' fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n",
-				response->code, server_uri, client_uri, response->client_state->fatal_retry_interval);
+				response->code, server_uri, client_uri, client_state->fatal_retry_interval);
 		} else {
 			/* Finally if there's no hope of registering give up */
-			update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
+			update_client_state_status(client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
 			sip_outbound_registration_send_ubus_event("UNREGISTERED",response->expiration,client_uri);
+			struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name);
+			if ((strncmp(endpoint->transport, "transport-tls", 13) == 0) && ast_strlen_zero(endpoint->transport2)){
+				ast_log_dt(LOG_EVENT_CODE_SV113);
+			} else if((strncmp(client_state->transport_name, "transport-udp", 13) == 0) &&!ast_strlen_zero(endpoint->transport2)) {
+				ast_log_dt(LOG_EVENT_CODE_SV112);
+			}
 			if (response->rdata) {
 				ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n",
 					response->code, server_uri, client_uri);
@@ -1700,15 +2287,17 @@ static int handle_registration_response(void *data)
 				ast_log(LOG_WARNING, "Fatal registration attempt to '%s', stopping outbound registration\n", client_uri);
 			}
 		}
+
+		ast_log_dt(LOG_EVENT_CODE_V002, client_num, "Fatal response received");
 	}
 
 	ast_system_publish_registry("PJSIP", client_uri, server_uri,
-		sip_outbound_registration_status_str(response->client_state->status), NULL);
+		sip_outbound_registration_status_str(client_state->status), NULL);
 
-	if (response->client_state->destroy) {
+	if (client_state->destroy) {
 		/* We have a pending deferred destruction to complete now. */
-		ao2_ref(response->client_state, +1);
-		handle_client_state_destruction(response->client_state);
+		ao2_ref(client_state, +1);
+		handle_client_state_destruction(client_state);
 	}
 
 	ao2_ref(response, -1);
@@ -1743,6 +2332,17 @@ static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *par
 	struct sip_outbound_registration_client_state *client_state = param->token;
 	struct registration_response *response;
 	int *callback_invoked;
+	pjsip_regc_info info;
+	char client_uri[PJSIP_MAX_URL_SIZE];
+	char *client_num = NULL;
+	char * saveptr = NULL;
+	pjsip_expires_hdr *expires = NULL;
+	pjsip_regc_get_info(client_state->client, &info);
+	ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
+
+
+	strtok_r(client_uri, ":", &saveptr);
+	client_num = strtok_r(NULL, "@", &saveptr);
 
 	callback_invoked = ast_threadstorage_get(&register_callback_invoked, sizeof(int));
 
@@ -1773,6 +2373,31 @@ static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *par
 
 	ast_debug(1, "Received REGISTER response %d(%.*s)\n",
 		param->code, (int) param->reason.slen, param->reason.ptr);
+	
+	/* get expire to distinguish register and unregister */
+	int exp_expiration = response->expiration;
+	if(response->old_request && (expires = pjsip_msg_find_hdr(response->old_request->msg, PJSIP_H_EXPIRES, NULL))){
+		exp_expiration = expires->ivalue;
+	}
+
+	if((param->code != 200) && (param->code != 401) && (param->code != 407) && (client_num != NULL)){
+		/* registration */
+		if(0 != exp_expiration){ 
+			if(strstr(param->reason.ptr, "PJLIB_UTIL_EDNSNOANSWERREC") != NULL && response->code == 503){
+				/* V001 */
+				ast_log_dt(LOG_EVENT_CODE_V001, client_num);
+			}
+			if(response->code == 408 || response->code == 404){
+				/* V004 */
+				ast_log_dt(LOG_EVENT_CODE_V004 ,client_num);
+			}
+		}
+		/* unregistration */
+		else{  
+			/* V005 */
+			ast_log_dt(LOG_EVENT_CODE_V005,client_num, (int) param->reason.slen, param->reason.ptr);
+		}
+	}
 
 	if (param->rdata) {
 		struct pjsip_retry_after_hdr *retry_after;
@@ -1858,6 +2483,7 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a
 {
 	struct sip_outbound_registration_state *state;
 	char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
+	struct ast_sip_endpoint *endpoint = NULL;
 
 	state = ao2_alloc(sizeof(*state), sip_outbound_registration_state_destroy);
 	if (!state) {
@@ -1873,9 +2499,10 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a
 	state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
 	pj_timer_entry_init(&state->client_state->timer, 0, state->client_state,
 		sip_outbound_registration_timer_cb);
-	state->client_state->transport_name = ast_strdup(registration->transport);
-	state->client_state->registration_name =
-		ast_strdup(ast_sorcery_object_get_id(registration));
+	state->client_state->registration_name = ast_strdup(ast_sorcery_object_get_id(registration));
+	endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->client_state->registration_name);
+	if (endpoint)
+		state->client_state->transport_name = ast_strdup(endpoint->transport);
 
 	ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "+1", 1.0);
 	ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0,
@@ -2132,6 +2759,18 @@ static int set_outbound_initial_authentication_credentials(pjsip_regc *regc,
 				ast_free((char *) access_token);
 			}
 			break;
+		case AST_SIP_AUTH_TYPE_USER_PASS:
+			pj_cstr(&auth_creds[0].realm, auths[idx]->realm);
+			pj_cstr(&auth_creds[0].scheme, "digest");
+			pj_cstr(&auth_creds[0].username, auths[idx]->auth_user);
+			auth_creds[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
+			pj_cstr(&auth_creds[0].data, auths[idx]->auth_pass);
+			pjsip_regc_set_credentials(regc, 1, auth_creds);
+			struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", ast_sorcery_object_get_id(auths[idx]));
+			endpoint->realms = ast_strdup(auths[idx]->realm);
+			// set credentials for local auth_sess endpoint->auth_sess_reg/auth_sess_inv
+			pjsip_auth_clt_set_credentials(&endpoint->auth_sess_reg, 1, auth_creds);
+			pjsip_auth_clt_set_credentials(&endpoint->auth_sess_inv, 1, auth_creds);
 		default:
 			/* other cases handled after receiving auth rejection */
 			break;
@@ -2201,7 +2840,11 @@ static int sip_outbound_registration_regc_alloc(void *data)
 		return -1;
 	}
 
-	ast_sip_set_tpselector_from_transport_name(registration->transport, &selector);
+	if (ast_sip_set_tpselector_from_transport_name(state->client_state->transport_name, &selector)) {
+		ast_log(LOG_ERROR, "Failed to set tpselector for %s\n", state->client_state->transport_name);
+		return -1;
+	}
+
 	pjsip_regc_set_transport(state->client_state->client, &selector);
 
 	if (!ast_strlen_zero(registration->outbound_proxy)) {
@@ -2245,6 +2888,77 @@ static int sip_outbound_registration_regc_alloc(void *data)
 		return -1;
 	}
 
+	struct pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
+	struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->client_state->registration_name);
+	pool = pjsip_endpt_create_pool(endpt, "sip_auth_session_reg%p", 1024, 1024);
+	if(pjsip_auth_clt_init(&endpoint->auth_sess_reg, endpt, pool, 0) != PJ_SUCCESS){
+		ast_log(LOG_WARNING, "auth_sess_reg init failed!\n");
+	}else{
+		ast_mutex_init(&endpoint->auth_sess_reg_lock);
+	}
+	pool = pjsip_endpt_create_pool(endpt, "sip_auth_session_inv%p", 1024, 1024);
+	if(pjsip_auth_clt_init(&endpoint->auth_sess_inv, endpt, pool, 0) != PJ_SUCCESS){
+		ast_log(LOG_WARNING, "auth_sess_inv init failed!\n");
+	}else{
+		ast_mutex_init(&endpoint->auth_sess_inv_lock);
+	}
+	return 0;
+}
+/*! \brief Helper function which performs a single registration for recovery flow*/
+static int sip_outbound_registration_recovery(void *data)
+{
+	struct sip_outbound_registration_state *state = data;
+	struct sip_outbound_registration *registration = ao2_bump(state->registration);
+	size_t i;
+
+	/* Just in case the client state is being reused for this registration, free the auth information */
+	ast_sip_auth_vector_destroy(&state->client_state->outbound_auths);
+	ast_sip_security_mechanisms_vector_destroy(&state->client_state->security_mechanisms);
+	ast_sip_security_mechanisms_vector_destroy(&state->client_state->server_security_mechanisms);
+
+	AST_VECTOR_INIT(&state->client_state->outbound_auths, AST_VECTOR_SIZE(&registration->outbound_auths));
+	for (i = 0; i < AST_VECTOR_SIZE(&registration->outbound_auths); ++i) {
+		char *name = ast_strdup(AST_VECTOR_GET(&registration->outbound_auths, i));
+
+		if (name && AST_VECTOR_APPEND(&state->client_state->outbound_auths, name)) {
+			ast_free(name);
+		}
+	}
+	ast_sip_security_mechanisms_vector_copy(&state->client_state->security_mechanisms,
+											&registration->security_mechanisms);
+	state->client_state->retry_interval = registration->retry_interval;
+	state->client_state->retry_percent = registration->retry_percent;
+	state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval;
+	state->client_state->fatal_retry_interval = registration->fatal_retry_interval;
+	state->client_state->max_retries = registration->max_retries;
+	if (state->client_state->registration_resend_timer_id != -1) {
+		if (ast_sched_del(sched, state->client_state->registration_resend_timer_id)) {
+			ast_log(LOG_ERROR, "Failed to remove scheduled registration resend timer\n");
+		}
+		state->client_state->registration_resend_timer_id = -1;
+	}
+	state->client_state->failover_retry_interval1 = registration->failover_retry_interval1;
+	state->client_state->failover_retry_interval2 = registration->failover_retry_interval2;
+	state->client_state->failover_max_retries = registration->failover_max_retries;
+	state->client_state->support_path = registration->support_path;
+	state->client_state->mediasec = registration->mediasec;
+	state->client_state->support_outbound = registration->support_outbound;
+	state->client_state->security_negotiation = registration->security_negotiation;
+	state->client_state->auth_rejection_permanent = registration->auth_rejection_permanent;
+
+	pjsip_regc_update_expires(state->client_state->client, registration->expiration);
+
+	// waiting_time is randomly between 50-100% of upper_bound according to RFC5626 Section 4.5
+	int waiting_time = RFC5626_RETRY_DELAY(state->client_state->waiting_time_upper_bound);
+	struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->client_state->registration_name);
+	if (endpoint && endpoint->failover_reg_addr == 1){
+		waiting_time = 1; // Trigger registration immediately for 504 with xml re-register during INVITE
+	}
+	ast_log(LOG_NOTICE, "Schedule registration for recovery flow after %d s\n", waiting_time);
+	schedule_registration(state->client_state, waiting_time);
+
+	ao2_ref(registration, -1);
+	ao2_ref(state, -1);
 	return 0;
 }
 
@@ -2272,14 +2986,24 @@ static int sip_outbound_registration_perform(void *data)
 	ast_sip_security_mechanisms_vector_copy(&state->client_state->security_mechanisms,
 											&registration->security_mechanisms);
 	state->client_state->retry_interval = registration->retry_interval;
+	state->client_state->retry_percent = registration->retry_percent;
 	state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval;
 	state->client_state->fatal_retry_interval = registration->fatal_retry_interval;
 	state->client_state->max_retries = registration->max_retries;
 	state->client_state->retries = 0;
+	if (state->client_state->registration_resend_timer_id != -1) {
+		if (ast_sched_del(sched, state->client_state->registration_resend_timer_id)) {
+			ast_log(LOG_WARNING, "Failed to remove scheduled registration resend timer\n");
+		}
+		state->client_state->registration_resend_timer_id = -1;
+	}
 	state->client_state->failover_retry_interval1 = registration->failover_retry_interval1;
 	state->client_state->failover_retry_interval2 = registration->failover_retry_interval2;
 	state->client_state->failover_max_retries = registration->failover_max_retries;
 	state->client_state->failover_retries = 0;
+	state->client_state->waiting_time_upper_bound = 0;
+	state->client_state->failures = 0;
+	state->client_state->attempts = 0;
 	state->client_state->support_path = registration->support_path;
 	state->client_state->mediasec = registration->mediasec;
 	state->client_state->support_outbound = registration->support_outbound;
@@ -2290,13 +3014,67 @@ 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 */
-	schedule_registration(state->client_state, (max_delay ? ast_random() % max_delay : 0) + 1);
-
+	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);
+	}
 	ao2_ref(registration, -1);
 	ao2_ref(state, -1);
 	return 0;
 }
 
+/*! \brief Helper function which performs re-registration with new state info during recovery flow */
+static void re_register_with_new_state(const struct ast_sorcery *sorcery, struct sip_outbound_registration *applied)
+{
+	RAII_VAR(struct ao2_container *, states, ao2_global_obj_ref(current_states), ao2_cleanup);
+	RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
+	RAII_VAR(struct sip_outbound_registration_state *, new_state, NULL, ao2_cleanup);
+
+	if (!states) {
+		/* Global container has gone.  Likely shutting down. */
+		return;
+	}
+	state = ao2_find(states, ast_sorcery_object_get_id(applied), OBJ_SEARCH_KEY);
+	if (state){
+		if (!(new_state = sip_outbound_registration_state_alloc(applied))) {
+			return;
+		}
+
+		if (ast_sip_push_task_wait_serializer(new_state->client_state->serializer,
+			sip_outbound_registration_regc_alloc, new_state)) {
+			return;
+		}
+		ast_debug(4, "Re-applying configuration to outbound registration '%s'\n", ast_sorcery_object_get_id(applied));
+
+		new_state->client_state->retries = state->client_state->retries;
+		new_state->client_state->failover_retries = state->client_state->failover_retries;
+		new_state->client_state->waiting_time_upper_bound = state->client_state->waiting_time_upper_bound;
+		new_state->client_state->failures = state->client_state->failures;
+		new_state->client_state->attempts = 0;
+		struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->client_state->registration_name);
+		if (endpoint && endpoint->failover_reg_addr==1){
+			// keep the original expires for 504 re-register in case of failure.
+			new_state->client_state->registration_expires = state->client_state->registration_expires;
+		}
+
+		if (ast_sip_push_task(new_state->client_state->serializer,
+				      sip_outbound_registration_recovery, ao2_bump(new_state))) {
+			ast_log(LOG_ERROR, "Failed to perform outbound registration on '%s'\n",
+				ast_sorcery_object_get_id(new_state->registration));
+			ao2_ref(new_state, -1);
+			return;
+		}
+
+		ao2_lock(states);
+		if (state) {
+			ao2_unlink(states, state);
+			ao2_cleanup(state);
+		}
+		ao2_link(states, new_state);
+		ao2_unlock(states);
+	}
+}
 /*! \brief Apply function which finds or allocates a state structure */
 static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, void *obj)
 {
@@ -2363,7 +3141,10 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo
 		sip_outbound_registration_regc_alloc, new_state)) {
 		return -1;
 	}
-
+	RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
+	endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", new_state->client_state->registration_name);
+	endpoint->failover_reg_addr=0;
+	endpoint->failover_reg_addr_timer_id = -1;
 	if (ast_sip_push_task(new_state->client_state->serializer,
 			      sip_outbound_registration_perform, ao2_bump(new_state))) {
 		ast_log(LOG_ERROR, "Failed to perform outbound registration on '%s'\n",
@@ -2382,6 +3163,39 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo
 	return 0;
 }
 
+/* for counting register request that has been sent out before success response */
+static pj_status_t log_register_on_tx_msg(pjsip_tx_data *tdata)
+{
+    SCOPE_ENTER(1, "log_register_on_tx_msg, check and update register request count\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){
+			client_state->failures++;
+			ast_log(LOG_NOTICE, "%s, requests sent:%d\n",client_state->registration_name, client_state->failures);
+		}
+	}
+	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_reg_hangup && 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;
@@ -2454,25 +3268,20 @@ static int unregister_task(void *obj)
 	struct sip_outbound_registration_state *state = obj;
 	struct pjsip_regc *client = state->client_state->client;
 	pjsip_tx_data *tdata;
-	pjsip_regc_info info;
-
-	pjsip_regc_get_info(client, &info);
+	pjsip_uri *uri = pjsip_parse_uri(pjsip_regc_get_pool(client), state->registration->client_uri, strlen(state->registration->client_uri), 0);
+	pjsip_sip_uri *sip_uri = pjsip_uri_get_uri(uri);
 	ast_debug(1, "Unregistering contacts with server '%s' from client '%s'\n",
 		state->registration->server_uri, state->registration->client_uri);
 
 	cancel_registration(state->client_state);
 
-	if (pjsip_regc_unregister(client, &tdata) == PJ_SUCCESS
-		&& add_configured_supported_headers(state->client_state, tdata)) {
-		if (state->client_state->mediasec) {
-			struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
-							state->client_state->registration_name);
-			ast_sip_add_header(tdata,"Security-Client","sdes-srtp;mediasec");
-			ast_sip_add_header(tdata,"Proxy-Require","mediasec");
-			ast_sip_add_header(tdata,"Require","mediasec");
-			clear_endpoint_security_mechanisms(endpoint);
+	if (pjsip_regc_unregister(client, &tdata) == PJ_SUCCESS &&
+			add_configured_supported_headers(state->client_state, tdata)) {
+		if (state->client_state->status == SIP_REGISTRATION_REGISTERED && registration_client_send(state->client_state, tdata) == PJ_SUCCESS) {
+			if (sip_uri)
+				ast_log_dt(LOG_EVENT_CODE_IPX002, (int) sip_uri->user.slen, sip_uri->user.ptr,
+												  (int) sip_uri->host.slen, sip_uri->host.ptr);
 		}
-		registration_client_send(state->client_state, tdata);
 	}
 
 	ao2_ref(state, -1);
@@ -2501,6 +3310,135 @@ static int queue_register(struct sip_outbound_registration_state *state)
 	return 0;
 }
 
+void update_emergency_registration_ongoing_status(const char *registration_name, bool action){
+	struct sip_outbound_registration_state *state;
+	state = get_state(registration_name);
+	if (!state) {
+		ast_log(LOG_WARNING, "Unable to retrieve registration %s\n", registration_name);
+	} else {
+		state->client_state->Emergency_call_ongoing = action;
+	}
+
+}
+
+void queue_registration_recovery_flow(const char *registration_name)
+{
+	struct sip_outbound_registration_state *state;
+	state = get_state(registration_name);
+	if (!state) {
+		ast_log(LOG_WARNING, "Unable to retrieve registration %s\n", registration_name);
+	} else {
+		cancel_registration(state->client_state);
+		ast_log(LOG_NOTICE, "Queue registration with new state \n");
+		re_register_with_new_state(ast_sip_get_sorcery(), state->registration);
+	}
+}
+
+void check_and_update_nextnonce(pjsip_auth_clt_sess *sess, const char *realm, pjsip_rx_data *rdata){
+	pjsip_generic_string_hdr *auth_info = NULL;
+	static const pj_str_t headerName = { "Authentication-Info", 19 };
+	auth_info = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &headerName, NULL);
+   if(auth_info){
+		char value[pj_strlen(&((pjsip_generic_string_hdr*)auth_info)->hvalue) + 1];
+		ast_copy_pj_str(value, &((pjsip_generic_string_hdr*)auth_info)->hvalue, sizeof(value));
+		char nextnonce[64];
+		char *_buf = strstr(value, "nextnonce=");
+		if (_buf) {
+			sscanf(_buf, "nextnonce=\"%s", nextnonce);
+			char *buf_s = strstr(nextnonce, "\"");
+			nextnonce[((int)strlen(nextnonce)-(int)strlen(buf_s))]='\0';
+			ast_log(LOG_NOTICE, "received nextnonce: %s\n", nextnonce);
+			pj_str_t nextnonce_pj_str = pj_str(nextnonce);
+			pj_str_t realms_pj_str = pj_str(realm);
+			pjsip_cached_auth *cached_auth;
+			cached_auth = pjsip_auth_find_cached_auth(sess, &realms_pj_str);
+			if (cached_auth) {
+				pj_strdup(cached_auth->pool, &cached_auth->last_chal->challenge.digest.nonce, &nextnonce_pj_str);
+				cached_auth->nc=0;
+				ast_debug(3, "update nextnonce to cached auth\n");
+			}
+		}
+	}
+}
+
+void sync_cached_auth_between_reg_inv(pjsip_auth_clt_sess *sess_from, pjsip_auth_clt_sess *sess_to, const pj_str_t *realm )
+{
+	pjsip_cached_auth *cached_auth_from, *cached_auth_to;
+	cached_auth_from = pjsip_auth_find_cached_auth(sess_from, realm);
+	if (cached_auth_from) {
+		const pjsip_cred_info *cred;
+		pjsip_authorization_hdr *hauth;
+		pj_status_t status;
+		cred = pjsip_auth_find_cred(sess_from, &cached_auth_from->realm, &cached_auth_from->last_chal->scheme );
+		if (!cred)
+			return;
+		// Find cached_auth for INVITE, create new if not exists.
+		cached_auth_to = pjsip_auth_find_cached_auth(sess_to, realm);
+		if (!cached_auth_to) {
+			cached_auth_to = PJ_POOL_ZALLOC_T(sess_to->pool, pjsip_cached_auth);
+			cached_auth_to->pool = pjsip_endpt_create_pool(sess_to->endpt, "auth_sync_cli%p", 1024, 1024);
+			pj_strdup(cached_auth_to->pool, &cached_auth_to->realm, realm);
+			cached_auth_to->is_proxy = PJ_TRUE;
+			pj_list_init(&cached_auth_to->cached_hdr);
+			pj_list_insert_before(&sess_to->cached_auth, cached_auth_to);
+			ast_debug(8, "New cached auth created\n");
+		}
+		// Sync with cached_auth info
+		cached_auth_to->nc = cached_auth_from->nc;
+		cached_auth_to->qop_value = cached_auth_from->qop_value;
+		cached_auth_to->stale_cnt = cached_auth_from->stale_cnt;
+		pj_strdup(cached_auth_to->pool, &cached_auth_to->cnonce, &cached_auth_from->cnonce);
+		pj_pool_t *pool = pjsip_endpt_create_pool(sess_from->endpt, "m_auth_cli%p", 1024, 1024);
+		pjsip_www_authenticate_hdr *hdr;
+		if (cached_auth_from->last_chal->type == PJSIP_H_WWW_AUTHENTICATE)
+			hdr = pjsip_proxy_authenticate_hdr_create(pool);
+		else
+			hdr = pjsip_www_authenticate_hdr_create(pool);
+
+		pj_strdup(pool, &hdr->scheme, &cached_auth_from->last_chal->scheme);
+		if (pj_stricmp2(&hdr->scheme, "digest") == 0) {
+			pj_strdup(pool, &hdr->challenge.digest.realm, &cached_auth_from->last_chal->challenge.digest.realm);
+			pj_strdup(pool, &hdr->challenge.digest.domain, &cached_auth_from->last_chal->challenge.digest.domain);
+			pj_strdup(pool, &hdr->challenge.digest.nonce, &cached_auth_from->last_chal->challenge.digest.nonce);
+			pj_strdup(pool, &hdr->challenge.digest.opaque, &cached_auth_from->last_chal->challenge.digest.opaque);
+			hdr->challenge.digest.stale = cached_auth_from->last_chal->challenge.digest.stale;
+			pj_strdup(pool, &hdr->challenge.digest.algorithm, &cached_auth_from->last_chal->challenge.digest.algorithm);
+			pj_strdup(pool, &hdr->challenge.digest.qop, &cached_auth_from->last_chal->challenge.digest.qop);
+			pjsip_param_clone(pool, &hdr->challenge.digest.other_param,
+							  &cached_auth_from->last_chal->challenge.digest.other_param);
+		}
+		pjsip_endpt_release_pool(sess_from->endpt, pool);
+		cached_auth_to->last_chal = (pjsip_www_authenticate_hdr*) pjsip_hdr_clone(cached_auth_to->pool, hdr);
+		ast_debug(8, "cached auth synced\n");
+	}
+}
+
+void de_register_all_with_transport(const char *id)
+{
+	struct sip_outbound_registration *registration;
+	struct ao2_container *registrations;
+	struct ao2_iterator i;
+	struct sip_outbound_registration_state *state;
+
+	registrations = get_registrations();
+	if (!registrations) {
+		return;
+	}
+
+	i = ao2_iterator_init(registrations, 0);
+	while ((registration = ao2_iterator_next(&i))) {
+		state = get_state(ast_sorcery_object_get_id(registration));
+		if (state && state->client_state && state->client_state->status == SIP_REGISTRATION_REGISTERED && strcmp(state->client_state->transport_name, id) == 0) {
+			queue_unregister(state);
+		}
+		ao2_ref(state, -1);
+		ao2_ref(registration, -1);
+	}
+	ao2_iterator_destroy(&i);
+	ao2_ref(registrations, -1);
+	return;
+}
+
 static void unregister_all(void)
 {
 	struct ao2_container *states;
@@ -2569,20 +3507,27 @@ static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
 {
 	struct sip_outbound_registration_state *state;
 	const char *registration_name;
+	bool emergency=false;
 
 	switch (cmd) {
 	case CLI_INIT:
 		e->command = "pjsip send unregister";
 		e->usage =
-			"Usage: pjsip send unregister <registration> | *all\n"
-			"       Unregisters the specified (or all) outbound registration(s) "
-			"and stops future registration attempts.\n";
+			"Usage: pjsip send unregister <registration> | *all | <transport> [e]\n"
+			"       Unregisters the specified(sipX) (or all) outbound registration(s),\n"
+			"       or unregister all registrations that registered with specified transport(transport-X),\n"
+			"       and stops future registration attempts.\n"
+			"       Optional flag e to reset the triggered emergency registration.\n";
 		return NULL;
 	case CLI_GENERATE:
 		return cli_complete_registration(a->line, a->word, a->pos, a->n);
 	}
 
-	if (a->argc != 4) {
+	if (a->argc == 5 && strcmp(a->argv[4], "e") == 0){
+		emergency=true; // user hangup after triggered emergency registration.
+	}
+
+	if (a->argc != 4 && !emergency) {
 		return CLI_SHOWUSAGE;
 	}
 
@@ -2592,6 +3537,10 @@ static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
 		unregister_all();
 		ast_cli(a->fd, "Unregister all queued\n");
 		return CLI_SUCCESS;
+	} else if (strncmp(registration_name, "transport", 9) == 0) {
+		de_register_all_with_transport(registration_name);
+		ast_cli(a->fd, "Unregister %s\n", registration_name);
+		return CLI_SUCCESS;;
 	}
 
 	state = get_state(registration_name);
@@ -2600,6 +3549,18 @@ static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
 		return CLI_FAILURE;
 	}
 
+	if (emergency){
+		ast_log(LOG_NOTICE,"Emergency Registration hangup\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){
+			// E-register hangup triggered while 1.emergency registered 2.emergency register still ongoing
+			state->client_state->Emergency_reg_hangup = true;
+		}
+		// else; E-register hangup triggered after registration reset to primary, just return.
+		ao2_ref(state, -1);
+		return CLI_SUCCESS;
+	}
+
 	if (queue_unregister(state)) {
 		ast_cli(a->fd, "Failed to queue unregistration\n");
 	}
@@ -2612,20 +3573,26 @@ static char *cli_register(struct ast_cli_entry *e, int cmd, struct ast_cli_args
 {
 	struct sip_outbound_registration_state *state;
 	const char *registration_name;
+	bool emergency=false;
 
 	switch (cmd) {
 	case CLI_INIT:
 		e->command = "pjsip send register";
 		e->usage =
-			"Usage: pjsip send register <registration> | *all \n"
+			"Usage: pjsip send register <registration> | *all [e]\n"
 			"       Unregisters the specified (or all) outbound "
-			"registration(s) then starts registration(s) and schedules re-registrations.\n";
+			"registration(s) then starts registration(s) and schedules re-registrations.\n"
+			"       Optional flag e to trigger emergency registration through UDP\n";
 		return NULL;
 	case CLI_GENERATE:
 		return cli_complete_registration(a->line, a->word, a->pos, a->n);
 	}
 
-	if (a->argc != 4) {
+	if (a->argc == 5 && strcmp(a->argv[4], "e") == 0){
+		emergency=true;
+	}
+
+	if (a->argc != 4 && !emergency) {
 		return CLI_SHOWUSAGE;
 	}
 
@@ -2646,7 +3613,14 @@ static char *cli_register(struct ast_cli_entry *e, int cmd, struct ast_cli_args
 	/* We need to serialize the unregister and register so they need
 	 * to be queued as separate tasks.
 	 */
-	if (queue_unregister(state)) {
+	if (emergency) {
+		state->client_state->Emergency_reg_ongoing=true;
+		state->client_state->Emergency_reg_hangup=false;
+		cancel_registration(state->client_state);
+		pjsip_tx_data *tdata;
+		pjsip_regc_unregister(state->client_state->client, &tdata);
+		queue_register(state);
+	} else if (queue_unregister(state)) {
 		ast_cli(a->fd, "Failed to queue unregistration\n");
 	} else if (queue_register(state)) {
 		ast_cli(a->fd, "Failed to queue registration\n");
@@ -2863,7 +3837,7 @@ static int cli_print_header(void *obj, void *arg, int flags)
 	ast_assert(context->output_buffer != NULL);
 
 	ast_str_append(&context->output_buffer, 0,
-		" <Registration/ServerURI..............................>  <Auth....................>  <Status.......>\n");
+		" <Registration/ServerURI(Primarily)...................>  <Auth..........>  <Latest Registration.....>  <Status.......>\n");
 
 	return 0;
 }
@@ -2880,7 +3854,17 @@ static int cli_print_body(void *obj, void *arg, int flags)
 	ast_assert(context->output_buffer != NULL);
 	expsecs = state ? state->client_state->registration_expires - ((int) time(NULL)) : 0;
 
-	ast_str_append(&context->output_buffer, 0, " %-s/%-*.*s  %-26s  %-16s %s%d%s\n",
+	char addr_buf[AST_SOCKADDR_BUFLEN] = {'\0'};
+	struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->client_state->registration_name);
+	if (endpoint && endpoint->register_dest) {
+		pj_sockaddr_print(&endpoint->register_dest->addr.entry[endpoint->register_dest->cur_addr].addr, addr_buf, sizeof(addr_buf), 1);
+	}
+	int flag_504 = 0;
+	if (endpoint){
+		flag_504 = endpoint->failover_reg_addr;
+	}
+
+	ast_str_append(&context->output_buffer, 0, " %-s/%-*.*s  %-16s  %-26s  %-16s %s%d%s %s\n",
 		id,
 		(int) (REGISTRATION_URI_FIELD_LEN - strlen(id)),
 		(int) (REGISTRATION_URI_FIELD_LEN - strlen(id)),
@@ -2888,8 +3872,10 @@ static int cli_print_body(void *obj, void *arg, int flags)
 		AST_VECTOR_SIZE(&registration->outbound_auths)
 			? AST_VECTOR_GET(&registration->outbound_auths, 0)
 			: "n/a",
+		addr_buf,
 		(state ? sip_outbound_registration_status_str(state->client_state->status) : "Unregistered"),
-		state ? " (exp. " : "", abs(expsecs), state ? (expsecs < 0 ? "s ago)" : "s)") : "");
+		state ? " (exp. " : "", abs(expsecs), state ? (expsecs < 0 ? "s ago)" : "s)") : "",
+		flag_504 ? (flag_504==1 ? "504_ongoing" : "504_failed") : "");
 	ao2_cleanup(state);
 
 	if (context->show_details
@@ -3097,7 +4083,10 @@ static int unload_module(void)
 			remaining);
 		return -1;
 	}
+	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");
 
 	ao2_cleanup(shutdown_group);
@@ -3114,6 +4103,15 @@ static int load_module(void)
 	if (!shutdown_group) {
 		return AST_MODULE_LOAD_DECLINE;
 	}
+	if (!(sched = ast_sched_context_create())) {
+		ast_log(LOG_ERROR, "Could not create scheduler for registration re attempt\n");
+		return AST_MODULE_LOAD_DECLINE;
+	}
+	if (ast_sched_start_thread(sched)) {
+		ast_log(LOG_ERROR, "Could not start scheduler thread for registration re attempt\n");
+		ast_sched_context_destroy(sched);
+		return AST_MODULE_LOAD_DECLINE;
+	}
 
 	/* Create outbound registration states container. */
 	new_states = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
@@ -3142,10 +4140,10 @@ static int load_module(void)
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "client_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, client_uri));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_user));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_header_params", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_header_params));
-	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, transport));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, outbound_proxy));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_random_initial_delay", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_random_initial_delay));
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_percent", "90", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_percent));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_interval", "1", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_interval));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "forbidden_retry_interval", "0", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, forbidden_retry_interval));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "fatal_retry_interval", "0", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, fatal_retry_interval));
@@ -3162,6 +4160,10 @@ static int load_module(void)
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "line", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, line));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, endpoint));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "mediasec", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, mediasec));
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_max_time", "1800", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_max_time));
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_base_time1", "30", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_base_time1));
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_base_time2", "90", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_base_time2));
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_after", "15", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_after));
 
 	/*
 	 * Register sorcery observers.
@@ -3215,6 +4217,8 @@ static int load_module(void)
 		network_change_stasis_cb, NULL);
 	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;
 }
@@ -3225,7 +4229,7 @@ static int reload_module(void)
 	return 0;
 }
 
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Outbound Registration Support",
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Outbound Registration Support",
 	.support_level = AST_MODULE_SUPPORT_CORE,
 	.load = load_module,
 	.reload = reload_module,
diff --git a/res/res_pjsip_outbound_registration.exports.in b/res/res_pjsip_outbound_registration.exports.in
new file mode 100644
index 0000000000000000000000000000000000000000..05c3efb00a057c91db3019db17449cea5d69241a
--- /dev/null
+++ b/res/res_pjsip_outbound_registration.exports.in
@@ -0,0 +1,9 @@
+{
+	global:
+		LINKER_SYMBOL_PREFIXqueue_registration_recovery_flow;
+		LINKER_SYMBOL_PREFIXupdate_emergency_registration_ongoing_status;
+		LINKER_SYMBOL_PREFIXcheck_and_update_nextnonce;
+		LINKER_SYMBOL_PREFIXsync_cached_auth_between_reg_inv;
+	local:
+		*;
+};
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index f2b785bab99b3a9899ed7ca52f8775386f031f93..71bdc961c3c3d15d8bfd6303958d3e51d75077d2 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -81,7 +81,7 @@ static int pj_max_hostname = PJ_MAX_HOSTNAME;
 static int pjsip_max_url_size = PJSIP_MAX_URL_SIZE;
 
 /*! \brief Internal function which returns the expiration time for a contact */
-static unsigned int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata)
+static unsigned int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata, int *expiration_err)
 {
 	pjsip_expires_hdr *expires;
 	unsigned int expiration = aor->default_expiration;
@@ -101,6 +101,8 @@ static unsigned int registrar_get_expiration(const struct ast_sip_aor *aor, cons
 
 	/* Enforce the range that we will allow for expiration */
 	if (expiration < aor->minimum_expiration) {
+		if (expiration_err)
+			*expiration_err = 1;
 		expiration = aor->minimum_expiration;
 	} else if (expiration > aor->maximum_expiration) {
 		expiration = aor->maximum_expiration;
@@ -139,7 +141,7 @@ static int registrar_find_contact(void *obj, void *arg, int flags)
 
 /*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */
 static int registrar_validate_contacts(const pjsip_rx_data *rdata, pj_pool_t *pool, struct ao2_container *contacts,
-	struct ast_sip_aor *aor, int permanent, int *added, int *updated, int *deleted)
+	struct ast_sip_aor *aor, int permanent, int *added, int *updated, int *deleted, int *expiration_err)
 {
 	pjsip_contact_hdr *previous = NULL;
 	pjsip_contact_hdr *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
@@ -148,10 +150,12 @@ static int registrar_validate_contacts(const pjsip_rx_data *rdata, pj_pool_t *po
 	};
 
 	for (; (contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next)); pj_pool_reset(pool)) {
-		unsigned int expiration = registrar_get_expiration(aor, contact, rdata);
+		unsigned int expiration = registrar_get_expiration(aor, contact, rdata, expiration_err);
 		struct ast_sip_contact *existing;
 		char contact_uri[pjsip_max_url_size];
 
+		if (expiration_err && *expiration_err)
+			return -1;
 		if (contact->star) {
 			/* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */
 			if (expiration != 0 || previous) {
@@ -403,6 +407,10 @@ static int registrar_contact_delete(enum contact_delete_type type, pjsip_transpo
 	struct ast_sip_contact *contact, const char *aor_name)
 {
 	int aor_size;
+	char * addr = NULL;
+	char * client_num = NULL;
+	char * saveptr = NULL;
+	char client_uri[PJSIP_MAX_URL_SIZE] = {0};
 
 	/* Permanent contacts can't be deleted */
 	if (ast_tvzero(contact->expiration_time)) {
@@ -463,6 +471,16 @@ static int registrar_contact_delete(enum contact_delete_type type, pjsip_transpo
 
 			ast_verb(3, "Removed contact '%s' from AOR '%s' due to %s\n",
 					 contact->uri, aor_name, reason);
+
+			/* fail reason is expireation */
+			if(!strcasecmp(reason, "expiration")){
+				strncpy(client_uri, contact->uri, sizeof(client_uri)-1);
+				strtok_r(client_uri, ":", &saveptr);
+				client_num = strtok_r(NULL, "@", &saveptr);
+				addr = strtok_r(NULL, ":", &saveptr);
+				/* IPX004 */
+				ast_log_dt(LOG_EVENT_CODE_IPX004, client_num, addr);
+			}
 		}
 
 		ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
@@ -681,6 +699,7 @@ static void register_aor_core(pjsip_rx_data *rdata,
 	pjsip_cid_hdr *call_id_hdr;
 	char *call_id = NULL;
 	size_t alloc_size;
+	int expiration_err = 0;
 
 	/* We create a single pool and use it throughout this function where we need one */
 	details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(),
@@ -697,12 +716,15 @@ static void register_aor_core(pjsip_rx_data *rdata,
 		permanent = ao2_container_count(aor->permanent_contacts);
 	}
 
-	if (registrar_validate_contacts(rdata, details.pool, contacts, aor, permanent, &added, &updated, &deleted)) {
+	if (registrar_validate_contacts(rdata, details.pool, contacts, aor, permanent, &added, &updated, &deleted, &expiration_err)) {
 		/* The provided Contact headers do not conform to the specification */
 		ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided");
 		ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n",
 				ast_sorcery_object_get_id(endpoint));
-		response->code = 400;
+		if (expiration_err)
+			response->code = 423;
+		else
+			response->code = 400;
 		pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
 		return;
 	}
@@ -828,7 +850,7 @@ static void register_aor_core(pjsip_rx_data *rdata,
 			continue;
 		}
 
-		expiration = registrar_get_expiration(aor, contact_hdr, rdata);
+		expiration = registrar_get_expiration(aor, contact_hdr, rdata, NULL);
 		details.uri = pjsip_uri_get_uri(contact_hdr->uri);
 		pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));
 
@@ -988,7 +1010,7 @@ static void register_aor_core(pjsip_rx_data *rdata,
 	ao2_callback(contacts, 0, registrar_add_contact, tdata);
 
 	if ((expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
-		expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(aor, NULL, rdata));
+		expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(aor, NULL, rdata, NULL));
 		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr);
 	}
 
@@ -1004,6 +1026,10 @@ static int register_aor(pjsip_rx_data *rdata,
 		.code = 500,
 	};
 	struct ao2_container *contacts = NULL;
+	pjsip_min_expires_hdr *hdr = NULL;
+	pjsip_hdr hdr_list = {};
+	pjsip_sip_uri *uri = NULL;
+	pjsip_contact_hdr *contact_hdr = NULL;
 
 	ao2_lock(aor);
 	contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor);
@@ -1017,13 +1043,37 @@ static int register_aor(pjsip_rx_data *rdata,
 	register_aor_core(rdata, endpoint, aor, aor_name, contacts, &response);
 	ao2_cleanup(contacts);
 	ao2_unlock(aor);
+	contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
+	if(contact_hdr){
+		uri = pjsip_uri_get_uri(contact_hdr->uri);
+	}
 
 	/* Now send the REGISTER response to the peer */
 	if (response.tdata) {
 		ast_sip_send_stateful_response(rdata, response.tdata, endpoint);
+
+		/* registration */
+		if(registrar_get_expiration(aor, NULL, rdata, NULL)){
+			/* register success. IPX001 */
+			ast_log_dt(LOG_EVENT_CODE_IPX001, (int) uri->user.slen, uri->user.ptr, (int) uri->host.slen, uri->host.ptr);
+		}
+		/* unregistration */
+		else{
+			/* unregister success. IPX002 */
+			ast_log_dt(LOG_EVENT_CODE_IPX002, (int) uri->user.slen, uri->user.ptr, (int) uri->host.slen, uri->host.ptr);
+		}
 	} else {
+		if (response.code == 423) {
+			pj_list_init(&hdr_list);
+			hdr = pjsip_min_expires_hdr_create(rdata->tp_info.pool, aor->minimum_expiration);
+			pj_list_push_back(&hdr_list, hdr);
+		}
+
 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(),
-			rdata, response.code, NULL, NULL, NULL);
+			rdata, response.code, NULL, (pjsip_hdr *)&hdr_list, NULL);
+		pj_list_erase(hdr);
+		/* sip error occured. IPX100 */
+		ast_log_dt(LOG_EVENT_CODE_IPX100, response.code, (int) uri->user.slen, uri->user.ptr);
 	}
 	return PJ_TRUE;
 }
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 8c6b8a4033288e7f4e35624bde713ca98b4f3404..32d3c5c6125d6e31a72bd19f58a9190b19a2b17f 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -172,6 +172,10 @@ static void enable_rtcp(struct ast_sip_session *session, struct ast_sip_session_
 	}
 
 	ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RTCP, rtcp_type);
+	/* set QoS/DSCP */
+	ast_rtp_instance_set_rtcp_qos(session_media->rtp, session->endpoint->media.tos_audio,
+				session->endpoint->media.cos_audio, "SIP RTCP Audio");
+
 }
 
 /*!
@@ -980,9 +984,11 @@ static enum ast_sip_session_media_encryption get_media_encryption_type(pj_str_t
  * \retval The encryption requested in the SDP
  */
 static enum ast_sip_session_media_encryption check_endpoint_media_transport(
-	struct ast_sip_endpoint *endpoint,
+	struct ast_sip_session *session,
 	const struct pjmedia_sdp_media *stream)
 {
+	struct ast_sip_endpoint *endpoint = session->endpoint;
+	enum ast_sip_session_media_encryption local_encryption = AST_SIP_MEDIA_ENCRYPT_NONE;
 	enum ast_sip_session_media_encryption incoming_encryption;
 	char transport_end = stream->desc.transport.ptr[stream->desc.transport.slen - 1];
 	unsigned int optimistic;
@@ -992,9 +998,12 @@ static enum ast_sip_session_media_encryption check_endpoint_media_transport(
 		return AST_SIP_MEDIA_TRANSPORT_INVALID;
 	}
 
+	if (session->transport_state->type == AST_TRANSPORT_TLS || session->transport_state->type == AST_TRANSPORT_WSS)
+		local_encryption = endpoint->media.rtp.encryption;
+
 	incoming_encryption = get_media_encryption_type(stream->desc.transport, stream, &optimistic);
 
-	if (incoming_encryption == endpoint->media.rtp.encryption) {
+	if (incoming_encryption == local_encryption) {
 		return incoming_encryption;
 	}
 
@@ -1168,6 +1177,11 @@ static int setup_media_encryption(struct ast_sip_session *session,
 	const struct pjmedia_sdp_session *sdp,
 	const struct pjmedia_sdp_media *stream)
 {
+	// Only enable media encryption for TLS/WSS transport
+	if (session->transport_state->type != AST_TRANSPORT_TLS &&
+	    session->transport_state->type != AST_TRANSPORT_WSS)
+		return 0;
+
 	switch (session_media->encryption) {
 	case AST_SIP_MEDIA_ENCRYPT_SDES:
 		if (setup_sdes_srtp(session_media, stream)) {
@@ -1517,7 +1531,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session,
 
 	/* Ensure incoming transport is compatible with the endpoint's configuration */
 	if (!session->endpoint->media.rtp.use_received_transport) {
-		encryption = check_endpoint_media_transport(session->endpoint, stream);
+		encryption = check_endpoint_media_transport(session, stream);
 
 		if (encryption == AST_SIP_MEDIA_TRANSPORT_INVALID) {
 			SCOPE_EXIT_RTN_VALUE(-1, "Incompatible transport\n");
@@ -1618,6 +1632,10 @@ static int add_crypto_to_stream(struct ast_sip_session *session,
 	static const pj_str_t STR_MEDSECREQ = { "requested", 9 };
 	enum ast_rtp_dtls_setup setup;
 
+	if (session->transport_state->type != AST_TRANSPORT_TLS &&
+	    session->transport_state->type != AST_TRANSPORT_WSS)
+		return 0;
+
 	switch (session_media->encryption) {
 	case AST_SIP_MEDIA_ENCRYPT_NONE:
 	case AST_SIP_MEDIA_TRANSPORT_INVALID:
@@ -1632,7 +1650,8 @@ static int add_crypto_to_stream(struct ast_sip_session *session,
 
 		tmp = session_media->srtp;
 
-		if (session->endpoint->mediasec) {
+		if (session->endpoint->mediasec ||
+		    session->endpoint->security_negotiation == AST_SIP_SECURITY_NEG_MEDIASEC) {
 			attr = pjmedia_sdp_attr_create(pool, "3ge2ae", &STR_MEDSECREQ);
 			media->attr[media->attr_count++] = attr;
 		}
@@ -1651,11 +1670,6 @@ static int add_crypto_to_stream(struct ast_sip_session *session,
 			media->attr[media->attr_count++] = attr;
 		} while ((tmp = AST_LIST_NEXT(tmp, sdp_srtp_list)));
 
-		if (session->endpoint->security_negotiation == AST_SIP_SECURITY_NEG_MEDIASEC) {
-			attr = pjmedia_sdp_attr_create(pool, "3ge2ae", &STR_MEDSECREQ);
-			media->attr[media->attr_count++] = attr;
-		}
-
 		break;
 	case AST_SIP_MEDIA_ENCRYPT_DTLS:
 		if (setup_dtls_srtp(session, session_media)) {
@@ -1842,6 +1856,8 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 		} else {
 			media->desc.transport = pj_str(ast_sdp_get_rtp_profile(
 				/* Optimistic encryption places crypto in the normal RTP/AVP profile */
+				(session->transport_state->type == AST_TRANSPORT_TLS ||
+				 session->transport_state->type == AST_TRANSPORT_WSS) &&
 				!session->endpoint->media.rtp.encryption_optimistic &&
 					(session_media->encryption == AST_SIP_MEDIA_ENCRYPT_SDES),
 				session_media->rtp, session->endpoint->media.rtp.use_avpf,
@@ -2115,16 +2131,18 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session,
 	char host[NI_MAXHOST];
 	int res;
 	struct ast_sip_session_media *session_media_transport;
+	struct ast_channel *bridged_chan;
 	SCOPE_ENTER(1, "%s Stream: %s\n", ast_sip_session_get_name(session),
 		ast_str_tmp(128, ast_stream_to_str(asterisk_stream, &STR_TMP)));
 
 	if (!session->channel) {
 		SCOPE_EXIT_RTN_VALUE(1, "No channel\n");
 	}
+	bridged_chan = ast_channel_bridged_chan(session->channel);
 
 	/* Ensure incoming transport is compatible with the endpoint's configuration */
 	if (!session->endpoint->media.rtp.use_received_transport &&
-		check_endpoint_media_transport(session->endpoint, remote_stream) == AST_SIP_MEDIA_TRANSPORT_INVALID) {
+		check_endpoint_media_transport(session, remote_stream) == AST_SIP_MEDIA_TRANSPORT_INVALID) {
 		SCOPE_EXIT_RTN_VALUE(-1, "Incompatible transport\n");
 	}
 
@@ -2197,12 +2215,16 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session,
 		if (caps) {
 			ast_format_cap_append(caps, ast_format_cap_get_format(ast_stream_get_formats(asterisk_stream), 0), ast_channel_ptime_get(session->channel));
 			ast_channel_nativeformats_set(session->channel, caps);
+			if (bridged_chan) {
+				ast_channel_nativeformats_set(bridged_chan, caps);
+			}
 			ao2_ref(caps, -1);
 		} else {
 			ao2_cleanup(caps);
 			ast_log(LOG_WARNING, "Unable to allocate caps\n");
 		}
 	}
+
 	/* Get and set the ptime, get from remote_stream if it has, otherwise get from local_stream */
 	pjmedia_sdp_attr *attr;
 	unsigned long framing;
@@ -2215,6 +2237,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session,
 		framing = pj_strtoul(pj_strltrim(&attr->value));
 		ast_channel_ptime_set(session->channel, framing);
 	}
+
 	pjmedia_sdp_rtpmap rtpmap;
 	for (int i = 0; i < remote_stream->desc.fmt_count; ++i) {
 		/* Look for the optional rtpmap attribute */
@@ -2227,15 +2250,20 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session,
 		char name[256];
 		ast_copy_pj_str(name, &rtpmap.enc_name, sizeof(name));
 		if ((strcmp(name, "telephone-event") == 0) && (8000 == rtpmap.clock_rate)) {
-		    char payload[4];
-		    ast_copy_pj_str(payload, &rtpmap.pt, sizeof(name));
-		    ast_channel_dtmf_pt_set(session->channel, atoi(payload));
-		    break;
+			char payload[4];
+			ast_copy_pj_str(payload, &rtpmap.pt, sizeof(name));
+			ast_channel_dtmf_pt_set(session->channel, atoi(payload));
+			break;
 		}
 	}
 
 	ast_channel_codec_set(session->channel, ast_format_get_name(ast_format_cap_get_format(ast_stream_get_formats(asterisk_stream), 0)));
-	ast_log(LOG_NOTICE, "channel %s, codec: %s, ptime: %d \n", ast_channel_name(session->channel), ast_channel_codec_get(session->channel), ast_channel_ptime_get(session->channel));
+	ast_log(LOG_NOTICE, "channel %s, codec: %s, ptime: %d \n", ast_channel_name(session->channel),
+			ast_channel_codec_get(session->channel), ast_channel_ptime_get(session->channel));
+	if (bridged_chan) {
+		ast_channel_codec_set(bridged_chan, ast_channel_codec_get(session->channel));
+		ast_channel_ptime_set(bridged_chan, ast_channel_ptime_get(session->channel));
+	}
 
 	/* Set the channel uniqueid on the RTP instance now that it is becoming active */
 	ast_rtp_instance_set_channel_id(session_media->rtp, ast_channel_uniqueid(session->channel));
@@ -2310,6 +2338,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session,
 		session_media->timeout_sched_id = ast_sched_add_variable(sched,	500, rtp_check_timeout,
 			session_media, 1);
 	}
+	ast_rtp_instance_sendcng(session_media->rtp, 0);
 
 	SCOPE_EXIT_RTN_VALUE(1, "Handled\n");
 }
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 89bd3b5319092de9566d987a82b3c0a09e62e4ff..bcea9dd36c18b045d69410bbf79011965a5276c5 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -51,7 +51,7 @@
 #include "asterisk/test.h"
 #include "asterisk/stream.h"
 #include "asterisk/vector.h"
-
+#include "asterisk/res_pjsip_outbound_registration.h"
 #define SDP_HANDLER_BUCKETS 11
 
 #define MOD_DATA_ON_RESPONSE "on_response"
@@ -60,11 +60,6 @@
 /* Most common case is one audio and one video stream */
 #define DEFAULT_NUM_SESSION_MEDIA 2
 
-/* Max call count per line and Max call count generally*/
-#define DEFAULT_MAX_SESSION_PER_LINE 	2
-#define DEFAULT_MAX_SESSION 		4
-static int max_sessions_per_line = DEFAULT_MAX_SESSION_PER_LINE;
-static int max_sessions = DEFAULT_MAX_SESSION;
 static int current_session_count = 0;          // current number of active sessions
 #define MAX_BUFFER_LEN 20
 /* Some forward declarations */
@@ -1686,7 +1681,7 @@ static void set_from_header(struct ast_sip_session *session)
 
 	if (session->endpoint->id.trust_outbound || !restricted) {
 		ast_sip_modify_id_header(dlg_pool, dlg_info, &connected_id);
-		if (ast_sip_get_use_callerid_contact() && ast_strlen_zero(session->endpoint->contact_user)) {
+		if (ast_sip_get_use_callerid_contact() && (connected_id.number.valid || ast_strlen_zero(session->endpoint->contact_user))) {
 			pj_strdup2(dlg_pool, &dlg_contact_uri->user, S_COR(connected_id.number.valid, connected_id.number.str, ""));
 		} else {
 			pj_strdup2(dlg_pool, &dlg_info_uri->user, session->endpoint->contact_user);
@@ -2523,11 +2518,10 @@ static int sip_session_refresh(struct ast_sip_session *session,
 		}
 	}
 
-	if (session->endpoint->mediasec && session->endpoint->media.rtp.encryption == AST_SIP_MEDIA_ENCRYPT_SDES) {
+	if ((session->transport_state->type == AST_TRANSPORT_TLS || session->transport_state->type == AST_TRANSPORT_WSS) &&
+	    session->endpoint->mediasec && session->endpoint->media.rtp.encryption == AST_SIP_MEDIA_ENCRYPT_SDES) {
 		ast_debug(3, "Session Refresh: Adding MEDIASEC headers\n");
-		ast_sip_add_header(tdata,"Security-Client","sdes-srtp;mediasec");
-		ast_sip_add_header(tdata,"Proxy-Require","mediasec");
-		ast_sip_add_header(tdata,"Require","mediasec");
+
 		if(!AST_LIST_EMPTY(&session->endpoint->secur_mechanisms)) {
 			struct security_mechanism *sec_mechanism;
 			AST_LIST_TRAVERSE(&session->endpoint->secur_mechanisms, sec_mechanism, entry) {
@@ -2674,6 +2668,7 @@ static pj_status_t log_sipipaddress_on_tx_msg(pjsip_tx_data *tdata)
 static int sdp_requires_deferral(struct ast_sip_session *session, const pjmedia_sdp_session *sdp)
 {
 	int i;
+	SCOPE_ENTER(3, "%s\n", ast_sip_session_get_name(session));
 
 	if (!session->pending_media_state->topology) {
 		session->pending_media_state->topology = ast_stream_topology_alloc();
@@ -2801,7 +2796,8 @@ static int sdp_requires_deferral(struct ast_sip_session *session, const pjmedia_
 			break;
 		}
 	}
-	return 0;
+
+	SCOPE_EXIT_RTN_VALUE(0, "%s\n", ast_sip_session_get_name(session));
 }
 
 static pj_bool_t session_reinvite_on_rx_request(pjsip_rx_data *rdata)
@@ -2810,6 +2806,7 @@ static pj_bool_t session_reinvite_on_rx_request(pjsip_rx_data *rdata)
 	RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup);
 	pjsip_rdata_sdp_info *sdp_info;
 	int deferred;
+	SCOPE_ENTER(3, "starts\n");
 
 	if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD ||
 		!(dlg = pjsip_ua_find_dialog(&rdata->msg_info.cid->id, &rdata->msg_info.to->tag, &rdata->msg_info.from->tag, PJ_FALSE)) ||
@@ -2866,7 +2863,7 @@ static pj_bool_t session_reinvite_on_rx_request(pjsip_rx_data *rdata)
 
 	pjsip_rx_data_clone(rdata, 0, &session->deferred_reinvite);
 
-	return PJ_TRUE;
+	SCOPE_EXIT_RTN_VALUE(PJ_TRUE, "ends\n");
 }
 
 void ast_sip_session_resume_reinvite(struct ast_sip_session *session)
@@ -2924,6 +2921,7 @@ int ast_sip_session_create_invite(struct ast_sip_session *session, pjsip_tx_data
 	pjsip_generic_string_hdr *content_header = NULL;
 	char buffer[MAX_BUFFER_LEN] = {0};
 	static const pj_str_t headerName = { "Content-Disposition", 19 };
+
 	SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session));
 
 	if (!(offer = create_local_sdp(session->inv_session, session, NULL, 0))) {
@@ -2945,15 +2943,21 @@ int ast_sip_session_create_invite(struct ast_sip_session *session, pjsip_tx_data
 	 */
 	set_from_header(session);
 
+
 	if (pjsip_inv_invite(session->inv_session, tdata) != PJ_SUCCESS) {
 		SCOPE_EXIT_RTN_VALUE(-1, "pjsip_inv_invite failed\n");
 	}
 
-	if (session->endpoint->mediasec && session->endpoint->media.rtp.encryption == AST_SIP_MEDIA_ENCRYPT_SDES) {
+	/* Send Invite to the same server address as the last registeraion */
+	if (session->endpoint->register_dest) {
+		pj_strdup2((*tdata)->pool, &(*tdata)->dest_info.name, session->endpoint->register_dest->host);
+		memcpy(&(*tdata)->dest_info.addr, &session->endpoint->register_dest->addr, sizeof(pjsip_server_addresses));
+		(*tdata)->dest_info.cur_addr = session->endpoint->register_dest->cur_addr;
+	}
+
+	if ((session->transport_state->type == AST_TRANSPORT_TLS || session->transport_state->type == AST_TRANSPORT_WSS) &&
+	    session->endpoint->mediasec && session->endpoint->media.rtp.encryption == AST_SIP_MEDIA_ENCRYPT_SDES) {
 		ast_debug(3, "INVITE: Adding MEDIASEC headers\n");
-		ast_sip_add_header(*tdata,"Security-Client","sdes-srtp;mediasec");
-		ast_sip_add_header(*tdata,"Proxy-Require","mediasec");
-		ast_sip_add_header(*tdata,"Require","mediasec");
 
 		if(!AST_LIST_EMPTY(&session->endpoint->secur_mechanisms)) {
 			struct security_mechanism *sec_mechanism;
@@ -2964,6 +2968,15 @@ int ast_sip_session_create_invite(struct ast_sip_session *session, pjsip_tx_data
 		}
 	}
 
+	if (session->endpoint->cached_auth){
+		ast_debug(3, "INVITE: Attach cached auth headers\n");
+		if (pjsip_msg_find_hdr((*tdata)->msg, PJSIP_H_PROXY_AUTHORIZATION, NULL) == NULL){
+			ast_mutex_lock(&session->endpoint->auth_sess_inv_lock);
+			pjsip_auth_clt_init_req(&session->endpoint->auth_sess_inv, *tdata);
+			ast_mutex_unlock(&session->endpoint->auth_sess_inv_lock);
+		}
+	}
+
 	content_header = pjsip_msg_find_hdr_by_name((*tdata)->msg, &headerName, NULL);
 	/*If content dispostion is present ,P-Early-Media header header is not added */
 	if (content_header)
@@ -2987,6 +3000,9 @@ int ast_sip_session_create_invite(struct ast_sip_session *session, pjsip_tx_data
 		ast_debug(3, "INVITE: Adding Priority and Resource-Priority headers \n");
 		ast_sip_add_header(*tdata,"Priority","emergency");
 		ast_sip_add_header(*tdata,"Resource-Priority","emrg.0");
+		if(session->endpoint){
+			update_emergency_registration_ongoing_status(ast_sorcery_object_get_id(session->endpoint), true);
+		}
 	}
 
 	SCOPE_EXIT_RTN_VALUE(0);
@@ -3335,6 +3351,25 @@ void ast_sip_session_unsuspend(struct ast_sip_session *session)
 	ast_taskprocessor_unsuspend(session->serializer);
 }
 
+static void invite_nextnonce_update(pjsip_rx_data *rdata){
+	pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
+	pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
+	if (!dlg || !tsx) {
+		return;
+	}
+	if (tsx->method.id != PJSIP_INVITE_METHOD) {
+		/* Not an INVITE that needs authentication */
+		return;
+	}
+	pjsip_inv_session *inv = pjsip_dlg_get_inv_session(dlg);
+	struct ast_sip_session *session = inv->mod_data[session_module.id];
+	if (session->endpoint && session->endpoint->realms){
+		ast_mutex_lock(&session->endpoint->auth_sess_inv_lock);
+		check_and_update_nextnonce(&session->endpoint->auth_sess_inv, session->endpoint->realms, rdata);
+		ast_mutex_unlock(&session->endpoint->auth_sess_inv_lock);
+	}
+}
+
 /*!
  * \internal
  * \brief Handle initial INVITE challenge response message.
@@ -3355,6 +3390,8 @@ static pj_bool_t outbound_invite_auth(pjsip_rx_data *rdata)
 
 	if (rdata->msg_info.msg->line.status.code != 401
 		&& rdata->msg_info.msg->line.status.code != 407) {
+		// check and update for nextnonce if needed
+		invite_nextnonce_update(rdata);
 		/* Doesn't pertain to us. Move on */
 		return PJ_FALSE;
 	}
@@ -3441,8 +3478,8 @@ int get_line_calls (struct ast_endpoint_snapshot * endpoint_snapshot)
 			continue;
 		}
 
-		if( (strcmp(snapshot->dialplan->appl, "Dial") == 0) && (strcmp(snapshot->dialplan->context, "call_line") == 0) ||
-		    (strcmp(snapshot->dialplan->appl, "AppDial") == 0) && (strcmp(snapshot->dialplan->context, "incoming_calls") == 0))
+		if ((!strcmp(snapshot->dialplan->appl, "Dial") && !strcmp(snapshot->dialplan->context, "call_line")) ||
+		    (!strcmp(snapshot->dialplan->appl, "AppDial") && !strcmp(snapshot->dialplan->context, "incoming_calls")))
 			count++;
 	}
 
@@ -3461,6 +3498,7 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
 	struct pjsip_inv_session *inv_session;
 	RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup);
 	struct ast_sip_session *ret_session;
+	struct ast_sip_transport_state *transport_state;
 	SCOPE_ENTER(1, "%s %s Topology: %s\n", ast_sorcery_object_get_id(endpoint), request_user,
 		ast_str_tmp(256, ast_stream_topology_to_str(req_topology, &STR_TMP)));
 
@@ -3484,12 +3522,15 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
 
 	int line_calls = get_line_calls(endpoint_snapshot);
 
-	if (line_calls >= max_sessions_per_line) {
-		ast_log(LOG_WARNING, "Max call per line limit exceeded [%d/%d]\n", line_calls, max_sessions_per_line);
+	if (line_calls >= ast_sip_get_max_sessions_per_line()) {
+		ast_log(LOG_WARNING, "Max call per line limit exceeded [%d/%d]\n", line_calls, ast_sip_get_max_sessions_per_line());
 		SCOPE_EXIT_RTN_VALUE(NULL, "Max call per line limit exceeded\n");
-	} else if (((current_session_count >= max_sessions) && (ast_active_calls() != 3)) ||
-		   ((current_session_count >= max_sessions) && (ast_active_channels() >= 7))) {
-		ast_log(LOG_WARNING, "Max call limit exceeded [%d/%d]\n", current_session_count, max_sessions);
+	} else if (endpoint->max_sessions && line_calls >= endpoint->max_sessions) {
+		ast_log(LOG_WARNING, "Max call per client limit exceeded [%d/%d]\n", line_calls, endpoint->max_sessions);
+		SCOPE_EXIT_RTN_VALUE(NULL, "Max call per client limit exceeded\n");
+	} else if (((current_session_count >= ast_sip_get_max_sessions_total()) && (ast_active_calls() != 3)) ||
+		   ((current_session_count >= ast_sip_get_max_sessions_total()) && (ast_active_channels() >= 7))) {
+		ast_log(LOG_WARNING, "Max call limit exceeded [%d/%d]\n", current_session_count, ast_sip_get_max_sessions_total());
 		SCOPE_EXIT_RTN_VALUE(NULL, "Max call limit exceeded\n");
 	}
 
@@ -3530,6 +3571,25 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
 	}
 	session->aor = ao2_bump(found_aor);
 	session->call_direction = AST_SIP_SESSION_OUTGOING_CALL;
+	// Checker for Emergency Registration and change transport to upd
+	if (endpoint->Emergency) {
+		ast_log(LOG_NOTICE,"===== EMERGENCY CALL get UDP transport =====\n");
+		transport_state = ast_sip_get_transport_state(endpoint->transport_emergency);
+		ast_log(LOG_NOTICE,"===== EMERGENCY CALL with UDP =====\n");
+	} if (endpoint->register_transport) {
+		transport_state = ast_sip_get_transport_state(endpoint->register_transport);
+	} else {
+		transport_state = ast_sip_get_transport_state(endpoint->transport);
+		if (transport_state && transport_state->type == AST_TRANSPORT_TLS
+				&& !ast_sip_is_X_RDK_NTP_Synchronized())
+			transport_state = NULL;
+	}
+	if (!transport_state) {
+		ast_log(LOG_NOTICE,"===== No Transport! =====\n");
+		pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
+		return NULL;
+	}
+	session->transport_state = transport_state;
 
 	ast_party_id_copy(&session->id, &endpoint->id.self);
 
@@ -4259,6 +4319,66 @@ end:
 	SCOPE_EXIT_RTN_VALUE(0, "%s\n", ast_sip_session_get_name(invite->session));
 }
 
+static struct ast_sip_transport_state* get_invite_transport_state(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
+{
+	struct ast_sip_transport_state *transport_state;
+	enum ast_transport transport_type;
+	pj_str_t type_name;
+
+	ast_debug(3, "Transport of incoming request: %s\n", rdata->tp_info.transport->type_name);
+
+	pj_cstr(&type_name, rdata->tp_info.transport->type_name);
+
+	switch (pjsip_transport_get_type_from_name(&type_name)) {
+		case PJSIP_TRANSPORT_UDP:
+		case PJSIP_TRANSPORT_UDP6:
+			transport_type = AST_TRANSPORT_UDP;
+			break;
+
+		case PJSIP_TRANSPORT_TLS:
+		case PJSIP_TRANSPORT_TLS6:
+			transport_type = AST_TRANSPORT_TLS;
+			break;
+
+		case PJSIP_TRANSPORT_TCP:
+		case PJSIP_TRANSPORT_TCP6:
+			transport_type = AST_TRANSPORT_TCP;
+			break;
+
+		default:
+			ast_debug(3, "Transport %s is not supported\n", rdata->tp_info.transport->type_name);
+			return NULL;
+	}
+
+	if (endpoint->transport) {
+		transport_state = ast_sip_get_transport_state(endpoint->transport);
+		if (transport_state && transport_state->type == transport_type) {
+			ast_debug(3, "Request from the primary transport %s\n", rdata->tp_info.transport->type_name);
+			return transport_state;
+		}
+	}
+
+	if (endpoint->transport2) {
+		transport_state = ast_sip_get_transport_state(endpoint->transport2);
+		if (transport_state && transport_state->type == transport_type) {
+			ast_debug(3, "Request from the secondary transport %s\n", rdata->tp_info.transport->type_name);
+			return transport_state;
+		}
+	}
+
+	if (endpoint->Emergency && endpoint->transport_emergency) {
+		transport_state = ast_sip_get_transport_state(endpoint->transport_emergency);
+		if (transport_state && transport_state->type == transport_type) {
+			ast_debug(3, "Request from the emergency transport %s\n", rdata->tp_info.transport->type_name);
+			return transport_state;
+		}
+	}
+
+	ast_debug(3, "Transport %s is not configured\n", rdata->tp_info.transport->type_name);
+
+	return NULL;
+}
+
 static void handle_new_invite_request(pjsip_rx_data *rdata)
 {
 	RAII_VAR(struct ast_sip_endpoint *, endpoint,
@@ -4269,6 +4389,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
 		strlen(AST_STIR_SHAKEN_RESPONSE_STR_USE_IDENTITY_HEADER)
 	};
 	pjsip_inv_session *inv_session = NULL;
+	struct ast_sip_transport_state *transport_state;
 	struct ast_sip_session *session;
 	struct new_invite invite;
 	char *req_uri = TRACE_ATLEAST(1) ? ast_alloca(256) : "";
@@ -4277,6 +4398,11 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
 
 	ast_assert(endpoint != NULL);
 
+	transport_state = get_invite_transport_state(endpoint, rdata);
+	if (!transport_state) {
+		 SCOPE_EXIT_RTN("Failure: No transport_state in enpoint\n");
+	}
+
 	if ((endpoint->stir_shaken & AST_SIP_STIR_SHAKEN_VERIFY) &&
 		!ast_sip_rdata_get_header_value(rdata, identity_str)) {
 		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
@@ -4296,16 +4422,24 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
 
 	int line_calls = get_line_calls(endpoint_snapshot);
 
-	if (line_calls >= max_sessions_per_line) {
-		ast_log(LOG_WARNING, "Max call per line limit exceeded [%d/%d]\n", line_calls, max_sessions_per_line);
+	if (line_calls >= ast_sip_get_max_sessions_per_line()) {
+		ast_log(LOG_WARNING, "Max call per line limit exceeded [%d/%d]\n", line_calls, ast_sip_get_max_sessions_per_line());
 		/* Dialog's lock and reference are removed in new_invite_initial_answer */
 		if (!new_invite_initial_answer(inv_session, rdata, 486, 486, PJ_FALSE)) {
 			/* Terminate the session if it wasn't done in the answer */
 			pjsip_inv_terminate(inv_session, 486, PJ_FALSE);
 		}
 		SCOPE_EXIT_RTN("Max call per line limit exceeded\n");
-	} else if ((current_session_count == max_sessions) || (ast_active_calls() >= max_sessions)) {
-		ast_log(LOG_WARNING, "Max call limit exceeded [%d/%d]\n", current_session_count, max_sessions);
+	} else if (endpoint->max_sessions && line_calls >= endpoint->max_sessions) {
+		ast_log(LOG_WARNING, "Max call per client limit exceeded [%d/%d]\n", line_calls, endpoint->max_sessions);
+		/* Dialog's lock and reference are removed in new_invite_initial_answer */
+		if (!new_invite_initial_answer(inv_session, rdata, 486, 486, PJ_FALSE)) {
+			/* Terminate the session if it wasn't done in the answer */
+			pjsip_inv_terminate(inv_session, 486, PJ_FALSE);
+		}
+		SCOPE_EXIT_RTN("Max call per client limit exceeded\n");
+	} else if ((current_session_count == ast_sip_get_max_sessions_total()) || (ast_active_calls() >= ast_sip_get_max_sessions_total())) {
+		ast_log(LOG_WARNING, "Max call limit exceeded [%d/%d]\n", current_session_count, ast_sip_get_max_sessions_total());
 		/* Dialog's lock and reference are removed in new_invite_initial_answer */
 		if (!new_invite_initial_answer(inv_session, rdata, 486, 486, PJ_FALSE)) {
 			/* Terminate the session if it wasn't done in the answer */
@@ -4347,6 +4481,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
 		SCOPE_EXIT_RTN("Couldn't create session\n");
 	}
 	session->call_direction = AST_SIP_SESSION_INCOMING_CALL;
+	session->transport_state = transport_state;
 
 	/*
 	 * The current thread is supposed be the session serializer to prevent
@@ -4409,10 +4544,8 @@ static int parse_earlymedia_direction(char *direction)
 	{
 		return 1;
 	}
-	if(!strcmp(direction ,"none"))
-	{
-		return 0;
-	}
+//	if(!strcmp(direction ,"none"))
+	return 0;
 }
 
 static pj_status_t session_on_tx_request(pjsip_tx_data *tdata)
@@ -4428,7 +4561,12 @@ static pj_status_t session_on_tx_request(pjsip_tx_data *tdata)
 			{
 				/* Always play backward (from UAS to UAC) early media if any in an outgoing call regardless the configuration
 				 * of early media  */
-				ast_sip_add_header(tdata,"P-Early-Media", "sendonly");
+				static const pj_str_t headerName = { "P-Early-Media", 13 };
+				int earlymedia = pjsip_msg_find_hdr_by_name(tdata->msg, &headerName, NULL);
+				/* avoid repeating the same headers*/
+				if (!earlymedia) {
+					ast_sip_add_header(tdata,"P-Early-Media", "supported");
+				}
 			}
 		}
 	}
@@ -4769,6 +4907,11 @@ static void handle_session_end(struct ast_sip_session *session)
 		current_session_count--;
 	if (session->channel && ast_channel_emergency_ongoing_get(session->channel))
 		ast_channel_emergency_ongoing_set(session->channel, 0);
+
+	if(session->endpoint){
+		// clear the call status for emergency registration.
+		update_emergency_registration_ongoing_status(ast_sorcery_object_get_id(session->endpoint), false);
+	}
 	/* Session is dead.  Notify the supplements. */
 	AST_LIST_TRAVERSE(&session->supplements, iter, next) {
 		if (iter->session_end) {
@@ -4782,6 +4925,7 @@ static void handle_incoming_response(struct ast_sip_session *session, pjsip_rx_d
 {
 	struct ast_sip_session_supplement *supplement;
 	struct pjsip_status_line status = rdata->msg_info.msg->line.status;
+
 	SCOPE_ENTER(3, "%s: Response is %d %.*s\n", ast_sip_session_get_name(session),
 		status.code, (int) pj_strlen(&status.reason), pj_strbuf(&status.reason));
 
@@ -4799,9 +4943,9 @@ static void handle_incoming_response(struct ast_sip_session *session, pjsip_rx_d
 			pjsip_generic_string_hdr *SessionID = NULL;
 			static const pj_str_t headerName = { "Session-ID", 10 };
 			SessionID = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &headerName, NULL);
-			char value[33] = {0};
+			char value[128] = {0};
 			if (SessionID)
-				ast_copy_pj_str(&value, &SessionID->hvalue, pj_strlen(&SessionID->hvalue) + 1);
+				ast_copy_pj_str(&value, &SessionID->hvalue, sizeof(value));
 
 			ast_channel_SIPSessionID_set(session->channel, value);
 		}
@@ -4829,8 +4973,36 @@ static void handle_outgoing_request(struct ast_sip_session *session, pjsip_tx_da
 	SCOPE_ENTER(3, "%s: Method is %.*s\n", ast_sip_session_get_name(session),
 		(int) pj_strlen(&req.method.name), pj_strbuf(&req.method.name));
 
-	ast_sip_message_apply_transport(session->endpoint->transport, tdata);
+	if(session->endpoint->Emergency){
+		ast_sip_message_apply_transport(session->endpoint->transport_emergency, tdata);
+   		RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup);
+		transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", session->endpoint->transport_emergency);
+		if (!transport) {
+			ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport transport-udp\n");
+			return;
+		}
+		ast_log(LOG_NOTICE,"===== EMERGENCY Requset with UDP =====\n");
 
+		if (req.method.id == PJSIP_INVITE_METHOD && session->endpoint->transport_emergency) {
+			struct ast_sip_transport_state *transport_emergency;
+			pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
+
+			transport_emergency = ast_sip_get_transport_state(session->endpoint->transport_emergency);
+			if (transport_emergency) {
+				ast_sip_set_tpselector_from_transport_name(session->endpoint->transport_emergency, &selector);
+				pjsip_inv_session *inv = session->inv_session;
+				pjsip_dlg_set_transport(inv->dlg, &selector);
+				session->transport_state = transport_emergency;
+			} else {
+				ast_log(LOG_ERROR, "transport_emergency %s is not available\n", session->endpoint->transport_emergency);
+			}
+		}
+
+		ast_log(LOG_NOTICE, "Sending SIP Request with transport (%s)\n", session->transport_state->id);
+
+	} else {
+		ast_sip_message_apply_transport(session->endpoint->transport, tdata);
+	}
 	AST_LIST_TRAVERSE(&session->supplements, supplement, next) {
 		if (supplement->outgoing_request && does_method_match(&req.method.name, supplement->method)) {
 			supplement->outgoing_request(session, tdata);
@@ -4913,28 +5085,124 @@ static int session_end_completion(void *vsession)
 	return 0;
 }
 
+static struct ast_sip_transport_state* get_udp_transport_state()
+{
+	struct ao2_container *transport_states = ast_sip_get_transport_states();
+	struct ast_sip_transport_state *state;
+	struct ao2_iterator it;
+
+	if (!transport_states)
+		return NULL;
+
+	it = ao2_iterator_init(transport_states, 0);
+	while (state = ao2_iterator_next(&it)) {
+		if (!state->flow && state->type == AST_TRANSPORT_UDP) {
+			ast_debug(3, "found UDP transport '%s'\n", state->id);
+			ao2_ref(state, -1);
+			break;
+		}
+
+		ao2_ref(state, -1);
+	}
+	ao2_iterator_destroy(&it);
+
+	ao2_ref(transport_states, -1);
+
+	return state;
+}
+
+static void sip_inv_transport_emergency(pjsip_inv_session *inv)
+{
+	struct ast_sip_session *session = inv->mod_data[session_module.id];
+	struct ast_sip_transport_state *transport_udp;
+	pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
+
+	ast_log(LOG_NOTICE, "Try UDP transport for emergency ongoing call\n");
+
+	transport_udp = get_udp_transport_state();
+
+	if (transport_udp && transport_udp != session->transport_state &&
+	    !ast_sip_set_tpselector_from_transport_name(transport_udp->id, &selector)) {
+		pjsip_sip_uri *target_uri;
+		pjsip_tx_data *tdata;
+
+		ast_log(LOG_NOTICE, "Sending SIP INVITE with transport (%s)\n", transport_udp->id);
+
+		session->transport_state = transport_udp;
+		pjsip_dlg_set_transport(inv->dlg, &selector);
+
+		pjsip_inv_uac_restart(inv, PJ_FALSE);
+		pjmedia_sdp_neg_cancel_offer(inv->neg);
+
+		target_uri = pjsip_uri_get_uri(inv->dlg->target);
+		if (target_uri)
+			target_uri->port = pj_sockaddr_get_port(&transport_udp->host);
+
+		ast_sip_session_create_invite(session, &tdata);
+		ast_sip_session_send_request(session, tdata);
+
+	} else if (session->endpoint) {
+		ast_log(LOG_NOTICE, "%s: Failed to INVITE, schedule re-registration\n", ast_sorcery_object_get_id(session->endpoint));
+
+		session->endpoint->failover_reg_addr = 1;
+		queue_registration_recovery_flow(ast_sorcery_object_get_id(session->endpoint));
+	}
+}
+
+static int check_content_xml(pjsip_rx_data *rdata)
+{
+	pjsip_msg_body *body = rdata->msg_info.msg->body;
+	pjsip_ctype_hdr *ctype_hdr = rdata->msg_info.ctype;
+	if (body && ctype_hdr &&
+		ast_sip_is_media_type_in(&ctype_hdr->media, &pjsip_media_type_application_3gpp_ims_xml, SENTINEL)) {
+		int res;
+		char buf[body->len + 1];
+
+		res = body->print_body(body, buf, sizeof(buf));
+		if (res <= 0) {
+			// no real content body
+			return 0;
+		}
+		buf[res] = '\0';
+		if (strcasestr(buf, "<ims-3gpp") && strcasestr(buf, "<action>initial-registration</action>")) {
+			// trigger registration recovery to the next address
+			return 1;
+		}
+	}
+	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 && tsx->status_code != 500) {
+	ast_debug(3, "%s: Received response '%d'\n", ast_sip_session_get_name(session), tsx->status_code);
+
+	if (tsx->status_code != 503 && tsx->status_code != 408 && tsx->status_code != 500 && tsx->status_code != 504 && tsx->status_code != 600) {
 		// DNS resolve: stick to the server address
 		ast_sip_resolve_track_cur_addr(tsx->last_tx);
 		return 0;
 	}
 
-	if (!ast_sip_failover_request(tsx->last_tx)) {
-		return 0;
+	if (tsx->status_code == 504 && e->body.rx_msg.rdata && check_content_xml(e->body.rx_msg.rdata) && session->endpoint) {
+		ast_log(LOG_NOTICE, "%s: Failed to INVITE, 504 with 3gpp-ims+xml action initial-registration, schedule re-registration\n",
+				ast_sorcery_object_get_id(session->endpoint));
+
+		session->endpoint->failover_reg_addr = 1;
+		queue_registration_recovery_flow(ast_sorcery_object_get_id(session->endpoint));
+		return 1; // hangup the current session with cause 504.
+	}
+	ast_log(LOG_NOTICE, "Failed to send SIP INVITE with transport '%s'\n", session->transport_state->id);
+
+	// Try more for emergency ongoing INVITE
+	if (session->channel && ast_channel_emergency_ongoing_get(session->channel)) {
+		struct pjsip_msg *msg = tsx->last_tx->msg;
+
+		if (msg && msg->type == PJSIP_REQUEST_MSG && msg->line.req.method.id == PJSIP_INVITE_METHOD)
+			sip_inv_transport_emergency(inv);
 	}
 
-	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;
 }
 
@@ -5335,6 +5603,25 @@ static int add_sdp_streams(struct ast_sip_session_media *session_media,
 
 	handler_list = ao2_find(sdp_handlers, ast_codec_media_type2str(session_media->type), OBJ_KEY);
 	if (!handler_list) {
+		/* If there is no handler for this media, set port as 0 in m line and copy all media specific
+		 * attributes from the remote (offer) */
+		if ((ast_stream_get_state(stream) == AST_STREAM_STATE_REMOVED || !ast_stream_get_formats(stream) ||
+			!ast_format_cap_count(ast_stream_get_formats(stream))) && remote && remote->media[ast_stream_get_position(stream)]) {
+			pj_pool_t *pool = session->inv_session->pool_prov;
+			struct pjmedia_sdp_media *media = pjmedia_sdp_media_clone(pool, remote->media[ast_stream_get_position(stream)]);
+			if (media) {
+				/* Update port */
+				media->desc.port = 0;
+				media->desc.port_count = 1;
+
+				/* Add this media to the local SDP */
+				answer->media[answer->media_count++] = media;
+				ast_stream_set_state(stream, AST_STREAM_STATE_REMOVED);
+
+				SCOPE_EXIT_RTN_VALUE(0, "Add a rejected media stream and its attributes\n");
+			}
+		}
+
 		SCOPE_EXIT_RTN_VALUE(0, "No handlers\n");
 	}
 
@@ -5502,7 +5789,6 @@ static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, stru
 		/* This code does not enforce any maximum stream count limitations as that is done on either
 		 * the handling of an incoming SDP offer or on the handling of a session refresh.
 		 */
-
 		session_media = ast_sip_session_media_state_add(session, session->pending_media_state, ast_stream_get_type(stream), i);
 		if (!session_media) {
 			local = NULL;
@@ -5858,6 +6144,39 @@ static pjsip_inv_callback inv_callback = {
 	.on_redirected = session_inv_on_redirected,
 };
 
+int ast_sip_session_sdp_answer(struct ast_sip_session *session)
+{
+	const pjmedia_sdp_session *offer = NULL, *answer;
+	struct ast_channel *chan;
+	SCOPE_ENTER(3, "%s\n", ast_sip_session_get_name(session));
+
+	if (!session || !session->inv_session || !session->inv_session->neg || !(chan = session->channel)) {
+		SCOPE_EXIT_RTN_VALUE(-1, "%s: invalid parameter(s)\n", ast_sip_session_get_name(session));
+	}
+
+	if (ast_channel_narrow_band_only(chan)) {
+		if (pjmedia_sdp_neg_get_neg_remote(session->inv_session->neg, &offer) != PJ_SUCCESS) {
+			SCOPE_EXIT_RTN_VALUE(-1, "%s: pjmedia_sdp_neg_get_neg_remote() failed\n", ast_sip_session_get_name(session));
+		}
+
+		if (handle_incoming_sdp(session, offer)) {
+			ast_sip_session_media_state_reset(session->pending_media_state);
+			SCOPE_EXIT_RTN_VALUE(-1, "%s: handle_incoming_sdp() failed and media state is reset\n", ast_sip_session_get_name(session));
+		}
+
+		answer = create_local_sdp(session->inv_session, session, offer, 0);
+		if (!answer) {
+			SCOPE_EXIT_RTN_VALUE(-1, "%s: create_local_sdp() failed\n", ast_sip_session_get_name(session));
+		}
+
+		if (pjsip_inv_set_sdp_answer(session->inv_session, answer) != PJ_SUCCESS) {
+			SCOPE_EXIT_RTN_VALUE(-1, "%s: pjsip_inv_set_sdp_answer() failed\n", ast_sip_session_get_name(session));
+		}
+	}
+
+	SCOPE_EXIT_RTN_VALUE(0, "%s\n", ast_sip_session_get_name(session));
+}
+
 /*! \brief Hook for modifying outgoing messages with SDP to contain the proper address information */
 static void session_outgoing_nat_hook(pjsip_tx_data *tdata, struct ast_sip_transport *transport)
 {
@@ -6490,6 +6809,7 @@ AST_TEST_DEFINE(test_resolve_refresh_media_states)
 static int load_module(void)
 {
 	pjsip_endpoint *endpt;
+	static const pj_str_t str_199 = { "199", 3 };
 
 	if (!ast_sip_get_sorcery() || !ast_sip_get_pjsip_endpoint()) {
 		return AST_MODULE_LOAD_DECLINE;
@@ -6508,6 +6828,9 @@ static int load_module(void)
 	pjsip_inv_usage_init(endpt, &inv_callback);
 	pjsip_100rel_init_module(endpt);
 	pjsip_timer_init_module(endpt);
+
+	pjsip_endpt_add_capability(endpt, NULL, PJSIP_H_SUPPORTED, NULL, 1, &str_199);
+
 	if (ast_sip_register_service(&session_module)) {
 		return AST_MODULE_LOAD_DECLINE;
 	}
@@ -6542,5 +6865,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_
 	.load = load_module,
 	.unload = unload_module,
 	.load_pri = AST_MODPRI_APP_DEPEND,
-	.requires = "res_pjsip",
+	.requires = "res_pjsip,res_pjsip_outbound_registration",
 );
diff --git a/res/res_pjsip_session/pjsip_session_caps.c b/res/res_pjsip_session/pjsip_session_caps.c
index d44f1a6bb0a1dbea7b2f4455e0fdf345e5bc4ed7..2735ae66950270456dc52f28c21d6da2bc261777 100644
--- a/res/res_pjsip_session/pjsip_session_caps.c
+++ b/res/res_pjsip_session/pjsip_session_caps.c
@@ -28,6 +28,7 @@
 #include "asterisk/res_pjsip.h"
 #include "asterisk/res_pjsip_session.h"
 #include "asterisk/res_pjsip_session_caps.h"
+#include "asterisk/format_cache.h"
 
 static void log_caps(int level, const char *file, int line, const char *function,
 	const struct ast_sip_session *session, enum ast_media_type media_type,
@@ -152,13 +153,29 @@ struct ast_format_cap *ast_sip_session_create_joint_call_cap(
 	const struct ast_sip_session *session, enum ast_media_type media_type,
 	const struct ast_format_cap *remote)
 {
-	struct ast_format_cap *joint = ast_sip_create_joint_call_cap(remote,
-		session->endpoint->media.codecs, media_type,
-		session->call_direction == AST_SIP_SESSION_OUTGOING_CALL
-				? session->endpoint->media.outgoing_call_offer_pref
-				: session->endpoint->media.incoming_call_offer_pref);
+	struct ast_format_cap *joint, *local = session->endpoint->media.codecs, *cap = NULL;
+	struct ast_format *format = ast_format_cache_get("g722");
+
+	if (local && session->call_direction == AST_SIP_SESSION_INCOMING_CALL && session->channel &&
+			ast_channel_narrow_band_only(session->channel) && format) {
+		cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+		if (!cap || ast_format_cap_append_from_cap(cap, local, AST_MEDIA_TYPE_UNKNOWN)) {
+			ast_log(LOG_ERROR, "Failed to create a new cap\n");
+			ao2_cleanup(cap);
+			cap = NULL;
+		} else {
+			ast_debug(3, "Remove g722 from the local capability\n");
+			ast_format_cap_remove(cap, format);
+			local = cap;
+		}
+	}
 
-	log_caps(LOG_DEBUG, session, media_type, session->endpoint->media.codecs, remote, joint);
+	joint = ast_sip_create_joint_call_cap(remote, local, media_type, session->call_direction == AST_SIP_SESSION_OUTGOING_CALL ?
+				session->endpoint->media.outgoing_call_offer_pref : session->endpoint->media.incoming_call_offer_pref);
+	log_caps(LOG_DEBUG, session, media_type, local, remote, joint);
+	if (cap) {
+		ao2_cleanup(cap);
+	}
 
 	return joint;
 }
diff --git a/res/res_resolver_unbound.c b/res/res_resolver_unbound.c
index 93bb3cadafd8d5f37ee0e6a96a8b5ed6adf47155..47cd883ceb35bf15ac2a8cef9850565a6a196e4f 100644
--- a/res/res_resolver_unbound.c
+++ b/res/res_resolver_unbound.c
@@ -257,6 +257,10 @@ static void unbound_resolver_callback(void *data, int err, struct ub_result *ub_
 {
 	struct ast_dns_query *query = data;
 
+	if (err || !ub_result) {
+		ast_log(LOG_ERROR, "Resolution query failed. Error: %s\n", ub_strerror(err));
+		goto Exit;
+	}
 	if (!ast_dns_resolver_set_result(query, ub_result->secure, ub_result->bogus, ub_result->rcode,
 		S_OR(ub_result->canonname, ast_dns_query_get_name(query)), ub_result->answer_packet, ub_result->answer_len)) {
 		int i;
@@ -269,7 +273,7 @@ static void unbound_resolver_callback(void *data, int err, struct ub_result *ub_
 			}
 		}
 	}
-
+Exit:
 	ast_dns_resolver_completed(query);
 	ao2_ref(query, -1);
 	ub_resolve_free(ub_result);
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 995ac58f83cee9571f5c7e6d867a257a418a8d6b..eaa0c75d0303e338d7f7905d8416196927b84ddd 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -213,6 +213,7 @@ static int dtmftimeout = DEFAULT_DTMF_TIMEOUT;
 
 static int rtpstart = DEFAULT_RTP_START;			/*!< First port for RTP sessions (set in rtp.conf) */
 static int rtpend = DEFAULT_RTP_END;			/*!< Last port for RTP sessions (set in rtp.conf) */
+static int next_rtp_port = DEFAULT_RTP_START;
 static int rtcpstats;			/*!< Are we debugging RTCP? */
 static int rtcpinterval = RTCP_DEFAULT_INTERVALMS; /*!< Time between rtcp reports in millisecs */
 static struct ast_sockaddr rtpdebugaddr;	/*!< Debug packets to/from this host */
@@ -664,6 +665,7 @@ static int ast_rtp_dtmf_compatible(struct ast_channel *chan0, struct ast_rtp_ins
 static void ast_rtp_stun_request(struct ast_rtp_instance *instance, struct ast_sockaddr *suggestion, const char *username);
 static void ast_rtp_stop(struct ast_rtp_instance *instance);
 static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char* desc);
+static void ast_rtcp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char* desc);
 static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level);
 static int ast_rtp_send_dtmf_bypass(struct ast_rtp_instance *instance, struct ast_frame *frame);
 static unsigned int ast_rtp_get_ssrc(struct ast_rtp_instance *instance);
@@ -2565,6 +2567,7 @@ static struct ast_rtp_engine asterisk_rtp_engine = {
 	.stun_request = ast_rtp_stun_request,
 	.stop = ast_rtp_stop,
 	.qos = ast_rtp_qos_set,
+	.rtcp_qos = ast_rtcp_qos_set,
 	.sendcng = ast_rtp_sendcng,
 	.dtmfbypass = ast_rtp_send_dtmf_bypass,
 #ifdef HAVE_PJPROJECT
@@ -2635,6 +2638,29 @@ static void dtls_perform_setup(struct dtls_details *dtls)
 }
 #endif
 
+static void ast_rtp_network_info_notify(struct ast_rtp_instance *instance, struct ast_sockaddr *addr)
+{
+	struct ast_sockaddr prev_address;
+	char *addr_str, *addr_str_tmp;
+
+	if (ast_sockaddr_isnull(addr)) {
+		ast_rtp_instance_get_remote_address(instance, &prev_address);
+		addr_str = ast_sockaddr_stringify(&prev_address);
+		addr_str_tmp = strstr(addr_str, ":");
+		if (addr_str_tmp)
+			*addr_str_tmp = '\0';
+		ast_send_network_info_ubus_event(0, "rtp", addr_str, ast_sockaddr_port(&prev_address));
+	}
+	else {
+		addr_str = ast_sockaddr_stringify(addr);
+		addr_str_tmp = strstr(addr_str, ":");
+		if (addr_str_tmp)
+			*addr_str_tmp = '\0';
+		ast_send_network_info_ubus_event(1, "rtp", addr_str, ast_sockaddr_port(addr));
+	}
+	ast_rtp_instance_set_remote_address(instance, addr);
+}
+
 #ifdef HAVE_PJPROJECT
 static void rtp_learning_start(struct ast_rtp *rtp);
 
@@ -2655,7 +2681,7 @@ static void ast_rtp_ice_start_media(pj_ice_sess *ice, pj_status_t status)
 			/* Symmetric RTP must be disabled for the remote address to not get overwritten */
 			ast_rtp_instance_set_prop(instance, AST_RTP_PROPERTY_NAT, 0);
 
-			ast_rtp_instance_set_remote_address(instance, &remote_address);
+			ast_rtp_network_info_notify(instance, &remote_address);
 		}
 
 		if (rtp->rtcp) {
@@ -3280,7 +3306,7 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
 				if (rtcp) {
 					ast_sockaddr_copy(&rtp->rtcp->them, sa);
 				} else {
-					ast_rtp_instance_set_remote_address(instance, sa);
+					ast_rtp_network_info_notify(instance, sa);
 				}
 			}
 			return 0;
@@ -3903,8 +3929,11 @@ static int rtp_allocate_transport(struct ast_rtp_instance *instance, struct ast_
 	}
 
 	/* Now actually find a free RTP port to use */
+	/* Comment out to make rtp port sequentially increased
 	x = (ast_random() % (rtpend - rtpstart)) + rtpstart;
 	x = x & ~1;
+	*/
+	x = next_rtp_port;
 	startplace = x;
 
 	/* Protection against infinite loops in the case there is a potential case where the loop is not broken such as an odd
@@ -3960,6 +3989,7 @@ static int rtp_allocate_transport(struct ast_rtp_instance *instance, struct ast_
 	rtp->dtls.timeout_timer = -1;
 #endif
 
+	next_rtp_port = x + 2;
 	return 0;
 }
 
@@ -8221,7 +8251,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
 		if ((ast_stun_handle_packet(rtp->s, &addr_tmp, read_area, res, NULL, NULL) == AST_STUN_ACCEPT) &&
 		    ast_sockaddr_isnull(&remote_address)) {
 			ast_sockaddr_from_sin(&addr, &addr_tmp);
-			ast_rtp_instance_set_remote_address(instance, &addr);
+			ast_rtp_network_info_notify(instance, &addr);
 		}
 		return &ast_null_frame;
 	}
@@ -9036,7 +9066,17 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct
 	for (index = 0; index < AST_VECTOR_SIZE(&rtp->ssrc_mapping); ++index) {
 		struct rtp_ssrc_mapping *mapping = AST_VECTOR_GET_ADDR(&rtp->ssrc_mapping, index);
 
-		ast_rtp_instance_set_remote_address(mapping->instance, addr);
+		ast_rtp_network_info_notify(mapping->instance, addr);
+	}
+
+	if (!ast_sockaddr_isnull(addr)) {
+		char *addr_str, *addr_str_tmp;
+
+		addr_str = ast_sockaddr_stringify(addr);
+		addr_str_tmp = strstr(addr_str, ":");
+		if (addr_str_tmp)
+			*addr_str_tmp = '\0';
+		ast_send_network_info_ubus_event(1, "rtp", addr_str, ast_sockaddr_port(addr));
 	}
 
 	/* Need to reset the DTMF last sequence number and the timestamp of the last END packet */
@@ -9315,7 +9355,7 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance)
 		rtp->red = NULL;
 	}
 
-	ast_rtp_instance_set_remote_address(instance, &addr);
+	ast_rtp_network_info_notify(instance, &addr);
 
 	ast_set_flag(rtp, FLAG_NEED_MARKER_BIT);
 }
@@ -9328,6 +9368,14 @@ static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos,
 	return ast_set_qos(rtp->s, tos, cos, desc);
 }
 
+static void ast_rtcp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char *desc)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+	if (rtp->rtcp && rtp->rtcp->s) {
+		ast_set_qos(rtp->rtcp->s, tos, cos, desc);
+	}
+}
+
 /*!
  * \brief generate comfort noice (CNG)
  *
@@ -9571,7 +9619,7 @@ static int ast_rtp_bundle(struct ast_rtp_instance *child, struct ast_rtp_instanc
 
 	ao2_lock(child);
 
-	ast_rtp_instance_set_remote_address(child, &them);
+	ast_rtp_network_info_notify(child, &them);
 
 	return 0;
 }
@@ -10010,6 +10058,7 @@ static int rtp_reload(int reload, int by_external_config)
 
 	rtpstart = DEFAULT_RTP_START;
 	rtpend = DEFAULT_RTP_END;
+	next_rtp_port = rtpstart;
 	rtcpinterval = RTCP_DEFAULT_INTERVALMS;
 	dtmftimeout = DEFAULT_DTMF_TIMEOUT;
 	strictrtp = DEFAULT_STRICT_RTP;
@@ -10263,6 +10312,7 @@ static int rtp_reload(int reload, int by_external_config)
 		rtpend = DEFAULT_RTP_END;
 	}
 	ast_verb(2, "RTP Allocating from port range %d -> %d\n", rtpstart, rtpend);
+	next_rtp_port = rtpstart;
 	return 0;
 }
 
diff --git a/sounds/audio_voip.gsm b/sounds/audio_voip.gsm
new file mode 100644
index 0000000000000000000000000000000000000000..6db498565e1f158357de1a6798e4ba379f15d3cb
Binary files /dev/null and b/sounds/audio_voip.gsm differ
diff --git a/sounds/audio_wan.gsm b/sounds/audio_wan.gsm
new file mode 100644
index 0000000000000000000000000000000000000000..82dec159991c1d7cf5519d50ee92f83dfa7feddc
Binary files /dev/null and b/sounds/audio_wan.gsm differ
diff --git a/third-party/apply_patches b/third-party/apply_patches
index 3c0a6bc76e70e7a341934da82aa5189967d7425b..f847fd60833c363d4c1645fe9b5a392a7db09189 100755
--- a/third-party/apply_patches
+++ b/third-party/apply_patches
@@ -21,7 +21,7 @@ if [ ! -d "$sourcedir" ] ; then
 	exit 1
 fi
 
-patches=$(${FIND} "$patchdir" -name "*.patch")
+patches=$(${FIND} "$patchdir" -name "*.patch" | sort)
 if [ x"$patches" = x"" ] ; then
 	echo "No patches in $patchdir"  >&2
 	exit 0
diff --git a/third-party/jansson/Makefile b/third-party/jansson/Makefile
index f24a1c6a6db9864ae493364075e7cd20d80585e3..e3b6f5aa0fe398c8628eba64492783a96a267901 100644
--- a/third-party/jansson/Makefile
+++ b/third-party/jansson/Makefile
@@ -84,8 +84,9 @@ source/.unpacked: $(DOWNLOAD_DIR)/$(TARBALL_FILE)
 	$(ECHO_PREFIX) Rebuilding
 	$(CMD_PREFIX) $(MAKE) clean $(REALLY_QUIET)
 
+JANSSON_CONFIG_OPTS += --host=aarch64-rdk-linux
 source/config.status: source/.unpacked Makefile.rules .rebuild_needed
-	$(ECHO_PREFIX) Configuring
+	$(ECHO_PREFIX) Configuring with $(JANSSON_CONFIG_OPTS)
 	$(CMD_PREFIX) (cd source ; ./configure $(QUIET_CONFIGURE) $(JANSSON_CONFIG_OPTS) --disable-shared \
 		--enable-static --prefix=$(JANSSON_DIR)/dest --libdir=$(JANSSON_DIR)/dest/lib CFLAGS="$(OPTIMIZE_CFLAGS)")
 
diff --git a/third-party/pjproject/patches/0011-Add-support-for-Session-ID-header.patch b/third-party/pjproject/patches/0011-Add-support-for-Session-ID-header.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b52f8449506ef72cd9d0a5dcc78a9175c18e635b
--- /dev/null
+++ b/third-party/pjproject/patches/0011-Add-support-for-Session-ID-header.patch
@@ -0,0 +1,691 @@
+From 3c5be9f8088766785db5f18e306d187fb0065280 Mon Sep 17 00:00:00 2001
+From: Grzegorz Sluja <grzegorz.sluja@iopsys.eu>
+Date: Mon, 24 Jun 2024 12:12:59 +0200
+Subject: [PATCH] Add support for Session-ID header
+
+According to RFC7329 there is a need for having a globally unique
+session identifier for the same SIP session that can be consistently
+maintained across SIP Proxies. There is a Call-ID header value already
+as a globally unique identifier but in practice it is often changed by
+SIP Back-to-Back User Agents. Therefore in order to provide an
+identifier that will not be modified/replaced by B2BUAs a new SIP header
+"Session-ID" is added.
+
+The general concept of "Session-ID" header is that the UAC generating an
+out-of-dialog request generates a new, unique value that remains
+constant for the duration of the transaction, any dialog created from
+that request or for a registration.
+---
+ pjsip-apps/src/samples/footprint.c  |  4 +--
+ pjsip-apps/src/samples/pjsip-perf.c |  4 +--
+ pjsip/include/pjsip/sip_dialog.h    |  1 +
+ pjsip/include/pjsip/sip_transport.h |  3 ++
+ pjsip/include/pjsip/sip_util.h      |  4 +++
+ pjsip/src/pjsip-simple/publishc.c   |  8 +++++
+ pjsip/src/pjsip-ua/sip_reg.c        |  8 +++++
+ pjsip/src/pjsip/sip_dialog.c        | 17 ++++++++++
+ pjsip/src/pjsip/sip_endpoint.c      |  3 +-
+ pjsip/src/pjsip/sip_util.c          | 52 ++++++++++++++++++++++++++---
+ pjsip/src/pjsua-lib/pjsua_acc.c     |  2 +-
+ pjsip/src/pjsua-lib/pjsua_im.c      |  4 +--
+ pjsip/src/test/transport_test.c     |  6 ++--
+ pjsip/src/test/tsx_basic_test.c     |  6 ++--
+ pjsip/src/test/tsx_bench.c          |  4 +--
+ pjsip/src/test/tsx_uac_test.c       |  2 +-
+ pjsip/src/test/tsx_uas_test.c       |  3 +-
+ pjsip/src/test/txdata_test.c        | 11 +++---
+ 18 files changed, 115 insertions(+), 27 deletions(-)
+
+diff --git a/pjsip-apps/src/samples/footprint.c b/pjsip-apps/src/samples/footprint.c
+index 6fa4425..7ac1170 100644
+--- a/pjsip-apps/src/samples/footprint.c
++++ b/pjsip-apps/src/samples/footprint.c
+@@ -275,10 +275,10 @@ int dummy_function()
+ #endif
+ 
+ #ifdef HAS_PJSIP_CORE_MSG_UTIL
+-    pjsip_endpt_create_request(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
++    pjsip_endpt_create_request(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                                -1, NULL, NULL);
+     pjsip_endpt_create_request_from_hdr(NULL, NULL, NULL, NULL, NULL, NULL,
+-                                        NULL, -1, NULL, NULL);
++                                        NULL, NULL, -1, NULL, NULL);
+     pjsip_endpt_create_response(NULL, NULL, -1, NULL, NULL);
+     pjsip_endpt_create_ack(NULL, NULL, NULL, NULL);
+     pjsip_endpt_create_cancel(NULL, NULL, NULL);
+diff --git a/pjsip-apps/src/samples/pjsip-perf.c b/pjsip-apps/src/samples/pjsip-perf.c
+index e13d593..df04017 100644
+--- a/pjsip-apps/src/samples/pjsip-perf.c
++++ b/pjsip-apps/src/samples/pjsip-perf.c
+@@ -1329,7 +1329,7 @@ static pj_status_t submit_stateless_job(void)
+     status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method, 
+                                         &app.client.dst_uri, &app.local_uri,
+                                         &app.client.dst_uri, &app.local_contact,
+-                                        NULL, -1, NULL, &tdata);
++                                        NULL, NULL, -1, NULL, &tdata);
+     if (status != PJ_SUCCESS) {
+         app_perror(THIS_FILE, "Error creating request", status);
+         report_completion(701);
+@@ -1395,7 +1395,7 @@ static pj_status_t submit_job(void)
+     status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method, 
+                                         &app.client.dst_uri, &app.local_uri,
+                                         &app.client.dst_uri, &app.local_contact,
+-                                        NULL, -1, NULL, &tdata);
++                                        NULL, NULL, -1, NULL, &tdata);
+     if (status != PJ_SUCCESS) {
+         app_perror(THIS_FILE, "Error creating request", status);
+         report_completion(701);
+diff --git a/pjsip/include/pjsip/sip_dialog.h b/pjsip/include/pjsip/sip_dialog.h
+index 49853ce..48cb048 100644
+--- a/pjsip/include/pjsip/sip_dialog.h
++++ b/pjsip/include/pjsip/sip_dialog.h
+@@ -161,6 +161,7 @@ struct pjsip_dialog
+     pj_bool_t           secure;     /**< Use secure transport?              */
+     pj_bool_t           add_allow;  /**< Add Allow header in requests?      */
+     pjsip_cid_hdr      *call_id;    /**< Call-ID header.                    */
++    pjsip_generic_string_hdr *session_id; /**< Session-ID header.           */
+     pjsip_route_hdr     route_set;  /**< Route set.                         */
+     pj_bool_t           route_set_frozen; /**< Route set has been set.      */
+     pjsip_auth_clt_sess auth_sess;  /**< Client authentication session.     */
+diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h
+index 87f982c..ea8acac 100644
+--- a/pjsip/include/pjsip/sip_transport.h
++++ b/pjsip/include/pjsip/sip_transport.h
+@@ -371,6 +371,9 @@ struct pjsip_rx_data
+         /** The Call-ID header as found in the message. */
+         pjsip_cid_hdr           *cid;
+ 
++        /** The Session-ID header as found in the message. */
++        pjsip_generic_string_hdr *sid;
++
+         /** The From header as found in the message. */
+         pjsip_from_hdr          *from;
+ 
+diff --git a/pjsip/include/pjsip/sip_util.h b/pjsip/include/pjsip/sip_util.h
+index bba120f..8f44f0f 100644
+--- a/pjsip/include/pjsip/sip_util.h
++++ b/pjsip/include/pjsip/sip_util.h
+@@ -259,6 +259,7 @@ PJ_DECL(pj_status_t) pjsip_target_assign_status(pjsip_target *target,
+  *                  can be tokens, or a quoted string, if a larger 
+  *                  character set is desired.
+  * @param call_id   Optional Call-ID (put NULL to generate unique Call-ID).
++ * @param session_id Optional Session-ID (put NULL to generate unique Session-ID).
+  * @param cseq      Optional CSeq (put -1 to generate random CSeq).
+  * @param text      Optional text body (put NULL to omit body).
+  * @param p_tdata   Pointer to receive the transmit data.
+@@ -272,6 +273,7 @@ PJ_DECL(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt,
+                                                  const pj_str_t *to, 
+                                                  const pj_str_t *contact,
+                                                  const pj_str_t *call_id,
++                                                 const pj_str_t *session_id,
+                                                  int cseq, 
+                                                  const pj_str_t *text,
+                                                  pjsip_tx_data **p_tdata);
+@@ -298,6 +300,7 @@ PJ_DECL(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt,
+  * @param to        To header.
+  * @param contact   Contact header.
+  * @param call_id   Optional Call-ID (put NULL to generate unique Call-ID).
++ * @param session_id Optional Session-ID (put NULL to generate unique Session-ID).
+  * @param cseq      Optional CSeq (put -1 to generate random CSeq).
+  * @param text      Optional text body (put NULL to omit body).
+  * @param p_tdata   Pointer to receive the transmit data.
+@@ -312,6 +315,7 @@ pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
+                                      const pjsip_to_hdr *to,
+                                      const pjsip_contact_hdr *contact,
+                                      const pjsip_cid_hdr *call_id,
++                                     const pjsip_generic_string_hdr *session_id,
+                                      int cseq,
+                                      const pj_str_t *text,
+                                      pjsip_tx_data **p_tdata);
+diff --git a/pjsip/src/pjsip-simple/publishc.c b/pjsip/src/pjsip-simple/publishc.c
+index a18a32f..8ed6c2a 100644
+--- a/pjsip/src/pjsip-simple/publishc.c
++++ b/pjsip/src/pjsip-simple/publishc.c
+@@ -85,6 +85,7 @@ struct pjsip_publishc
+     pj_str_t                     str_target_uri;
+     pjsip_uri                   *target_uri;
+     pjsip_cid_hdr               *cid_hdr;
++    pjsip_generic_string_hdr    *sid_hdr;
+     pjsip_cseq_hdr              *cseq_hdr;
+     pj_str_t                     from_uri;
+     pjsip_from_hdr              *from_hdr;
+@@ -255,6 +256,7 @@ PJ_DEF(pj_status_t) pjsip_publishc_init(pjsip_publishc *pubc,
+                                         pj_uint32_t expires)
+ {
+     pj_str_t tmp;
++    const pj_str_t STR_SESSION = { "Session-ID", 10 };
+ 
+     PJ_ASSERT_RETURN(pubc && event && target_uri && from_uri && to_uri && 
+                      expires, PJ_EINVAL);
+@@ -299,6 +301,11 @@ PJ_DEF(pj_status_t) pjsip_publishc_init(pjsip_publishc *pubc,
+     pubc->cid_hdr = pjsip_cid_hdr_create(pubc->pool);
+     pj_create_unique_string(pubc->pool, &pubc->cid_hdr->id);
+ 
++    /* Generate Session-ID header. */
++    pj_str_t session_id_uuid;
++    pj_create_unique_string(pubc->pool, &session_id_uuid);
++    pubc->sid_hdr = pjsip_generic_string_hdr_create(pubc->pool, &STR_SESSION, &session_id_uuid);
++
+     /* Set "CSeq" header. */
+     pubc->cseq_hdr = pjsip_cseq_hdr_create(pubc->pool);
+     pubc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
+@@ -387,6 +394,7 @@ static pj_status_t create_request(pjsip_publishc *pubc,
+                                                   pubc->to_hdr,
+                                                   NULL,
+                                                   pubc->cid_hdr,
++                                                  pubc->sid_hdr,
+                                                   pubc->cseq_hdr->cseq,
+                                                   NULL,
+                                                   &tdata);
+diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c
+index 947e9ed..fbb1dde 100644
+--- a/pjsip/src/pjsip-ua/sip_reg.c
++++ b/pjsip/src/pjsip-ua/sip_reg.c
+@@ -79,6 +79,7 @@ struct pjsip_regc
+     pj_str_t                     str_srv_url;
+     pjsip_uri                   *srv_url;
+     pjsip_cid_hdr               *cid_hdr;
++    pjsip_generic_string_hdr    *sid_hdr;
+     pjsip_cseq_hdr              *cseq_hdr;
+     pj_str_t                     from_uri;
+     pjsip_from_hdr              *from_hdr;
+@@ -350,6 +351,7 @@ PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
+ {
+     pj_str_t tmp;
+     pj_status_t status;
++    const pj_str_t STR_SESSION = { "Session-ID", 10 };
+ 
+     PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url && 
+                      expires, PJ_EINVAL);
+@@ -400,6 +402,11 @@ PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
+     regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
+     pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
+ 
++    /* Generate Session-ID header. */
++    pj_str_t session_id_uuid;
++    pj_create_unique_string(regc->pool, &session_id_uuid);
++    regc->sid_hdr = pjsip_generic_string_hdr_create(regc->pool, &STR_SESSION, &session_id_uuid);
++
+     /* Set "CSeq" header. */
+     regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
+     regc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
+@@ -522,6 +529,7 @@ static pj_status_t create_request(pjsip_regc *regc,
+                                                   regc->to_hdr,
+                                                   NULL,
+                                                   regc->cid_hdr,
++                                                  regc->sid_hdr,
+                                                   regc->cseq_hdr->cseq,
+                                                   NULL,
+                                                   &tdata);
+diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c
+index bba63b7..a8e15ac 100644
+--- a/pjsip/src/pjsip/sip_dialog.c
++++ b/pjsip/src/pjsip/sip_dialog.c
+@@ -177,6 +177,7 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uac2(
+     pj_status_t status;
+     pj_str_t tmp;
+     pjsip_dialog *dlg;
++    const pj_str_t STR_SESSION = { "Session-ID", 10 };
+ 
+     /* Check arguments. */
+     PJ_ASSERT_RETURN(create_param->ua && create_param->local_uri.slen &&
+@@ -327,6 +328,11 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uac2(
+     dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
+     pj_create_unique_string(dlg->pool, &dlg->call_id->id);
+ 
++    /* Generate Session-ID header. */
++    pj_str_t session_id_uuid;
++    pj_create_unique_string(dlg->pool, &session_id_uuid);
++    dlg->session_id = pjsip_generic_string_hdr_create(dlg->pool, &STR_SESSION, &session_id_uuid);
++
+     /* Initial route set is empty. */
+     pj_list_init(&dlg->route_set);
+ 
+@@ -522,6 +528,11 @@ pj_status_t create_uas_dialog( pjsip_user_agent *ua,
+     dlg->call_id = (pjsip_cid_hdr*)
+                    pjsip_hdr_clone(dlg->pool, rdata->msg_info.cid);
+ 
++    /* Session-ID */
++    if (rdata->msg_info.sid)
++        dlg->session_id = (pjsip_generic_string_hdr*)
++                    pjsip_hdr_clone(dlg->pool, rdata->msg_info.sid);
++
+     /* Route set.
+      *  RFC 3261 Section 12.1.1:
+      *  The route set MUST be set to the list of URIs in the Record-Route
+@@ -773,6 +784,11 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg,
+     dlg->call_id = (pjsip_cid_hdr*)
+                    pjsip_hdr_clone(dlg->pool, first_dlg->call_id);
+ 
++    /* Session-ID */
++    if (first_dlg->session_id)
++        dlg->session_id = (pjsip_generic_string_hdr*)
++                   pjsip_hdr_clone(dlg->pool, first_dlg->session_id);
++
+     /* Get route-set from the response. */
+     pj_list_init(&dlg->route_set);
+     end_hdr = &msg->hdr;
+@@ -1166,6 +1182,7 @@ static pj_status_t dlg_create_request_throw( pjsip_dialog *dlg,
+                                                  dlg->remote.info,
+                                                  contact,
+                                                  dlg->call_id,
++                                                 dlg->session_id,
+                                                  cseq,
+                                                  NULL,
+                                                  &tdata);
+diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c
+index f77e217..e70a0f6 100644
+--- a/pjsip/src/pjsip/sip_endpoint.c
++++ b/pjsip/src/pjsip/sip_endpoint.c
+@@ -967,7 +967,6 @@ static void endpt_on_rx_msg( pjsip_endpoint *endpt,
+     pj_bool_t handled = PJ_FALSE;
+ 
+     PJ_UNUSED_ARG(msg);
+-
+     if (status != PJ_SUCCESS) {
+         char info[30];
+         char errmsg[PJ_ERR_MSG_SIZE];
+@@ -981,6 +980,8 @@ static void endpt_on_rx_msg( pjsip_endpoint *endpt,
+ 
+             if (rdata->msg_info.cid == NULL || rdata->msg_info.cid->id.slen)
+                 pj_strcpy2(&p, "Call-ID");
++            if (rdata->msg_info.sid == NULL || rdata->msg_info.sid->hvalue.slen)
++                pj_strcpy2(&p, "Session-ID");
+             if (rdata->msg_info.from == NULL)
+                 pj_strcpy2(&p, " From");
+             if (rdata->msg_info.to == NULL)
+diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c
+index 86853f4..2d08cd9 100644
+--- a/pjsip/src/pjsip/sip_util.c
++++ b/pjsip/src/pjsip/sip_util.c
+@@ -199,6 +199,7 @@ static void init_request_throw( pjsip_endpoint *endpt,
+                                 pjsip_to_hdr *param_to, 
+                                 pjsip_contact_hdr *param_contact,
+                                 pjsip_cid_hdr *param_call_id,
++                                pjsip_generic_string_hdr *param_session_id,
+                                 pjsip_cseq_hdr *param_cseq, 
+                                 const pj_str_t *param_text)
+ {
+@@ -239,6 +240,9 @@ static void init_request_throw( pjsip_endpoint *endpt,
+     /* Add Call-ID header. */
+     pjsip_msg_add_hdr(msg, (pjsip_hdr*)param_call_id);
+ 
++    /* Add Session-ID header. */
++    pjsip_msg_add_hdr(msg, (pjsip_hdr*)param_session_id);
++
+     /* Add CSeq header. */
+     pjsip_msg_add_hdr(msg, (pjsip_hdr*)param_cseq);
+ 
+@@ -293,6 +297,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request(  pjsip_endpoint *endpt,
+                                                  const pj_str_t *param_to, 
+                                                  const pj_str_t *param_contact,
+                                                  const pj_str_t *param_call_id,
++                                                 const pj_str_t *param_session_id,
+                                                  int param_cseq, 
+                                                  const pj_str_t *param_text,
+                                                  pjsip_tx_data **p_tdata)
+@@ -304,6 +309,8 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request(  pjsip_endpoint *endpt,
+     pjsip_contact_hdr *contact;
+     pjsip_cseq_hdr *cseq = NULL;    /* = NULL, warning in VC6 */
+     pjsip_cid_hdr *call_id;
++    pjsip_generic_string_hdr *session_id;
++    const pj_str_t STR_SESSION = { "Session-ID", 10 };
+     pj_str_t tmp;
+     pj_status_t status;
+     const pj_str_t STR_CONTACT = { "Contact", 7 };
+@@ -367,6 +374,15 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request(  pjsip_endpoint *endpt,
+         else
+             pj_create_unique_string(tdata->pool, &call_id->id);
+ 
++        /* Session-ID */
++        if (param_session_id != NULL && param_session_id->slen) {
++            session_id = pjsip_generic_string_hdr_create(tdata->pool, &STR_SESSION, param_session_id);
++        } else {
++            pj_str_t session_id_uuid;
++            pj_create_unique_string(tdata->pool, &session_id_uuid);
++            session_id = pjsip_generic_string_hdr_create(tdata->pool, &STR_SESSION, &session_id_uuid);
++        }
++
+         /* CSeq */
+         cseq = pjsip_cseq_hdr_create(tdata->pool);
+         if (param_cseq >= 0)
+@@ -379,7 +395,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request(  pjsip_endpoint *endpt,
+ 
+         /* Create the request. */
+         init_request_throw( endpt, tdata, &cseq->method, target, from, to, 
+-                            contact, call_id, cseq, param_text);
++                            contact, call_id, session_id, cseq, param_text);
+     }
+     PJ_CATCH_ANY {
+         status = PJ_ENOMEM;
+@@ -402,6 +418,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
+                                      const pjsip_to_hdr *param_to,
+                                      const pjsip_contact_hdr *param_contact,
+                                      const pjsip_cid_hdr *param_call_id,
++                                     const pjsip_generic_string_hdr *param_session_id,
+                                      int param_cseq,
+                                      const pj_str_t *param_text,
+                                      pjsip_tx_data **p_tdata)
+@@ -412,6 +429,8 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
+     pjsip_to_hdr *to;
+     pjsip_contact_hdr *contact;
+     pjsip_cid_hdr *call_id;
++    pjsip_generic_string_hdr *session_id;
++    const pj_str_t STR_SESSION = { "Session-ID", 10 };
+     pjsip_cseq_hdr *cseq = NULL; /* The NULL because warning in VC6 */
+     pj_status_t status;
+     PJ_USE_EXCEPTION;
+@@ -447,6 +466,15 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
+         else
+             pj_create_unique_string(tdata->pool, &call_id->id);
+ 
++        /* Session-ID */
++        if (param_session_id != NULL && param_session_id->hvalue.slen) {
++            session_id = pjsip_generic_string_hdr_create(tdata->pool, &STR_SESSION, &param_session_id->hvalue);
++        } else {
++            pj_str_t session_id_uuid;
++            pj_create_unique_string(tdata->pool, &session_id_uuid);
++            session_id = pjsip_generic_string_hdr_create(tdata->pool, &STR_SESSION, &session_id_uuid);
++        }
++
+         cseq = pjsip_cseq_hdr_create(tdata->pool);
+         if (param_cseq >= 0)
+             cseq->cseq = param_cseq;
+@@ -456,7 +484,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
+ 
+         /* Copy headers to the request. */
+         init_request_throw(endpt, tdata, &cseq->method, target, from, to, 
+-                           contact, call_id, cseq, param_text);
++                           contact, call_id, session_id, cseq, param_text);
+     }
+     PJ_CATCH_ANY {
+         status = PJ_ENOMEM;
+@@ -488,6 +516,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
+     pjsip_via_hdr *top_via = NULL, *via;
+     pjsip_rr_hdr *rr;
+     pj_status_t status;
++    const pj_str_t STR_SESSION = { "Session-ID", 10 };
+ 
+     /* Check arguments. */
+     PJ_ASSERT_RETURN(endpt && rdata && p_tdata, PJ_EINVAL);
+@@ -559,6 +588,11 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
+     hdr = (pjsip_hdr*) pjsip_msg_find_hdr( req_msg, PJSIP_H_CALL_ID, NULL);
+     pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr));
+ 
++    /* Copy Session-ID header. */
++    hdr = (pjsip_hdr*) pjsip_msg_find_hdr_by_name( req_msg, &STR_SESSION, NULL);
++    if (hdr)
++        pjsip_msg_add_hdr(msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, hdr));
++
+     /* Copy From header. */
+     hdr = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, rdata->msg_info.from);
+     pjsip_msg_add_hdr( msg, hdr);
+@@ -604,6 +638,8 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
+     const pjsip_from_hdr *from_hdr;
+     const pjsip_to_hdr *to_hdr;
+     const pjsip_cid_hdr *cid_hdr;
++    const pj_str_t STR_SESSION = { "Session-ID", 10 };
++    const pjsip_generic_string_hdr *sid_hdr;
+     const pjsip_cseq_hdr *cseq_hdr;
+     const pjsip_hdr *hdr;
+     pjsip_hdr *via;
+@@ -632,6 +668,9 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
+     cid_hdr = (const pjsip_cid_hdr*) FIND_HDR(invite_msg, CALL_ID);
+     PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
+ 
++    sid_hdr = (const pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(invite_msg, &STR_SESSION, NULL);
++    PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
++
+     cseq_hdr = (const pjsip_cseq_hdr*) FIND_HDR(invite_msg, CSEQ);
+     PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
+ 
+@@ -642,7 +681,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
+                                                  pjsip_get_ack_method(),
+                                                  tdata->msg->line.req.uri,
+                                                  from_hdr, to_hdr,
+-                                                 NULL, cid_hdr,
++                                                 NULL, cid_hdr, sid_hdr,
+                                                  cseq_hdr->cseq, NULL,
+                                                  &ack);
+ 
+@@ -701,6 +740,8 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
+     const pjsip_from_hdr *from_hdr;
+     const pjsip_to_hdr *to_hdr;
+     const pjsip_cid_hdr *cid_hdr;
++    const pj_str_t STR_SESSION = { "Session-ID", 10 };
++    const pjsip_generic_string_hdr *sid_hdr;
+     const pjsip_cseq_hdr *cseq_hdr;
+     const pjsip_hdr *hdr;
+     pjsip_hdr *via;
+@@ -723,6 +764,9 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
+     cid_hdr = (const pjsip_cid_hdr*) FIND_HDR(req_tdata->msg, CALL_ID);
+     PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
+ 
++    sid_hdr = (const pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(req_tdata->msg, &STR_SESSION, NULL);
++    PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
++
+     cseq_hdr = (const pjsip_cseq_hdr*) FIND_HDR(req_tdata->msg, CSEQ);
+     PJ_ASSERT_ON_FAIL(to_hdr != NULL, goto on_missing_hdr);
+ 
+@@ -733,7 +777,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
+                                                  pjsip_get_cancel_method(),
+                                                  req_tdata->msg->line.req.uri,
+                                                  from_hdr, to_hdr,
+-                                                 NULL, cid_hdr,
++                                                 NULL, cid_hdr, sid_hdr,
+                                                  cseq_hdr->cseq, NULL,
+                                                  &cancel_tdata);
+ 
+diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c
+index 9982b03..d41ebe6 100644
+--- a/pjsip/src/pjsua-lib/pjsua_acc.c
++++ b/pjsip/src/pjsua-lib/pjsua_acc.c
+@@ -3307,7 +3307,7 @@ PJ_DEF(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id,
+ 
+     status = pjsip_endpt_create_request(pjsua_var.endpt, method, target, 
+                                         &acc->cfg.id, target,
+-                                        NULL, NULL, -1, NULL, &tdata);
++                                        NULL, NULL, NULL, -1, NULL, &tdata);
+     if (status != PJ_SUCCESS) {
+         pjsua_perror(THIS_FILE, "Unable to create request", status);
+         return status;
+diff --git a/pjsip/src/pjsua-lib/pjsua_im.c b/pjsip/src/pjsua-lib/pjsua_im.c
+index eb8212e..3567749 100644
+--- a/pjsip/src/pjsua-lib/pjsua_im.c
++++ b/pjsip/src/pjsua-lib/pjsua_im.c
+@@ -561,7 +561,7 @@ PJ_DEF(pj_status_t) pjsua_im_send( pjsua_acc_id acc_id,
+                                         (msg_data && msg_data->target_uri.slen? 
+                                          &msg_data->target_uri: to),
+                                         &acc->cfg.id,
+-                                        to, NULL, NULL, -1, NULL, &tdata);
++                                        to, NULL, NULL, NULL, -1, NULL, &tdata);
+     if (status != PJ_SUCCESS) {
+         pjsua_perror(THIS_FILE, "Unable to create request", status);
+         return status;
+@@ -683,7 +683,7 @@ PJ_DEF(pj_status_t) pjsua_im_typing( pjsua_acc_id acc_id,
+     /* Create request. */
+     status = pjsip_endpt_create_request( pjsua_var.endpt, &pjsip_message_method,
+                                          to, &acc->cfg.id,
+-                                         to, NULL, NULL, -1, NULL, &tdata);
++                                         to, NULL, NULL, NULL, -1, NULL, &tdata);
+     if (status != PJ_SUCCESS) {
+         pjsua_perror(THIS_FILE, "Unable to create request", status);
+         return status;
+diff --git a/pjsip/src/test/transport_test.c b/pjsip/src/test/transport_test.c
+index 766c47a..62bcd95 100644
+--- a/pjsip/src/test/transport_test.c
++++ b/pjsip/src/test/transport_test.c
+@@ -213,7 +213,7 @@ int transport_send_recv_test( pjsip_transport_type_e tp_type,
+ 
+     pjsip_method_set(&method, PJSIP_OPTIONS_METHOD);
+     status = pjsip_endpt_create_request( endpt, &method, &target, &from, &to,
+-                                         &contact, &call_id, CSEQ_VALUE, 
++                                         &contact, &call_id, NULL, CSEQ_VALUE,
+                                          &body, &tdata );
+     if (status != PJ_SUCCESS) {
+         app_perror("   error: unable to create request", status);
+@@ -386,7 +386,7 @@ static pj_status_t rt_send_request(int thread_id)
+ 
+     status = pjsip_endpt_create_request( endpt, &pjsip_options_method, 
+                                          &target, &from, &to,
+-                                         &contact, &call_id, -1, 
++                                         &contact, &call_id, NULL, -1,
+                                          NULL, &tdata );
+     if (status != PJ_SUCCESS) {
+         app_perror("    error: unable to create request", status);
+@@ -730,7 +730,7 @@ int transport_load_test(char *target_url)
+         call_id = pj_str("thecallid");
+         status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, 
+                                             &target, &from, 
+-                                            &target, &from, &call_id, 
++                                            &target, &from, &call_id, NULL,
+                                             i, NULL, &tdata );
+         if (status != PJ_SUCCESS) {
+             app_perror("error creating request", status);
+diff --git a/pjsip/src/test/tsx_basic_test.c b/pjsip/src/test/tsx_basic_test.c
+index 8c1317e..8e4209c 100644
+--- a/pjsip/src/test/tsx_basic_test.c
++++ b/pjsip/src/test/tsx_basic_test.c
+@@ -41,7 +41,7 @@ static int tsx_layer_test(void)
+     from = pj_str(FROM_URI);
+ 
+     status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target,
+-                                        &from, &target, NULL, NULL, -1, NULL,
++                                        &from, &target, NULL, NULL, NULL, -1, NULL,
+                                         &tdata);
+     if (status != PJ_SUCCESS) {
+         app_perror("  error: unable to create request", status);
+@@ -86,7 +86,7 @@ static int double_terminate(void)
+ 
+     /* Create request. */
+     status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target,
+-                                        &from, &target, NULL, NULL, -1, NULL,
++                                        &from, &target, NULL, NULL, NULL, -1, NULL,
+                                         &tdata);
+     if (status != PJ_SUCCESS) {
+         app_perror("  error: unable to create request", status);
+@@ -238,7 +238,7 @@ static int tsx_create_and_send_req(void *arg)
+ 
+     status = pjsip_endpt_create_request(endpt, &pjsip_options_method,
+                                         &dst_uri, &from_uri, &dst_uri,
+-                                        NULL, NULL, -1, NULL,
++                                        NULL, NULL, -1, NULL, NULL,
+                                         &tdata);
+     if (status != PJ_SUCCESS)
+         return status;
+diff --git a/pjsip/src/test/tsx_bench.c b/pjsip/src/test/tsx_bench.c
+index 87fb35e..42a4c3c 100644
+--- a/pjsip/src/test/tsx_bench.c
++++ b/pjsip/src/test/tsx_bench.c
+@@ -42,7 +42,7 @@ static int uac_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed)
+ 
+     status = pjsip_endpt_create_request(endpt, &pjsip_invite_method,
+                                         &str_target, &str_from, &str_to,
+-                                        &str_contact, NULL, -1, NULL,
++                                        &str_contact, NULL, NULL, -1, NULL,
+                                         &request);
+     if (status != PJ_SUCCESS) {
+         app_perror("    error: unable to create request", status);
+@@ -114,7 +114,7 @@ static int uas_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed)
+ 
+     status = pjsip_endpt_create_request(endpt, &pjsip_invite_method,
+                                         &str_target, &str_from, &str_to,
+-                                        &str_contact, NULL, -1, NULL,
++                                        &str_contact, NULL, NULL, -1, NULL,
+                                         &request);
+     if (status != PJ_SUCCESS) {
+         app_perror("    error: unable to create request", status);
+diff --git a/pjsip/src/test/tsx_uac_test.c b/pjsip/src/test/tsx_uac_test.c
+index ff1e682..c9d80d5 100644
+--- a/pjsip/src/test/tsx_uac_test.c
++++ b/pjsip/src/test/tsx_uac_test.c
+@@ -977,7 +977,7 @@ static int perform_tsx_test(int dummy, char *target_uri, char *from_uri,
+ 
+     /* Create request. */
+     status = pjsip_endpt_create_request( endpt, method, &target,
+-                                         &from, &target, NULL, NULL, -1, 
++                                         &from, &target, NULL, NULL, NULL, -1,
+                                          NULL, &tdata);
+     if (status != PJ_SUCCESS) {
+         app_perror("   Error: unable to create request", status);
+diff --git a/pjsip/src/test/tsx_uas_test.c b/pjsip/src/test/tsx_uas_test.c
+index 6a0a9a4..2e4931d 100644
+--- a/pjsip/src/test/tsx_uas_test.c
++++ b/pjsip/src/test/tsx_uas_test.c
+@@ -1107,6 +1107,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
+                             rdata->msg_info.to,
+                             NULL,
+                             rdata->msg_info.cid,
++                            NULL,
+                             rdata->msg_info.cseq->cseq,
+                             NULL,
+                             &tdata);
+@@ -1214,7 +1215,7 @@ static int perform_test( char *target_uri, char *from_uri,
+ 
+     /* Create request. */
+     status = pjsip_endpt_create_request( endpt, method, &target,
+-                                         &from, &target, NULL, NULL, -1,
++                                         &from, &target, NULL, NULL, NULL, -1,
+                                          NULL, &tdata);
+     if (status != PJ_SUCCESS) {
+         app_perror("   Error: unable to create request", status);
+diff --git a/pjsip/src/test/txdata_test.c b/pjsip/src/test/txdata_test.c
+index 3fd9330..e738fd6 100644
+--- a/pjsip/src/test/txdata_test.c
++++ b/pjsip/src/test/txdata_test.c
+@@ -54,7 +54,7 @@ static int core_txdata_test(void)
+     body = pj_str("Hello world!");
+ 
+     status = pjsip_endpt_create_request( endpt, &pjsip_invite_method, &target,
+-                                         &from, &to, &contact, NULL, 10, &body,
++                                         &from, &to, &contact, NULL, NULL, 10, &body,
+                                          &invite);
+     if (status != PJ_SUCCESS) {
+         app_perror("   error: unable to create request", status);
+@@ -107,6 +107,7 @@ static int core_txdata_test(void)
+                                                   HFIND(invite->msg,to,TO),
+                                                   HFIND(invite->msg,contact,CONTACT),
+                                                   HFIND(invite->msg,cid,CALL_ID),
++                                                  NULL,
+                                                   10, &body, &invite2);
+     if (status != PJ_SUCCESS) {
+         app_perror("   error: create second request failed", status);
+@@ -354,7 +355,7 @@ static int gcc_test()
+ 
+     /* Create request with header param in target URI. */
+     status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target,
+-                                        &target, &target, &target, NULL, -1,
++                                        &target, &target, &target, NULL, NULL, -1,
+                                         NULL, &tdata);
+     if (status != 0) {
+         app_perror("   error: Unable to create request", status);
+@@ -445,7 +446,7 @@ static int txdata_test_uri_params(void)
+ 
+     /* Create request with header param in target URI. */
+     status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &target,
+-                                        &target, &target, &contact, NULL, -1,
++                                        &target, &target, &contact, NULL, NULL, -1,
+                                         NULL, &tdata);
+     if (status != 0) {
+         app_perror("   error: Unable to create request", status);
+@@ -650,7 +651,7 @@ static int create_request_bench(pj_timestamp *p_elapsed)
+         for (j=0; j<COUNT; ++j) {
+             status = pjsip_endpt_create_request(endpt, &pjsip_invite_method,
+                                                 &str_target, &str_from, &str_to,
+-                                                &str_contact, NULL, -1, NULL,
++                                                &str_contact, NULL, NULL, -1, NULL,
+                                                 &tdata[j]);
+             if (status != PJ_SUCCESS) {
+                 app_perror("    error: unable to create request", status);
+@@ -701,7 +702,7 @@ static int create_response_bench(pj_timestamp *p_elapsed)
+ 
+     status = pjsip_endpt_create_request(endpt, &pjsip_invite_method,
+                                         &str_target, &str_from, &str_to,
+-                                        &str_contact, NULL, -1, NULL,
++                                        &str_contact, NULL, NULL, -1, NULL,
+                                         &request);
+     if (status != PJ_SUCCESS) {
+         app_perror("    error: unable to create request", status);
+-- 
+2.34.1
+
diff --git a/third-party/pjproject/patches/0012-increase-cseq-for-PRACK-per-each-dialog.patch b/third-party/pjproject/patches/0012-increase-cseq-for-PRACK-per-each-dialog.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a91a86e64985ec6a23f9a0c69422271901213ec7
--- /dev/null
+++ b/third-party/pjproject/patches/0012-increase-cseq-for-PRACK-per-each-dialog.patch
@@ -0,0 +1,188 @@
+From d64d70b72b3b00a8fb26267518f555a7f9a33aab Mon Sep 17 00:00:00 2001
+From: "wenpeng.song" <wenpeng.song@iopsys.eu>
+Date: Tue, 9 Jul 2024 13:58:23 +0200
+Subject: [PATCH] increase cseq for PRACK per each dialog
+
+---
+ pjsip/include/pjsip-ua/sip_100rel.h | 19 +++++++++++++
+ pjsip/include/pjsip/sip_dialog.h    |  2 ++
+ pjsip/src/pjsip-ua/sip_100rel.c     | 22 +++++----------
+ pjsip/src/pjsip/sip_dialog.c        | 42 +++++++++++++++++++++++++++++
+ 4 files changed, 69 insertions(+), 16 deletions(-)
+
+diff --git a/pjsip/include/pjsip-ua/sip_100rel.h b/pjsip/include/pjsip-ua/sip_100rel.h
+index 089ab91fd..6e0dbde45 100644
+--- a/pjsip/include/pjsip-ua/sip_100rel.h
++++ b/pjsip/include/pjsip-ua/sip_100rel.h
+@@ -27,6 +27,25 @@
+ 
+ #include <pjsip-ua/sip_inv.h>
+ 
++typedef struct uas_state_t uas_state_t;
++/* UAC state */
++typedef struct uac_state_t
++{
++    pj_str_t            tag;    /* To tag               */
++    pj_int32_t          cseq;
++    pj_int32_t          out_cseq;  /* current outgoing cseq for PRACK */
++    pj_uint32_t         rseq;   /* Initialized to -1    */
++    struct uac_state_t *next;   /* next call leg        */
++} uac_state_t;
++
++/* State attached to each dialog. */
++struct dlg_data
++{
++        pjsip_inv_session       *inv;
++        uas_state_t             *uas_state;
++        uac_state_t             *uac_state_list;
++};
++typedef struct dlg_data dlg_data;
+ 
+ /**
+  * @defgroup PJSIP_100REL 100rel/PRACK - Reliability of Provisional Responses
+diff --git a/pjsip/include/pjsip/sip_dialog.h b/pjsip/include/pjsip/sip_dialog.h
+index 49853ce47..7a2086f61 100644
+--- a/pjsip/include/pjsip/sip_dialog.h
++++ b/pjsip/include/pjsip/sip_dialog.h
+@@ -190,6 +190,8 @@ struct pjsip_dialog
+      */
+     pjsip_host_port     via_addr;   /**< Via address.                       */
+     const void         *via_tp;     /**< Via transport.                     */
++    int    prack_flag;
++    int    mod_100rel_id;
+ };
+ 
+ /**
+diff --git a/pjsip/src/pjsip-ua/sip_100rel.c b/pjsip/src/pjsip-ua/sip_100rel.c
+index 9fa1b9bfd..f31ab1522 100644
+--- a/pjsip/src/pjsip-ua/sip_100rel.c
++++ b/pjsip/src/pjsip-ua/sip_100rel.c
+@@ -38,7 +38,6 @@ PJ_DEF_DATA(const pjsip_method) pjsip_prack_method =
+     { "PRACK", 5 }
+ };
+ 
+-typedef struct dlg_data dlg_data;
+ 
+ /*
+  * Static prototypes.
+@@ -101,23 +100,8 @@ typedef struct uas_state_t
+ } uas_state_t;
+ 
+ 
+-/* UAC state */
+-typedef struct uac_state_t
+-{
+-    pj_str_t            tag;    /* To tag               */
+-    pj_int32_t          cseq;
+-    pj_uint32_t         rseq;   /* Initialized to -1    */
+-    struct uac_state_t *next;   /* next call leg        */
+-} uac_state_t;
+ 
+ 
+-/* State attached to each dialog. */
+-struct dlg_data
+-{
+-        pjsip_inv_session       *inv;
+-        uas_state_t             *uas_state;
+-        uac_state_t             *uac_state_list;
+-};
+ 
+ 
+ /*****************************************************************************
+@@ -281,6 +265,7 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv,
+     if (uac_state == NULL) {
+         uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool, uac_state_t);
+         uac_state->cseq = rdata->msg_info.cseq->cseq;
++        uac_state->out_cseq = uac_state->cseq;
+         uac_state->rseq = rseq - 1;
+         pj_strdup(dd->inv->dlg->pool, &uac_state->tag, to_tag);
+         uac_state->next = dd->uac_state_list;
+@@ -290,6 +275,7 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv,
+     /* If this is from new INVITE transaction, reset UAC state. */
+     if (rdata->msg_info.cseq->cseq != uac_state->cseq) {
+         uac_state->cseq = rdata->msg_info.cseq->cseq;
++        uac_state->out_cseq = uac_state->cseq;
+         uac_state->rseq = rseq - 1;
+     }
+ 
+@@ -310,6 +296,10 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv,
+     /* Update our RSeq */
+     uac_state->rseq = rseq;
+ 
++    /* Update outgoing cseq*/
++    uac_state->out_cseq++;
++    inv->dlg->mod_100rel_id = mod_100rel.mod.id;
++
+     /* Create PRACK */
+     status = pjsip_dlg_create_request(dd->inv->dlg, &pjsip_prack_method,
+                                       -1, &tdata);
+diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c
+index bba63b78c..30dfd7168 100644
+--- a/pjsip/src/pjsip/sip_dialog.c
++++ b/pjsip/src/pjsip/sip_dialog.c
+@@ -16,6 +16,7 @@
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+  */
++#include <pjsip-ua/sip_100rel.h>
+ #include <pjsip/sip_dialog.h>
+ #include <pjsip/sip_ua_layer.h>
+ #include <pjsip/sip_errno.h>
+@@ -97,6 +98,7 @@ static pj_status_t create_dialog( pjsip_user_agent *ua,
+     dlg->endpt = endpt;
+     dlg->state = PJSIP_DIALOG_STATE_NULL;
+     dlg->add_allow = pjsip_include_allow_hdr_in_dlg;
++    dlg->prack_flag = 0;
+ 
+     pj_list_init(&dlg->inv_hdr);
+     pj_list_init(&dlg->rem_cap_hdr);
+@@ -1321,6 +1323,46 @@ PJ_DEF(pj_status_t) pjsip_dlg_send_request( pjsip_dialog *dlg,
+         ch = PJSIP_MSG_CSEQ_HDR(msg);
+         PJ_ASSERT_RETURN(ch!=NULL, PJ_EBUG);
+ 
++        if (pj_strncmp2(&msg->line.req.method.name, "PRACK", 5) == 0 ){
++            if(dlg->prack_flag == 1){
++                /* sync cseq for each dlg if the previous request was PRACK as well */
++                /* Find UAC state for the specified dlg */
++                const pj_str_t *to_tag = &dlg->remote.info->tag;
++                dlg_data *dd;
++                dd = (dlg_data*) dlg->mod_data[dlg->mod_100rel_id];
++                uac_state_t *uac_state = dd->uac_state_list;
++                while (uac_state) {
++                    if (pj_stricmp(&uac_state->tag, to_tag)==0)
++                        break;
++                    uac_state = uac_state->next;
++                }
++                /* sync cseq */
++                if(uac_state){
++                    dlg->local.cseq = uac_state->out_cseq;
++                }
++            } else {
++                /* just set the flag for PRACK if its the first PRACK request */
++                dlg->prack_flag = 1;
++            }
++        } else if (dlg->prack_flag == 1){
++            /* other request after PRACK */
++            /* Find UAC state for the specified dlg */
++            const pj_str_t *to_tag = &dlg->remote.info->tag;
++            dlg_data *dd;
++            dd = (dlg_data*) dlg->mod_data[dlg->mod_100rel_id];
++            uac_state_t *uac_state = dd->uac_state_list;
++            while (uac_state) {
++                if (pj_stricmp(&uac_state->tag, to_tag)==0)
++                    break;
++                dlg->local.cseq = uac_state->cseq + 1;  // update cseq with the original request cseq + 1
++                uac_state = uac_state->next;
++            }
++            if(uac_state){
++                // if previous PRACK exists for the same dlg, then sync and continue the cseq
++                dlg->local.cseq = uac_state->out_cseq + 1;
++            }
++            dlg->prack_flag = 0;
++        }
+         ch->cseq = dlg->local.cseq++;
+ 
+         /* Force the whole message to be re-printed. */
+-- 
+2.34.1
+
diff --git a/third-party/pjproject/patches/0013-1TR114-failover.patch b/third-party/pjproject/patches/0013-1TR114-failover.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a551237ae4171f31c0e9a3e8406b5eb08fb73e6c
--- /dev/null
+++ b/third-party/pjproject/patches/0013-1TR114-failover.patch
@@ -0,0 +1,15 @@
+--- a/pjsip/src/pjsip/sip_util.c
++++ a/pjsip/src/pjsip/sip_util.c
+@@ -1200,7 +1200,12 @@ static void stateless_send_transport_cb(
+          * first invocation. 
+          */
+         if (sent != -PJ_EPENDING) {
++#if 0  // Disable the failover, as it breaks the retry timers required by 1TR114 
+             tdata->dest_info.cur_addr++;
++#else
++            pjsip_tx_data_dec_ref(tdata);
++            return;
++#endif
+         }
+ 
+         /* Have next address? */
diff --git a/third-party/pjproject/patches/0014-Implementation-for-pjproject-DT-logging-mechanism.patch b/third-party/pjproject/patches/0014-Implementation-for-pjproject-DT-logging-mechanism.patch
new file mode 100644
index 0000000000000000000000000000000000000000..cceec3808fd3251b18359d0dd37bdbd5d6318ad5
--- /dev/null
+++ b/third-party/pjproject/patches/0014-Implementation-for-pjproject-DT-logging-mechanism.patch
@@ -0,0 +1,283 @@
+From bd4d4c18c876168af26bc1ee94cbd4239c334b28 Mon Sep 17 00:00:00 2001
+From: Grzegorz Sluja <grzegorz.sluja@iopsys.eu>
+Date: Wed, 2 Oct 2024 10:58:33 +0200
+Subject: [PATCH] Implementation for pjproject DT logging mechanism
+
+---
+ pjlib/build/Makefile         |   2 +-
+ pjlib/build/pjlib.vcxproj    |   3 +-
+ pjlib/include/pj/dt_logger.h |  46 +++++++++++
+ pjlib/include/pj/log.h       |   1 +
+ pjlib/src/pj/dt_logger.c     | 149 +++++++++++++++++++++++++++++++++++
+ pjlib/src/pj/ssl_sock_ossl.c |   1 +
+ 6 files changed, 200 insertions(+), 2 deletions(-)
+ create mode 100644 pjlib/include/pj/dt_logger.h
+ create mode 100644 pjlib/src/pj/dt_logger.c
+
+diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile
+index 7d16780..67c2081 100644
+--- a/pjlib/build/Makefile
++++ b/pjlib/build/Makefile
+@@ -34,7 +34,7 @@ export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
+ 	activesock.o array.o config.o ctype.o errno.o except.o fifobuf.o \
+ 	guid.o hash.o ip_helper_generic.o list.o lock.o log.o os_time_common.o \
+ 	os_info.o pool.o pool_buf.o pool_caching.o pool_dbg.o rand.o \
+-	rbtree.o sock_common.o sock_qos_common.o \
++	rbtree.o sock_common.o sock_qos_common.o dt_logger.o \
+ 	ssl_sock_common.o ssl_sock_ossl.o ssl_sock_gtls.o ssl_sock_dump.o \
+ 	ssl_sock_darwin.o string.o timer.o types.o
+ export PJLIB_CFLAGS += $(_CFLAGS)
+diff --git a/pjlib/build/pjlib.vcxproj b/pjlib/build/pjlib.vcxproj
+index 227c3f0..204f60e 100644
+--- a/pjlib/build/pjlib.vcxproj
++++ b/pjlib/build/pjlib.vcxproj
+@@ -817,6 +817,7 @@
+     <ClCompile Include="..\src\pj\list.c" />
+     <ClCompile Include="..\src\pj\lock.c" />
+     <ClCompile Include="..\src\pj\log.c" />
++    <ClCompile Include="..\src\pj\dt_logger.c" />
+     <ClCompile Include="..\src\pj\log_writer_printk.c">
+       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Dynamic|Win32'">true</ExcludedFromBuild>
+       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Dynamic|ARM'">true</ExcludedFromBuild>
+@@ -1105,4 +1106,4 @@
+   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+   <ImportGroup Label="ExtensionTargets">
+   </ImportGroup>
+-</Project>
+\ No newline at end of file
++</Project>
+diff --git a/pjlib/include/pj/dt_logger.h b/pjlib/include/pj/dt_logger.h
+new file mode 100644
+index 0000000..d8e7fe2
+--- /dev/null
++++ b/pjlib/include/pj/dt_logger.h
+@@ -0,0 +1,46 @@
++/*
++ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
++ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++/*! \file
++ *
++ * \author Grzegorz Sluja <grzegorz.sluja@genexis.eu>
++ *
++ * \brief Support for the DT syslog logging mechanism
++ */
++
++#ifndef __PJPROJECT_DT_LOGGER_H
++#define __PJPROJECT_DT_LOGGER_H
++
++// Define language support
++typedef enum {
++    PJ_LOG_LANGUAGE_DT_ENGLISH = 0,
++    PJ_LOG_LANGUAGE_DT_GERMAN,
++    // Add more languages here
++    PJ_LOG_LANGUAGE_DT_MAX
++} PJ_LOG_LANGUAGE_DT;
++
++// Define log event codes
++typedef enum {
++    PJ_LOG_EVENT_CODE_SV005 = 1,
++    // Add more event codes here
++} PJ_LOG_EVENT_CODE_DT;
++
++void pjproject_dt_set_log_language(PJ_LOG_LANGUAGE_DT language);
++void pjproject_log_dt(PJ_LOG_EVENT_CODE_DT event_code, ...);
++#endif /* __PJPROJECT_DT_LOGGER_H */
+\ No newline at end of file
+diff --git a/pjlib/include/pj/log.h b/pjlib/include/pj/log.h
+index c89f2b5..8f61b22 100644
+--- a/pjlib/include/pj/log.h
++++ b/pjlib/include/pj/log.h
+@@ -26,6 +26,7 @@
+ 
+ #include <pj/types.h>
+ #include <stdarg.h>
++#include <pj/dt_logger.h>
+ 
+ PJ_BEGIN_DECL
+ 
+diff --git a/pjlib/src/pj/dt_logger.c b/pjlib/src/pj/dt_logger.c
+new file mode 100644
+index 0000000..7654032
+--- /dev/null
++++ b/pjlib/src/pj/dt_logger.c
+@@ -0,0 +1,149 @@
++/*
++ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
++ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++/*! \file
++ *
++ * \author Grzegorz Sluja <grzegorz.sluja@genexis.eu>
++ *
++ * \brief Support for the DT syslog logging mechanism
++ */
++
++#include <stdio.h>
++#include <syslog.h>
++#include <stdarg.h>
++#include <time.h>
++#include <pthread.h>
++#include <sys/file.h>
++#include <pj/log.h>
++
++
++#define LOG_FILE_PATH "/rdklogs/logs/VoIPlog.txt"
++
++// Define flags for extended syslog
++#define LOG_FLAG_EXTENDED_SYSLOG_ONLY 0x01
++
++// Current language setting
++static PJ_LOG_LANGUAGE_DT current_language = PJ_LOG_LANGUAGE_DT_ENGLISH;
++
++// Structure to store log messages in different languages and log options (flags)
++struct ast_log_message_table_dt {
++    PJ_LOG_EVENT_CODE_DT code;
++    const char *code_string;
++    const char *messages[PJ_LOG_LANGUAGE_DT_MAX];
++    pj_uint32_t category; // priority of syslog message:
++                       // (LOG_EMERG|LOG_ALERT|LOG_CRIT|LOG_ERR|LOG_WARNING|LOG_NOTICE|LOG_INFO|LOG_DEBUG)
++    pj_uint32_t flags;    // Flags, not used for now
++};
++
++// Log message table with messages in different languages and flags
++struct ast_log_message_table_dt log_message_table_dt[] = {
++    {
++        .code = PJ_LOG_EVENT_CODE_SV005,
++        .code_string = "SV005",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Handshake error",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Handshake Fehler"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    }
++    // More log entries can be added here...
++};
++
++static void get_current_timestamp(char *buffer, size_t size) {
++    time_t now = time(NULL);
++    struct tm *tstruct = localtime(&now);
++    strftime(buffer, size, "%Y-%m-%dT%H:%M:%S", tstruct);
++}
++
++static const char *translate_syslog_priority(int priority) {
++    switch (priority) {
++        case LOG_EMERG:
++            return "EMERG";
++        case LOG_ALERT:
++            return "ALERT";
++        case LOG_CRIT:
++            return "CRIT";
++        case LOG_ERR:
++            return "ERROR";
++        case LOG_WARNING:
++            return "WARN";
++        case LOG_NOTICE:
++            return "NOTICE";
++        case LOG_INFO:
++            return "INFO";
++        case LOG_DEBUG:
++            return "DEBUG";
++        default:
++            return "UNKNOWN";
++    }
++}
++
++// Function to set language
++void pjproject_dt_set_log_language(PJ_LOG_LANGUAGE_DT language) {
++    if (language >= 0 && language < PJ_LOG_LANGUAGE_DT_MAX) {
++        current_language = language;
++    }
++}
++
++// Function to log a message based on the event code
++void pjproject_log_dt(PJ_LOG_EVENT_CODE_DT event_code, ...) {
++    const char *message = NULL;
++    const char *code_string = NULL;
++    pj_uint32_t category = 0;
++    int flags = 0;
++    char full_message[1024];
++    char log_message[1280];
++    va_list args;
++
++    // Find the log message for the event code in the correct language
++    for (size_t i = 0; i < sizeof(log_message_table_dt) / sizeof(log_message_table_dt[0]); i++) {
++        if (log_message_table_dt[i].code == event_code) {
++            message = log_message_table_dt[i].messages[current_language];
++            code_string = log_message_table_dt[i].code_string;
++            category = log_message_table_dt[i].category;
++            flags = log_message_table_dt[i].flags;
++            break;
++        }
++    }
++
++    if (!message)
++        return;
++
++    // Format the log message with variable arguments
++    va_start(args, message);
++    vsnprintf(full_message, sizeof(full_message), message, args);
++    va_end(args);
++
++    // get additional data for the final log message
++    char timestamp[64];
++    get_current_timestamp(timestamp, sizeof(timestamp));
++    const char *log_level = translate_syslog_priority(category);
++    unsigned long tid = gettid();
++
++    snprintf(log_message, sizeof(log_message), "%s telekom: %s %s.%s [tid=%lu] %s",
++       timestamp, code_string, "VOIP", log_level, tid, full_message);
++
++    // Write the log message to a file
++    FILE *log_file = fopen(LOG_FILE_PATH, "a");
++    if (log_file) {
++        fprintf(log_file, "%s\n", log_message);
++        fclose(log_file);
++    }
++}
+diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
+index 7d17302..96ca03e 100644
+--- a/pjlib/src/pj/ssl_sock_ossl.c
++++ b/pjlib/src/pj/ssl_sock_ossl.c
+@@ -2346,6 +2346,7 @@ static pj_status_t ssl_do_handshake(pj_ssl_sock_t *ssock)
+         {
+             /* Handshake fails */
+             status = STATUS_FROM_SSL_ERR2("Handshake", ssock, err, err2, 0);
++            pjproject_log_dt(PJ_LOG_EVENT_CODE_SV005);
+             return status;
+         }
+     }
+-- 
+2.34.1
+
diff --git a/third-party/pjproject/patches/0015-Scure-Sip-syslog-template-related-code-in-pjproject.patch b/third-party/pjproject/patches/0015-Scure-Sip-syslog-template-related-code-in-pjproject.patch
new file mode 100644
index 0000000000000000000000000000000000000000..e2e881a288df54b894905f846436e695ec4d2dea
--- /dev/null
+++ b/third-party/pjproject/patches/0015-Scure-Sip-syslog-template-related-code-in-pjproject.patch
@@ -0,0 +1,313 @@
+From dceea86c7b0a67cc1d513ab64ebbe69320c18df6 Mon Sep 17 00:00:00 2001
+From: macdux <macdux@telekom.de>
+Date: Thu, 10 Oct 2024 20:29:04 +0800
+Subject: [PATCH] Scure Sip syslog template related code in pjproject
+
+---
+ pjlib/include/pj/dt_logger.h |  28 +++-
+ pjlib/src/pj/dt_logger.c     | 240 +++++++++++++++++++++++++++++++++++
+ 2 files changed, 266 insertions(+), 2 deletions(-)
+
+diff --git a/pjlib/include/pj/dt_logger.h b/pjlib/include/pj/dt_logger.h
+index d8e7fe2e1..34557edc9 100644
+--- a/pjlib/include/pj/dt_logger.h
++++ b/pjlib/include/pj/dt_logger.h
+@@ -37,10 +37,34 @@ typedef enum {
+ 
+ // Define log event codes
+ typedef enum {
+-    PJ_LOG_EVENT_CODE_SV005 = 1,
++    PJ_LOG_EVENT_CODE_SV001 = 1,
++    PJ_LOG_EVENT_CODE_SV002,
++    PJ_LOG_EVENT_CODE_SV003,
++    PJ_LOG_EVENT_CODE_SV004,
++    PJ_LOG_EVENT_CODE_SV005,
++    PJ_LOG_EVENT_CODE_SV006,
++    PJ_LOG_EVENT_CODE_SV007,
++    PJ_LOG_EVENT_CODE_SV008,
++    PJ_LOG_EVENT_CODE_SV009,
++    PJ_LOG_EVENT_CODE_SV010,
++    PJ_LOG_EVENT_CODE_SV011,
++    PJ_LOG_EVENT_CODE_SV012,
++    PJ_LOG_EVENT_CODE_SV013,
++    PJ_LOG_EVENT_CODE_SV014,
++    PJ_LOG_EVENT_CODE_SV015,
++    PJ_LOG_EVENT_CODE_SV016,
++    PJ_LOG_EVENT_CODE_SV017,
++    PJ_LOG_EVENT_CODE_SV018,
++    PJ_LOG_EVENT_CODE_SV019,
++    PJ_LOG_EVENT_CODE_SV020,
++    PJ_LOG_EVENT_CODE_SV021,
++    PJ_LOG_EVENT_CODE_SV105,
++    PJ_LOG_EVENT_CODE_SV106,
++    PJ_LOG_EVENT_CODE_SV110,
++    PJ_LOG_EVENT_CODE_SV111,
+     // Add more event codes here
+ } PJ_LOG_EVENT_CODE_DT;
+ 
+ void pjproject_dt_set_log_language(PJ_LOG_LANGUAGE_DT language);
+ void pjproject_log_dt(PJ_LOG_EVENT_CODE_DT event_code, ...);
+-#endif /* __PJPROJECT_DT_LOGGER_H */
+\ No newline at end of file
++#endif /* __PJPROJECT_DT_LOGGER_H */
+diff --git a/pjlib/src/pj/dt_logger.c b/pjlib/src/pj/dt_logger.c
+index 765403242..90050d268 100644
+--- a/pjlib/src/pj/dt_logger.c
++++ b/pjlib/src/pj/dt_logger.c
+@@ -53,6 +53,46 @@ struct ast_log_message_table_dt {
+ 
+ // Log message table with messages in different languages and flags
+ struct ast_log_message_table_dt log_message_table_dt[] = {
++    {
++        .code = PJ_LOG_EVENT_CODE_SV001,
++        .code_string = "SV001",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Unexpected message",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unerwartete Meldung"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV002,
++        .code_string = "SV002",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Message authentication error",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Message Authentication fehlerhaft"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV003,
++        .code_string = "SV003",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "TLS ciphertext too long",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "TLS Ciphertext zu lang"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV004,
++        .code_string = "SV004",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Error unpacking",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Fehler beim Entpacken"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV005,
+         .code_string = "SV005",
+@@ -62,6 +102,206 @@ struct ast_log_message_table_dt log_message_table_dt[] = {
+         },
+         .category = LOG_ERR,
+         .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV006,
++        .code_string = "SV006",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate damaged",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat beschädigt"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV007,
++        .code_string = "SV007",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate not supported",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat nicht unterstützt"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV008,
++        .code_string = "SV008",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate withdrawn",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat zurückgezogen"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV009,
++        .code_string = "SV009",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate expired",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat abgelaufen"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV010,
++        .code_string = "SV010",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate expired",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unknown certificate"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV011,
++        .code_string = "SV011",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Incorrect handshake parameters",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Fehlerhafte Handshake Parameter"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV012,
++        .code_string = "SV012",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Unknown certification authority",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unbekannte Zertifizierungsstelle"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV013,
++        .code_string = "SV013",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Access denied",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zugang verweigert"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV014,
++        .code_string = "SV014",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Message incorrect",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Nachricht fehlerhaft"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV015,
++        .code_string = "SV015",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Error decrypting",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Fehler beim entschlüsseln"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV016,
++        .code_string = "SV016",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Version not supported",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Version nicht unterstützt"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV017,
++        .code_string = "SV017",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Insecure encryption",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unsichere Verschlüsselung"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV018,
++        .code_string = "SV018",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "General internal error",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Allgemeiner interner Fehler"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV019,
++        .code_string = "SV019",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Cancellation by user",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Abbruch durch Benutzer"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV020,
++        .code_string = "SV020",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "No repetition desired",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Keine Wiederholung erwünscht"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV021,
++        .code_string = "SV021",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Extension not supported",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Erweiterung nicht unterstützt"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV105,
++        .code_string = "SV105",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "TLS connection successful",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "TLS Verbindung erfolgreich"
++        },
++        .category = LOG_INFO,
++        .flags = 0
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV106,
++        .code_string = "SV106",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "TLS connection unsuccessful",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "TLS Verbindung erfolglos"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV110,
++        .code_string = "SV110",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "SRTP negotiation failed",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "SRTP Aushandlung fehlgeschlagen"
++        },
++        .category = LOG_ERR,
++        .flags = 1
++    },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV111,
++        .code_string = "SV111",
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Port <%s> not available", //Port <port number> not available
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Port <%s> nicht verfügbar" //Port <port number> nicht verfügbar
++        },
++        .category = LOG_ERR,
++        .flags = 1
+     }
+     // More log entries can be added here...
+ };
+-- 
+2.17.1
+
diff --git a/third-party/pjproject/patches/0016-Remove-event-code-from-log-string-in-VoIPlog.txt.patch b/third-party/pjproject/patches/0016-Remove-event-code-from-log-string-in-VoIPlog.txt.patch
new file mode 100644
index 0000000000000000000000000000000000000000..5a5858ce6f7ff4d46c617c32855d651e3c12b642
--- /dev/null
+++ b/third-party/pjproject/patches/0016-Remove-event-code-from-log-string-in-VoIPlog.txt.patch
@@ -0,0 +1,336 @@
+From 793c260813acbeabd612fe06da9de0349df709fc Mon Sep 17 00:00:00 2001
+From: Grzegorz Sluja <grzegorz.sluja@iopsys.eu>
+Date: Fri, 11 Oct 2024 11:46:14 +0200
+Subject: [PATCH] Remove event code from log string in VoIPlog.txt
+
+---
+ pjlib/src/pj/dt_logger.c | 82 +++++++++++++---------------------------
+ 1 file changed, 27 insertions(+), 55 deletions(-)
+
+diff --git a/pjlib/src/pj/dt_logger.c b/pjlib/src/pj/dt_logger.c
+index 90050d2..378aaf9 100644
+--- a/pjlib/src/pj/dt_logger.c
++++ b/pjlib/src/pj/dt_logger.c
+@@ -44,7 +44,6 @@ static PJ_LOG_LANGUAGE_DT current_language = PJ_LOG_LANGUAGE_DT_ENGLISH;
+ // Structure to store log messages in different languages and log options (flags)
+ struct ast_log_message_table_dt {
+     PJ_LOG_EVENT_CODE_DT code;
+-    const char *code_string;
+     const char *messages[PJ_LOG_LANGUAGE_DT_MAX];
+     pj_uint32_t category; // priority of syslog message:
+                        // (LOG_EMERG|LOG_ALERT|LOG_CRIT|LOG_ERR|LOG_WARNING|LOG_NOTICE|LOG_INFO|LOG_DEBUG)
+@@ -55,217 +54,195 @@ struct ast_log_message_table_dt {
+ struct ast_log_message_table_dt log_message_table_dt[] = {
+     {
+         .code = PJ_LOG_EVENT_CODE_SV001,
+-        .code_string = "SV001",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Unexpected message",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unerwartete Meldung"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV002,
+-        .code_string = "SV002",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Message authentication error",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Message Authentication fehlerhaft"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV003,
+-        .code_string = "SV003",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "TLS ciphertext too long",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "TLS Ciphertext zu lang"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV004,
+-        .code_string = "SV004",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Error unpacking",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Fehler beim Entpacken"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV005,
+-        .code_string = "SV005",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Handshake error",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Handshake Fehler"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV006,
+-        .code_string = "SV006",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate damaged",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat beschädigt"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV007,
+-        .code_string = "SV007",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate not supported",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat nicht unterstützt"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV008,
+-        .code_string = "SV008",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate withdrawn",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat zurückgezogen"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV009,
+-        .code_string = "SV009",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate expired",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zertifikat abgelaufen"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV010,
+-        .code_string = "SV010",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Certificate expired",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unknown certificate"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV011,
+-        .code_string = "SV011",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Incorrect handshake parameters",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Fehlerhafte Handshake Parameter"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV012,
+-        .code_string = "SV012",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Unknown certification authority",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unbekannte Zertifizierungsstelle"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV013,
+-        .code_string = "SV013",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Access denied",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Zugang verweigert"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV014,
+-        .code_string = "SV014",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Message incorrect",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Nachricht fehlerhaft"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV015,
+-        .code_string = "SV015",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Error decrypting",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Fehler beim entschlüsseln"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV016,
+-        .code_string = "SV016",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Version not supported",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Version nicht unterstützt"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV017,
+-        .code_string = "SV017",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Insecure encryption",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Unsichere Verschlüsselung"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV018,
+-        .code_string = "SV018",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "General internal error",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Allgemeiner interner Fehler"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV019,
+-        .code_string = "SV019",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Cancellation by user",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Abbruch durch Benutzer"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV020,
+-        .code_string = "SV020",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "No repetition desired",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Keine Wiederholung erwünscht"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV021,
+-        .code_string = "SV021",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Extension not supported",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Erweiterung nicht unterstützt"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV105,
+-        .code_string = "SV105",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "TLS connection successful",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "TLS Verbindung erfolgreich"
+@@ -275,33 +252,30 @@ struct ast_log_message_table_dt log_message_table_dt[] = {
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV106,
+-        .code_string = "SV106",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "TLS connection unsuccessful",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "TLS Verbindung erfolglos"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV110,
+-        .code_string = "SV110",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "SRTP negotiation failed",
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "SRTP Aushandlung fehlgeschlagen"
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV111,
+-        .code_string = "SV111",
+         .messages = {
+             [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Port <%s> not available", //Port <port number> not available
+             [PJ_LOG_LANGUAGE_DT_GERMAN] = "Port <%s> nicht verfügbar" //Port <port number> nicht verfügbar
+         },
+         .category = LOG_ERR,
+-        .flags = 1
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     }
+     // More log entries can be added here...
+ };
+@@ -345,7 +319,6 @@ void pjproject_dt_set_log_language(PJ_LOG_LANGUAGE_DT language) {
+ // Function to log a message based on the event code
+ void pjproject_log_dt(PJ_LOG_EVENT_CODE_DT event_code, ...) {
+     const char *message = NULL;
+-    const char *code_string = NULL;
+     pj_uint32_t category = 0;
+     int flags = 0;
+     char full_message[1024];
+@@ -356,7 +329,6 @@ void pjproject_log_dt(PJ_LOG_EVENT_CODE_DT event_code, ...) {
+     for (size_t i = 0; i < sizeof(log_message_table_dt) / sizeof(log_message_table_dt[0]); i++) {
+         if (log_message_table_dt[i].code == event_code) {
+             message = log_message_table_dt[i].messages[current_language];
+-            code_string = log_message_table_dt[i].code_string;
+             category = log_message_table_dt[i].category;
+             flags = log_message_table_dt[i].flags;
+             break;
+@@ -367,7 +339,7 @@ void pjproject_log_dt(PJ_LOG_EVENT_CODE_DT event_code, ...) {
+         return;
+ 
+     // Format the log message with variable arguments
+-    va_start(args, message);
++    va_start(args, event_code);
+     vsnprintf(full_message, sizeof(full_message), message, args);
+     va_end(args);
+ 
+@@ -377,8 +349,8 @@ void pjproject_log_dt(PJ_LOG_EVENT_CODE_DT event_code, ...) {
+     const char *log_level = translate_syslog_priority(category);
+     unsigned long tid = gettid();
+ 
+-    snprintf(log_message, sizeof(log_message), "%s telekom: %s %s.%s [tid=%lu] %s",
+-       timestamp, code_string, "VOIP", log_level, tid, full_message);
++    snprintf(log_message, sizeof(log_message), "%s telekom: %s.%s [tid=%lu] %s",
++       timestamp, "VOIP", log_level, tid, full_message);
+ 
+     // Write the log message to a file
+     FILE *log_file = fopen(LOG_FILE_PATH, "a");
+-- 
+2.34.1
+
diff --git a/third-party/pjproject/patches/0017-securesip-syslog-under-pjproject-part-1-implementati.patch b/third-party/pjproject/patches/0017-securesip-syslog-under-pjproject-part-1-implementati.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ae14a55d67c7215f0b99a4406d742760127935da
--- /dev/null
+++ b/third-party/pjproject/patches/0017-securesip-syslog-under-pjproject-part-1-implementati.patch
@@ -0,0 +1,170 @@
+From 0161cd583156e16069ab975e0d3354d1f2fa4c24 Mon Sep 17 00:00:00 2001
+From: macdux <macdux@telekom.de>
+Date: Mon, 14 Oct 2024 08:46:16 +0800
+Subject: [PATCH] securesip syslog under pjproject part 1 implementation
+
+---
+ pjlib/include/pj/dt_logger.h |  1 +
+ pjlib/src/pj/dt_logger.c     |  9 ++++
+ pjlib/src/pj/ssl_sock_ossl.c | 99 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 109 insertions(+)
+
+diff --git a/pjlib/include/pj/dt_logger.h b/pjlib/include/pj/dt_logger.h
+index 34557edc9..d3b64c629 100644
+--- a/pjlib/include/pj/dt_logger.h
++++ b/pjlib/include/pj/dt_logger.h
+@@ -58,6 +58,7 @@ typedef enum {
+     PJ_LOG_EVENT_CODE_SV019,
+     PJ_LOG_EVENT_CODE_SV020,
+     PJ_LOG_EVENT_CODE_SV021,
++    PJ_LOG_EVENT_CODE_SV104,
+     PJ_LOG_EVENT_CODE_SV105,
+     PJ_LOG_EVENT_CODE_SV106,
+     PJ_LOG_EVENT_CODE_SV110,
+diff --git a/pjlib/src/pj/dt_logger.c b/pjlib/src/pj/dt_logger.c
+index 378aaf943..c1942e523 100644
+--- a/pjlib/src/pj/dt_logger.c
++++ b/pjlib/src/pj/dt_logger.c
+@@ -241,6 +241,15 @@ struct ast_log_message_table_dt log_message_table_dt[] = {
+         .category = LOG_ERR,
+         .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
+     },
++    {
++        .code = PJ_LOG_EVENT_CODE_SV104,
++        .messages = {
++            [PJ_LOG_LANGUAGE_DT_ENGLISH] = "Connection error in level 2",
++            [PJ_LOG_LANGUAGE_DT_GERMAN] = "Verbindungsfehler in Stufe 2"
++        },
++        .category = LOG_INFO,
++        .flags = LOG_FLAG_EXTENDED_SYSLOG_ONLY
++    },
+     {
+         .code = PJ_LOG_EVENT_CODE_SV105,
+         .messages = {
+diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
+index 96ca03e9f..63b42f444 100644
+--- a/pjlib/src/pj/ssl_sock_ossl.c
++++ b/pjlib/src/pj/ssl_sock_ossl.c
+@@ -2347,6 +2347,73 @@ static pj_status_t ssl_do_handshake(pj_ssl_sock_t *ssock)
+             /* Handshake fails */
+             status = STATUS_FROM_SSL_ERR2("Handshake", ssock, err, err2, 0);
+             pjproject_log_dt(PJ_LOG_EVENT_CODE_SV005);
++            if (err2 == SSL_ERROR_ZERO_RETURN){
++                pjproject_log_dt(PJ_LOG_EVENT_CODE_SV019);
++            }
++            else if (err2 == SSL_ERROR_SSL)
++            {
++                unsigned long err_code = ERR_get_error();
++                unsigned long r;
++                r = ERR_GET_REASON(err_code);
++                switch (r)
++                {
++                    case SSL_R_UNEXPECTED_MESSAGE:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV001);
++                        break;
++                    case SSL_R_CERTIFICATE_VERIFY_FAILED:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV006);
++                        break;
++                    case SSL_AD_UNSUPPORTED_CERTIFICATE:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV007);
++                        break;
++                    case X509_V_ERR_CERT_REVOKED:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV008);
++                        break;
++                    case X509_V_ERR_CERT_HAS_EXPIRED:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV009);
++                        break;
++                    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV010);
++                        break;
++                    case SSL_R_BAD_HANDSHAKE_LENGTH:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV011);
++                        break;
++                    case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV012);
++                        break;
++                    case SSL_R_NO_CIPHER_MATCH:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV013);
++                        break;
++                    case SSL_R_RECORD_LENGTH_MISMATCH:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV014);
++                        break;
++                    case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV015);
++                        break;
++                    case SSL_R_UNSUPPORTED_PROTOCOL:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV016);
++                        break;
++                    case SSL_R_NO_SHARED_CIPHER:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV017);
++                        break;
++                    case ERR_R_INTERNAL_ERROR:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV018);
++                        break;
++                    case SSL_R_NO_RENEGOTIATION:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV020);
++                        break;
++                    case X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV021);
++                        break;
++                    case SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG:
++                    case SSL_R_NO_SRTP_PROFILES:
++                    case SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE:
++                        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV110);
++                        break;
++                    default:
++                        break;
++                }
++            }
+             return status;
+         }
+     }
+@@ -2424,6 +2491,27 @@ static pj_status_t ssl_read(pj_ssl_sock_t *ssock, void *data, int *size)
+          * re-negotiation.
+          */
+         if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) {
++            unsigned long err_code = ERR_get_error();
++            unsigned long r;
++            r = ERR_GET_REASON(err_code);
++            switch (r)
++            {
++                case SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC:
++                    pjproject_log_dt(PJ_LOG_EVENT_CODE_SV002);
++                    break;
++                case SSL_R_ENCRYPTED_LENGTH_TOO_LONG:
++                    pjproject_log_dt(PJ_LOG_EVENT_CODE_SV003);
++                    break;
++                case SSL_R_BAD_DECOMPRESSION:
++                    pjproject_log_dt(PJ_LOG_EVENT_CODE_SV004);
++                    break;
++                case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
++                    pjproject_log_dt(PJ_LOG_EVENT_CODE_SV015);
++                    break;
++                default:
++                    break;
++            }
++
+             if (err == SSL_ERROR_SYSCALL && size_ == -1 &&
+                 ERR_peek_error() == 0 && errno == 0)
+             {
+@@ -2467,6 +2555,17 @@ static pj_status_t ssl_write(pj_ssl_sock_t *ssock, const void *data,
+         if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_NONE) {
+             status = PJ_EEOF;
+         } else {
++            unsigned long err_code = ERR_get_error();
++            unsigned long r;
++            r = ERR_GET_REASON(err_code);
++            switch (r)
++            {
++                case SSL_R_ENCRYPTED_LENGTH_TOO_LONG:
++                    pjproject_log_dt(PJ_LOG_EVENT_CODE_SV003);
++                    break;
++                default:
++                    break;
++            }
+             /* Some problem occured */
+             status = STATUS_FROM_SSL_ERR2("Write", ssock, *nwritten,
+                                           err, size);
+-- 
+2.17.1
+
diff --git a/third-party/pjproject/patches/0018-implementation-of-securesip-syslog-part2-under-pjpro.patch b/third-party/pjproject/patches/0018-implementation-of-securesip-syslog-part2-under-pjpro.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6c50ffdf72d6145e8c405802d5c4502dce721abb
--- /dev/null
+++ b/third-party/pjproject/patches/0018-implementation-of-securesip-syslog-part2-under-pjpro.patch
@@ -0,0 +1,70 @@
+From 961449f28b023dd26f27d81e13ba0cc0922c50e5 Mon Sep 17 00:00:00 2001
+From: macdux <macdux@telekom.de>
+Date: Mon, 14 Oct 2024 20:50:34 +0800
+Subject: [PATCH] implementation of securesip syslog part2 under pjproject
+
+---
+ pjlib/src/pj/activesock.c           | 9 ++++++++-
+ pjsip/src/pjsip/sip_transport_tls.c | 5 +++++
+ 2 files changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/pjlib/src/pj/activesock.c b/pjlib/src/pj/activesock.c
+index f901545b7..963190ed7 100644
+--- a/pjlib/src/pj/activesock.c
++++ b/pjlib/src/pj/activesock.c
+@@ -932,7 +932,14 @@ PJ_DEF(pj_status_t) pj_activesock_start_connect( pj_activesock_t *asock,
+     if (asock->shutdown)
+         return PJ_EINVALIDOP;
+ 
+-    return pj_ioqueue_connect(asock->key, remaddr, addr_len);
++    pj_status_t connect_status = pj_ioqueue_connect(asock->key, remaddr, addr_len);
++    if (connect_status != PJ_SUCCESS) {
++        if (errno == ECONNREFUSED){
++            pj_uint16_t port = pj_sockaddr_get_port(remaddr);
++            pjproject_log_dt(PJ_LOG_EVENT_CODE_SV111, port);
++        }
++    }
++    return connect_status;
+ }
+ 
+ static void ioqueue_on_connect_complete(pj_ioqueue_key_t *key, 
+diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c
+index ad59a1e60..903a640f8 100644
+--- a/pjsip/src/pjsip/sip_transport_tls.c
++++ b/pjsip/src/pjsip/sip_transport_tls.c
+@@ -1271,6 +1271,8 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
+     if (status == PJ_SUCCESS) {
+         on_connect_complete(tls->ssock, PJ_SUCCESS);
+     } else if (status != PJ_EPENDING) {
++        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV104);
++        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV106);
+         tls_destroy(&tls->base, status);
+         return status;
+     }
+@@ -1995,6 +1997,7 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
+         status = tls->close_reason;
+         tls_perror(tls->base.obj_name, "TLS connect() error", status, 
+                    &tls->remote_name);
++        pjproject_log_dt(PJ_LOG_EVENT_CODE_SV106);
+ 
+         /* Cancel all delayed transmits */
+         while (!pj_list_empty(&tls->delayed_list)) {
+@@ -2024,6 +2027,7 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
+               pj_addr_str_print(&tls->base.remote_name.host, 
+                                 tls->base.remote_name.port, remote_addr_buf, 
+                                 sizeof(remote_addr_buf), 1)));
++    pjproject_log_dt(PJ_LOG_EVENT_CODE_SV105);
+ 
+     /* Start pending read */
+     status = tls_start_read(tls);
+@@ -2046,6 +2050,7 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
+     return PJ_TRUE;
+ 
+ on_error:
++    pjproject_log_dt(PJ_LOG_EVENT_CODE_SV106);
+     tls_init_shutdown(tls, status);
+ 
+     return PJ_FALSE;
+-- 
+2.17.1
+
diff --git a/third-party/pjproject/patches/0019-TLSv13-ciphers.patch b/third-party/pjproject/patches/0019-TLSv13-ciphers.patch
new file mode 100644
index 0000000000000000000000000000000000000000..1ebefe0ce9a4cb0b29b49bdb134e4253b89d69ce
--- /dev/null
+++ b/third-party/pjproject/patches/0019-TLSv13-ciphers.patch
@@ -0,0 +1,197 @@
+diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
+index 63b42f4..4844d5e 100644
+--- a/pjlib/src/pj/ssl_sock_ossl.c
++++ b/pjlib/src/pj/ssl_sock_ossl.c
+@@ -231,6 +231,57 @@ typedef struct ossl_sock_t
+ /* Expected maximum value of reason component in OpenSSL error code */
+ #define MAX_OSSL_ERR_REASON             1200
+ 
++/* TLSv13 ciphers */
++static struct tlsv13_cipher {
++    int id;
++    const char *name;
++} tlsv13_ciphers[] = {
++    {0x1301, "TLS_AES_128_GCM_SHA256"},
++    {0x1302, "TLS_AES_256_GCM_SHA384"},
++    {0x1303, "TLS_CHACHA20_POLY1305_SHA256"},
++    {0x1304, "TLS_AES_128_CCM_SHA256"},
++    {0x1305, "TLS_AES_128_CCM_8_SHA256"},
++    {0, NULL}
++};
++
++/* all TLS v1.2 ciphers required by 1tr114 */
++#define PJ_OSSL_TLS1_2_CIPHERS_DT_DEFAULT \
++	"ECDHE-RSA-AES256-GCM-SHA384:"\
++	"AES256-GCM-SHA384:"\
++	"AES256-SHA256:"\
++	"ECDHE-RSA-AES128-GCM-SHA256:"\
++	"ECDHE-RSA-AES128-SHA256:"\
++	"AES128-GCM-SHA256:"\
++	"AES128-SHA256"
++
++/* all TLS v1.3 ciphers supported by openssl 1.1.11 */
++#define PJ_OSSL_TLS1_3_CIPHERS_ALL \
++	"TLS_AES_128_GCM_SHA256:"\
++	"TLS_AES_256_GCM_SHA384:"\
++	"TLS_AES_128_CCM_SHA256:"\
++	"TLS_AES_128_CCM_8_SHA256:"\
++	"TLS_CHACHA20_POLY1305_SHA256"
++
++/* all TLS v1.3 ciphers required by 1tr114 */
++#define PJ_OSSL_TLS1_3_CIPHERS_DT_DEFAULT \
++	"TLS_AES_128_GCM_SHA256:"\
++	"TLS_AES_256_GCM_SHA384:"\
++	"TLS_AES_128_CCM_SHA256:"\
++	"TLS_CHACHA20_POLY1305_SHA256"
++
++static const char * get_tlsv13_cipher_name(int id)
++{
++    struct tlsv13_cipher *cipher = &tlsv13_ciphers[0];
++
++    while (cipher->id) {
++        if (cipher->id == id)
++            return cipher->name;
++
++        ++cipher;
++    }
++
++    return NULL;
++}
+ 
+ static char *SSLErrorString (int err)
+ {
+@@ -732,6 +783,7 @@ static pj_status_t init_openssl(void)
+ 
+         ctx=SSL_CTX_new(meth);
+         SSL_CTX_set_cipher_list(ctx, "ALL:COMPLEMENTOFALL");
++        SSL_CTX_set_ciphersuites(ctx, PJ_OSSL_TLS1_3_CIPHERS_ALL);
+ 
+         ssl = SSL_new(ctx);
+ 
+@@ -1765,11 +1817,20 @@ static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock)
+     int ret;
+ 
+     if (ssock->param.ciphers_num == 0) {
+-        ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, PJ_SSL_SOCK_OSSL_CIPHERS);
+-        if (ret < 1) {
+-            return GET_SSL_STATUS(ssock);
+-        }    
+-        
++        if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_3) {
++            ret = SSL_CTX_set_ciphersuites(ossock->ossl_ctx, PJ_OSSL_TLS1_3_CIPHERS_DT_DEFAULT);
++            if (ret < 1) {
++                return GET_SSL_STATUS(ssock);
++            }
++        }
++
++        if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2) {
++            ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, PJ_OSSL_TLS1_2_CIPHERS_DT_DEFAULT);
++            if (ret < 1) {
++                return GET_SSL_STATUS(ssock);
++            }
++        }
++
+         return PJ_SUCCESS;
+     }
+ 
+@@ -1785,16 +1846,20 @@ static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock)
+ 
+     /* Generate user specified cipher list in OpenSSL format */
+     for (i = 0; i < ssock->param.ciphers_num; ++i) {
++        // Skip TLS 1.3 cipers
++        if (get_tlsv13_cipher_name(ssock->param.ciphers[i]))
++            continue;
++
+         for (j = 0; j < ssl_cipher_num; ++j) {
+             if (ssock->param.ciphers[i] == ssl_ciphers[j].id)
+             {
+                 const char *c_name = ssl_ciphers[j].name;
+ 
+                 /* Check buffer size */
+-                if (cipher_list.slen + pj_ansi_strlen(c_name) + 2 >
+-                    BUF_SIZE)
++                if (cipher_list.slen + pj_ansi_strlen(c_name) + 2 > BUF_SIZE)
+                 {
+                     pj_assert(!"Insufficient temporary buffer for cipher");
++                    pj_pool_release(tmp_pool);
+                     return PJ_ETOOMANY;
+                 }
+ 
+@@ -1806,14 +1871,61 @@ static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock)
+                 pj_strcat2(&cipher_list, c_name);
+                 break;
+             }
+-        }       
++        }
+     }
+ 
+     /* Put NULL termination in the generated cipher list */
+     cipher_list.ptr[cipher_list.slen] = '\0';
+ 
+-    /* Finally, set chosen cipher list */
+-    ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, buf);
++    /* Set chosen cipher list */
++    if (cipher_list.slen)
++        ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, buf);
++    else
++        ret = SSL_CTX_set_cipher_list(ossock->ossl_ctx, "COMPLEMENTOFALL");
++
++    if (ret < 1) {
++        pj_pool_release(tmp_pool);
++        return GET_SSL_STATUS(ssock);
++    }
++
++    /* Only apply TLSv13 cipher suites for TLS1_3 PROTO */
++    if (!ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_3) {
++        pj_pool_release(tmp_pool);
++        return PJ_SUCCESS;
++    }
++
++    pj_strset(&cipher_list, buf, 0);
++
++    /* Generate user specified TLSv13 cipher suites in OpenSSL format */
++    for (i = 0; i < ssock->param.ciphers_num; ++i) {
++        const char *c_name = get_tlsv13_cipher_name(ssock->param.ciphers[i]);
++        if (c_name) {
++            /* Check buffer size */
++            if (cipher_list.slen + pj_ansi_strlen(c_name) + 2 > BUF_SIZE)
++            {
++                pj_assert(!"Insufficient temporary buffer for cipher");
++                pj_pool_release(tmp_pool);
++                return PJ_ETOOMANY;
++            }
++
++            /* Add colon separator */
++            if (cipher_list.slen)
++                pj_strcat2(&cipher_list, ":");
++
++            /* Add the cipher */
++            pj_strcat2(&cipher_list, c_name);
++        }
++    }
++
++    /* Put NULL termination in the generated cipher list */
++    cipher_list.ptr[cipher_list.slen] = '\0';
++
++    /* Set chosen TLSv13 cipher suites */
++    if (cipher_list.slen)
++        ret = SSL_CTX_set_ciphersuites(ossock->ossl_ctx, buf);
++    else
++        ret = SSL_CTX_set_ciphersuites(ossock->ossl_ctx, PJ_OSSL_TLS1_3_CIPHERS_DT_DEFAULT);
++
+     if (ret < 1) {
+         pj_pool_release(tmp_pool);
+         return GET_SSL_STATUS(ssock);
+diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c
+index 903a640..9e7f264 100644
+--- a/pjsip/src/pjsip/sip_transport_tls.c
++++ b/pjsip/src/pjsip/sip_transport_tls.c
+@@ -223,7 +223,7 @@ static pj_uint32_t ssl_get_proto(pjsip_ssl_method ssl_method, pj_uint32_t proto)
+         out_proto = PJ_SSL_SOCK_PROTO_TLS1_2;
+         break;
+     case PJSIP_TLSV1_3_METHOD:
+-        out_proto = PJ_SSL_SOCK_PROTO_TLS1_3;
++        out_proto = PJ_SSL_SOCK_PROTO_TLS1_2 | PJ_SSL_SOCK_PROTO_TLS1_3;
+         break;
+     case PJSIP_SSLV23_METHOD:
+         out_proto = PJ_SSL_SOCK_PROTO_SSL23;
diff --git a/third-party/pjproject/patches/0020-return-444-while-register-request-timeout.patch b/third-party/pjproject/patches/0020-return-444-while-register-request-timeout.patch
new file mode 100644
index 0000000000000000000000000000000000000000..bd1f918824066cef030207c524942afc9ddcf660
--- /dev/null
+++ b/third-party/pjproject/patches/0020-return-444-while-register-request-timeout.patch
@@ -0,0 +1,43 @@
+From 42a3189210f3790acdd128845403b96c5d03d090 Mon Sep 17 00:00:00 2001
+From: "wenpeng.song" <wenpeng.song@iopsys.eu>
+Date: Mon, 3 Mar 2025 12:48:19 +0100
+Subject: [PATCH] return 444 while register request timeout
+
+---
+ pjsip/src/pjsip/sip_msg.c         | 2 ++
+ pjsip/src/pjsip/sip_transaction.c | 6 +++++-
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c
+index c375ca9b1..7a67783c4 100644
+--- a/pjsip/src/pjsip/sip_msg.c
++++ b/pjsip/src/pjsip/sip_msg.c
+@@ -243,6 +243,8 @@ static int init_status_phrase()
+     pj_strset2( &status_phrase[702], "Unable to resolve destination server");
+     pj_strset2( &status_phrase[703], "Error sending message to destination server");
+ 
++    pj_strset2( &status_phrase[444], "No response received");
++
+     return 1;
+ }
+ 
+diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
+index cf9795dbb..7d3ebe258 100644
+--- a/pjsip/src/pjsip/sip_transaction.c
++++ b/pjsip/src/pjsip/sip_transaction.c
+@@ -2630,7 +2630,11 @@ static pj_status_t tsx_on_state_calling( pjsip_transaction *tsx,
+         tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED);
+ 
+         /* Set status code */
+-        tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL);
++        if(tsx->method.id == PJSIP_REGISTER_METHOD){
++            tsx_set_status_code(tsx, 444, NULL);
++        } else {
++            tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL);
++        }
+ 
+         /* Inform TU. */
+         tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+-- 
+2.43.0
+
diff --git a/third-party/pjproject/patches/0021-Update-for-Authorization-header-handling.patch b/third-party/pjproject/patches/0021-Update-for-Authorization-header-handling.patch
new file mode 100644
index 0000000000000000000000000000000000000000..286675b2ef1980050eaf21e8d439bf51f636092f
--- /dev/null
+++ b/third-party/pjproject/patches/0021-Update-for-Authorization-header-handling.patch
@@ -0,0 +1,117 @@
+From d0879fbe17d213a267b6015b85f5810fb34ec78b Mon Sep 17 00:00:00 2001
+From: "wenpeng.song" <wenpeng.song@iopsys.eu>
+Date: Wed, 14 May 2025 15:14:16 +0200
+Subject: [PATCH] Update for Authorization header handling
+
+ * Call callback to let Asterisk control the REGISTER challenge handling
+ * Add wrapper api for find_cached_auth, auth_find_cred
+---
+ build.symbian/pjsipU.def          |  2 ++
+ pjsip/include/pjsip/sip_auth.h    |  8 ++++++++
+ pjsip/src/pjsip-ua/sip_reg.c      | 22 ++++++++++++----------
+ pjsip/src/pjsip/sip_auth_client.c | 12 ++++++++++++
+ 4 files changed, 34 insertions(+), 10 deletions(-)
+
+diff --git a/build.symbian/pjsipU.def b/build.symbian/pjsipU.def
+index 68d31eba3..5490d1e33 100644
+--- a/build.symbian/pjsipU.def
++++ b/build.symbian/pjsipU.def
+@@ -278,3 +278,5 @@ EXPORTS
+ 	pjsip_warning_hdr_create                 @ 277 NONAME
+ 	pjsip_warning_hdr_create_from_status     @ 278 NONAME
+ 	pjsip_www_authenticate_hdr_create        @ 279 NONAME
++	pjsip_auth_find_cached_auth              @ 280 NONAME
++	pjsip_auth_find_cred                     @ 281 NONAME
+diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h
+index fa55830fd..02a8a7cde 100644
+--- a/pjsip/include/pjsip/sip_auth.h
++++ b/pjsip/include/pjsip/sip_auth.h
+@@ -628,6 +628,14 @@ PJ_DEF(pj_status_t) pjsip_auth_create_digestSHA256(pj_str_t* result,
+  * @}
+  */
+ 
++/* Find cached authentication in the list for the specified realm. */
++PJ_DECL(pjsip_cached_auth) *pjsip_auth_find_cached_auth( pjsip_auth_clt_sess *sess,
++                                            const pj_str_t *realm );
++
++/* Find credential to use for the specified realm and auth scheme. */
++PJ_DECL(pjsip_cred_info*) pjsip_auth_find_cred( const pjsip_auth_clt_sess *sess,
++                                              const pj_str_t *realm,
++                                              const pj_str_t *auth_scheme);
+ 
+ 
+ PJ_END_DECL
+diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c
+index 947e9edb7..6a3aad194 100644
+--- a/pjsip/src/pjsip-ua/sip_reg.c
++++ b/pjsip/src/pjsip-ua/sip_reg.c
+@@ -1210,25 +1210,27 @@ static void regc_tsx_callback(void *token, pjsip_event *event)
+                 pj_list_erase(chdr);
+             }
+         }
+-
++        status = PJ_SUCCESS;
++/*
+         status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
+                                             rdata, 
+                                             tsx->last_tx,  
+                                             &tdata);
+-
+-        if (status == PJ_SUCCESS) {
++*/
++// Call callback anyway to let asterisk control the handling of the challenge
++//        if (status == PJ_SUCCESS) {
+             /* Need to unlock the regc temporarily while sending the message
+              * to prevent deadlock (see ticket #2260 and #1247).
+              * It should be safe to do this since the regc's refcount has been
+              * incremented.
+              */
+-            pj_lock_release(regc->lock);
+-            status = pjsip_regc_send(regc, tdata);
+-            pj_lock_acquire(regc->lock);
+-        }
++//            pj_lock_release(regc->lock);
++//            status = pjsip_regc_send(regc, tdata);
++//            pj_lock_acquire(regc->lock);
++//        }
+         
+-        if (status != PJ_SUCCESS) {
+-
++//        if (status != PJ_SUCCESS) {
++//
+             /* Only call callback if application is still interested
+              * in it.
+              */
+@@ -1242,7 +1244,7 @@ static void regc_tsx_callback(void *token, pjsip_event *event)
+                               rdata, NOEXP, 0, NULL, is_unreg);
+                 pj_lock_acquire(regc->lock);
+             }
+-        }
++//        }
+ 
+     } else if (regc->_delete_flag) {
+ 
+diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c
+index ee2876d99..fd5c93007 100644
+--- a/pjsip/src/pjsip/sip_auth_client.c
++++ b/pjsip/src/pjsip/sip_auth_client.c
+@@ -1528,3 +1528,15 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req(  pjsip_auth_clt_sess *sess,
+ 
+ }
+ 
++/* Find cached authentication in the list for the specified realm. */
++PJ_DEF(pjsip_cached_auth) *pjsip_auth_find_cached_auth( pjsip_auth_clt_sess *sess,
++                                            const pj_str_t *realm ){
++    return find_cached_auth(sess, realm);
++}
++
++/* Find credential to use for the specified realm and auth scheme. */
++PJ_DEF(pjsip_cred_info*) pjsip_auth_find_cred( const pjsip_auth_clt_sess *sess,
++                                              const pj_str_t *realm,
++                                              const pj_str_t *auth_scheme){
++    return auth_find_cred(sess, realm, auth_scheme);
++}
+-- 
+2.43.0
+
+
diff --git a/third-party/pjproject/patches/0021-stateless-send-transport-handle-no-dest-addr.patch b/third-party/pjproject/patches/0021-stateless-send-transport-handle-no-dest-addr.patch
new file mode 100644
index 0000000000000000000000000000000000000000..1b74418a6612680cb851446856d6af10ac842a0e
--- /dev/null
+++ b/third-party/pjproject/patches/0021-stateless-send-transport-handle-no-dest-addr.patch
@@ -0,0 +1,28 @@
+diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c
+index 3d5c3912c..1e0e31627 100644
+--- a/pjsip/src/pjsip/sip_util.c
++++ b/pjsip/src/pjsip/sip_util.c
+@@ -1157,7 +1157,7 @@ static void stateless_send_transport_cb( void *token,
+              * (2) Failure (i.e. sent <= 0)
+              */
+             cont = (sent > 0) ? PJ_FALSE :
+-                   (tdata->dest_info.cur_addr<tdata->dest_info.addr.count-1);
++                   (tdata->dest_info.addr.count > 0 && tdata->dest_info.cur_addr<tdata->dest_info.addr.count-1);
+             if (stateless_data->app_cb) {
+                 (*stateless_data->app_cb)(stateless_data, sent, &cont);
+             } else {
+@@ -1187,6 +1187,14 @@ static void stateless_send_transport_cb( void *token,
+             tdata->dest_info.cur_addr++;
+         }
+ 
++	if (!tdata->dest_info.cur_addr && !tdata->dest_info.addr.count) {
++	    if (stateless_data->app_cb) {
++                (*stateless_data->app_cb)(stateless_data, sent, &cont);
++            }
++            pjsip_tx_data_dec_ref(tdata);
++            return;
++	}
++
+         /* Have next address? */
+         if (tdata->dest_info.cur_addr >= tdata->dest_info.addr.count) {
+             /* This only happens when a rather buggy application has
diff --git a/third-party/pjproject/patches/config_site.h b/third-party/pjproject/patches/config_site.h
index 9f4d6787822e2aa2ac75578ef2c8e1f2a871cf06..3536af964a9402781245375bc06f622e96b0937c 100644
--- a/third-party/pjproject/patches/config_site.h
+++ b/third-party/pjproject/patches/config_site.h
@@ -19,8 +19,9 @@
  * versions, newer versions of pjproject won't compile with them.
  */
 #define PJMEDIA_HAS_SRTP 0
+#define PJSIP_HAS_RESOLVER 0
 
-#define PJ_HAS_IPV6 1
+#define PJ_HAS_IPV6 0
 #if !defined(AST_DEVMODE) && !defined(PJPROJECT_BUNDLED_ASSERTIONS)
 #define NDEBUG 1
 #endif
@@ -91,3 +92,5 @@
 #define PJSIP_TSX_UAS_CONTINUE_ON_TP_ERROR 0
 #define PJ_SSL_SOCK_OSSL_USE_THREAD_CB 0
 #define PJSIP_AUTH_ALLOW_MULTIPLE_AUTH_HEADER 1
+#define PJSIP_AUTH_HEADER_CACHING     1
+#define PJSIP_AUTH_AUTO_SEND_NEXT     1