Browse Source

dialog: add option to increment cseq upon local authentication to next hop

- feature has to be enabled via module parameter track_cseq_updates
- it does it only for downstream direction (requests from caller and
  callee, as it is the typical use case of calling via a provider, after
  authenticating the caller locally, provider asks for another 'trunk'
  authentication)
- diff of cseq value is stored in dialog var named 'cseq_diff',
  therefore be sure it is not overwritten from config
Daniel-Constantin Mierla 11 years ago
parent
commit
94f18a9574

+ 6 - 0
modules/dialog/dialog.c

@@ -86,6 +86,7 @@
 #include "dlg_profile.h"
 #include "dlg_var.h"
 #include "dlg_transfer.h"
+#include "dlg_cseq.h"
 
 MODULE_VERSION
 
@@ -110,6 +111,7 @@ static int db_fetch_rows = 200;
 int initial_cbs_inscript = 1;
 int dlg_wait_ack = 1;
 static int dlg_timer_procs = 0;
+static int _dlg_track_cseq_updates = 0;
 
 int dlg_event_rt[DLG_EVENTRT_MAX];
 
@@ -294,6 +296,7 @@ static param_export_t mod_params[]={
 	{ "ka_interval",           INT_PARAM, &dlg_ka_interval          },
 	{ "timeout_noreset",       INT_PARAM, &dlg_timeout_noreset      },
 	{ "timer_procs",           PARAM_INT, &dlg_timer_procs          },
+	{ "track_cseq_updates",    PARAM_INT, &_dlg_track_cseq_updates  },
 	{ 0,0,0 }
 };
 
@@ -696,6 +699,9 @@ static int mod_init(void)
 	/* timer process to clean old unconfirmed dialogs */
 	register_sync_timers(1);
 
+	if(_dlg_track_cseq_updates!=0)
+		dlg_register_cseq_callbacks();
+
 	return 0;
 }
 

+ 324 - 0
modules/dialog/dlg_cseq.c

