Browse Source

modules/isc: first commit of ISC module (IMS Service Control)

Jason Penton 12 years ago
parent
commit
49ef3f0279

+ 20 - 0
modules/isc/Makefile

@@ -0,0 +1,20 @@
+# $Id$
+#
+# ISC make file
+#
+# 
+
+include ../../Makefile.defs
+auto_gen=
+NAME=isc.so
+LIBS=
+
+DEFS += -DOPENSER_MOD_INTERFACE
+
+DEFS += -I/usr/include/libxml2
+LIBS += -L$(LOCALBASE)/lib -lxml2 -lrt
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/ims/kamailio_ims
+
+include ../../Makefile.modules

+ 44 - 0
modules/isc/blurb

@@ -0,0 +1,44 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ * 
+ */

+ 545 - 0
modules/isc/checker.c

@@ -0,0 +1,545 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ * 
+ */
+
+#include "checker.h"
+
+/**
+ *	Check if a Service Point Trigger for Header matches the SDP body
+ *	@param spt - the service point trigger
+ *	@param headers - the headers of the message
+ *	@returns - 1 on success, 0 on failure
+ */
+static int isc_check_headers(ims_spt *spt, struct hdr_field *headers) {
+	struct hdr_field *i;
+	char c, ch;
+	char buf[256];
+	regex_t header_comp, content_comp;
+	i = headers;
+	/* compile the regex for header name */
+	memcpy(buf, spt->sip_header.header.s, spt->sip_header.header.len);
+	buf[spt->sip_header.header.len] = 0;
+	regcomp(&(header_comp), buf, REG_ICASE | REG_EXTENDED);
+
+	/* compile the regex for content */
+	memcpy(buf, spt->sip_header.content.s, spt->sip_header.content.len);
+	buf[spt->sip_header.content.len] = 0;
+	regcomp(&(content_comp), buf, REG_ICASE | REG_EXTENDED);
+
+	LM_DBG("isc_check_headers: Looking for Header[%.*s(%d)] %.*s \n",
+			spt->sip_header.header.len, spt->sip_header.header.s, spt->sip_header.type, spt->sip_header.content.len, spt->sip_header.content.s);
+	while (i != NULL) {
+		ch = i->name.s[i->name.len];
+		i->name.s[i->name.len] = 0;
+
+		if ((spt->sip_header.type > 0 && spt->sip_header.type == i->type) || //matches known type
+				(regexec(&(header_comp), i->name.s, 0, NULL, 0) == 0) //or matches the name
+				) {
+
+			i->name.s[i->name.len] = ch;
+			LM_DBG("isc_check_headers: Found Header[%.*s(%d)] %.*s \n",
+					i->name.len, i->name.s, i->type, i->body.len, i->body.s);
+			//if the header should be absent but found it
+
+			if (spt->sip_header.content.s == NULL)
+				if (spt->condition_negated) {
+					regfree(&(header_comp));
+					regfree(&(content_comp));
+					return FALSE;
+				}
+
+			//check regex
+			c = i->body.s[i->body.len];
+			i->body.s[i->body.len] = 0;
+
+			if (regexec(&(content_comp), i->body.s, 0, NULL, 0) == 0) //regex match
+			{
+				regfree(&(header_comp));
+				regfree(&(content_comp));
+				i->body.s[i->body.len] = c;
+				return TRUE;
+			}
+
+			i->body.s[i->body.len] = c;
+		} else
+			i->name.s[i->name.len] = ch;
+		i = i->next;
+	}
+
+	regfree(&(header_comp));
+	regfree(&(content_comp));
+	return FALSE;
+}
+
+static str sdp = { "application/sdp", 15 };
+
+/**
+ *	Check if a Service Point Trigger for Session Description matches the SDP body
+ *	@param spt - the service point trigger
+ *	@param msg - the message
+ *	@returns - 1 on success, 0 on failure
+ */
+static int isc_check_session_desc(ims_spt *spt, struct sip_msg *msg) {
+	int len;
+	char *body, c;
+	char *x;
+	regex_t comp;
+
+	if (msg->content_type == NULL)
+		return FALSE;
+	if (strncasecmp(msg->content_type->body.s, sdp.s,
+			msg->content_type->body.len) != 0)
+		return FALSE;
+	LM_DBG("ifc_check_session_desc:      Found Content-Type == appliction/sdp\n");
+	//check for sdp line
+	body = get_body(msg);
+	if (body == 0)
+		return FALSE;
+	if (msg->content_length->parsed == NULL) {
+		parse_content_length(msg->content_length->body.s,
+				msg->content_length->body.s + msg->content_length->body.len,
+				&len);
+		msg->content_length->parsed = (void*) (long) len;
+	} else
+		len = (long) msg->content_length->parsed;
+
+	c = body[len];
+	body[len] = 0;
+	x =	pkg_malloc(spt->session_desc.line.len + 2 + spt->session_desc.content.len);
+	sprintf(x, "%.*s=%.*s", spt->session_desc.line.len,
+			spt->session_desc.line.s, spt->session_desc.content.len,
+			spt->session_desc.content.s);
+	/* compile the whole  regexp */
+	regcomp(&(comp), x, REG_ICASE | REG_EXTENDED);
+	if (regexec(&(comp), body, 0, NULL, 0) == 0) //regex match
+	{
+		body[len] = c;
+		LM_DBG("ifc_check_session_desc:      Found Session Desc. > %s\n", body);
+		pkg_free(x);
+		return TRUE;
+	}
+	body[len] = c;
+	pkg_free(x);
+	return FALSE;
+}
+
+/**
+ *	Check if a Service Point Trigger matches a message 
+ *	@param spt - the service point trigger
+ *	@param msg - the message
+ *	@param direction - if filter criteria is for originating/terminating/terminating_unregistered
+ *	@param registration_type - if the message is initial/re/de registration
+ *	@returns - 1 on success, 0 on failure
+ */
+static int isc_check_spt(ims_spt *spt, struct sip_msg *msg, char direction,
+		char registration_type) {
+	int r = FALSE;
+	switch (spt->type) {
+	case IFC_REQUEST_URI:
+		LM_DBG("ifc_check_spt:             SPT type %d -> RequestURI == %.*s ?\n",
+				spt->type, spt->request_uri.len, spt->request_uri.s);
+		LM_DBG("ifc_check_spt:               Found Request URI %.*s \n",
+				msg->first_line.u.request.uri.len, msg->first_line.u.request.uri.s);
+		r = (strncasecmp(spt->request_uri.s, msg->first_line.u.request.uri.s,
+				spt->request_uri.len) == 0);
+		break;
+	case IFC_METHOD:
+		LM_DBG("ifc_check_spt:             SPT type %d -> Method == %.*s ?\n",
+				spt->type, spt->method.len, spt->method.s);
+		LM_DBG("ifc_check_spt:               Found method %.*s \n",
+				msg->first_line.u.request.method.len, msg->first_line.u.request.method.s);
+		r = (strncasecmp(spt->method.s, msg->first_line.u.request.method.s,
+				spt->method.len) == 0);
+		if (r && spt->method.len == 8
+				&& strncasecmp(spt->method.s, "REGISTER", 8) == 0
+				&& !(spt->registration_type == 0
+						|| (registration_type & spt->registration_type)))
+			r = 0;
+		break;
+	case IFC_SIP_HEADER:
+		LM_DBG("ifc_check_spt:             SPT type %d -> Header[%.*s]  %%= %.*s ?\n",
+				spt->type, spt->sip_header.header.len, spt->sip_header.header.s, spt->sip_header.content.len, spt->sip_header.content.s);
+		if (parse_headers(msg, HDR_EOH_F, 0) != 0) {
+			LM_ERR("ifc_checker: can't parse all headers\n");
+			r = FALSE;
+		} else
+			r = isc_check_headers(spt, msg->headers);
+		break;
+	case IFC_SESSION_CASE:
+		LM_DBG("ifc_check_spt:             SPT type %d -> Session Case  == %d ?\n",
+				spt->type, spt->session_case);
+		LM_DBG("ifc_check_spt:               Found session_case %d \n",
+				direction);
+		r = (direction == spt->session_case);
+		break;
+	case IFC_SESSION_DESC:
+		LM_DBG("ifc_check_spt:             SPT type %d -> Session Desc.[%.*s]  %%= %.*s ?\n",
+				spt->type, spt->session_desc.line.len, spt->session_desc.line.s, spt->session_desc.content.len, spt->session_desc.content.s);
+
+		if (parse_headers(msg, HDR_CONTENTTYPE_F | HDR_CONTENTLENGTH_F, 0) != 0) {
+			LM_ERR("ifc_checker: can't parse all headers \n");
+			r = FALSE;
+		}
+		r = isc_check_session_desc(spt, msg);
+		break;
+	default:
+		LM_ERR("ifc_checker: unknown spt type %d \n", spt->type);
+		return FALSE;
+	}
+	if (spt->condition_negated)
+		return !r;
+	else
+		return r;
+}
+
+/**
+ *	Check if an entire filter criteria matches a message 
+ *	@param fc - the filter criteria
+ *	@param msg - the message
+ *	@param direction - if filter criteria is for originating/terminating/terminating_unregistered
+ *	@param registration_type - if the message is initial/re/de registration
+ *	@returns - 1 on success, 0 on failure
+ */
+static int isc_check_filter_criteria(ims_filter_criteria *fc,
+		struct sip_msg *msg, char direction, char registration_type) {
+
+	int i, partial, total, inside, outside, group;
+	ims_trigger_point *t;
+	t = fc->trigger_point;
+
+	/* If the trigger is missing -> always fwd */
+	if (t == NULL)
+		return TRUE;
+	/* This shouldn't happen */
+	if (msg == NULL)
+		return FALSE;
+
+	if (t->condition_type_cnf == IFC_CNF) { //CNF
+		inside = TRUE;
+		outside = FALSE;
+		partial = FALSE;
+		total = TRUE;
+	} else { //DNF
+		inside = FALSE;
+		outside = TRUE;
+		partial = TRUE;
+		total = FALSE;
+	}
+	LM_DBG("ifc_checker_trigger: Starting expression check: \n");
+	group = t->spt[0].group;
+	for (i = 0; i < t->spt_cnt; i++) {
+		if (group != t->spt[i].group) { //jump to other group
+			total = t->condition_type_cnf == IFC_CNF ?
+					total && partial : total || partial;
+			if (total == outside) {
+				LM_DBG("ifc_checker_trigger: Total compromised, aborting...\n");
+				return outside; // will never match from now on, so get out
+			}
+
+			group = t->spt[i].group;
+			partial = isc_check_spt(t->spt + i, msg, direction,
+					registration_type);
+			LM_DBG("ifc_checker_trigger:  - group %d => %d. \n", group, partial);
+		} else { //in same group
+			partial = t->condition_type_cnf == IFC_CNF ? partial || isc_check_spt(t->spt + i, msg, direction, registration_type) : partial
+									&& isc_check_spt(t->spt + i, msg, direction, registration_type);
+		}
+
+		if (partial == inside) { // can't change partial from now, so next group
+			LM_DBG("ifc_checker_trigger:       - group compromised, skipping to next group\n");
+			while (i + 1 < t->spt_cnt && t->spt[i + 1].group == group)
+				i++;
+			continue;
+		}
+	}
+	total = t->condition_type_cnf == IFC_CNF ?
+			total && partial : total || partial;
+	LM_DBG("ifc_checker_trigger: Check finished => %d\n", total);
+	return total;
+}
+
+/**
+ * Create a new matching instance
+ * @param fc - filter criteria that match
+ * @param index - index of the filter that matches
+ * @returns the new isc_match* structure or NULL on error 
+ */
+static inline isc_match* isc_new_match(ims_filter_criteria *fc, int index) {
+	isc_match *r = 0;
+
+	r = pkg_malloc(sizeof (isc_match));
+	if (!r) {
+		LM_ERR("isc_new_match(): error allocating %lx bytes\n", sizeof (isc_match));
+		return 0;
+	}
+	memset(r, 0, sizeof(isc_match));
+	if (fc->application_server.server_name.len) {
+		r->server_name.s = pkg_malloc(fc->application_server.server_name.len);
+		if (!r->server_name.s) {
+			LM_ERR("isc_new_match(): error allocating %d bytes\n",
+					fc->application_server.server_name.len);
+			return 0;
+		}
+		r->server_name.len = fc->application_server.server_name.len;
+		memcpy(r->server_name.s, fc->application_server.server_name.s,
+				fc->application_server.server_name.len);
+	}
+	r->default_handling = fc->application_server.default_handling;
+	if (fc->application_server.service_info.len) {
+		r->service_info.s = pkg_malloc(fc->application_server.service_info.len);
+		if (!r->service_info.s) {
+			LM_ERR("isc_new_match(): error allocating %d bytes\n",
+					fc->application_server.service_info.len);
+			return 0;
+		}
+		r->service_info.len = fc->application_server.service_info.len;
+		memcpy(r->service_info.s, fc->application_server.service_info.s,
+				fc->application_server.service_info.len);
+	}
+	r->index = index;
+	return r;
+}
+
+/**
+ * Find the next match and fill up the ifc_match structure with the position of the match
+ * @param uri - URI of the user for which to apply the IFC
+ * @param direction - direction of the session
+ * @param skip - how many IFCs to skip because already matched
+ * @param msg - the SIP initial request to check on 
+ * @return - TRUE if found, FALSE if none found, end of search space 
+ */
+isc_match* isc_checker_find(str uri, char direction, int skip,
+		struct sip_msg *msg, int registered, udomain_t *d) {
+	int expires;
+	char registration_type;
+	int i, j, k, cnt, si, sj, next;
+
+	impurecord_t *p;
+	int ret;
+
+	ims_service_profile *sp;
+	ims_filter_criteria *fc;
+	isc_match *r;
+
+	if (skip == 0)
+		LM_DBG("isc_checker_find: starting search\n");
+	else
+		LM_DBG("isc_checker_find: resuming search from %d\n", skip);
+
+	expires = cscf_get_expires(msg);
+	if (!registered)
+		registration_type = IFC_INITIAL_REGISTRATION;
+	else if (expires > 0)
+		registration_type = IFC_RE_REGISTRATION;
+	else
+		registration_type = IFC_DE_REGISTRATION;
+
+	isc_ulb.lock_udomain(d, &uri);
+
+	//need to get the urecord
+	if ((ret = isc_ulb.get_impurecord(d, &uri, &p)) != 0) {
+		isc_ulb.unlock_udomain(d, &uri);
+		LM_ERR("Failure getting record");
+		return 0;
+	};
+
+	LM_DBG("isc_checker_find(): got a r_public for the user %.*s\n",
+			uri.len, uri.s);
+	if (!p->s) {
+		LM_DBG("isc_checker_find() : got an user without a subscription\n");
+		//need to free the record somewhere
+		//isc_ulb.release_impurecord(p);
+		//need to do an unlock on the domain somewhere
+		isc_ulb.unlock_udomain(d, &uri);
+		return 0;
+	}
+
+	/* find the starting fc as the skip-th one*/
+	cnt = 0;
+	si = 0;
+	sj = 0;
+
+	LM_DBG("About to try p->s->service_profiles_cnt!! #profiles is %d\n",
+			p->s->service_profiles_cnt);
+	while (si < p->s->service_profiles_cnt) {
+		LM_DBG("About to try p->s->service_profiles[a].filter_criterai_cnt\n");
+		next = cnt + p->s->service_profiles[si].filter_criteria_cnt;
+		if (cnt <= skip && skip < next) {
+			sj = skip - cnt;
+			cnt += sj;
+			break;
+		}
+		cnt = next;
+		si++;
+	}
+
+	LM_DBG("DEBUG ISC: SECOND TIME About to try p->s->service_profiles_cnt!!\n");
+	/* iterate through the rest and check for matches */
+	i = si;
+	while (i < p->s->service_profiles_cnt) {
+		LM_DBG("DEBUG ISC : About to try p->s->service_profiles\n");
+		sp = p->s->service_profiles + i;
+		k = 0;
+		LM_DBG("DEBUG ISC : About to try public identities\n");
+		for (j = 0; j < sp->public_identities_cnt; j++) {
+
+			LM_DBG("DEBUG ISC : About to try WPSI\n");
+			if (p->s->wpsi) {
+				// here i should regexec again!
+				// to check this , but anyway if i already got p
+				// from the get_r_public , that is already checked...
+				// or not if there is no wildcardPSI but ... then ...
+				//isc_check_wpsi_match();
+				k = 1;
+				break;
+
+			} else {
+				if (sp->public_identities[j].public_identity.len == uri.len
+						&& strncasecmp(
+								sp->public_identities[j].public_identity.s,
+								uri.s, uri.len) == 0) {
+					k = 1;
+					break;
+				}
+			}
+		}
+
+		if (!k) {/* this sp is not for this id */
+
+			cnt += sp->filter_criteria_cnt;
+		} else {
+
+			for (j = sj; j < sp->filter_criteria_cnt; j++) {
+				fc = sp->filter_criteria + j;
+				if (fc->profile_part_indicator) {
+					if (((registered == IMS_USER_REGISTERED)
+							&& (*fc->profile_part_indicator))
+							|| ((registered == IMS_USER_UNREGISTERED)
+									&& !(*fc->profile_part_indicator))) {
+						LM_DBG("isc_checker_find: this one is not good... ppindicator wrong \n");
+						cnt++;
+						continue;
+					}
+				}
+
+				if (isc_check_filter_criteria(fc, msg, direction, registration_type)) {
+					LM_DBG("isc_checker_find: MATCH -> %.*s (%.*s) handling %d \n",
+							fc->application_server.server_name.len, fc->application_server.server_name.s, fc->application_server.service_info.len, fc->application_server.service_info.s, fc->application_server.default_handling);
+					r = isc_new_match(fc, cnt);
+
+					//need to free the record somewhere
+					//isc_ulb.release_urecord(p);
+					//need to do an unlock on the domain somewhere
+					isc_ulb.unlock_udomain(d, &uri);
+
+					return r;
+				} else {
+					cnt++;
+					continue;
+				}
+			}
+		}
+		i++;
+		sj = 0;
+	}
+	//need to free the record somewhere
+//	isc_ulb.release_urecord(p);
+	//need to do an unlock on the domain somewhere
+	isc_ulb.unlock_udomain(d, &uri);
+
+	return 0;
+}
+
+/**
+ *	Free up all memory taken by a isc_match.
+ * @param m - match to deallocate
+ */
+void isc_free_match(isc_match *m) {
+	if (m) {
+		if (m->server_name.s)
+			pkg_free(m->server_name.s);
+		if (m->service_info.s)
+			pkg_free(m->service_info.s);
+		pkg_free(m);
+	}
+	LM_DBG("isc_match_free: match position freed\n");
+}
+
+/**
+ *	Find if user is registered or not => TRUE/FALSE.
+ * This uses the S-CSCF registrar to get the state.
+ * @param uri - uri of the user to check
+ * @returns the reg_state
+ */
+int isc_is_registered(str *uri, udomain_t *d) {
+	int result = 0;
+
+	int ret = 0;
+	impurecord_t *p;
+
+	LM_DBG("locking domain\n");
+	isc_ulb.lock_udomain(d, uri);
+
+	LM_DBG("Searching in usrloc\n");
+	//need to get the urecord
+	if ((ret = isc_ulb.get_impurecord(d, uri, &p)) != 0) {
+		LM_DBG("no record exists for [%.*s]\n", uri->len, uri->s);
+		isc_ulb.unlock_udomain(d, uri);
+		return result;
+	}
+
+	LM_DBG("Finished searching usrloc\n");
+	if (p) {
+		result = p->reg_state;
+		//need to free the record somewhere
+//		isc_ulb.release_urecord(p);
+		//need to do an unlock on the domain somewhere
+		isc_ulb.unlock_udomain(d, uri);
+
+	}
+
+	isc_ulb.unlock_udomain(d, uri);
+	return result;
+}
+

