Prechádzať zdrojové kódy

dialog: Add setting to loop BYE through proxy

The setting "keep_proxy_rr" will add the Record-Route headers added by the
proxy to the route_set stored in the dialog. When in use, sending locally
generated in-dialog requests will loop back to the proxy with a proper
Record-Route header, including any parameters.
Alex Hermann 11 rokov pred
rodič
commit
0b3562ace8

+ 8 - 1
src/modules/dialog/dialog.c

@@ -103,6 +103,7 @@ static char* profiles_nv_s = NULL;
 str dlg_extra_hdrs = {NULL,0};
 static int db_fetch_rows = 200;
 static int db_skip_load = 0;
+static int dlg_keep_proxy_rr = 0;
 int initial_cbs_inscript = 1;
 int dlg_wait_ack = 1;
 static int dlg_timer_procs = 0;
@@ -322,6 +323,7 @@ static param_export_t mod_params[]={
 	{ "end_timeout",           PARAM_INT, &dlg_end_timeout          },
 	{ "h_id_start",            PARAM_INT, &dlg_h_id_start           },
 	{ "h_id_step",             PARAM_INT, &dlg_h_id_step            },
+	{ "keep_proxy_rr",         INT_PARAM, &dlg_keep_proxy_rr        },
 	{ 0,0,0 }
 };
 
@@ -528,6 +530,11 @@ static int mod_init(void)
 		return -1;
 	}
 