@@ -0,0 +1,324 @@
+/**
+ * Copyright (C) 2014 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file 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 file 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../events.h"
+#include "../../ut.h"
+#include "../../parser/parse_to.h"
+#include "../../parser/parse_from.h"
+#include "../../modules/tm/tm_load.h"
+
+#include "dlg_handlers.h"
+#include "dlg_var.h"
+#include "dlg_cseq.h"
+
+extern struct tm_binds d_tmb;
+
+static str _dlg_cseq_diff_var_name = str_init("cseq_diff");
+
+/**
+ *
+ */
+int dlg_cseq_prepare_msg(sip_msg_t *msg)
+{
+	if (parse_msg(msg->buf, msg->len, msg)!=0) {
+		LM_DBG("outbuf buffer parsing failed!");
+		return 1;
+	}
+
+	if(msg->first_line.type==SIP_REQUEST) {
+		if(!IS_SIP(msg))
+		{
+			LM_DBG("non sip request message\n");
+			return 1;
+		}
+	} else if(msg->first_line.type!=SIP_REPLY) {
+		LM_DBG("non sip message\n");
+		return 1;
+	}
+
+	if (parse_headers(msg, HDR_CSEQ_F, 0)==-1) {
+		LM_DBG("parsing cseq header failed\n");
+		return 2;
+	}
+
+	if(msg->first_line.type==SIP_REPLY) {
+		/* reply to local transaction -- nothing to do */
+		if (parse_headers(msg, HDR_VIA2_F, 0)==-1
+				|| (msg->via2==0) || (msg->via2->error!=PARSE_OK)) {
+			LM_DBG("no second via in this message \n");
+			return 3;
+		}
+	}
+
+	if(parse_from_header(msg)<0)
+	{
+		LM_ERR("cannot parse FROM header\n");
+		return 3;
+	}
+
+	if(parse_to_header(msg)<0 || msg->to==NULL)
+	{
+		LM_ERR("cannot parse TO header\n");
+		return 3;
+	}
+
+	if(get_to(msg)==NULL)
+	{
+		LM_ERR("cannot get TO header\n");
+		return 3;
+	}
+
+	return 0;
+}
+
+/**
+ *
+ */
+int dlg_cseq_msg_received(void *data)
+{
+	sip_msg_t msg;
+	str *obuf;
+	struct via_body *via;
+	str vcseq;
+
+	obuf = (str*)data;
+	memset(&msg, 0, sizeof(sip_msg_t));
+	msg.buf = obuf->s;
+	msg.len = obuf->len;
+
+	if(dlg_cseq_prepare_msg(&msg)!=0) {
+		goto done;
+	}
+
+	if(msg.first_line.type==SIP_REQUEST) {
+		/* nothing to do for incoming requests */
+		goto done;
+	}
+
+	via = (struct via_body*)msg.h_via1->parsed;
+
+	if(via->branch==NULL || via->branch->value.len<=0) {
+		LM_DBG("no branch parameter in top via\n");
+		goto done;
+	}
+	vcseq.s = via->branch->value.s + via->branch->value.len - 1;
+	vcseq.len = 0;
+	while(vcseq.s>via->branch->value.s) {
+		if(*vcseq.s=='.') {
+			if(vcseq.len<3) {
+				LM_DBG("no matching the starting point length\n");
+				goto done;
+			}
+			if(vcseq.s[1]!='c' || vcseq.s[2]!='s') {
+				LM_DBG("no matching the starting placeholder\n");
+				goto done;
+			}
+			vcseq.len++;
+			break;
+		}
+		vcseq.len++;
+		vcseq.s--;
+	}
+	LM_DBG("via cseq cookie [%.*s] val [%.*s]\n", vcseq.len, vcseq.s,
+			vcseq.len-3, vcseq.s+3);
+	if(vcseq.len-3<get_cseq(&msg)->number.len) {
+		/* higher lenght to update - wrong */
+		LM_DBG("cseq in message (%d) longer than in via (%d)\n",
+				get_cseq(&msg)->number.len, vcseq.len-3);
+		goto done;
+	}
+	if(vcseq.len-3==get_cseq(&msg)->number.len) {
+		/* same length - overwrite in the buffer */
+		memcpy(get_cseq(&msg)->number.s, vcseq.s+3, vcseq.len-3);
+	} else {
+		/* pad beginning with space */
+		strncpy(get_cseq(&msg)->number.s, "                  ",
+				get_cseq(&msg)->number.len - vcseq.len + 3);
+		memcpy(get_cseq(&msg)->number.s + get_cseq(&msg)->number.len
+				- vcseq.len + 3, vcseq.s+3, vcseq.len-3);
+	}
+	/* our via, will be removed anyhow - let's change cseq part of branch
+	 * to a z parameter instead of shifting content to remove it */
+	vcseq.s[0] = ';';
+	vcseq.s[1] = 'z';
+	vcseq.s[2] = '=';
+
+done:
+	free_sip_msg(&msg);
+	return 0;
+}
+
+/**
+ *
+ */
+int dlg_cseq_msg_sent(void *data)
+{
+	sip_msg_t msg;
+	str *obuf;
+	unsigned int direction;
+	tm_cell_t *t;
+	unsigned int ninc = 0;
+	unsigned int vinc = 0;
+	dlg_cell_t *dlg = NULL;
+	str nval;
+	str *pval;
+	char tbuf[BUF_SIZE];
+	int tbuf_len = 0;
+	struct via_body *via;
+
+	obuf = (str*)data;
+	memset(&msg, 0, sizeof(sip_msg_t));
+	msg.buf = obuf->s;
+	msg.len = obuf->len;
+
+	if(dlg_cseq_prepare_msg(&msg)!=0) {
+		goto done;
+	}
+
+	if(msg.first_line.type==SIP_REPLY) {
+		/* nothing to do for outgoing replies */
+		goto done;
+	}
+
+	LM_DBG("traking cseq updates\n");
+	via = (struct via_body*)msg.h_via1->parsed;
+
+	if(via->branch==NULL || via->branch->value.len<=0) {
+		LM_DBG("no branch parameter in top via\n");
+		goto done;
+	}
+
+	direction = DLG_DIR_NONE;
+	dlg = dlg_lookup_msg_dialog(&msg, &direction);
+	
+	if(dlg == NULL) {
+		LM_DBG("no dialog for this request\n");
+		goto done;
+	}
+
+	/* supported only for downstrem direction */
+	if(direction != DLG_DIR_DOWNSTREAM) {
+		LM_DBG("request not going downstream (%u)\n", direction);
+		goto done;
+	}
+
+	/* check if transaction is marked for a new increment */
+	if(get_cseq(&msg)->method_id!=METHOD_ACK) {
+		t = d_tmb.t_gett();
+		if(t==NULL || t==T_UNDEFINED) {
+			LM_DBG("no transaction for request\n");
+			goto done;
+		}
+		if(t->uas.request==0) {
+			LM_DBG("no uas request - no cseq update needed\n");
+			goto done;
+		}
+		if((t->uas.request->msg_flags&FL_UAC_AUTH)==FL_UAC_AUTH) {
+			LM_DBG("uac auth request - cseq inc needed\n");
+			ninc = 1;
+		}
+	}
+
+	/* take the increment value from dialog */
+	if((dlg->iflags&DLG_IFLAG_CSEQ_DIFF)==DLG_IFLAG_CSEQ_DIFF) {
+		/* get dialog variable holding cseq diff */
+		pval = get_dlg_variable(dlg, &_dlg_cseq_diff_var_name);
+		if(pval==NULL || pval->s==NULL || pval->len<=0) {
+			LM_DBG("dialog marked with cseq diff but no variable set yet\n");
+			goto done;
+		}
+		if(str2int(pval, &vinc)<0) {
+			LM_ERR("invalid dlg cseq diff var value: %.*s\n",
+					pval->len, pval->s);
+			goto done;
+		}
+	}
+	vinc += ninc;
+	if(vinc==0) {
+		LM_DBG("nothing to increment\n");
+		goto done;
+	}
+	nval.s = int2str(vinc, &nval.len);
+	if(msg.len + 3 + 2*nval.len>=BUF_SIZE) {
+		LM_ERR("new messages is too big\n");
+		goto done;
+	}
+	if(set_dlg_variable(dlg, &_dlg_cseq_diff_var_name, &nval) <0) {
+		LM_ERR("failed to set the dlg cseq diff var\n");
+		goto done;
+	}
+	str2int(&get_cseq(&msg)->number, &ninc);
+	vinc += ninc;
+	nval.s = int2str(vinc, &nval.len);
+
+	/* new cseq value */
+	dlg->iflags |= DLG_IFLAG_CSEQ_DIFF;
+	/* copy first part till after via branch */
+	tbuf_len = via->branch->value.s + via->branch->value.len - msg.buf;
+	memcpy(tbuf, msg.buf, tbuf_len);
+	/* complete via branch */
+	tbuf[tbuf_len++] = '.';
+	tbuf[tbuf_len++] = 'c';
+	tbuf[tbuf_len++] = 's';
+	memcpy(tbuf+tbuf_len, get_cseq(&msg)->number.s, get_cseq(&msg)->number.len);
+	tbuf_len += get_cseq(&msg)->number.len;
+	/* copy till beginning of cseq number */
+	memcpy(tbuf+tbuf_len, via->branch->value.s + via->branch->value.len,
+			get_cseq(&msg)->number.s - via->branch->value.s
+			- via->branch->value.len);
+	tbuf_len += get_cseq(&msg)->number.s - via->branch->value.s
+					- via->branch->value.len;
+	/* add new value */
+	memcpy(tbuf+tbuf_len, nval.s, nval.len);
+	tbuf_len += nval.len;
+	/* copy from after cseq number to the end of sip message */
+	memcpy(tbuf+tbuf_len, get_cseq(&msg)->number.s+get_cseq(&msg)->number.len,
+			msg.buf + msg.len - get_cseq(&msg)->number.s
+			- get_cseq(&msg)->number.len);
+	tbuf_len += msg.buf+msg.len - get_cseq(&msg)->number.s
+				- get_cseq(&msg)->number.len;
+
+	/* replace old msg content */
+	memcpy(obuf->s, tbuf, tbuf_len);
+	obuf->s[tbuf_len] = 0;
+	obuf->len = tbuf_len;
+
+done:
+	if(dlg!=NULL) dlg_release(dlg);
+	free_sip_msg(&msg);
+	return 0;
+}
+
+/**
+ *
+ */
+int dlg_register_cseq_callbacks(void)
+{
+	sr_event_register_cb(SREV_NET_DATA_IN, dlg_cseq_msg_received);
+	sr_event_register_cb(SREV_NET_DATA_OUT, dlg_cseq_msg_sent);
+	return 0;
+}