+ 86 - 0
modules/isc/checker.h

@@ -0,0 +1,86 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ * 
+ */
+
+#ifndef _ISC_CHECKER_H
+#define _ISC_CHECKER_H
+
+#include <regex.h>
+#include "../../sr_module.h"
+#include "../../lib/ims/ims_getters.h"
+#include "../../data_lump.h"
+#include "../../data_lump_rpl.h"
+#include "../../parser/parse_content.h"
+#include "../../str.h"
+#include "../../mem/mem.h"
+#include "mod.h"
+
+extern usrloc_api_t isc_ulb;/*!< Structure containing pointers to usrloc functions*/
+
+#define TRUE 1
+#define FALSE 0
+
+/** ISC match structure */
+typedef struct {
+	str server_name;		/**< SIP URI of the AS to forward to */
+	char default_handling;	/**< handling to apply on failure to contact the AS */
+	str service_info;		/**< additional service information */
+	int index;				/**< index of the matching IFC */
+} isc_match;
+
+
+/**
+ * Find the next match and fill up the ifc_match structure with the position of the match
+ * @param uri - URI of the user for which to apply the IFC
+ * @param direction - direction of the session
+ * @param skip - how many IFCs to skip because already matched
+ * @param msg - the SIP initial request to check on 
+ * @return - TRUE if found, FALSE if none found, end of search space 
+ */
+isc_match* isc_checker_find(str uri,char direction,int skip,struct sip_msg *msg,int registered, udomain_t *d);
+
+void isc_free_match(isc_match *m);
+int isc_is_registered(str *uri, udomain_t *d);
+
+#endif