+	if (dlg_keep_proxy_rr < 0 || dlg_keep_proxy_rr > 3) {
+		LM_ERR("invalid value for keep_proxy_rr\n");
+		return -1;
+	}
+
 	if (timeout_spec.s) {
 		if ( pv_parse_spec(&timeout_spec, &timeout_avp)==0
 				&& (timeout_avp.type!=PVT_AVP)){
@@ -655,7 +662,7 @@ static int mod_init(void)
 
 	/* init handlers */
 	init_dlg_handlers( rr_param, dlg_flag,
-		timeout_spec.s?&timeout_avp:0, default_timeout, seq_match_mode);
+		timeout_spec.s?&timeout_avp:0, default_timeout, seq_match_mode, dlg_keep_proxy_rr);
 
 	/* init timer */
 	if (init_dlg_timer(dlg_ontimeout)!=0) {

+ 25 - 4
src/modules/dialog/dlg_handlers.c

@@ -62,6 +62,7 @@ static int       dlg_flag_mask=0;	/*!< flag for dialog tracking */
 static pv_spec_t *timeout_avp;		/*!< AVP for timeout setting */
 static int       default_timeout;	/*!< default dialog timeout */
 static int       seq_match_mode;	/*!< dlg_match mode */
+static int       keep_proxy_rr;		/*!< keep the proxy's record-route in both route-sets */
 static int       shutdown_done = 0;	/*!< 1 when destroy_dlg_handlers was called */
 extern int       detect_spirals;
 extern int       dlg_timeout_noreset;
@@ -103,7 +104,7 @@ int dlg_set_tm_waitack(tm_cell_t *t, dlg_cell_t *dlg);
  */
 void init_dlg_handlers(char *rr_param_p, int dlg_flag_p,
 		pv_spec_t *timeout_avp_p ,int default_timeout_p,
-		int seq_match_mode_p)
+		int seq_match_mode_p, int keep_proxy_rr_p)
 {
 	rr_param.s = rr_param_p;
 	rr_param.len = strlen(rr_param.s);
@@ -113,6 +114,7 @@ void init_dlg_handlers(char *rr_param_p, int dlg_flag_p,
 	timeout_avp = timeout_avp_p;
 	default_timeout = default_timeout_p;
 	seq_match_mode = seq_match_mode_p;
+	keep_proxy_rr = keep_proxy_rr_p;
 }
 
 
@@ -184,7 +186,7 @@ static inline int add_dlg_rr_param(struct sip_msg *req, unsigned int entry,
 int populate_leg_info( struct dlg_cell *dlg, struct sip_msg *msg,
 	struct cell* t, unsigned int leg, str *tag)
 {
-	unsigned int skip_recs;
+	unsigned int skip_recs, own_rr = 0;
 	str cseq;
 	str contact;
 	str rr_set;
@@ -233,11 +235,11 @@ int populate_leg_info( struct dlg_cell *dlg, struct sip_msg *msg,
 		skip_recs = 0;
 	} else {
 		/* was the 200 OK received or local generated */
-		skip_recs = dlg->from_rr_nb +
-			((t->relayed_reply_branch>=0)?
+		own_rr = ((t->relayed_reply_branch>=0)?
 				((t->uac[t->relayed_reply_branch].flags&TM_UAC_FLAG_R2)?2:
 				 ((t->uac[t->relayed_reply_branch].flags&TM_UAC_FLAG_RR)?1:0))
 				:0);
+		skip_recs = dlg->from_rr_nb + ((keep_proxy_rr & 1) > 0 ? 0 : own_rr);
 	}
 
 	if(msg->record_route){
@@ -269,6 +271,25 @@ int populate_leg_info( struct dlg_cell *dlg, struct sip_msg *msg,
 
 	if (rr_set.s) pkg_free(rr_set.s);
 
+	if ((keep_proxy_rr & 2) > 0 && leg==DLG_CALLEE_LEG && msg->record_route && own_rr > 0) {
+		/* skip_recs contains the number of RR's for the callee */
+		skip_recs -= own_rr;
+		/* Add local RR's to caller's routeset */
+		if( print_rr_body(msg->record_route, &rr_set, DLG_CALLER_LEG,
+		                 &skip_recs) != 0) {
+			LM_ERR("failed to print route records \n");
+			goto error0;
+		}
+		LM_DBG("updating caller route_set %.*s\n",
+			rr_set.len, rr_set.s);
+		if (dlg_update_rr_set( dlg, DLG_CALLER_LEG, &rr_set)!=0) {
+			LM_ERR("dlg_update_rr_set failed\n");
+			if (rr_set.s) pkg_free(rr_set.s);
+			goto error0;
+		}
+		if (rr_set.s) pkg_free(rr_set.s);
+	}
+
 	return 0;
 error0:
 	return -1;

+ 1 - 1
src/modules/dialog/dlg_handlers.h

@@ -55,7 +55,7 @@
  */
 void init_dlg_handlers(char *rr_param, int dlg_flag,
 		pv_spec_t *timeout_avp, int default_timeout,
-		int seq_match_mode);
+		int seq_match_mode, int keep_proxy_rr);
 
 
 /*!

+ 74 - 8
src/modules/dialog/dlg_hash.c

@@ -394,6 +394,12 @@ void destroy_dlg(struct dlg_cell *dlg)
 	if (dlg->cseq[DLG_CALLEE_LEG].s)
 		shm_free(dlg->cseq[DLG_CALLEE_LEG].s);
 
+	if (dlg->route_set[DLG_CALLER_LEG].s)
+		shm_free(dlg->route_set[DLG_CALLER_LEG].s);
+
+	if (dlg->route_set[DLG_CALLEE_LEG].s)
+		shm_free(dlg->route_set[DLG_CALLEE_LEG].s);
+
 	if (dlg->toroute_name.s)
 		shm_free(dlg->toroute_name.s);
 
@@ -518,7 +524,6 @@ struct dlg_cell* build_new_dlg( str *callid, str *from_uri, str *to_uri,
 int dlg_set_leg_info(struct dlg_cell *dlg, str* tag, str *rr, str *contact,
 					str *cseq, unsigned int leg)
 {
-	char *p;
 	str cs = {"0", 1};
 
 	/* if we don't have cseq, set it to 0 */
@@ -528,7 +533,7 @@ int dlg_set_leg_info(struct dlg_cell *dlg, str* tag, str *rr, str *contact,
 
 	if(dlg->tag[leg].s)
 		shm_free(dlg->tag[leg].s);
-	dlg->tag[leg].s = (char*)shm_malloc( tag->len + rr->len );
+	dlg->tag[leg].s = (char*)shm_malloc(tag->len);
 
 	if(dlg->cseq[leg].s) {
 		if (dlg->cseq[leg].len < cs.len) {
@@ -548,8 +553,17 @@ int dlg_set_leg_info(struct dlg_cell *dlg, str* tag, str *rr, str *contact,
 		dlg->contact[leg].s = (char*)shm_malloc( contact->len );
 	}
 
+	if(dlg->route_set[leg].s) {
+		if (dlg->route_set[leg].len < rr->len) {
+			shm_free(dlg->route_set[leg].s);
+			dlg->route_set[leg].s = (char*)shm_malloc(rr->len);
+		}
+	} else {
+		dlg->route_set[leg].s = (char*)shm_malloc(rr->len);
+	}
+
 	if ( dlg->tag[leg].s==NULL || dlg->cseq[leg].s==NULL
-			|| dlg->contact[leg].s==NULL) {
+			|| dlg->contact[leg].s==NULL || dlg->route_set[leg].s==NULL) {
 		LM_ERR("no more shm mem\n");
 		if (dlg->tag[leg].s)
 		{
@@ -566,20 +580,23 @@ int dlg_set_leg_info(struct dlg_cell *dlg, str* tag, str *rr, str *contact,
 			shm_free(dlg->contact[leg].s);
 			dlg->contact[leg].s = NULL;
 		}
+		if (dlg->route_set[leg].s)
+		{
+			shm_free(dlg->route_set[leg].s);
+			dlg->route_set[leg].s = NULL;
+		}
 
 		return -1;
 	}
-	p = dlg->tag[leg].s;
 
 	/* tag */
 	dlg->tag[leg].len = tag->len;
-	memcpy( p, tag->s, tag->len);
-	p += tag->len;
+	memcpy( dlg->tag[leg].s, tag->s, tag->len);
+
 	/* rr */
 	if (rr->len) {
-		dlg->route_set[leg].s = p;
 		dlg->route_set[leg].len = rr->len;
-		memcpy( p, rr->s, rr->len);
+		memcpy(dlg->route_set[leg].s, rr->s, rr->len);
 	}
 
 	/* contact */
@@ -689,6 +706,55 @@ error:
 }
 
 
+/*!
+ * \brief Update or set the routeset for an existing dialog
+ * \param dlg dialog
+ * \param leg must be either DLG_CALLER_LEG, or DLG_CALLEE_LEG
+ * \param rr routeset
+ * \return 0 on success, -1 on failure
+ */
+int dlg_update_rr_set(struct dlg_cell * dlg, unsigned int leg, str *rr)
+{
+	dlg_entry_t *d_entry;
+
+	d_entry = &(d_table->entries[dlg->h_entry]);
+
+	dlg_lock(d_table, d_entry);
+
+	if ( dlg->route_set[leg].s ) {
+		if(dlg->route_set[leg].len == rr->len
+				&& memcmp(dlg->route_set[leg].s, rr->s, rr->len)==0) {
+			LM_DBG("same route_set for leg[%d] - [%.*s]\n", leg,
+				dlg->route_set[leg].len, dlg->route_set[leg].s);
+			goto done;
+		}
+		if (dlg->route_set[leg].len < rr->len) {
+			shm_free(dlg->route_set[leg].s);
+			dlg->route_set[leg].s = (char*)shm_malloc(rr->len);
+			if (dlg->route_set[leg].s==NULL)
+				goto error;
+		}
+	} else {
+		dlg->route_set[leg].s = (char*)shm_malloc(rr->len);
+		if (dlg->route_set[leg].s==NULL)
+			goto error;
+	}
+
+	memcpy( dlg->route_set[leg].s, rr->s, rr->len );
+	dlg->route_set[leg].len = rr->len;
+
+	LM_DBG("route_set of leg[%d] is %.*s\n", leg,
+			dlg->route_set[leg].len, dlg->route_set[leg].s);
+done:
+	dlg_unlock(d_table, d_entry);
+	return 0;
+error:
+	dlg_unlock(d_table, d_entry);
+	LM_ERR("not more shm mem\n");
+	return -1;
+}
+
+
 /*!
  * \brief Lookup a dialog in the global list
  *

+ 9 - 0
src/modules/dialog/dlg_hash.h

@@ -291,6 +291,15 @@ int dlg_update_contact(struct dlg_cell * dlg, unsigned int leg, str *ct);
  */
 int dlg_update_cseq(dlg_cell_t *dlg, unsigned int leg, str *cseq);
 
+/*!
+ * \brief Update or set the routeset for an existing dialog
+ * \param dlg dialog
+ * \param leg must be either DLG_CALLER_LEG, or DLG_CALLEE_LEG
+ * \param rr routeset
+ * \return 0 on success, -1 on failure
+ */
+int dlg_update_rr_set(struct dlg_cell * dlg, unsigned int leg, str *rr);
+
 /*!
  * \brief Set time-out route
  * \param dlg dialog

+ 36 - 0
src/modules/dialog/doc/dialog_admin.xml

@@ -1604,6 +1604,42 @@ modparam("dialog", "h_id_step", 10)
 
 	</section>
 
+	<section id="dialog.p.keep_proxy_rr">
+		<title><varname>keep_proxy_rr</varname> (string)</title>
+		<para>
+			Whether to keep the record-route header added by the proxy.
+			When enabled, it will keep this proxy&apos;s record-route
+			header from the reply. The result is that generated requests
+			like the BYE from the dlg_end_dlg mi function will pass
+			through the proxy (looped).
+		</para>
+		<para>
+			Valid values are:
+		</para>
+		<itemizedlist>
+			<listitem><para>
+				<emphasis>0</emphasis> - Don&apos;t keep any proxy Record-Route headers
+			</para></listitem>
+			<listitem><para>
+				<emphasis>1</emphasis> - Keep Record-route headers for the callee leg
+			</para></listitem>
+			<listitem><para>
+				<emphasis>2</emphasis> - Keep Record-route headers for the caller leg
+			</para></listitem>
+			<listitem><para>
+				<emphasis>3</emphasis> - Keep Record-route headers for both legs
+			</para></listitem>
+		</itemizedlist>
+		<emphasis>
+			Default value is <quote>0</quote>.
+		</emphasis>
+		<title>Set <varname>dlg_keep_proxy_rr</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("dialog", "keep_proxy_rr", 1)
+</programlisting>
+		</example>
+	</section>
 
 	<section>
 	<title>Functions</title>