+ 30 - 0
modules/dialog/dlg_cseq.h

@@ -0,0 +1,30 @@
+/**
+ * Copyright (C) 2014 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file 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 file 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef _DLG_CSEQ_H_
+#define _DLG_CSEQ_H_
+
+#include "../../parser/msg_parser.h"
+
+int dlg_register_cseq_callbacks(void);
+
+#endif

+ 35 - 6
modules/dialog/dlg_handlers.c

@@ -1056,32 +1056,61 @@ static void unref_dlg_from_cb(struct cell* t, int type, struct tmcb_params *para
 	dlg_unref(dlg, 2);
 }
 
-
-dlg_cell_t *dlg_get_msg_dialog(sip_msg_t *msg)
+/*!
+ *
+ */
+dlg_cell_t *dlg_lookup_msg_dialog(sip_msg_t *msg, unsigned int *dir)
 {
 	dlg_cell_t *dlg = NULL;
 	str callid;
 	str ftag;
 	str ttag;
-	unsigned int dir;
+	unsigned int vdir;
 
 	/* Retrieve the current dialog */
 	dlg = dlg_get_ctx_dialog();
-	if(dlg!=NULL)
+	if(dlg!=NULL) {
+		if(dir) {
+			if (pre_match_parse(msg, &callid, &ftag, &ttag, 0)<0) {
+				dlg_release(dlg);
+				return NULL;
+			}
+			if (dlg->tag[DLG_CALLER_LEG].len == ftag.len &&
+					   strncmp(dlg->tag[DLG_CALLER_LEG].s, ftag.s, ftag.len)==0 &&
+					   strncmp(dlg->callid.s, callid.s, callid.len)==0) {
+				*dir = DLG_DIR_DOWNSTREAM;
+			} else {
+				if (ttag.len>0 && dlg->tag[DLG_CALLER_LEG].len == ttag.len &&
+						   strncmp(dlg->tag[DLG_CALLER_LEG].s, ttag.s, ttag.len)==0 &&
+						   strncmp(dlg->callid.s, callid.s, callid.len)==0) {
+					*dir = DLG_DIR_UPSTREAM;
+				}
+			}
+		}
 		return dlg;
+	}
 	
 	if (pre_match_parse(msg, &callid, &ftag, &ttag, 0)<0)
 		return NULL;
-	dir = DLG_DIR_NONE;
-	dlg = get_dlg(&callid, &ftag, &ttag, &dir);
+	vdir = DLG_DIR_NONE;
+	dlg = get_dlg(&callid, &ftag, &ttag, &vdir);
 	if (dlg==NULL){
 		LM_DBG("dlg with callid '%.*s' not found\n",
 				msg->callid->body.len, msg->callid->body.s);
 		return NULL;
 	}
+	if(dir) *dir = vdir;
 	return dlg;
 }
 