+ 115 - 0
modules/isc/isc.c

@@ -0,0 +1,115 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ * 
+ */
+
+
+#include "isc.h"
+
+/**
+ *	Forwards the message to the application server.
+ * - Marks the message
+ * - fills routes
+ * - replaces dst_uri
+ * @param msg - the SIP message
+ * @param m  - the isc_match that matched with info about where to forward it 
+ * @param mark  - the isc_mark that should be used to mark the message
+ * @returns #ISC_RETURN_TRUE if OK, #ISC_RETURN_ERROR if not
+ */
+int isc_forward(struct sip_msg *msg, isc_match *m, isc_mark *mark) {
+	struct cell *t;
+	unsigned int hash, label;
+	ticks_t fr_timeout, fr_inv_timeout;
+	LM_DBG("marking for AS <%.*s>\n", m->server_name.len, m->server_name.s);
+
+	isc_mark_set(msg, m, mark);
+	/* change destination so it forwards to the app server */
+	if (msg->dst_uri.s)
+		pkg_free(msg->dst_uri.s);
+	msg->dst_uri.s = pkg_malloc(m->server_name.len);
+	if (!msg->dst_uri.s) {
+		LM_ERR("error allocating %d bytes\n", m->server_name.len);
+		return ISC_RETURN_ERROR;
+	}
+	msg->dst_uri.len = m->server_name.len;
+	memcpy(msg->dst_uri.s, m->server_name.s, m->server_name.len);
+
+	/* append branch if last trigger failed */
+	if (is_route_type(FAILURE_ROUTE))
+		append_branch(msg, &(msg->first_line.u.request.uri), &(msg->dst_uri), 0, Q_UNSPECIFIED, 0, 0, 0, 0);
+
+	// Determines the tm transaction identifiers.
+	// If no transaction, then creates one
+
+	if (isc_tmb.t_get_trans_ident(msg, &hash, &label) < 0) {
+		LM_DBG("SIP message without transaction. OK - first request\n");
+		if (isc_tmb.t_newtran(msg) < 0)
+			LM_INFO("Failed creating SIP transaction\n");
+		if (isc_tmb.t_get_trans_ident(msg, &hash, &label) < 0) {
+			LM_INFO("SIP message still without transaction\n");
+		} else {
+			LM_DBG("New SIP message transaction %u %u\n", hash, label);
+		}
+	} else {
+		LM_INFO("Transaction %u %u exists. Retransmission?\n", hash, label);
+	}
+
+	/* set the timeout timers to a lower value */
+
+	t = isc_tmb.t_gett();
+	fr_timeout = t->fr_timeout;
+	fr_inv_timeout = t->fr_inv_timeout;
+	t->fr_timeout = S_TO_TICKS(isc_fr_timeout) / 1000;
+	t->fr_inv_timeout = S_TO_TICKS(isc_fr_inv_timeout) / 1000;
+
+	/* send it */
+	isc_tmb.t_relay(msg, 0, 0);
+
+	/* recover the timeouts */
+	t->fr_timeout = fr_timeout;
+	t->fr_inv_timeout = fr_inv_timeout;
+
+	LM_INFO(">>       msg was fwded to AS\n");
+
+	return ISC_RETURN_TRUE;
+}

+ 72 - 0
modules/isc/isc.h

@@ -0,0 +1,72 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ * 
+ */
+
+#ifndef _ISC_ISC_H
+#define _ISC_ISC_H
+
+#include "../../parser/msg_parser.h"
+#include "../../lib/ims/ims_getters.h"
+#include "../../dset.h"
+#include "mod.h"
+#include "checker.h"
+#include "mark.h"
+
+#define IFC_AS_UNAVAILABLE_STATUS_CODE 555
+
+extern struct tm_binds isc_tmb;		/**< Structure with pointers to tm funcs 		*/
+
+extern str isc_my_uri;				/**< Uri of myself to loop the message in str	*/
+extern str isc_my_uri_sip;			/**< Uri of myself to loop the message in str with leading "sip:" */
+
+extern int isc_fr_timeout;			/**< default ISC response timeout in ms */
+extern int isc_fr_inv_timeout;		/**< default ISC INVITE response timeout in ms */
+
+/**	SIP Status Code to send to client on Session Termination because AS did not respond */
+
+
+int isc_forward( struct sip_msg *msg, isc_match *m,isc_mark *mark);
+
+
+#endif

+ 293 - 0
modules/isc/mark.c

@@ -0,0 +1,293 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ * 
+ */
+
+#include "mark.h"
+
+/** base16 char constants */
+char *hexchars = "0123456789abcdef";
+/**
+ * Converts a binary encoded value to its base16 representation.
+ * @param from - buffer containing the input data
+ * @param len - the size of from
+ * @param to - the output buffer  !!! must have at least len*2 allocated memory 
+ * @returns the written length 
+ */
+int bin_to_base16(char *from, int len, char *to) {
+	int i, j;
+	for (i = 0, j = 0; i < len; i++, j += 2) {
+		to[j] = hexchars[(((unsigned char) from[i]) >> 4) & 0x0F];
+		to[j + 1] = hexchars[(((unsigned char) from[i])) & 0x0F];
+	}
+	return 2 * len;
+}
+
+/** char to hex convertor */
+#define HEX_VAL(c)	( (c<='9')?(c-'0'):(c-'A'+10) )
+
+/**
+ *	Retrieves the mark from message.
+ *		- the marking should be in a header like described before
+ *	@param msg - SIP mesage to mark
+ *  @param mark - mark to load into
+ *	@returns 1 if found, 0 if not
+ */
+int isc_mark_get_from_msg(struct sip_msg *msg, isc_mark *mark) {
+	struct hdr_field *hdr;
+	rr_t *rr;
+	str x;
+	LM_DBG("isc_mark_get_from_msg: Trying to get the mark from the message \n");
+
+	memset(mark, 0, sizeof(isc_mark));
+
+	parse_headers(msg, HDR_EOH_F, 0);
+	hdr = msg->headers;
+	while (hdr) {
+		if (hdr->type == HDR_ROUTE_T) {
+			if (!hdr->parsed) {
+				if (parse_rr(hdr) < 0) {
+					LM_ERR("isc_mark_get_from_msg: Error while parsing Route HF\n");
+					hdr = hdr->next;
+					continue;
+				}
+			}
+			rr = (rr_t*) hdr->parsed;
+			while (rr) {
+				x = rr->nameaddr.uri;
+				if (x.len >= ISC_MARK_USERNAME_LEN + 1 + isc_my_uri.len
+						&& strncasecmp(x.s, ISC_MARK_USERNAME,
+								ISC_MARK_USERNAME_LEN) == 0
+						&& strncasecmp(x.s + ISC_MARK_USERNAME_LEN + 1,
+								isc_my_uri.s, isc_my_uri.len) == 0) {
+					LM_DBG("isc_mark_get_from_msg: Found <%.*s>\n",	x.len, x.s);
+					isc_mark_get(x, mark);
+					return 1;
+				}
+				rr = rr->next;
+			}
+		}
+		hdr = hdr->next;
+	}
+	return 0;
+}
+
+/**
+ * Load the mark from a string.
+ * @param x - string with the mark, as found in the Route header
+ * @param mark - mark to load into
+ */
+void isc_mark_get(str x, isc_mark *mark) {
+	int i, j, k;
+	str aor_hex = { 0, 0 };
+	if (mark->aor.s)
+		pkg_free(mark->aor.s);
+	mark->aor = aor_hex;
+	for (i = 0; i < x.len && x.s[i] != ';'; i++)
+		;
+	while (i < x.len) {
+		if (x.s[i + 1] == '=') {
+			k = 0;
+			for (j = i + 2; j < x.len && x.s[j] != ';'; j++)
+				k = k * 10 + (x.s[j] - '0');
+			switch (x.s[i]) {
+			case 's':
+				mark->skip = k;
+				break;
+			case 'h':
+				mark->handling = k;
+				break;
+			case 'd':
+				mark->direction = k;
+				break;
+			case 'a':
+				aor_hex.s = x.s + i + 2;
+				aor_hex.len = 0;
+				for (j = i + 2; j < x.len && x.s[j] != ';'; j++)
+					aor_hex.len++;
+				mark->aor.len = aor_hex.len / 2;
+				mark->aor.s = pkg_malloc(mark->aor.len);
+				if (!mark->aor.s) {
+					LM_ERR("isc_mark_get: Error allocating %d bytes\n",	mark->aor.len);
+					mark->aor.len = 0;
+				} else {
+					mark->aor.len = base16_to_bin(aor_hex.s, aor_hex.len, mark->aor.s);
+				}
+				break;
+			default:
+				LM_ERR("isc_mark_get: unkown parameter found: %c !\n", x.s[i]);
+			}
+			i = j + 1;
+		} else
+			i++;
+	}
+}
+
+/** from base16 char to int */
+#define HEX_DIGIT(x) \
+	((x>='0'&&x<='9')?x-'0':((x>='a'&&x<='f')?x-'a'+10:((x>='A'&&x<='F')?x-'A'+10:0)))
+/**
+ * Converts a hex encoded value to its binary value
+ * @param from - buffer containing the input data
+ * @param len - the size of from
+ * @param to - the output buffer  !!! must have at least len/2 allocated memory 
+ * @returns the written length 
+ */
+int base16_to_bin(char *from, int len, char *to) {
+	int i, j;
+	for (i = 0, j = 0; j < len; i++, j += 2) {
+		to[i] = (unsigned char) (HEX_DIGIT(from[j]) << 4 | HEX_DIGIT(from[j+1]));
+	}
+	return i;
+}
+
+/**
+ *	Deletes the previous marking attempts (lumps).
+ *
+ *	@param msg - SIP mesage to mark
+ *	@returns 1 on success
+ */
+inline int isc_mark_drop_route(struct sip_msg *msg) {
+	struct lump* lmp, *tmp;
+
+	parse_headers(msg, HDR_EOH_F, 0);
+
+	anchor_lump(msg, msg->headers->name.s - msg->buf, 0, 0);
+
+	LM_DBG("ifc_mark_drop_route: Start --------- \n");
+	lmp = msg->add_rm;
+	while (lmp) {
+		tmp = lmp->before;
+		if (tmp && tmp->op == LUMP_ADD && tmp->u.value
+				&& strstr(tmp->u.value, ISC_MARK_USERNAME)) {
+			LM_DBG("ifc_mark_drop_route: Found lump %s ... dropping\n", tmp->u.value);
+			//tmp->op=LUMP_NOP;			
+			tmp->len = 0;
+			/*lmp->before = tmp->before;
+			 free_lump(tmp);	*/
+		}
+		lmp = lmp->next;
+	}
+	LM_DBG("ifc_mark_drop_route: ---------- End \n");
+
+	return 1;
+}
+
+/**
+ *	Mark the message with the given mark.
+ *		- old marking attempts are deleted
+ *		- marking is performed by inserting the following header
+ *	@param msg - SIP mesage to mark
+ *	@param match - the current IFC match
+ *	@param mark - pointer to the mark
+ *	@returns 1 on success or 0 on failure
+ */
+int isc_mark_set(struct sip_msg *msg, isc_match *match, isc_mark *mark) {
+	str route = { 0, 0 };
+	str as = { 0, 0 };
+	char chr_mark[256];
+	char aor_hex[256];
+	int len;
+
+	/* Drop all the old Header Lump "Route: <as>, <my>" */
+	isc_mark_drop_route(msg);
+
+	len = bin_to_base16(mark->aor.s, mark->aor.len, aor_hex);
+	/* Create the Marking */
+	sprintf(chr_mark, "%s@%.*s;lr;s=%d;h=%d;d=%d;a=%.*s", ISC_MARK_USERNAME,
+			isc_my_uri.len, isc_my_uri.s, mark->skip, mark->handling,
+			mark->direction, len, aor_hex);
+	/* Add it in a lump */
+	route.s = chr_mark;
+	route.len = strlen(chr_mark);
+	if (match)
+		as = match->server_name;
+	isc_mark_write_route(msg, &as, &route);
+	LM_DBG("isc_mark_set: NEW mark <%s>\n", chr_mark);
+
+	return 1;
+}
+
+/**
+ *	Inserts the Route header for marking, before first header.
+ * - the marking will be in a header like below
+ * - if the "as" parameter is empty: \code Route: <[iscmark]> \endcode
+ * - else: \code Route: <sip:[email protected];lr>, <[iscmark]> \endcode
+ * 			 
+ *
+ *	@param msg - SIP mesage to mark
+ *	@param as - SIP addres of the application server to forward to
+ *	@param iscmark - the mark to write
+ *	@returns 1 on success, else 0
+ */
+inline int isc_mark_write_route(struct sip_msg *msg, str *as, str *iscmark) {
+	struct hdr_field *first;
+	struct lump* anchor;
+	str route;
+
+	parse_headers(msg, HDR_EOH_F, 0);
+	first = msg->headers;
+	if (as && as->len) {
+		route.s = pkg_malloc(21+as->len+iscmark->len);
+		sprintf(route.s, "Route: <%.*s;lr>, <%.*s>\r\n", as->len, as->s,
+				iscmark->len, iscmark->s);
+	} else {
+		route.s = pkg_malloc(18+iscmark->len);
+		sprintf(route.s, "Route: <%.*s>\r\n", iscmark->len, iscmark->s);
+	}
+
+	route.len = strlen(route.s);
+	LM_DBG("isc_mark_write_route: <%.*s>\n", route.len, route.s);
+
+	anchor = anchor_lump(msg, first->name.s - msg->buf, 0, HDR_ROUTE_T);
+	if (anchor == NULL) {
+		LM_ERR("isc_mark_write_route: anchor_lump failed\n");
+		return 0;
+	}
+
+	if (!insert_new_lump_before(anchor, route.s, route.len, HDR_ROUTE_T)) {
+		LM_ERR("isc_mark_write_route: error creating lump for header_mark\n");
+	}
+	return 1;
+}
+