+/*!
+ *
+ */
+dlg_cell_t *dlg_get_msg_dialog(sip_msg_t *msg)
+{
+	return dlg_lookup_msg_dialog(msg, NULL);
+}
+
 /*!
  * \brief Function that is registered as RR callback for dialog tracking
  * 

+ 6 - 0
modules/dialog/dlg_handlers.h

@@ -39,6 +39,7 @@
 #include "../../str.h"
 #include "../../pvar.h"
 #include "../../modules/tm/t_hooks.h"
+#include "dlg_hash.h"
 #include "dlg_timer.h"
 
 #define MAX_DLG_RR_PARAM_NAME 32
@@ -167,6 +168,11 @@ void dlg_tmcb_dummy(tm_cell_t *t, int type, struct tmcb_params *param);
  */
 dlg_cell_t *dlg_get_msg_dialog(sip_msg_t *msg);
 
+/*!
+ * \brief Get the dialog structure and direction for the SIP message
+ */
+dlg_cell_t *dlg_lookup_msg_dialog(sip_msg_t *msg, unsigned int *dir);
+
 /*!
  * \brief Clone dialog internal unique id to shared memory
  */

+ 1 - 1
modules/dialog/dlg_hash.c

@@ -347,7 +347,7 @@ error0:
  * \brief Destroy a dialog, run callbacks and free memory
  * \param dlg destroyed dialog
  */
-inline void destroy_dlg(struct dlg_cell *dlg)
+void destroy_dlg(struct dlg_cell *dlg)
 {
 	int ret = 0;
 	struct dlg_var *var;

+ 2 - 1
modules/dialog/dlg_hash.h

@@ -86,6 +86,7 @@
 #define DLG_IFLAG_KA_SRC            (1<<1) /*!< send keep alive to src */
 #define DLG_IFLAG_KA_DST            (1<<2) /*!< send keep alive to dst */
 #define DLG_IFLAG_TIMER_NORESET     (1<<3) /*!< don't reset dialog timers on in-dialog messages reception */
+#define DLG_IFLAG_CSEQ_DIFF         (1<<4) /*!< CSeq changed in dialog */
 
 #define DLG_CALLER_LEG         0 /*!< attribute that belongs to a caller leg */
 #define DLG_CALLEE_LEG         1 /*!< attribute that belongs to a callee leg */
@@ -214,7 +215,7 @@ static inline void unlink_unsafe_dlg(dlg_entry_t *d_entry, dlg_cell_t *dlg)
  * \brief Destroy a dialog, run callbacks and free memory
  * \param dlg destroyed dialog
  */
-inline void destroy_dlg(dlg_cell_t *dlg);
+void destroy_dlg(dlg_cell_t *dlg);
 
 
 /*!