+ 81 - 0
modules/isc/mark.h

@@ -0,0 +1,81 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ * 
+ */
+
+#ifndef _ISC_MARK_H
+#define _ISC_MARK_H
+
+#include "mod.h"
+
+#include "checker.h"
+//#include "third_party_reg.h"
+
+
+#include "../../lib/ims/ims_getters.h"
+
+/** username to be used in the Route */ 
+#define ISC_MARK_USERNAME "sip:iscmark"
+/** length of #ISC_MARK_USERNAME */
+#define ISC_MARK_USERNAME_LEN 11
+
+extern str isc_my_uri;				/**< Uri of myself to loop the message in str	*/
+
+/** ISC marking structure */
+typedef struct _isc_mark{
+	int skip;		/**< how many IFCs to skip */
+	char handling;	/**< handling to apply on failure to contact the AS */
+	char direction;	/**< session case: orig,term,term unreg */
+	str aor;		/**< the save user aor - terminating or originating */
+} isc_mark;
+
+
+int isc_mark_get_from_msg(struct sip_msg *msg,isc_mark *mark);
+void isc_mark_get(str x,isc_mark *mark);
+int base16_to_bin(char *from,int len, char *to);
+inline int isc_mark_drop_route(struct sip_msg *msg);
+int isc_mark_set(struct sip_msg *msg, isc_match *match, isc_mark *mark);
+inline int isc_mark_write_route(struct sip_msg *msg,str *as,str *iscmark);
+int bin_to_base16(char *from,int len, char *to);
+
+#endif

+ 521 - 0
modules/isc/mod.c

@@ -0,0 +1,521 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ * 
+ */
+
+#include "mod.h"
+
+#include "checker.h"
+#include "mark.h"
+#include "third_party_reg.h"
+#include "isc.h"
+
+MODULE_VERSION
+
+struct tm_binds isc_tmb;
+usrloc_api_t isc_ulb; /*!< Structure containing pointers to usrloc functions*/
+
+/* fixed parameter storage */
+char *isc_my_uri_c = "scscf.ims.smilecoms.com:6060"; /**< Uri of myself to loop the message*/
+str isc_my_uri = {0, 0}; /**< Uri of myself to loop the message in str	*/
+str isc_my_uri_sip = {0, 0}; /**< Uri of myself to loop the message in str with leading "sip:" */
+int isc_expires_grace = 120; /**< expires value to add to the expires in the 3rd party register*/
+int isc_fr_timeout = 5000; /**< default ISC response timeout in ms */
+int isc_fr_inv_timeout = 20000; /**< default ISC invite response timeout in ms */
+
+/** module functions */
+static int mod_init(void);
+int isc_match_filter(struct sip_msg *msg, char *str1, udomain_t* d);
+int isc_match_filter_reg(struct sip_msg *msg, char *str1, udomain_t* d);
+int isc_from_as(struct sip_msg *msg, char *str1, char *str2);
+
+/*! \brief Fixup functions */
+static int domain_fixup(void** param, int param_no);
+static int w_isc_match_filter_reg(struct sip_msg* _m, char* str1, char* str2);
+static int w_isc_match_filter(struct sip_msg* _m, char* str1, char* str2);
+
+static cmd_export_t cmds[] = {
+    { "isc_match_filter_reg", (cmd_function) w_isc_match_filter_reg, 2, domain_fixup, 0, REQUEST_ROUTE},
+    { "isc_from_as", (cmd_function) isc_from_as, 1, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE},
+    { "isc_match_filter", (cmd_function) w_isc_match_filter, 2, domain_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE},
+    { 0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[] = {
+    { "my_uri", STR_PARAM, &isc_my_uri_c}, /**< SIP Uri of myself for getting the messages back */
+    { "expires_grace", INT_PARAM, &isc_expires_grace}, /**< expires value to add to the expires in the 3rd party register to prevent expiration in AS */
+    { "isc_fr_timeout", INT_PARAM, &isc_fr_timeout}, /**< Time in ms that we are waiting for a AS response until we
+ 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 consider it dead. Has to be lower than SIP transaction timeout
+ 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 to prevent downstream timeouts. Not too small though because
+ 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 AS are usually slow as hell... */
+    { "isc_fr_inv_timeout", INT_PARAM, &isc_fr_inv_timeout}, /**< Time in ms that we are waiting for a AS INVITE response until we
+ 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 consider it dead. Has to be lower than SIP transaction timeout
+ 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 to prevent downstream timeouts. Not too small though because
+ 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 AS are usually slow as hell... */
+    { 0, 0, 0}
+};
+
+/** module exports */
+struct module_exports exports = {"isc", DEFAULT_DLFLAGS, /* dlopen flags */
+    cmds, /* Exported functions */
+    params, 0, /* exported statistics */
+    0, /* exported MI functions */
+    0, /* exported pseudo-variables */
+    0, /* extra processes */
+    mod_init, /* module initialization function */
+    0, 0, 0 /* per-child init function */};
+
+/*! \brief
+ * Convert char* parameter to udomain_t* pointer
+ */
+static int domain_fixup(void** param, int param_no) {
+    udomain_t* d;
+
+    if (param_no == 2) {
+        if (isc_ulb.register_udomain((char*) *param, &d) < 0) {
+            LM_ERR("failed to register domain\n");
+            return E_UNSPEC;
+        }
+        *param = (void*) d;
+    }
+    return 0;
+}
+
+/*! \brief
+ * Wrapper to do isc match filter refg
+ */
+static int w_isc_match_filter_reg(struct sip_msg* _m, char* str1, char* str2) {
+    return isc_match_filter_reg(_m, str1, (udomain_t*) str2);
+}
+
+/*! \brief
+ * Wrapper to do isc match filter
+ */
+static int w_isc_match_filter(struct sip_msg* _m, char* str1, char* str2) {
+    return isc_match_filter(_m, str1, (udomain_t*) str2);
+}
+
+static int fix_parameters() {
+    return 1;
+}
+
+/**
+ * init module function
+ */
+static int mod_init(void) {
+
+    bind_usrloc_t bind_usrloc;
+    /* fix the parameters */
+    if (!fix_parameters())
+        goto error;
+
+    /* load the TM API */
+    if (load_tm_api(&isc_tmb) != 0) {
+        LM_ERR("can't load TM API\n");
+        goto error;
+    }
+
+    bind_usrloc = (bind_usrloc_t) find_export("ul_bind_usrloc", 1, 0);
+    if (!bind_usrloc) {
+        LM_ERR("can't bind usrloc\n");
+        return -1;
+    }
+
+    if (bind_usrloc(&isc_ulb) < 0) {
+        return -1;
+    }
+
+    /* Init the isc_my_uri parameter */
+    if (!isc_my_uri_c) {
+        LM_CRIT("mandatory parameter \"isc_my_uri\" found empty\n");
+        goto error;
+    }
+
+    isc_my_uri.s = isc_my_uri_c;
+    isc_my_uri.len = strlen(isc_my_uri_c);
+    isc_my_uri_sip.len = 4 + isc_my_uri.len;
+    isc_my_uri_sip.s = shm_malloc(isc_my_uri_sip.len + 1);
+    memcpy(isc_my_uri_sip.s, "sip:", 4);
+    memcpy(isc_my_uri_sip.s + 4, isc_my_uri.s, isc_my_uri.len);
+    isc_my_uri_sip.s[isc_my_uri_sip.len] = 0;
+
+    LM_DBG("ISC module successfully initialised\n");
+
+    return 0;
+error:
+    LM_ERR("Failed to initialise ISC module\n");
+    return -1;
+}
+
+/**
+ * Returns the direction of the dialog as int dialog_direction from a string.
+ * @param direction - "orig" or "term"
+ * @returns DLG_MOBILE_ORIGINATING, DLG_MOBILE_TERMINATING if successful, or 
+ * DLG_MOBILE_UNKNOWN on error
+ */
+static inline enum dialog_direction get_dialog_direction(char *direction) {
+    switch (direction[0]) {
+        case 'o':
+        case 'O':
+        case '0':
+            return DLG_MOBILE_ORIGINATING;
+        case 't':
+        case 'T':
+        case '1':
+            return DLG_MOBILE_TERMINATING;
+        default:
+            LM_ERR("Unknown direction %s", direction);
+            return DLG_MOBILE_UNKNOWN;
+    }
+}
+
+/**
+ * Checks if there is a match.
+ * Inserts route headers and set the dst_uri
+ * @param msg - the message to check
+ * @param str1 - the direction of the request orig/term
+ * @param str2 - not used
+ * @returns #ISC_RETURN_TRUE if found, #ISC_RETURN_FALSE if not, #ISC_RETURN_BREAK on error
+ */
+int isc_match_filter(struct sip_msg *msg, char *str1, udomain_t* d) {
+    int k = 0;
+    isc_match *m = NULL;
+    str s = {0, 0};
+
+    //sometimes s is populated by an ims_getter method cscf_get_terminating_user that alloc memory that must be free-ed at the end
+    int free_s = 0;
+
+
+    int ret = ISC_RETURN_FALSE;
+    isc_mark new_mark, old_mark;
+
+    enum dialog_direction dir = get_dialog_direction(str1);
+
+    LM_INFO("Checking triggers\n");
+
+    if (dir == DLG_MOBILE_UNKNOWN)
+        return ISC_RETURN_BREAK;
+
+    if (!cscf_is_initial_request(msg))
+        return ISC_RETURN_FALSE;
+
+    /* starting or resuming? */
+    memset(&old_mark, 0, sizeof (isc_mark));
+    memset(&new_mark, 0, sizeof (isc_mark));
+    if (isc_mark_get_from_msg(msg, &old_mark)) {
+        LM_DBG("Message returned s=%d;h=%d;d=%d;a=%.*s\n", old_mark.skip, old_mark.handling, old_mark.direction, old_mark.aor.len, old_mark.aor.s);
+    } else {
+        LM_DBG("Starting triggering\n");
+    }
+
+    if (is_route_type(FAILURE_ROUTE)) {
+        /* need to find the handling for the failed trigger */
+        if (dir == DLG_MOBILE_ORIGINATING) {
+            k = cscf_get_originating_user(msg, &s);
+            if (k) {
+                k = isc_is_registered(&s, d);
+                if (k == IMPU_NOT_REGISTERED) {
+                    ret = ISC_RETURN_FALSE;
+                    goto done;
+                }
+                new_mark.direction = IFC_ORIGINATING_SESSION;
+                LM_DBG("Orig User <%.*s> [%d]\n", s.len, s.s, k);
+            } else
+                goto done;
+        }
+        if (dir == DLG_MOBILE_TERMINATING) {
+            k = cscf_get_terminating_user(msg, &s);
+            //sometimes s is populated by an ims_getter method cscf_get_terminating_user that alloc memory that must be free-ed at the end
+            free_s = 1;
+
+            if (k) {
+                k = isc_is_registered(&s, d);
+                //LOG(L_DBG,"after isc_is_registered in ISC_match_filter\n");
+                if (k == IMPU_REGISTERED) {
+                    new_mark.direction = IFC_TERMINATING_SESSION;
+                } else {
+                    new_mark.direction = IFC_TERMINATING_UNREGISTERED;
+                }
+                LM_DBG("Term User <%.*s> [%d]\n", s.len, s.s, k);
+            } else {
+                goto done;
+            }
+        }
+        struct cell * t = isc_tmb.t_gett();
+        LM_CRIT("SKIP: %d\n", old_mark.skip);
+        int index = old_mark.skip;
+        for (k = 0; k < t->nr_of_outgoings; k++) {
+            m = isc_checker_find(s, new_mark.direction, index, msg,
+                    isc_is_registered(&s, d), d);
+            if (m) {
+                index = m->index;
+                if (k < t->nr_of_outgoings - 1)
+                    isc_free_match(m);
+            } else {
+                LM_ERR("On failure, previously matched trigger no longer matches?!\n");
+                ret = ISC_RETURN_BREAK;
+                goto done;
+            }
+        }
+        if (m->default_handling == IFC_SESSION_TERMINATED) {
+            /* Terminate the session */
+            LM_DBG("Terminating session.\n");
+            isc_tmb.t_reply(msg, IFC_AS_UNAVAILABLE_STATUS_CODE, "AS Contacting Failed - iFC terminated dialog");
+            LM_DBG("Responding with %d to URI: %.*s\n",
+                    IFC_AS_UNAVAILABLE_STATUS_CODE, msg->first_line.u.request.uri.len, msg->first_line.u.request.uri.s);
+            isc_free_match(m);
+            ret = ISC_RETURN_BREAK;
+            goto done;
+        }
+        /* skip the failed triggers (IFC_SESSION_CONTINUED) */
+        old_mark.skip = index + 1;
+
+        isc_free_match(m);
+        isc_mark_drop_route(msg);
+    }
+
+    LM_DBG("Checking if ISC is for originating user\n");
+    /* originating leg */
+    if (dir == DLG_MOBILE_ORIGINATING) {
+        k = cscf_get_originating_user(msg, &s);
+        LM_DBG("ISC is for Orig user\n");
+        if (k) {
+            LM_DBG("Orig user is [%.*s]\n", s.len, s.s);
+            k = isc_is_registered(&s, d);
+            if (k == IMPU_NOT_REGISTERED) {
+                LM_DBG("User is not registered\n");
+                return ISC_RETURN_FALSE;
+            }
+
+            LM_DBG("Orig User <%.*s> [%d]\n", s.len, s.s, k);
+            //CHECK if this is a new call (According to spec if the new uri and old mark URI are different then this is a new call and should
+            //be triggered accordingly
+            LM_DBG("Checking if RURI has changed...comparing: <%.*s> and <%.*s>\n",
+                    old_mark.aor.len, old_mark.aor.s,
+                    s.len, s.s);
+            if ((old_mark.aor.len == s.len) && memcmp(old_mark.aor.s, s.s, s.len) != 0) {
+                LM_DBG("This is a new call....... trigger accordingly\n");
+                m = isc_checker_find(s, old_mark.direction, 0, msg,
+                        isc_is_registered(&s, d), d);
+            } else {
+                m = isc_checker_find(s, old_mark.direction, old_mark.skip, msg,
+                        isc_is_registered(&s, d), d);
+            }
+            if (m) {
+                new_mark.direction = IFC_ORIGINATING_SESSION;
+                new_mark.skip = m->index + 1;
+                new_mark.handling = m->default_handling;
+                new_mark.aor = s;
+                ret = isc_forward(msg, m, &new_mark);
+                isc_free_match(m);
+                goto done;
+            }
+        }
+        goto done;
+    }
+    LM_DBG("Checking if ISC is for terminating user\n");
+    /* terminating leg */
+    if (dir == DLG_MOBILE_TERMINATING) {
+        k = cscf_get_terminating_user(msg, &s);
+        //sometimes s is populated by an ims_getter method cscf_get_terminating_user that alloc memory that must be free-ed at the end
+        free_s = 1;
+        LM_DBG("ISC is for Term user\n");
+        if (k) {
+            k = isc_is_registered(&s, d);
+            if (k == IMPU_REGISTERED) {
+                new_mark.direction = IFC_TERMINATING_SESSION;
+            } else {
+                new_mark.direction = IFC_TERMINATING_UNREGISTERED;
+            }
+            LM_DBG("Term User <%.*s> [%d]\n", s.len, s.s, k);
+            //CHECK if this is a new call (According to spec if the new uri and old mark URI are different then this is a new call and should
+            //be triggered accordingly
+            LM_DBG("Checking if RURI has changed...comparing: <%.*s> and <%.*s>\n",
+                    old_mark.aor.len, old_mark.aor.s,
+                    s.len, s.s);
+            if ((old_mark.aor.len == s.len) && memcmp(old_mark.aor.s, s.s, s.len) != 0) {
+                LM_DBG("This is a new call....... trigger accordingly\n");
+                m = isc_checker_find(s, new_mark.direction, 0, msg, isc_is_registered(&s, d), d);
+            } else {
+                LM_DBG("Resuming triggering\n");
+                m = isc_checker_find(s, new_mark.direction, old_mark.skip, msg, isc_is_registered(&s, d), d);
+            }
+            if (m) {
+                new_mark.skip = m->index + 1;
+                new_mark.handling = m->default_handling;
+                new_mark.aor = s;
+                ret = isc_forward(msg, m, &new_mark);
+                isc_free_match(m);
+                goto done;
+            }
+        }
+        goto done;
+    }
+
+done:
+
+    if (s.s && free_s == 1)
+        shm_free(s.s); // shm_malloc in cscf_get_terminating_user  
+
+    if (old_mark.aor.s)
+        pkg_free(old_mark.aor.s);
+    return ret;
+}
+
+/**
+ * Checks if there is a match on REGISTER.
+ * Inserts route headers and set the dst_uri
+ * @param msg - the message to check
+ * @param str1 - if the user was previously registered 0 - for initial registration, 1 for re/de-registration
+ * @param str2 - not used
+ * @returns #ISC_RETURN_TRUE if found, #ISC_RETURN_FALSE if not
+ */
+int isc_match_filter_reg(struct sip_msg *msg, char *str1, udomain_t* d) {
+    int k;
+    isc_match *m;
+    str s = {0, 0};
+    int ret = ISC_RETURN_FALSE;
+    isc_mark old_mark;
+
+    enum dialog_direction dir = DLG_MOBILE_ORIGINATING;
+
+    LM_DBG("Checking triggers\n");
+
+    /* starting or resuming? */
+    memset(&old_mark, 0, sizeof (isc_mark));
+    LM_DBG("Starting triggering\n");
+
+    /* originating leg */
+    if (dir == DLG_MOBILE_ORIGINATING) {
+        k = cscf_get_originating_user(msg, &s);
+        if (k) {
+            if (str1 == 0 || strlen(str1) != 1) {
+                LM_ERR("wrong parameter - must be \"0\" (initial registration) or \"1\"(previously registered) \n");
+                return ret;
+            } else if (str1[0] == '0')
+                k = 0;
+            else
+                k = 1;
+
+            LM_DBG("Orig User <%.*s> [%d]\n", s.len, s.s, k);
+            m = isc_checker_find(s, old_mark.direction, old_mark.skip, msg, k, d);
+            while (m) {
+                LM_DBG("REGISTER match found in filter criteria\n");
+                ret = isc_third_party_reg(msg, m, &old_mark);
+                old_mark.skip = m->index + 1;
+                isc_free_match(m);
+                m = isc_checker_find(s, old_mark.direction, old_mark.skip, msg, k, d);
+            }
+
+            if (ret == ISC_RETURN_FALSE)
+                LM_DBG("No REGISTER match found in filter criteria\n");
+        }
+    }
+    return ret;
+}
+
+/**
+ * Check if the message is from the AS.
+ * Inserts route headers and set the dst_uri
+ * @param msg - the message to check
+ * @param str1 - the direction of the request orig/term
+ * @param str2 - not used
+ * @returns #ISC_RETURN_TRUE if from AS, #ISC_RETURN_FALSE if not, #ISC_RETURN_BREAK on error
+ */
+int isc_from_as(struct sip_msg *msg, char *str1, char *str2) {
+    int ret = ISC_RETURN_FALSE;
+    isc_mark old_mark;
+    str s = {0, 0};
+    //sometimes s is populated by an ims_getter method cscf_get_terminating_user that alloc memory that must be free-ed at the end
+    int free_s = 0;
+    
+    
+    enum dialog_direction dir = get_dialog_direction(str1);
+
+    if (dir == DLG_MOBILE_UNKNOWN)
+        return ISC_RETURN_BREAK;
+
+    if (!cscf_is_initial_request(msg))
+        return ISC_RETURN_FALSE;
+
+    /* starting or resuming? */
+    if (isc_mark_get_from_msg(msg, &old_mark)) {
+        LM_DBG("Message returned s=%d;h=%d;d=%d\n", old_mark.skip, old_mark.handling, old_mark.direction);
+
+        /*according to spec 24.229, 5.4.3.3 if the URI is different then the RURI is retargeted and we can do one of 2 things,
+         * a) mark as originating session and forward according to normal RURI procedures,
+         * b) run IFC criteria on new retrageturi  and route accordingly.
+         *
+         * We will therefore leave it in the hands of the config writer to decide. We will make them aware here that retargeting has happened
+         * by retirning a special error code ISC_RETURN_RETARGET(-2).
+         */
+        if (dir == DLG_MOBILE_TERMINATING) {
+            cscf_get_terminating_user(msg, &s);
+            //sometimes s is populated by an ims_getter method cscf_get_terminating_user that alloc memory that must be free-ed at the end
+            free_s = 1;
+            if (memcmp(old_mark.aor.s, s.s, s.len) != 0) {
+                LM_DBG("This is a new call....... RURI has been retargeted\n");
+                return ISC_RETURN_RETARGET;
+            }
+        }
+        if (old_mark.direction == IFC_ORIGINATING_SESSION
+                && dir != DLG_MOBILE_ORIGINATING)
+            ret = ISC_RETURN_FALSE;
+        else if ((old_mark.direction == IFC_TERMINATING_SESSION
+                || old_mark.direction == IFC_TERMINATING_UNREGISTERED)
+                && dir != DLG_MOBILE_TERMINATING)
+            ret = ISC_RETURN_FALSE;
+        else
+            ret = ISC_RETURN_TRUE;
+    } else {
+        ret = ISC_RETURN_FALSE;
+    }
+    if (old_mark.aor.s)
+        pkg_free(old_mark.aor.s);
+    
+    if (s.s && free_s == 1)
+        shm_free(s.s); // shm_malloc in cscf_get_terminating_user  
+    
+    return ret;
+}
+

+ 85 - 0
modules/isc/mod.h

@@ -0,0 +1,85 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ * 
+ */
+
+#ifndef ISC_MOD_H
+#define ISC_MOD_H
+
+#include "../../sr_module.h"
+#include "../../modules/tm/tm_load.h"
+#include "../../qvalue.h"
+#include "../usrloc_scscf/usrloc.h"
+
+#define STR_APPEND(dst,src)\
+        {memcpy((dst).s+(dst).len,(src).s,(src).len);\
+        (dst).len = (dst).len + (src).len;}
+
+/** SER routing script return and break execution */
+#define ISC_RETURN_BREAK	 0
+/** SER routing script return true */
+#define ISC_RETURN_TRUE		 1
+/** SER routing script return false */
+#define ISC_RETURN_FALSE	-1
+/** retargeting has happened */
+#define ISC_RETURN_RETARGET	-2
+/** SER routing script return error */
+#define ISC_RETURN_ERROR 	-3
+
+/** Direction of the dialog */
+enum dialog_direction {
+	DLG_MOBILE_ORIGINATING = 0, /** Originating */
+	DLG_MOBILE_TERMINATING = 1, /** Terminating */
+	DLG_MOBILE_UNKNOWN = 2
+/** Unknown 	*/
+};
+
+/* Various constants */
+/** User Not Registered */
+#define IMS_USER_NOT_REGISTERED 0
+/** User registered */
+#define IMS_USER_REGISTERED 1
+/** User unregistered (not registered but with services for unregistered state) */
+#define IMS_USER_UNREGISTERED -1
+
+#endif /* ISC_MOD_H */

+ 253 - 0
modules/isc/third_party_reg.c

@@ -0,0 +1,253 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ * 
+ */
+
+#include "third_party_reg.h"
+
+/**
+ * Handle third party registration
+ * @param msg - the SIP REGISTER message
+ * @param m  - the isc_match that matched with info about where to forward it 
+ * @param mark  - the isc_mark that should be used to mark the message
+ * @returns #ISC_RETURN_TRUE if allowed, #ISC_RETURN_FALSE if not
+ */
+int isc_third_party_reg(struct sip_msg *msg, isc_match *m, isc_mark *mark) {
+	r_third_party_registration r;
+	int expires = 0;
+	str req_uri = { 0, 0 };
+	str to = { 0, 0 };
+	str pvni = { 0, 0 };
+	str pani = { 0, 0 };
+	str cv = { 0, 0 };
+
+	struct hdr_field *hdr;
+
+	LM_DBG("isc_third_party_reg: Enter\n");
+
+	/* Set Request Uri to IFC matching server name */
+	req_uri.len = m->server_name.len;
+	req_uri.s = m->server_name.s;
+
+	/* Get To header*/
+	to = cscf_get_public_identity(msg);
+
+	/*TODO - check if the min/max expires is in the acceptable limits
+	 * this does not work correctly if the user has multiple contacts
+	 * and register/deregisters them individually!!!
+	 */
+	expires = cscf_get_max_expires(msg, 0);
+
+	/* Get P-Visited-Network-Id header */
+	pvni = cscf_get_visited_network_id(msg, &hdr);
+	/* Get P-Access-Network-Info header */
+	pani = cscf_get_access_network_info(msg, &hdr);
+
+	/* Get P-Charging-Vector header */
+	/* Just forward the charging header received from P-CSCF */
+	/* Todo: implement also according to TS 24.229, chap 5.4.1.7 */
+	cv = cscf_get_charging_vector(msg, &hdr);
+
+	if (req_uri.s) {
+
+		memset(&r, 0, sizeof(r_third_party_registration));
+
+		r.req_uri = req_uri;
+		r.to = to;
+		r.from = isc_my_uri_sip;
+		r.pvni = pvni;
+		r.pani = pani;
+		r.cv = cv;
+		r.service_info = m->service_info;
+
+		if (expires <= 0)
+			r_send_third_party_reg(&r, 0);
+		else
+			r_send_third_party_reg(&r, expires + isc_expires_grace);
+		return ISC_RETURN_TRUE;
+	} else {
+		return ISC_RETURN_FALSE;
+	}
+}
+
+static str method = { "REGISTER", 8 };
+static str event_hdr = { "Event: registration\r\n", 21 };
+static str max_fwds_hdr = { "Max-Forwards: 10\r\n", 18 };
+static str expires_s = { "Expires: ", 9 };
+static str expires_e = { "\r\n", 2 };
+static str contact_s = { "Contact: <", 10 };
+static str contact_e = { ">\r\n", 3 };
+
+static str p_visited_network_id_s = { "P-Visited-Network-ID: ", 22 };
+static str p_visited_network_id_e = { "\r\n", 2 };
+
+static str p_access_network_info_s = { "P-Access-Network-Info: ", 23 };
+static str p_access_network_info_e = { "\r\n", 2 };
+
+static str p_charging_vector_s = { "P-Charging-Vector: ", 19 };
+static str p_charging_vector_e = { "\r\n", 2 };
+static str body_s = { "<ims-3gpp version=\"1\"><service-info>", 36 };
+static str body_e = { "</service-info></ims-3gpp>", 26 };
+
+/**
+ * Send a third party registration
+ * @param r - the register to send for
+ * @param expires - expires time
+ * @returns true if OK, false if not
+ */
+
+int r_send_third_party_reg(r_third_party_registration *r, int expires) {
+	str h = { 0, 0 };
+	str b = { 0, 0 };
+	uac_req_t req;
+
+	LM_DBG("r_send_third_party_reg: REGISTER to <%.*s>\n",
+			r->req_uri.len, r->req_uri.s);
+
+	h.len = event_hdr.len + max_fwds_hdr.len;
+	h.len += expires_s.len + 12 + expires_e.len;
+
+	h.len += contact_s.len + isc_my_uri_sip.len + contact_e.len;
+
+	if (r->pvni.len)
+		h.len += p_visited_network_id_s.len + p_visited_network_id_e.len
+				+ r->pvni.len;
+
+	if (r->pani.len)
+		h.len += p_access_network_info_s.len + p_access_network_info_e.len
+				+ r->pani.len;
+
+	if (r->cv.len)
+		h.len += p_charging_vector_s.len + p_charging_vector_e.len + r->cv.len;
+
+	h.s = pkg_malloc(h.len);
+	if (!h.s) {
+		LM_ERR("r_send_third_party_reg: Error allocating %d bytes\n", h.len);
+		h.len = 0;
+		return 0;
+	}
+
+	h.len = 0;
+	STR_APPEND(h, event_hdr);
+
+	STR_APPEND(h, max_fwds_hdr);
+
+	STR_APPEND(h, expires_s);
+	sprintf(h.s + h.len, "%d", expires);
+	h.len += strlen(h.s + h.len);
+	STR_APPEND(h, expires_e);
+
+	STR_APPEND(h, contact_s);
+	STR_APPEND(h, isc_my_uri_sip);
+	STR_APPEND(h, contact_e);
+
+	if (r->pvni.len) {
+		STR_APPEND(h, p_visited_network_id_s);
+		STR_APPEND(h, r->pvni);
+		STR_APPEND(h, p_visited_network_id_e);
+	}
+
+	if (r->pani.len) {
+		STR_APPEND(h, p_access_network_info_s);
+		STR_APPEND(h, r->pani);
+		STR_APPEND(h, p_access_network_info_e);
+	}
+
+	if (r->cv.len) {
+		STR_APPEND(h, p_charging_vector_s);
+		STR_APPEND(h, r->cv);
+		STR_APPEND(h, p_charging_vector_e);
+	}
+	LM_CRIT("SRV INFO:<%.*s>\n", r->service_info.len, r->service_info.s);
+	if (r->service_info.len) {
+		b.len = body_s.len + r->service_info.len + body_e.len;
+		b.s = pkg_malloc(b.len);
+		if (!b.s) {
+			LM_ERR("r_send_third_party_reg: Error allocating %d bytes\n", b.len);
+			b.len = 0;
+			return 0;
+		}
+
+		b.len = 0;
+		STR_APPEND(b, body_s);
+		STR_APPEND(b, r->service_info);
+		STR_APPEND(b, body_e);
+	}
+
+	set_uac_req(&req, &method, &h, &b, 0,
+			TMCB_RESPONSE_IN | TMCB_ON_FAILURE | TMCB_LOCAL_COMPLETED,
+			r_third_party_reg_response, &(r->req_uri));
+	if (isc_tmb.t_request(&req, &(r->req_uri), &(r->to), &(r->from), 0) < 0) {
+		LM_ERR("r_send_third_party_reg: Error sending in transaction\n");
+		goto error;
+	}
+	if (h.s)
+		pkg_free(h.s);
+	return 1;
+
+	error: if (h.s)
+		pkg_free(h.s);
+	return 0;
+}
+
+/**
+ * Response callback for third party register
+ */
+void r_third_party_reg_response(struct cell *t, int type, struct tmcb_params *ps) {
+	LM_DBG("r_third_party_reg_response: code %d\n", ps->code);
+	if (!ps->rpl) {
+		LM_ERR("r_third_party_reg_response: No reply\n");
+		return;
+	}
+
+	if (ps->code >= 200 && ps->code < 300) {
+		if (ps->rpl)
+			cscf_get_expires_hdr(ps->rpl, 0);
+		else
+			return;
+	} else if (ps->code == 404) {
+	} else {
+		LM_DBG("r_third_party_reg_response: code %d\n", ps->code);
+	}
+}
+

+ 84 - 0
modules/isc/third_party_reg.h

@@ -0,0 +1,84 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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
+ * 
+ */
+
+#ifndef _ISC_THIRD_PARTY_REG_H_
+#define _ISC_THIRD_PARTY_REG_H_
+
+#include "mod.h"
+
+#include "checker.h"
+#include "mark.h"
+
+#include "../../sr_module.h"
+#include "../../locking.h"
+#include "../../modules/tm/tm_load.h"
+
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+
+extern str isc_my_uri_sip; 			/**< Uri of myself to loop the message in str with leading "sip:" */
+extern int isc_expires_grace; 		/**< expires value to add to the expires in the 3rd party register
+ 	 	 	 	 	 	 	 	 	 to prevent expiration in AS */
+extern struct tm_binds isc_tmb; 	/**< Structure with pointers to tm funcs 		*/
+
+/** reg event notification structure */
+typedef struct _r_third_party_reg {
+	str req_uri; 		/* AS sip uri:  	*/
+	str from; 			/* SCSCF uri            */
+	str to; 			/* Public user id       */
+	str pvni; 			/* Visited network id 	*/
+	str pani; 			/* Access Network info 	*/
+	str cv; 			/* Charging vector 	*/
+	str service_info; 	/* Service info body */
+} r_third_party_registration;
+
+int isc_third_party_reg(struct sip_msg *msg, isc_match *m, isc_mark *mark);
+
+int r_send_third_party_reg(r_third_party_registration *r, int duration);
+
+void r_third_party_reg_response(struct cell *t, int type,
+		struct tmcb_params *ps);
+
+#endif //_ISC_THIRD_PARTY_REG_H_