Browse Source

ims_diameter_server: Generic Diameter Server module [New]

Carsten Bock 8 years ago
parent
commit
629a3b0151

+ 1 - 1
src/Makefile.groups

@@ -161,7 +161,7 @@ mod_list_mono=app_mono
 # - modules related to IMS extensions
 mod_list_ims=cdp cdp_avp ims_dialog ims_auth ims_isc ims_icscf ims_qos \
 			   ims_registrar_pcscf ims_registrar_scscf ims_usrloc_pcscf \
-			   ims_usrloc_scscf ims_charging ims_ocs
+			   ims_usrloc_scscf ims_charging ims_ocs ims_diameter_server
 
 # - modules depending on osp toolkit library
 mod_list_osp=osp

+ 21 - 0
src/modules/ims_diameter_server/Makefile

@@ -0,0 +1,21 @@
+#
+# ims_qos make file
+#
+# 
+
+include ../../Makefile.defs
+auto_gen=
+NAME=ims_diameter_server.so
+LIBS=-lm
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/ims/kamailio_ims
+
+ifneq ($(OS),darwin)
+	LIBS += -lrt
+	LIBS += -lpthread
+endif
+
+include ../../Makefile.modules

+ 386 - 0
src/modules/ims_diameter_server/avp_helper.c

@@ -0,0 +1,386 @@
+/*
+ *
+ * Copyright (C) 2016-2017 ng-voice GmbH, Carsten Bock, [email protected]
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ */
+
+#include "../cdp/cdp_load.h"
+#include "../cdp_avp/cdp_avp_mod.h"
+#include "avp_helper.h"
+#include "ims_diameter_server.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "cJSON.h"
+
+#include "../../core/dprint.h"
+#include "../../core/mem/mem.h"
+#include "../../core/ut.h" 
+#include "../../core/trim.h" 
+#include "../../core/pvapi.h"
+#include "../../core/dset.h"
+#include "../../core/basex.h"
+
+#define STRSIZE 8*1024
+
+// ID of current message
+static unsigned int current_msg_id = 0;
+static unsigned int current_msg_id_repl = 0;
+
+cJSON * avp2json(AAA_AVP *avp_t) {
+	int l, i;
+	AAA_AVP *avp_it;	
+	char dest[STRSIZE];
+
+	cJSON * avp, * array;
+
+	avp=cJSON_CreateObject();
+	LM_DBG("AVP(%p < %p >%p);code=%u,"
+		"flags=%x;\nDataType=%u;VendorID=%u;DataLen=%u;\n",
+		avp_t->prev,avp_t,avp_t->next,avp_t->code,avp_t->flags,
+		avp_t->type,avp_t->vendorId,avp_t->data.len);
+	cJSON_AddNumberToObject(avp,"avpCode", avp_t->code);
+	cJSON_AddNumberToObject(avp,"vendorId",	avp_t->vendorId);
+	cJSON_AddNumberToObject(avp,"DataType",	avp_t->type);
+	cJSON_AddNumberToObject(avp,"Flags",	avp_t->flags);
+	memset(dest, 0, STRSIZE);
+	switch(avp_t->type) {
+		case AAA_AVP_STRING_TYPE:
+			snprintf(dest, STRSIZE, "%.*s", avp_t->data.len, avp_t->data.s);
+			cJSON_AddStringToObject(avp, "string", dest);
+			break;
+		case AAA_AVP_INTEGER32_TYPE:
+			cJSON_AddNumberToObject(avp,"int32", htonl(*((unsigned int*)avp_t->data.s)));
+			break;
+		case AAA_AVP_INTEGER64_TYPE:
+			cJSON_AddNumberToObject(avp,"int64", htonl(*((unsigned int*)avp_t->data.s)));
+			break;
+		case AAA_AVP_ADDRESS_TYPE:
+			i = 1;
+			switch (avp_t->data.len) {
+				case 4: i=i*0;
+				case 6: i=i*2;
+					snprintf(dest, STRSIZE,"%d.%d.%d.%d",
+							(unsigned char)avp_t->data.s[i+0],
+							(unsigned char)avp_t->data.s[i+1],
+							(unsigned char)avp_t->data.s[i+2],
+							(unsigned char)avp_t->data.s[i+3]);
+					cJSON_AddStringToObject(avp, "address", dest);
+					break;
+				case 16: i=i*0;
+				case 18: i=i*2;
+					snprintf(dest, STRSIZE, "%x.%x.%x.%x.%x.%x.%x.%x",
+							((avp_t->data.s[i+0]<<8)+avp_t->data.s[i+1]),
+							((avp_t->data.s[i+2]<<8)+avp_t->data.s[i+3]),
+							((avp_t->data.s[i+4]<<8)+avp_t->data.s[i+5]),
+							((avp_t->data.s[i+6]<<8)+avp_t->data.s[i+7]),
+							((avp_t->data.s[i+8]<<8)+avp_t->data.s[i+9]),
+							((avp_t->data.s[i+10]<<8)+avp_t->data.s[i+11]),
+							((avp_t->data.s[i+12]<<8)+avp_t->data.s[i+13]),
+							((avp_t->data.s[i+14]<<8)+avp_t->data.s[i+15]));
+					cJSON_AddStringToObject(avp, "address", dest);
+					break;
+			}
+			break;
+		case AAA_AVP_TIME_TYPE:
+		default:
+			LM_WARN("AAAConvertAVPToString: don't know how to print"
+					" this data type [%d] -> tryng hexa\n",avp_t->type);
+		case AAA_AVP_DATA_TYPE:
+			l = 0;
+			for (i=0; i < avp_t->data.len; i++) {
+				l+=snprintf(dest+l,STRSIZE-l-1,"%x", ((unsigned char*)avp_t->data.s)[i]);
+			}
+			cJSON_AddStringToObject(avp, "data", dest);
+			if (avp_t->data.len == 4) {
+				cJSON_AddNumberToObject(avp,"int32", htonl(*((unsigned int*)avp_t->data.s)));
+			}
+			if (avp_t->data.len > 4) {
+				memset(dest, 0, STRSIZE);
+				l = snprintf(dest, STRSIZE, "%.*s", avp_t->data.len, avp_t->data.s);
+				LM_DBG("%.*s (%i/%i)\n", l, dest, l, (int)strlen(dest));
+				if (strlen(dest) > 0) {
+					cJSON_AddStringToObject(avp, "string", dest);
+				} else {
+					AAA_AVP_LIST list;
+					list = cdp_avp->cdp->AAAUngroupAVPS(avp_t->data);
+					array = cJSON_CreateArray();
+					avp_it = list.head;
+					while(avp_it) {
+						LM_DBG("  AVP(%p < %p >%p);code=%u,"
+							"flags=%x;\nDataType=%u;VendorID=%u;DataLen=%u;\n",
+							avp_it->prev,avp_it,avp_it->next,avp_it->code,avp_it->flags,
+							avp_it->type,avp_it->vendorId,avp_it->data.len);
+
+						cJSON_AddItemToArray(array, avp2json(avp_it));
+
+						avp_it = avp_it->next;
+					}
+					cJSON_AddItemToObject(avp, "list", array);
+					cdpb.AAAFreeAVPList(&list);
+				}
+			}
+	}
+	return avp;
+}
+
+int AAAmsg2json(AAAMessage * request, str * dest) {
+	cJSON *root;
+	AAA_AVP *avp_t;
+	root=cJSON_CreateArray();
+
+	avp_t=request->avpList.head;
+	while(avp_t) {
+		cJSON_AddItemToArray(root, avp2json(avp_t));
+		avp_t = avp_t->next;
+	}
+
+	char * out = cJSON_Print(root);
+	cJSON_Delete(root);
+
+	if (dest->s) {
+		pkg_free(dest->s);
+	}
+
+	dest->len = strlen(out);
+	dest->s = pkg_malloc(dest->len);
+	if (dest->s) {
+		memcpy(dest->s, out, dest->len);
+		free(out);
+	} else {
+		LM_WARN("Failed to allocate %d bytes for the JSON\n", dest->len);
+		free(out);
+		return -1;
+	}
+	return 1;
+}
+
+
+int pv_get_request(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
+	if (msg->id != current_msg_id) {
+		current_msg_id = msg->id;
+		AAAmsg2json(request, &requestjson);
+	}
+	return pv_get_strval(msg, param, res, &requestjson);
+}
+
+
+/**
+ * Create and add an AVP to a Diameter message.
+ * @param m - Diameter message to add to
+ * @param d - the payload data
+ * @param len - length of the payload data
+ * @param avp_code - the code of the AVP
+ * @param flags - flags for the AVP
+ * @param vendorid - the value of the vendor id or 0 if none
+ * @param data_do - what to do with the data when done
+ * @param func - the name of the calling function, for debugging purposes
+ * @returns 1 on success or 0 on failure
+ */
+int diameterserver_add_avp(AAAMessage *m, char *d, int len, int avp_code, int flags, int vendorid, int data_do, const char *func) {
+    AAA_AVP *avp;
+    if (vendorid != 0) flags |= AAA_AVP_FLAG_VENDOR_SPECIFIC;
+    avp = cdpb.AAACreateAVP(avp_code, flags, vendorid, d, len, data_do);
+    if (!avp) {
+        LM_ERR("%s: Failed creating avp\n", func);
+        return 0;
+    }
+    if (cdpb.AAAAddAVPToMessage(m, avp, m->avpList.tail) != AAA_ERR_SUCCESS) {
+        LM_ERR("%s: Failed adding avp to message\n", func);
+       cdpb.AAAFreeAVP(&avp);
+        return 0;
+    }
+    return 1;
+}
+
+
+/**
+ * Create and add an AVP to a list of AVPs.
+ * @param list - the AVP list to add to
+ * @param d - the payload data
+ * @param len - length of the payload data
+ * @param avp_code - the code of the AVP
+ * @param flags - flags for the AVP
+ * @param vendorid - the value of the vendor id or 0 if none
+ * @param data_do - what to do with the data when done
+ * @param func - the name of the calling function, for debugging purposes
+ * @returns 1 on success or 0 on failure
+ */
+int diameterserver_add_avp_list(AAA_AVP_LIST *list, char *d, int len, int avp_code,
+	int flags, int vendorid, int data_do, const char *func) {
+    AAA_AVP *avp;
+    if (vendorid != 0) flags |= AAA_AVP_FLAG_VENDOR_SPECIFIC;
+    avp = cdpb.AAACreateAVP(avp_code, flags, vendorid, d, len, data_do);
+    if (!avp) {
+	LM_ERR("%s: Failed creating avp\n", func);
+	return 0;
+    }
+    if (list->tail) {
+	avp->prev = list->tail;
+	avp->next = 0;
+	list->tail->next = avp;
+	list->tail = avp;
+    } else {
+	list->head = avp;
+	list->tail = avp;
+	avp->next = 0;
+	avp->prev = 0;
+    }
+
+    return 1;
+}
+
+void parselist(AAAMessage *response, AAA_AVP_LIST *list, cJSON * item, int level) {
+	int flags;
+	char x[4];
+	AAA_AVP_LIST avp_list;
+	str avp_list_s;
+
+	LM_DBG("------------------------------------------------------------------\n");
+	LM_DBG("%i) Item %s (%i / %s)\n", level, item->string, item->valueint, item->valuestring);
+	// LM_ERR("Got JSON:\n%s\n",  cJSON_Print(item));
+
+	if (cJSON_GetObjectItem(item,"avpCode")) {
+		LM_DBG("%i) avp-Code: %i\n", level, cJSON_GetObjectItem(item,"avpCode")->valueint);
+	}
+	if (cJSON_GetObjectItem(item,"vendorId")) {
+		LM_DBG("%i) vendorId: %i\n", level, cJSON_GetObjectItem(item,"vendorId")->valueint);
+	}
+	flags = 0;
+	if (cJSON_GetObjectItem(item,"Flags")) {
+		LM_DBG("%i) Flags: %i\n", level, cJSON_GetObjectItem(item,"Flags")->valueint);
+		flags = cJSON_GetObjectItem(item,"Flags")->valueint;
+	}
+	if (cJSON_GetObjectItem(item,"string")) {
+		LM_DBG("%i) String: %s\n", level, cJSON_GetObjectItem(item,"string")->valuestring);
+	}
+	if (cJSON_GetObjectItem(item,"int32")) {
+		LM_DBG("%i) Integer: %i\n", level, cJSON_GetObjectItem(item,"int32")->valueint);
+	}
+
+	if (!cJSON_GetObjectItem(item,"avpCode")) {
+		LM_WARN("mandatory field missing: avpCode\n");
+		return;
+	}
+	if (!cJSON_GetObjectItem(item,"vendorId")) {
+		LM_WARN("mandatory field missing: vendorId (avpCode %i)\n", cJSON_GetObjectItem(item,"avpCode")->valueint);
+		return;
+	}
+
+	if ((response == 0) && (list == 0)) {
+		LM_WARN("No response nor list provided?!? (%i:%i)\n", cJSON_GetObjectItem(item,"avpCode")->valueint,
+			cJSON_GetObjectItem(item,"vendorId")->valueint);
+		return;
+	}
+
+	if (cJSON_GetObjectItem(item,"list")) {
+		LM_DBG("%i) It has a list...\n", level);
+		int i;
+		avp_list.head = 0;
+		avp_list.tail = 0;
+
+		for (i = 0 ; i < cJSON_GetArraySize(cJSON_GetObjectItem(item,"list")) ; i++) {
+			cJSON * subitem = cJSON_GetArrayItem(cJSON_GetObjectItem(item,"list"), i);
+			parselist(0, &avp_list, subitem, level + 1);
+		}
+		avp_list_s = cdpb.AAAGroupAVPS(avp_list);
+		cdpb.AAAFreeAVPList(&avp_list);
+
+		diameterserver_add_avp(response, avp_list_s.s, avp_list_s.len, cJSON_GetObjectItem(item,"avpCode")->valueint, flags,
+		  cJSON_GetObjectItem(item,"vendorId")->valueint, AVP_FREE_DATA, __FUNCTION__);
+	} else if (cJSON_GetObjectItem(item,"int32")) {
+		set_4bytes(x, cJSON_GetObjectItem(item,"int32")->valueint);
+		if (list) {
+			diameterserver_add_avp_list(list, x, 4, cJSON_GetObjectItem(item,"avpCode")->valueint, flags,
+			  cJSON_GetObjectItem(item,"vendorId")->valueint, AVP_DUPLICATE_DATA, __FUNCTION__);
+		} else {
+			diameterserver_add_avp(response, x, 4, cJSON_GetObjectItem(item,"avpCode")->valueint, flags,
+			  cJSON_GetObjectItem(item,"vendorId")->valueint, AVP_DUPLICATE_DATA, __FUNCTION__);
+		}
+	} else if (cJSON_GetObjectItem(item,"string")) {
+		if (list) {
+			diameterserver_add_avp_list(list, cJSON_GetObjectItem(item,"string")->valuestring,
+			 strlen(cJSON_GetObjectItem(item,"string")->valuestring), cJSON_GetObjectItem(item,"avpCode")->valueint, flags,
+			 cJSON_GetObjectItem(item,"vendorId")->valueint, AVP_DUPLICATE_DATA, __FUNCTION__);
+		} else {
+			diameterserver_add_avp(response, cJSON_GetObjectItem(item,"string")->valuestring,
+			 strlen(cJSON_GetObjectItem(item,"string")->valuestring), cJSON_GetObjectItem(item,"avpCode")->valueint, flags,
+			 cJSON_GetObjectItem(item,"vendorId")->valueint, AVP_DUPLICATE_DATA, __FUNCTION__);
+		}
+	} else {
+		LM_WARN("Not a string, int32, list? Invalid field definition... (%i:%i)\n",
+			cJSON_GetObjectItem(item,"avpCode")->valueint, cJSON_GetObjectItem(item,"vendorId")->valueint);
+	}
+}
+
+int addAVPsfromJSON(AAAMessage *response, str * json) {
+	if (json == NULL) {
+		json = &responsejson;
+	}
+	if (json->len <= 0) {
+		LM_WARN("No JSON Response\n");
+		return 0;
+	}
+	cJSON * root = cJSON_Parse(json->s);
+	if (root) {
+		int i;
+		for (i = 0 ; i < cJSON_GetArraySize(root) ; i++) {
+			cJSON * subitem = cJSON_GetArrayItem(root, i);
+			parselist(response, 0, subitem, 1);
+		}
+		// parselist(root, 0);
+		cJSON_Delete(root);
+		return 1;
+	}
+	return 0;
+}
+
+int pv_get_command(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
+	return pv_get_uintval(msg, param, res, request->commandCode);
+}
+
+
+int pv_get_application(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
+	return pv_get_uintval(msg, param, res, request->applicationId);
+}
+
+int pv_get_response(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
+	if ((msg->id != current_msg_id_repl) || (responsejson.len < 0)) {
+		return pv_get_null(msg, param, res);
+	}
+	return pv_get_strval(msg, param, res, &responsejson);
+}
+
+int pv_set_response(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val) {
+	if (val == NULL)
+		return 0;
+	if (val->flags&PV_VAL_STR) {
+		LM_DBG("Setting response to \"%.*s\" (String)\n", val->rs.len, val->rs.s);
+		responsejson.s = val->rs.s;
+		responsejson.len = val->rs.len;
+		current_msg_id_repl = msg->id;
+		return 0;
+	}
+	return 0;
+}
+

+ 34 - 0
src/modules/ims_diameter_server/avp_helper.h

@@ -0,0 +1,34 @@
+/*
+ *
+ * Copyright (C) 2016-2017 ng-voice GmbH, Carsten Bock, [email protected]
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ */
+
+#ifndef AVP_HELPER_H
+#define AVP_HELPER_H
+
+int pv_get_request(struct sip_msg *, pv_param_t *, pv_value_t *);
+int pv_get_response(struct sip_msg *, pv_param_t *, pv_value_t *);
+int pv_set_response(struct sip_msg *, pv_param_t *, int, pv_value_t *);
+int pv_get_command(struct sip_msg *, pv_param_t *, pv_value_t *);
+int pv_get_application(struct sip_msg *, pv_param_t *, pv_value_t *);
+int addAVPsfromJSON(AAAMessage *, str * json);
+int AAAmsg2json(AAAMessage *, str *);
+
+#endif /* AVP_HELPER_H */

+ 766 - 0
src/modules/ims_diameter_server/cJSON.c

@@ -0,0 +1,766 @@
+/*
+  Copyright (c) 2009 Dave Gamble
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <float.h>
+#include <limits.h>
+#include <ctype.h>
+#include "cJSON.h"
+
+static const char *global_ep;
+
+const char *cJSON_GetErrorPtr(void) {return global_ep;}
+
+static int cJSON_strcasecmp(const char *s1,const char *s2)
+{
+	if (!s1) return (s1==s2)?0:1;
+	if (!s2) return 1;
+	for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)	if(*s1 == 0)	return 0;
+	return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
+}
+
+static void *(*cJSON_malloc)(size_t sz) = malloc;
+static void (*cJSON_free)(void *ptr) = free;
+
+static char* cJSON_strdup(const char* str)
+{
+      size_t len;
+      char* copy;
+
+      len = strlen(str) + 1;
+      if (!(copy = (char*)cJSON_malloc(len))) return 0;
+      memcpy(copy,str,len);
+      return copy;
+}
+
+void cJSON_InitHooks(cJSON_Hooks* hooks)
+{
+    if (!hooks) { /* Reset hooks */
+        cJSON_malloc = malloc;
+        cJSON_free = free;
+        return;
+    }
+
+	cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
+	cJSON_free	 = (hooks->free_fn)?hooks->free_fn:free;
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_New_Item(void)
+{
+	cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
+	if (node) memset(node,0,sizeof(cJSON));
+	return node;
+}
+
+/* Delete a cJSON structure. */
+void cJSON_Delete(cJSON *c)
+{
+	cJSON *next;
+	while (c)
+	{
+		next=c->next;
+		if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
+		if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
+		if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string);
+		cJSON_free(c);
+		c=next;
+	}
+}
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static const char *parse_number(cJSON *item,const char *num)
+{
+	double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
+
+	if (*num=='-') sign=-1,num++;	/* Has sign? */
+	if (*num=='0') num++;			/* is zero */
+	if (*num>='1' && *num<='9')	do	n=(n*10.0)+(*num++ -'0');	while (*num>='0' && *num<='9');	/* Number? */
+	if (*num=='.' && num[1]>='0' && num[1]<='9') {num++;		do	n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');}	/* Fractional part? */
+	if (*num=='e' || *num=='E')		/* Exponent? */
+	{	num++;if (*num=='+') num++;	else if (*num=='-') signsubscale=-1,num++;		/* With sign? */
+		while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0');	/* Number? */
+	}
+
+	n=sign*n*pow(10.0,(scale+subscale*signsubscale));	/* number = +/- number.fraction * 10^+/- exponent */
+	
+	item->valuedouble=n;
+	item->valueint=(int)n;
+	item->type=cJSON_Number;
+	return num;
+}
+
+static int pow2gt (int x)	{	--x;	x|=x>>1;	x|=x>>2;	x|=x>>4;	x|=x>>8;	x|=x>>16;	return x+1;	}
+
+typedef struct {char *buffer; int length; int offset; } printbuffer;
+
+static char* ensure(printbuffer *p,int needed)
+{
+	char *newbuffer;int newsize;
+	if (!p || !p->buffer) return 0;
+	needed+=p->offset;
+	if (needed<=p->length) return p->buffer+p->offset;
+
+	newsize=pow2gt(needed);
+	newbuffer=(char*)cJSON_malloc(newsize);
+	if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;}
+	if (newbuffer) memcpy(newbuffer,p->buffer,p->length);
+	cJSON_free(p->buffer);
+	p->length=newsize;
+	p->buffer=newbuffer;
+	return newbuffer+p->offset;
+}
+
+static int update(printbuffer *p)
+{
+	char *str;
+	if (!p || !p->buffer) return 0;
+	str=p->buffer+p->offset;
+	return p->offset+strlen(str);
+}
+
+/* Render the number nicely from the given item into a string. */
+static char *print_number(cJSON *item,printbuffer *p)
+{
+	char *str=0;
+	double d=item->valuedouble;
+	if (d==0)
+	{
+		if (p)	str=ensure(p,2);
+		else	str=(char*)cJSON_malloc(2);	/* special case for 0. */
+		if (str) strcpy(str,"0");
+	}
+	else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
+	{
+		if (p)	str=ensure(p,21);
+		else	str=(char*)cJSON_malloc(21);	/* 2^64+1 can be represented in 21 chars. */
+		if (str)	sprintf(str,"%d",item->valueint);
+	}
+	else
+	{
+		if (p)	str=ensure(p,64);
+		else	str=(char*)cJSON_malloc(64);	/* This is a nice tradeoff. */
+		if (str)
+		{
+			if (fpclassify(d) != FP_ZERO && !isnormal(d))				sprintf(str,"null");
+			else if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)	sprintf(str,"%.0f",d);
+			else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9)					sprintf(str,"%e",d);
+			else														sprintf(str,"%f",d);
+		}
+	}
+	return str;
+}
+
+static unsigned parse_hex4(const char *str)
+{
+	unsigned h=0;
+	if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
+	h=h<<4;str++;
+	if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
+	h=h<<4;str++;
+	if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
+	h=h<<4;str++;
+	if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
+	return h;
+}
+
+/* Parse the input text into an unescaped cstring, and populate item. */
+static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+static const char *parse_string(cJSON *item,const char *str,const char **ep)
+{
+	const char *ptr=str+1,*end_ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
+	if (*str!='\"') {*ep=str;return 0;}	/* not a string! */
+	
+	while (*end_ptr!='\"' && *end_ptr && ++len) if (*end_ptr++ == '\\') end_ptr++;	/* Skip escaped quotes. */
+	
+	out=(char*)cJSON_malloc(len+1);	/* This is how long we need for the string, roughly. */
+	if (!out) return 0;
+	item->valuestring=out; /* assign here so out will be deleted during cJSON_Delete() later */
+	item->type=cJSON_String;
+	
+	ptr=str+1;ptr2=out;
+	while (ptr < end_ptr)
+	{
+		if (*ptr!='\\') *ptr2++=*ptr++;
+		else
+		{
+			ptr++;
+			switch (*ptr)
+			{
+				case 'b': *ptr2++='\b';	break;
+				case 'f': *ptr2++='\f';	break;
+				case 'n': *ptr2++='\n';	break;
+				case 'r': *ptr2++='\r';	break;
+				case 't': *ptr2++='\t';	break;
+				case 'u':	 /* transcode utf16 to utf8. */
+					uc=parse_hex4(ptr+1);ptr+=4;	/* get the unicode char. */
+					if (ptr >= end_ptr) {*ep=str;return 0;}	/* invalid */
+					
+					if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0)    {*ep=str;return 0;}	/* check for invalid.   */
+					
+					if (uc>=0xD800 && uc<=0xDBFF)	/* UTF16 surrogate pairs.	*/
+					{
+						if (ptr+6 > end_ptr)    {*ep=str;return 0;}	/* invalid */
+						if (ptr[1]!='\\' || ptr[2]!='u')    {*ep=str;return 0;}	/* missing second-half of surrogate.    */
+						uc2=parse_hex4(ptr+3);ptr+=6;
+						if (uc2<0xDC00 || uc2>0xDFFF)       {*ep=str;return 0;}	/* invalid second-half of surrogate.    */
+						uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
+					}
+
+					len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
+					
+					switch (len) {
+						case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+						case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+						case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+						case 1: *--ptr2 =(uc | firstByteMark[len]);
+					}
+					ptr2+=len;
+					break;
+				default:  *ptr2++=*ptr; break;
+			}
+			ptr++;
+		}
+	}
+	*ptr2=0;
+	if (*ptr=='\"') ptr++;
+	return ptr;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static char *print_string_ptr(const char *str,printbuffer *p)
+{
+	const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token;
+
+	if (!str)
+	{
+		if (p)	out=ensure(p,3);
+		else	out=(char*)cJSON_malloc(3);
+		if (!out) return 0;
+		strcpy(out,"\"\"");
+		return out;
+	}
+	
+	for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0;
+	if (!flag)
+	{
+		len=ptr-str;
+		if (p) out=ensure(p,len+3);
+		else		out=(char*)cJSON_malloc(len+3);
+		if (!out) return 0;
+		ptr2=out;*ptr2++='\"';
+		strcpy(ptr2,str);
+		ptr2[len]='\"';
+		ptr2[len+1]=0;
+		return out;
+	}
+	
+	ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
+	
+	if (p)	out=ensure(p,len+3);
+	else	out=(char*)cJSON_malloc(len+3);
+	if (!out) return 0;
+
+	ptr2=out;ptr=str;
+	*ptr2++='\"';
+	while (*ptr)
+	{
+		if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
+		else
+		{
+			*ptr2++='\\';
+			switch (token=*ptr++)
+			{
+				case '\\':	*ptr2++='\\';	break;
+				case '\"':	*ptr2++='\"';	break;
+				case '\b':	*ptr2++='b';	break;
+				case '\f':	*ptr2++='f';	break;
+				case '\n':	*ptr2++='n';	break;
+				case '\r':	*ptr2++='r';	break;
+				case '\t':	*ptr2++='t';	break;
+				default: sprintf(ptr2,"u%04x",token);ptr2+=5;	break;	/* escape and print */
+			}
+		}
+	}
+	*ptr2++='\"';*ptr2++=0;
+	return out;
+}
+/* Invote print_string_ptr (which is useful) on an item. */
+static char *print_string(cJSON *item,printbuffer *p)	{return print_string_ptr(item->valuestring,p);}
+
+/* Predeclare these prototypes. */
+static const char *parse_value(cJSON *item,const char *value,const char **ep);
+static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p);
+static const char *parse_array(cJSON *item,const char *value,const char **ep);
+static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p);
+static const char *parse_object(cJSON *item,const char *value,const char **ep);
+static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p);
+
+/* Utility to jump whitespace and cr/lf */
+static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
+
+/* Parse an object - create a new root, and populate. */
+cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
+{
+	const char *end=0,**ep=return_parse_end?return_parse_end:&global_ep;
+	cJSON *c=cJSON_New_Item();
+	*ep=0;
+	if (!c) return 0;       /* memory fail */
+
+	end=parse_value(c,skip(value),ep);
+	if (!end)	{cJSON_Delete(c);return 0;}	/* parse failure. ep is set. */
+
+	/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
+	if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);*ep=end;return 0;}}
+	if (return_parse_end) *return_parse_end=end;
+	return c;
+}
+/* Default options for cJSON_Parse */
+cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
+
+/* Render a cJSON item/entity/structure to text. */
+char *cJSON_Print(cJSON *item)				{return print_value(item,0,1,0);}
+char *cJSON_PrintUnformatted(cJSON *item)	{return print_value(item,0,0,0);}
+
+char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt)
+{
+	printbuffer p;
+	p.buffer=(char*)cJSON_malloc(prebuffer);
+	p.length=prebuffer;
+	p.offset=0;
+	return print_value(item,0,fmt,&p);
+}
+
+
+/* Parser core - when encountering text, process appropriately. */
+static const char *parse_value(cJSON *item,const char *value,const char **ep)
+{
+	if (!value)						return 0;	/* Fail on null. */
+	if (!strncmp(value,"null",4))	{ item->type=cJSON_NULL;  return value+4; }
+	if (!strncmp(value,"false",5))	{ item->type=cJSON_False; return value+5; }
+	if (!strncmp(value,"true",4))	{ item->type=cJSON_True; item->valueint=1;	return value+4; }
+	if (*value=='\"')				{ return parse_string(item,value,ep); }
+	if (*value=='-' || (*value>='0' && *value<='9'))	{ return parse_number(item,value); }
+	if (*value=='[')				{ return parse_array(item,value,ep); }
+	if (*value=='{')				{ return parse_object(item,value,ep); }
+
+	*ep=value;return 0;	/* failure. */
+}
+
+/* Render a value to text. */
+static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p)
+{
+	char *out=0;
+	if (!item) return 0;
+	if (p)
+	{
+		switch ((item->type)&255)
+		{
+			case cJSON_NULL:	{out=ensure(p,5);	if (out) strcpy(out,"null");	break;}
+			case cJSON_False:	{out=ensure(p,6);	if (out) strcpy(out,"false");	break;}
+			case cJSON_True:	{out=ensure(p,5);	if (out) strcpy(out,"true");	break;}
+			case cJSON_Number:	out=print_number(item,p);break;
+			case cJSON_String:	out=print_string(item,p);break;
+			case cJSON_Array:	out=print_array(item,depth,fmt,p);break;
+			case cJSON_Object:	out=print_object(item,depth,fmt,p);break;
+		}
+	}
+	else
+	{
+		switch ((item->type)&255)
+		{
+			case cJSON_NULL:	out=cJSON_strdup("null");	break;
+			case cJSON_False:	out=cJSON_strdup("false");break;
+			case cJSON_True:	out=cJSON_strdup("true"); break;
+			case cJSON_Number:	out=print_number(item,0);break;
+			case cJSON_String:	out=print_string(item,0);break;
+			case cJSON_Array:	out=print_array(item,depth,fmt,0);break;
+			case cJSON_Object:	out=print_object(item,depth,fmt,0);break;
+		}
+	}
+	return out;
+}
+
+/* Build an array from input text. */
+static const char *parse_array(cJSON *item,const char *value,const char **ep)
+{
+	cJSON *child;
+	if (*value!='[')	{*ep=value;return 0;}	/* not an array! */
+
+	item->type=cJSON_Array;
+	value=skip(value+1);
+	if (*value==']') return value+1;	/* empty array. */
+
+	item->child=child=cJSON_New_Item();
+	if (!item->child) return 0;		 /* memory fail */
+	value=skip(parse_value(child,skip(value),ep));	/* skip any spacing, get the value. */
+	if (!value) return 0;
+
+	while (*value==',')
+	{
+		cJSON *new_item;
+		if (!(new_item=cJSON_New_Item())) return 0; 	/* memory fail */
+		child->next=new_item;new_item->prev=child;child=new_item;
+		value=skip(parse_value(child,skip(value+1),ep));
+		if (!value) return 0;	/* memory fail */
+	}
+
+	if (*value==']') return value+1;	/* end of array */
+	*ep=value;return 0;	/* malformed. */
+}
+
+/* Render an array to text */
+static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p)
+{
+	char **entries;
+	char *out=0,*ptr,*ret;int len=5;
+	cJSON *child=item->child;
+	int numentries=0,i=0,fail=0;
+	size_t tmplen=0;
+	
+	/* How many entries in the array? */
+	while (child) numentries++,child=child->next;
+	/* Explicitly handle numentries==0 */
+	if (!numentries)
+	{
+		if (p)	out=ensure(p,3);
+		else	out=(char*)cJSON_malloc(3);
+		if (out) strcpy(out,"[]");
+		return out;
+	}
+
+	if (p)
+	{
+		/* Compose the output array. */
+		i=p->offset;
+		ptr=ensure(p,1);if (!ptr) return 0;	*ptr='[';	p->offset++;
+		child=item->child;
+		while (child && !fail)
+		{
+			print_value(child,depth+1,fmt,p);
+			p->offset=update(p);
+			if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;}
+			child=child->next;
+		}
+		ptr=ensure(p,2);if (!ptr) return 0;	*ptr++=']';*ptr=0;
+		out=(p->buffer)+i;
+	}
+	else
+	{
+		/* Allocate an array to hold the values for each */
+		entries=(char**)cJSON_malloc(numentries*sizeof(char*));
+		if (!entries) return 0;
+		memset(entries,0,numentries*sizeof(char*));
+		/* Retrieve all the results: */
+		child=item->child;
+		while (child && !fail)
+		{
+			ret=print_value(child,depth+1,fmt,0);
+			entries[i++]=ret;
+			if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
+			child=child->next;
+		}
+		
+		/* If we didn't fail, try to malloc the output string */
+		if (!fail)	out=(char*)cJSON_malloc(len);
+		/* If that fails, we fail. */
+		if (!out) fail=1;
+
+		/* Handle failure. */
+		if (fail)
+		{
+			for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
+			cJSON_free(entries);
+			return 0;
+		}
+		
+		/* Compose the output array. */
+		*out='[';
+		ptr=out+1;*ptr=0;
+		for (i=0;i<numentries;i++)
+		{
+			tmplen=strlen(entries[i]);memcpy(ptr,entries[i],tmplen);ptr+=tmplen;
+			if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
+			cJSON_free(entries[i]);
+		}
+		cJSON_free(entries);
+		*ptr++=']';*ptr++=0;
+	}
+	return out;	
+}
+
+/* Build an object from the text. */
+static const char *parse_object(cJSON *item,const char *value,const char **ep)
+{
+	cJSON *child;
+	if (*value!='{')	{*ep=value;return 0;}	/* not an object! */
+	
+	item->type=cJSON_Object;
+	value=skip(value+1);
+	if (*value=='}') return value+1;	/* empty array. */
+	
+	item->child=child=cJSON_New_Item();
+	if (!item->child) return 0;
+	value=skip(parse_string(child,skip(value),ep));
+	if (!value) return 0;
+	child->string=child->valuestring;child->valuestring=0;
+	if (*value!=':') {*ep=value;return 0;}	/* fail! */
+	value=skip(parse_value(child,skip(value+1),ep));	/* skip any spacing, get the value. */
+	if (!value) return 0;
+	
+	while (*value==',')
+	{
+		cJSON *new_item;
+		if (!(new_item=cJSON_New_Item()))	return 0; /* memory fail */
+		child->next=new_item;new_item->prev=child;child=new_item;
+		value=skip(parse_string(child,skip(value+1),ep));
+		if (!value) return 0;
+		child->string=child->valuestring;child->valuestring=0;
+		if (*value!=':') {*ep=value;return 0;}	/* fail! */
+		value=skip(parse_value(child,skip(value+1),ep));	/* skip any spacing, get the value. */
+		if (!value) return 0;
+	}
+	
+	if (*value=='}') return value+1;	/* end of array */
+	*ep=value;return 0;	/* malformed. */
+}
+
+/* Render an object to text. */
+static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p)
+{
+	char **entries=0,**names=0;
+	char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
+	cJSON *child=item->child;
+	int numentries=0,fail=0;
+	size_t tmplen=0;
+	/* Count the number of entries. */
+	while (child) numentries++,child=child->next;
+	/* Explicitly handle empty object case */
+	if (!numentries)
+	{
+		if (p) out=ensure(p,fmt?depth+4:3);
+		else	out=(char*)cJSON_malloc(fmt?depth+4:3);
+		if (!out)	return 0;
+		ptr=out;*ptr++='{';
+		if (fmt) {*ptr++='\n';for (i=0;i<depth;i++) *ptr++='\t';}
+		*ptr++='}';*ptr++=0;
+		return out;
+	}
+	if (p)
+	{
+		/* Compose the output: */
+		i=p->offset;
+		len=fmt?2:1;	ptr=ensure(p,len+1);	if (!ptr) return 0;
+		*ptr++='{';	if (fmt) *ptr++='\n';	*ptr=0;	p->offset+=len;
+		child=item->child;depth++;
+		while (child)
+		{
+			if (fmt)
+			{
+				ptr=ensure(p,depth);	if (!ptr) return 0;
+				for (j=0;j<depth;j++) *ptr++='\t';
+				p->offset+=depth;
+			}
+			print_string_ptr(child->string,p);
+			p->offset=update(p);
+			
+			len=fmt?2:1;
+			ptr=ensure(p,len);	if (!ptr) return 0;
+			*ptr++=':';if (fmt) *ptr++='\t';
+			p->offset+=len;
+			
+			print_value(child,depth,fmt,p);
+			p->offset=update(p);
+
+			len=(fmt?1:0)+(child->next?1:0);
+			ptr=ensure(p,len+1); if (!ptr) return 0;
+			if (child->next) *ptr++=',';
+			if (fmt) *ptr++='\n';
+			*ptr=0;
+			p->offset+=len;
+			child=child->next;
+		}
+		ptr=ensure(p,fmt?(depth+1):2);	 if (!ptr) return 0;
+		if (fmt)	for (i=0;i<depth-1;i++) *ptr++='\t';
+		*ptr++='}';*ptr=0;
+		out=(p->buffer)+i;
+	}
+	else
+	{
+		/* Allocate space for the names and the objects */
+		entries=(char**)cJSON_malloc(numentries*sizeof(char*));
+		if (!entries) return 0;
+		names=(char**)cJSON_malloc(numentries*sizeof(char*));
+		if (!names) {cJSON_free(entries);return 0;}
+		memset(entries,0,sizeof(char*)*numentries);
+		memset(names,0,sizeof(char*)*numentries);
+
+		/* Collect all the results into our arrays: */
+		child=item->child;depth++;if (fmt) len+=depth;
+		while (child && !fail)
+		{
+			names[i]=str=print_string_ptr(child->string,0);
+			entries[i++]=ret=print_value(child,depth,fmt,0);
+			if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
+			child=child->next;
+		}
+		
+		/* Try to allocate the output string */
+		if (!fail)	out=(char*)cJSON_malloc(len);
+		if (!out) fail=1;
+
+		/* Handle failure */
+		if (fail)
+		{
+			for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
+			cJSON_free(names);cJSON_free(entries);
+			return 0;
+		}
+		
+		/* Compose the output: */
+		*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
+		for (i=0;i<numentries;i++)
+		{
+			if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
+			tmplen=strlen(names[i]);memcpy(ptr,names[i],tmplen);ptr+=tmplen;
+			*ptr++=':';if (fmt) *ptr++='\t';
+			strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
+			if (i!=numentries-1) *ptr++=',';
+			if (fmt) *ptr++='\n';
+			*ptr=0;
+			cJSON_free(names[i]);cJSON_free(entries[i]);
+		}
+		
+		cJSON_free(names);cJSON_free(entries);
+		if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
+		*ptr++='}';*ptr++=0;
+	}
+	return out;	
+}
+
+/* Get Array size/item / object item. */
+int    cJSON_GetArraySize(cJSON *array)							{cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
+cJSON *cJSON_GetArrayItem(cJSON *array,int item)				{cJSON *c=array?array->child:0;while (c && item>0) item--,c=c->next; return c;}
+cJSON *cJSON_GetObjectItem(cJSON *object,const char *string)	{cJSON *c=object?object->child:0;while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
+int cJSON_HasObjectItem(cJSON *object,const char *string)		{return cJSON_GetObjectItem(object,string)?1:0;}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
+/* Utility for handling references. */
+static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
+
+/* Add item to array/object. */
+void   cJSON_AddItemToArray(cJSON *array, cJSON *item)						{cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
+void   cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item)	{if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
+void   cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item)	{if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);}
+void	cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)						{cJSON_AddItemToArray(array,create_reference(item));}
+void	cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item)	{
+	cJSON_AddItemToObject(object,string,create_reference(item));
+}
+cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {
+	cJSON *c=array->child;
+	while (c && which>0) c=c->next,which--;
+	if (!c) return 0;
+	if (c->prev) c->prev->next=c->next;
+	if (c->next) c->next->prev=c->prev;
+	if (c==array->child) array->child=c->next;
+	c->prev=c->next=0;
+	return c;
+}
+void   cJSON_DeleteItemFromArray(cJSON *array,int which)			{cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
+cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
+void   cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
+
+/* Replace array/object items with new ones. */
+void   cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem)		{cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;}
+	newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;}
+void   cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem)		{cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
+	newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
+	if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
+void   cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
+
+/* Create basic types: */
+cJSON *cJSON_CreateNull(void)					{cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
+cJSON *cJSON_CreateTrue(void)					{cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
+cJSON *cJSON_CreateFalse(void)					{cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
+cJSON *cJSON_CreateBool(int b)					{cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
+cJSON *cJSON_CreateNumber(double num)			{cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
+cJSON *cJSON_CreateString(const char *string)	{cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);if(!item->valuestring){cJSON_Delete(item);return 0;}}return item;}
+cJSON *cJSON_CreateArray(void)					{cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
+cJSON *cJSON_CreateObject(void)					{cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
+
+/* Create Arrays: */
+cJSON *cJSON_CreateIntArray(const int *numbers,int count)		{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!n){cJSON_Delete(a);return 0;}if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+cJSON *cJSON_CreateFloatArray(const float *numbers,int count)	{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!n){cJSON_Delete(a);return 0;}if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+cJSON *cJSON_CreateDoubleArray(const double *numbers,int count)	{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!n){cJSON_Delete(a);return 0;}if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+cJSON *cJSON_CreateStringArray(const char **strings,int count)	{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!n){cJSON_Delete(a);return 0;}if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+
+/* Duplication */
+cJSON *cJSON_Duplicate(cJSON *item,int recurse)
+{
+	cJSON *newitem,*cptr,*nptr=0,*newchild;
+	/* Bail on bad ptr */
+	if (!item) return 0;
+	/* Create new item */
+	newitem=cJSON_New_Item();
+	if (!newitem) return 0;
+	/* Copy over all vars */
+	newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
+	if (item->valuestring)	{newitem->valuestring=cJSON_strdup(item->valuestring);	if (!newitem->valuestring)	{cJSON_Delete(newitem);return 0;}}
+	if (item->string)		{newitem->string=cJSON_strdup(item->string);			if (!newitem->string)		{cJSON_Delete(newitem);return 0;}}
+	/* If non-recursive, then we're done! */
+	if (!recurse) return newitem;
+	/* Walk the ->next chain for the child. */
+	cptr=item->child;
+	while (cptr)
+	{
+		newchild=cJSON_Duplicate(cptr,1);		/* Duplicate (with recurse) each item in the ->next chain */
+		if (!newchild) {cJSON_Delete(newitem);return 0;}
+		if (nptr)	{nptr->next=newchild,newchild->prev=nptr;nptr=newchild;}	/* If newitem->child already set, then crosswire ->prev and ->next and move on */
+		else		{newitem->child=newchild;nptr=newchild;}					/* Set newitem->child and move to it */
+		cptr=cptr->next;
+	}
+	return newitem;
+}
+
+void cJSON_Minify(char *json)
+{
+	char *into=json;
+	while (*json)
+	{
+		if (*json==' ') json++;
+		else if (*json=='\t') json++;	/* Whitespace characters. */
+		else if (*json=='\r') json++;
+		else if (*json=='\n') json++;
+		else if (*json=='/' && json[1]=='/')  while (*json && *json!='\n') json++;	/* double-slash comments, to end of line. */
+		else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;}	/* multiline comments. */
+		else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} /* string literals, which are \" sensitive. */
+		else *into++=*json++;			/* All other characters. */
+	}
+	*into=0;	/* and null-terminate. */
+}

+ 153 - 0
src/modules/ims_diameter_server/cJSON.h

@@ -0,0 +1,153 @@
+/*
+  Copyright (c) 2009 Dave Gamble
+ 
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+ 
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+ 
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* cJSON Types: */
+#define cJSON_False  (1 << 0)
+#define cJSON_True   (1 << 1)
+#define cJSON_NULL   (1 << 2)
+#define cJSON_Number (1 << 3)
+#define cJSON_String (1 << 4)
+#define cJSON_Array  (1 << 5)
+#define cJSON_Object (1 << 6)
+	
+#define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
+
+/* The cJSON structure: */
+typedef struct cJSON {
+	struct cJSON *next,*prev;	/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+	struct cJSON *child;		/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+
+	int type;					/* The type of the item, as above. */
+
+	char *valuestring;			/* The item's string, if type==cJSON_String */
+	int valueint;				/* The item's number, if type==cJSON_Number */
+	double valuedouble;			/* The item's number, if type==cJSON_Number */
+
+	char *string;				/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+} cJSON;
+
+typedef struct cJSON_Hooks {
+      void *(*malloc_fn)(size_t sz);
+      void (*free_fn)(void *ptr);
+} cJSON_Hooks;
+
+/* Supply malloc, realloc and free functions to cJSON */
+extern void cJSON_InitHooks(cJSON_Hooks* hooks);
+
+
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
+extern cJSON *cJSON_Parse(const char *value);
+/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
+extern char  *cJSON_Print(cJSON *item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
+extern char  *cJSON_PrintUnformatted(cJSON *item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
+extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
+/* Delete a cJSON entity and all subentities. */
+extern void   cJSON_Delete(cJSON *c);
+
+/* Returns the number of items in an array (or object). */
+extern int	  cJSON_GetArraySize(cJSON *array);
+/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
+extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
+/* Get item "string" from object. Case insensitive. */
+extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
+extern int cJSON_HasObjectItem(cJSON *object,const char *string);
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+extern const char *cJSON_GetErrorPtr(void);
+	
+/* These calls create a cJSON item of the appropriate type. */
+extern cJSON *cJSON_CreateNull(void);
+extern cJSON *cJSON_CreateTrue(void);
+extern cJSON *cJSON_CreateFalse(void);
+extern cJSON *cJSON_CreateBool(int b);
+extern cJSON *cJSON_CreateNumber(double num);
+extern cJSON *cJSON_CreateString(const char *string);
+extern cJSON *cJSON_CreateArray(void);
+extern cJSON *cJSON_CreateObject(void);
+
+/* These utilities create an Array of count items. */
+extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
+extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
+extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
+extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
+
+/* Append item to the specified array/object. */
+extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
+extern void	cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
+extern void	cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item);	/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+extern void	cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
+
+/* Remove/Detatch items from Arrays/Objects. */
+extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
+extern void   cJSON_DeleteItemFromArray(cJSON *array,int which);
+extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
+extern void   cJSON_DeleteItemFromObject(cJSON *object,const char *string);
+	
+/* Update array items. */
+extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem);	/* Shifts pre-existing items to the right. */
+extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
+extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+
+/* Duplicate a cJSON item */
+extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
+/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
+need to be released. With recurse!=0, it will duplicate any children connected to the item.
+The item->next and ->prev pointers are always zero on return from Duplicate. */
+
+/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
+/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */
+extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
+
+extern void cJSON_Minify(char *json);
+
+/* Macros for creating things quickly. */
+#define cJSON_AddNullToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateNull())
+#define cJSON_AddTrueToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
+#define cJSON_AddFalseToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
+#define cJSON_AddBoolToObject(object,name,b)	cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
+#define cJSON_AddNumberToObject(object,name,n)	cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
+#define cJSON_AddStringToObject(object,name,s)	cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
+
+/* When assigning an integer value, it needs to be propagated to valuedouble too. */
+#define cJSON_SetIntValue(object,val)			((object)?(object)->valueint=(object)->valuedouble=(val):(val))
+#define cJSON_SetNumberValue(object,val)		((object)?(object)->valueint=(object)->valuedouble=(val):(val))
+
+/* Macro for iterating over an array */
+#define cJSON_ArrayForEach(pos, head)			for(pos = (head)->child; pos != NULL; pos = pos->next)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 4 - 0
src/modules/ims_diameter_server/doc/Makefile

@@ -0,0 +1,4 @@
+docs = ims_diameter_server.xml
+
+docbook_dir = ../../../../doc/docbook
+include $(docbook_dir)/Makefile.module

+ 334 - 0
src/modules/ims_diameter_server/doc/ims_diameter_server.xml

@@ -0,0 +1,334 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<book id="print" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+        <title>IMS Diameter Server Module</title>
+	<authorgroup>
+	    <author>
+		<firstname>Carsten</firstname>
+		<surname>Bock</surname>
+		<affiliation><orgname>ng-voice GmbH</orgname></affiliation>
+		<address>
+		    <email>[email protected]</email>
+		</address>
+	    </author>
+	</authorgroup>
+	<copyright>
+	    <year>2016-2017</year>
+	    <holder>ng-voice GmbH</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+
+    <chapter>
+	<title>Admin Guide</title>
+    <section id="ims_diameter_server.overview">
+	<title>Overview</title>
+	<para>
+	    This module implements a generic Diameter Server.
+	</para>
+	<para>
+	    This module translates incoming Diameter Messages 
+            into a JSON structure and will pass this on to the routing engine for 
+            further operations.
+	</para>
+	<para>
+            The module expects a reply (again in JSON), which then is translated
+            into a Diameter Response.
+	</para>
+	<para>
+	    Additionally, it allows you to send Diameter-Requests to another peer.
+	</para>
+	<para>
+	    The JSON contains an array with all AVP's in the Diameter-Message
+		and it's attributes. The format is identical for both requests
+		and replies.
+	</para>
+	<programlisting format="linespecific">
+[
+   {
+      "avpCode":277,
+      "vendorId":0,
+      "Flags":64,
+      "int32":0
+   },
+   {
+      "avpCode":260,
+      "vendorId":0,
+      "Flags":64,
+      "list":[
+         {
+            "avpCode":266,
+            "vendorId":0,
+            "Flags":64,
+            "int32":10415
+         },
+         {
+            "avpCode":258,
+            "vendorId":0,
+            "Flags":64,
+            "int32":16777216
+         }
+      ]
+   },
+   {
+      "avpCode":1,
+      "vendorId":0,
+      "Flags":64,
+      "string":"[email protected]"
+   },
+   {
+      "avpCode":618,
+      "vendorId":10415,
+      "Flags":64,
+      "list":[
+         {
+            "avpCode":621,
+            "vendorId":10415,
+            "Flags":64,
+            "string":"pcscf.kamailio.org"
+         }
+      ]
+   },
+   {
+      "avpCode":268,
+      "vendorId":0,
+      "Flags":64,
+      "int32":2001
+   }
+]
+	</programlisting>
+	<para>
+            The module could be used (for example) for:
+	</para>
+      <itemizedlist>
+        <listitem>
+          <para>a Home-Subscriber-Server (it was written do be used as one)</para>
+        </listitem>
+        <listitem>
+          <para>a Charging-Server (Ro/Rf)</para>
+        </listitem>
+        <listitem>
+          <para>for testing Diameter-Applications</para>
+        </listitem>
+        <listitem>
+          <para>a PCRF/PCEF Emulator/Gateway</para>
+        </listitem>
+        <listitem>
+          <para>a Diameter-Routing-Agent (DRA)</para>
+        </listitem>
+        <listitem>
+          <para>...</para>
+        </listitem>
+      </itemizedlist>
+
+    </section>
+
+  <section id="ims_diameter_server.dependencies">
+    <title>Dependencies</title>
+
+    <section>
+      <title>Kamailio Modules</title>
+
+      <para>The Following mouldes must be loaded before this module:</para>
+
+      <itemizedlist>
+        <listitem>
+          <para>CDP - C Diameter Peer</para>
+        </listitem>
+        <listitem>
+          <para>CDP_AVP - CDP AVP Applications</para>
+        </listitem>
+      </itemizedlist>
+    </section>
+    <section>
+      <title>External Libraries or Applications</title>
+      <para>No external libraries are required.</para>
+    </section>
+    </section>
+	<section>
+	<title>Functions</title>
+	<section id="ims_diameter_server.f.diameter_request">
+		<title>
+		<function moreinfo="none">diameter_request([peer], appid, commandcode, message)</function>
+		</title>
+		<para>
+		This method will send a Diameter Request.
+		</para>
+		<para>Meaning of the parameters is as follows:</para>
+		<itemizedlist>
+		<listitem>
+			<para>
+			<emphasis>peer</emphasis> - send the diameter request directly to a diameter peer [optional]. If this parameter is omitted, the default routing is used (see CDP).
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			<emphasis>appid</emphasis> - Diameter-Application, e.g.:
+                        </para>
+			<para>
+			Typical App-ID's are:
+                        </para>
+		      <itemizedlist>
+			<listitem>
+			  <para>16777216 - Diameter Cx/Dx</para>
+			</listitem>
+			<listitem>
+			  <para>16777217 - Diameter Sh</para>
+			</listitem>
+			<listitem>
+			  <para>4 - Diameter Ro (Online Charging)</para>
+			</listitem>
+			<listitem>
+			  <para>...</para>
+			</listitem>
+		      </itemizedlist>
+                </listitem>
+		<listitem>
+			<para>
+			<emphasis>commandcode</emphasis> - Diameter-Command-Code, e.g.:
+                        </para>
+		      <itemizedlist>
+			<listitem>
+			  <para>300 - Diameter Cx/Dx User-Assignment Request (UAR)</para>
+			</listitem>
+			<listitem>
+			  <para>301 - Diameter Cx/Dx Server-Assignment Request (SAR)</para>
+			</listitem>
+			<listitem>
+			  <para>...</para>
+			</listitem>
+		      </itemizedlist>
+                </listitem>
+		<listitem>
+			<para>
+			<emphasis>message</emphasis> - the Diameter Message (as JSON), which should be sent.
+			</para>
+		</listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from any route.
+		</para>
+		<example>
+		<title><function>diameter_request</function> usage</title>
+		<programlisting format="linespecific">
+...
+diameter_request("16777216", "300", "{ some json }");
+...
+		</programlisting>
+		</example>
+	</section>
+	<section id="ims_diameter_server.f.diameter_request_async">
+		<title>
+		<function moreinfo="none">diameter_request_async([peer], appid, commandcode, message)</function>
+		</title>
+		<para>
+		This method will send a Diameter Request asynchronously. The Reply to this request will be visible in the event-route "diameter:response".
+		</para>
+		<para>The meaning of the parameters are identical to the diameter_request function.</para>
+		<para>This function is only available, if the diameter:response event-route is defined.</para>
+	</section>
+	</section>
+        <section id="ims_diameter_server.ex.pv">
+        <title>Exported Pseudo Variables</title>
+        <section id="ims_diameter_server.pv.diameter_application">
+                <title>
+                <function moreinfo="none">$diameter_application</function>
+                </title>
+                <para>
+			This PV provides the requested Diameter Application,
+                        for example:
+                </para>
+      <itemizedlist>
+        <listitem>
+          <para>16777216 - Diameter Cx/Dx</para>
+        </listitem>
+        <listitem>
+          <para>16777217 - Diameter Sh</para>
+        </listitem>
+        <listitem>
+          <para>4 - Diameter Ro (Online Charging)</para>
+        </listitem>
+        <listitem>
+          <para>...</para>
+        </listitem>
+      </itemizedlist>
+	</section>
+        <section id="ims_diameter_server.pv.diameter_command">
+                <title>
+                <function moreinfo="none">$diameter_command</function>
+                </title>
+                <para>
+			This PV provides the requested Diameter Command,
+                        for example:
+                </para>
+      <itemizedlist>
+        <listitem>
+          <para>300 - Diameter Cx/Dx User-Assignment Request (UAR)</para>
+        </listitem>
+        <listitem>
+          <para>301 - Diameter Cx/Dx Server-Assignment Request (SAR)</para>
+        </listitem>
+        <listitem>
+          <para>...</para>
+        </listitem>
+      </itemizedlist>
+	</section>
+      <section id="ims_diameter_server.pv.diameter_request">
+                <title>
+                <function moreinfo="none">$diameter_request</function>
+                </title>
+                <para>
+			This PV provides the Diameter Request as JSON.
+                </para>
+	</section>
+      <section id="ims_diameter_server.pv.diameter_response">
+                <title>
+                <function moreinfo="none">$diameter_response</function>
+                </title>
+                <para>
+			The Response is read from the PVAR.
+                </para>
+	</section>
+    </section>
+        <section id="ims_diameter_server.ex.event_routes">
+        <title>Event routes</title>
+        <section>
+                <title>
+                <function moreinfo="none">diameter:request</function>
+                </title>
+                <para>
+			This route is called for any incoming Diameter Request
+                </para>
+        <programlisting  format="linespecific">
+...
+event_route[diameter:request] {
+	$var(res) = http_connect("hss", "/application/$diameter_application/command/$diameter_command", "application/json", "$diameter_request", "$var(response)");
+	if ($var(res) == 200) {
+		$diameter_response = $var(response);
+	}
+}
+...
+                </programlisting>
+	</section>
+<section>
+                <title>
+                <function moreinfo="none">diameter:response</function>
+                </title>
+                <para>
+			This route is called for incoming Diameter replies, if the request was processed asynchronously.
+                </para>
+        <programlisting  format="linespecific">
+...
+event_route[diameter:response] {
+	xlog("Reply to Diameter request $diameter_request is $diameter_response\n");
+}
+...
+                </programlisting>
+	</section>
+    </section>
+    </chapter>
+</book>
+

+ 31 - 0
src/modules/ims_diameter_server/examples/hss.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<DiameterPeer 
+	FQDN="hss-dev.mnc260.mcc310.3gppnetwork.org"
+	Realm="ims.mnc260.mcc310.3gppnetwork.org"
+	Vendor_Id="10415"
+	Product_Name="CDiameterPeer"
+	AcceptUnknownPeers="1"
+	DropUnknownOnDisconnect="1"
+	Tc="30"
+	Workers="4"
+	QueueLength="8"
+	TransactionTimeout="5"
+	SessionsHashSize="128"
+	DefaultAuthSessionTimeout="3600"
+	MaxAuthSessionTimeout="3600"
+>
+	
+<Peer FQDN="icscf-1.mnc260.mcc310.3gppnetwork.org" Realm="ims.mnc260.mcc310.3gppnetwork.org" port="3869"/>
+<Peer FQDN="icscf-2.mnc260.mcc310.3gppnetwork.org" Realm="ims.mnc260.mcc310.3gppnetwork.org" port="3869"/>
+<!-- <Peer FQDN="scscf.mnc260.mcc310.3gppnetwork.org" Realm="ims.mnc260.mcc310.3gppnetwork.org" port="3870"/> -->
+		
+	<Acceptor port="3868" bind="138.68.93.183"/>
+
+	<Auth id="16777216" vendor="10415"/><!-- 3GPP Cx -->
+	<Auth id="16777216" vendor="4491"/><!-- CableLabs Cx -->
+	<Auth id="16777216" vendor="13019"/><!-- ETSI/TISPAN Cx -->
+	<Auth id="16777216" vendor="0"/><!-- ETSI/TISPAN Cx -->
+	<Auth id="16777217" vendor="10415"/><!-- 3GPP Sh -->
+	<Auth id="16777221" vendor="10415"/>
+
+</DiameterPeer>

+ 122 - 0
src/modules/ims_diameter_server/examples/kamailio.cfg

@@ -0,0 +1,122 @@
+#!KAMAILIO
+#
+# Loadbalancer Config-File
+#
+# Kamailio (OpenSER) SIP Server v3.1 - default configuration script
+#     - web: http://www.kamailio.org
+#     - git: http://sip-router.org
+#
+# Refer to the Core CookBook at http://www.kamailio.org/dokuwiki/doku.php
+# for an explanation of possible statements, functions and parameters.
+#
+
+# IP-Adress for incoming SIP-Traffic, in the following format:
+listen=udp:127.0.0.1:5099
+listen=tcp:127.0.0.1:5099
+
+####### Defined Values #########
+
+# *** Value defines - IDs used later in config
+
+# - flags
+#	FLT_ - per transaction (message) flags
+#!define FLT_CAPTURE 1
+#!define FLT_IPV6 2
+#!define FLT_ACC 3
+#!define FLT_DIALOG 4
+#!define FLT_ANONYMIZE 5
+
+#!define DISPATCHER_LIST_QUERYSERVER "1"
+
+####### Global Parameters #########
+
+
+debug=2
+log_stderror=yes
+
+memdbg=5
+memlog=5
+
+log_facility=LOG_LOCAL0
+
+fork=yes
+children=2
+tcp_children=3
+
+# Do SRV-Loadbalancing:
+dns_srv_lb=yes
+# Always prefer IPv6:
+dns_try_ipv6=yes
+# DNS-Based failover
+use_dns_failover = on
+# Query NAPTR-Records as well:
+dns_try_naptr=no
+
+/* uncomment the next line to disable the auto discovery of local aliases
+   based on reverse DNS on IPs (default on) */
+auto_aliases=no
+
+system.shutdownmode = 0 desc "System shutdown mode"
+
+####### Modules Section ########
+
+# set paths to location of modules
+mpath="/usr/lib64/kamailio/modules_k/:/usr/lib64/kamailio/modules/:/usr/local/lib/kamailio/modules/"
+
+loadmodule "xlog"
+loadmodule "kex"
+loadmodule "pv"
+loadmodule "ctl"
+loadmodule "cdp"
+loadmodule "cdp_avp"
+loadmodule "ims_diameter_server"
+loadmodule "corex"
+loadmodule "http_client"
+loadmodule "statistics"
+
+# ----- cdp params -----
+modparam("cdp","config_file","/etc/kamailio_hss/hss.xml")
+
+# ----- ctl params -----
+modparam("ctl", "binrpc", "unix:/var/run/kamailio_hss/kamailio_ctl")
+
+# ----- http_client params -----
+modparam("http_client", "httpcon", "hss=>https://hss:[email protected]/hss")
+
+# -- statistics params --
+modparam("statistics", "variable", "diameter_requests")
+modparam("statistics", "variable", "diameter_failed_requests")
+modparam("statistics", "variable", "diameter_request_time")
+
+####### Routing Logic ########
+
+# Main SIP request routing logic
+# - processing of any incoming SIP request starts with this route
+route {
+	drop();
+	exit;
+}
+
+event_route[diameter:request] {
+	#xlog("Application: $diameter_application, Command $diameter_command\n");
+	#file_write("/tmp/diameter.json", $diameter_request);
+	# Provide some statistics
+	$var(start_time) = $TV(Sn);
+	$var(res) = http_connect("hss", "/application/$diameter_application/command/$diameter_command", "application/json", "$diameter_request", "$var(response)");
+	#xlog("$diameter_request\n");
+	#xlog("API-server HTTP connection: $var(response) Result code $var(res)\n");
+	if ($var(res) == 200) {
+		$diameter_response = $var(response);
+	} else {
+		update_stat("diameter_failed_requests", "+1");
+	}
+	$var(start_secs) = $(var(start_time){s.select,0,.});
+	$var(start_usecs) = $(var(start_time){s.select,1,.});
+	$var(diff_secs) = $TV(s) - $var(start_secs);
+	$var(diff_usecs) = $TV(u) - $var(start_usecs);
+	$var(diff_ms) = $var(diff_secs)*1000 + ($var(diff_usecs)/1000);
+	$var(stat_add) = "+" + $var(diff_ms);
+	#xlog("Reqest took $var(stat_add)ms\n"); 
+	update_stat("diameter_requests", "+1");
+	update_stat("diameter_request_time", "$var(stat_add)");
+}

+ 344 - 0
src/modules/ims_diameter_server/ims_diameter_server.c

@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2016-2017 ng-voice GmbH, [email protected]
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ */
+
+#include "../../core/sr_module.h"
+#include "../../core/route.h"
+#include "../cdp/cdp_load.h"
+#include "../cdp_avp/cdp_avp_mod.h"
+#include "ims_diameter_server.h"
+#include "avp_helper.h"
+#include "../../core/fmsg.h"
+
+MODULE_VERSION
+
+extern gen_lock_t* process_lock; /* lock on the process table */
+
+struct cdp_binds cdpb;
+
+cdp_avp_bind_t *cdp_avp;
+
+/** module functions */
+static int mod_init(void);
+static int mod_child_init(int);
+static void mod_destroy(void);
+
+int * callback_singleton; /*< Callback singleton */
+
+int event_route_diameter = 0;
+int event_route_diameter_response = 0;
+
+static int diameter_request(struct sip_msg * msg, char* peer, char* appid, char* commandcode, char* message, int async);
+static int w_diameter_request(struct sip_msg * msg, char* appid, char* commandcode, char* message);
+static int w_diameter_request_peer(struct sip_msg *msg, char* peer, char* appid, char* commandcode, char* message);
+static int w_diameter_request_async(struct sip_msg * msg, char* appid, char* commandcode, char* message);
+static int w_diameter_request_peer_async(struct sip_msg *msg, char* peer, char* appid, char* commandcode, char* message);
+
+
+static cmd_export_t cmds[] = {
+	{"diameter_request", (cmd_function)w_diameter_request, 3, fixup_var_pve_str_12, 0, ANY_ROUTE},
+	{"diameter_request", (cmd_function)w_diameter_request_peer, 4, fixup_var_pve_str_12, 0, ANY_ROUTE},
+	{"diameter_request_async", (cmd_function)w_diameter_request_async, 3, fixup_var_pve_str_12, 0, ANY_ROUTE},
+	{"diameter_request_async", (cmd_function)w_diameter_request_peer_async, 4, fixup_var_pve_str_12, 0, ANY_ROUTE},
+	{ 0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[] = {
+    { 0, 0, 0}
+};
+
+static pv_export_t mod_pvs[] = {
+	{ {"diameter_command", sizeof("diameter_command")-1}, PVT_OTHER, pv_get_command, 0, 0, 0, 0, 0 },
+	{ {"diameter_application", sizeof("diameter_application")-1}, PVT_OTHER, pv_get_application, 0, 0, 0, 0, 0 },
+	{ {"diameter_request", sizeof("diameter_request")-1}, PVT_OTHER, pv_get_request, 0, 0, 0, 0, 0 },
+	{ {"diameter_response", sizeof("diameter_response")-1}, PVT_OTHER, pv_get_response, pv_set_response, 0, 0, 0, 0 },
+	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+/** module exports */
+struct module_exports exports = {"ims_diameter_server", DEFAULT_DLFLAGS, /* dlopen flags */
+    cmds, /* Exported functions */
+    params, 0, /* exported statistics */
+    0, /* exported MI functions */
+    mod_pvs, /* exported pseudo-variables */
+    0, /* extra processes */
+    mod_init, /* module initialization function */
+    0, mod_destroy, mod_child_init /* per-child init function */};
+
+/**
+ * init module function
+ */
+static int mod_init(void) {
+	LM_DBG("Loading...\n");
+
+	request = 0;
+	responsejson.s = 0;
+	responsejson.len = 0;
+	requestjson.s = 0;
+	requestjson.len = 0;
+
+	callback_singleton = shm_malloc(sizeof (int));
+	*callback_singleton = 0;
+
+	cdp_avp = 0;
+	/* load the CDP API */
+	if (load_cdp_api(&cdpb) != 0) {
+		LM_ERR("can't load CDP API\n");
+        	goto error;
+	    }
+
+	cdp_avp = load_cdp_avp();
+	if (!cdp_avp) {
+		LM_ERR("can't load CDP_AVP API\n");
+		goto error;
+	}
+
+	event_route_diameter = route_get(&event_rt, "diameter:request");
+	if (event_route_diameter < 0) {
+		LM_ERR("No diameter:request event route found\n");
+		goto error;
+	}
+	LM_DBG("Found Route diameter:request: %i\n", event_route_diameter);
+
+	event_route_diameter_response = route_get(&event_rt, "diameter:response");
+	if (event_route_diameter_response < 0) {
+		LM_WARN("No diameter:response event route found, asynchronous operations disabled.\n");
+	} else {
+		LM_DBG("Found Route diameter:response: %i\n", event_route_diameter_response);
+	}
+
+	return 0;
+error:
+	LM_ERR("Failed to initialise ims_diameter_server module\n");
+	return -1;
+}
+
+/**
+ * Initializes the module in child.
+ */
+static int mod_child_init(int rank) {
+    LM_DBG("Initialization of module in child [%d] \n", rank);
+
+    /* don't do anything for main process and TCP manager process */
+    if (rank == PROC_MAIN || rank == PROC_TCP_MAIN) {
+        return 0;
+    }
+
+    lock_get(process_lock);
+    if ((*callback_singleton) == 0) {
+        *callback_singleton = 1;
+        cdpb.AAAAddRequestHandler(callback_cdp_request, NULL);
+    }
+    lock_release(process_lock);
+
+    return 0;
+}
+
+
+static void mod_destroy(void) {
+
+}
+
+/**
+ * Handler for incoming Diameter requests.
+ * @param request - the received request
+ * @param param - generic pointer
+ * @returns the answer to this request
+ */
+AAAMessage* callback_cdp_request(AAAMessage *request_in, void *param) {
+	struct sip_msg *fmsg;
+	int backup_rt;
+	struct run_act_ctx ctx;
+	AAAMessage *response;
+
+	LM_DBG("Got DIAMETER-Request!\n");
+
+	if (is_req(request_in)) {
+		LM_DBG("is request!\n");
+		LM_DBG("Found Route diameter:request: %i\n", event_route_diameter);
+
+		request = request_in;
+		response = cdpb.AAACreateResponse(request_in);
+		if (!response) return 0;
+
+		backup_rt = get_route_type();
+		set_route_type(REQUEST_ROUTE);
+		init_run_actions_ctx(&ctx);
+		fmsg = faked_msg_next();
+		responsejson.s = 0;
+		responsejson.len = 0;
+
+		run_top_route(event_rt.rlist[event_route_diameter], fmsg, &ctx);
+
+		set_route_type(backup_rt);
+		LM_DBG("Processed Event-Route!\n");
+
+		if (addAVPsfromJSON(response, NULL)) {
+			return response;
+		} else {
+			return 0;
+		}
+	}
+	return 0;
+}
+
+int w_diameter_request(struct sip_msg * msg, char* appid, char* commandcode, char* message) {
+	return diameter_request(msg, 0, appid, commandcode, message, 0);
+}
+
+static int w_diameter_request_peer(struct sip_msg *msg, char* peer, char* appid, char* commandcode, char* message) {
+	return diameter_request(msg, peer, appid, commandcode, message, 0);
+}
+
+static int w_diameter_request_async(struct sip_msg * msg, char* appid, char* commandcode, char* message) {
+	return diameter_request(msg, 0, appid, commandcode, message, 1);
+}
+
+static int w_diameter_request_peer_async(struct sip_msg *msg, char* peer, char* appid, char* commandcode, char* message) {
+	return diameter_request(msg, peer, appid, commandcode, message, 1);
+}
+
+void async_cdp_diameter_callback(int is_timeout, void *request, AAAMessage *response, long elapsed_msecs) {
+	struct sip_msg *fmsg;
+	int backup_rt;
+	struct run_act_ctx ctx;
+
+	if (is_timeout != 0) {
+		LM_ERR("Error timeout when sending message via CDP\n");
+		goto error;
+	}
+
+	if (!response) {
+		LM_ERR("Error sending message via CDP\n");
+		goto error;
+	}
+	if (AAAmsg2json(response, &responsejson) != 1) {
+		LM_ERR("Failed to convert response to JSON\n");
+	}
+	request = (AAAMessage*)request;
+
+	backup_rt = get_route_type();
+	set_route_type(REQUEST_ROUTE);
+	init_run_actions_ctx(&ctx);
+	fmsg = faked_msg_next();
+
+	run_top_route(event_rt.rlist[event_route_diameter_response], fmsg, &ctx);
+
+	set_route_type(backup_rt);
+	LM_DBG("Processed Event-Route!\n");
+error:
+	//free memory
+	if (response) cdpb.AAAFreeMessage(&response);
+}
+
+
+int diameter_request(struct sip_msg * msg, char* peer, char* appid, char* commandcode, char* message, int async) {
+	str s_appid, s_commandcode, s_peer, s_message;
+	AAAMessage *req = 0;
+	AAASession *session = 0;
+	AAAMessage *resp = 0;
+
+	unsigned int i_appid, i_commandcode;
+
+	if (async && (event_route_diameter_response < 0)) {
+		LM_ERR("Asynchronous operations disabled\n");
+		return -1;
+	}
+
+	if (peer) {
+		if (get_str_fparam(&s_peer, msg, (fparam_t*)peer) < 0) {
+		    LM_ERR("failed to get Peer\n");
+		    return -1;
+		}
+		LM_DBG("Peer %.*s\n", s_peer.len, s_peer.s);
+	}
+	if (get_str_fparam(&s_appid, msg, (fparam_t*)appid) < 0) {
+		LM_ERR("failed to get App-ID\n");
+		return -1;
+	}
+	if (str2int(&s_appid, &i_appid) != 0) {
+		LM_ERR("Invalid App-ID (%.*s)\n", s_appid.len, s_appid.s);
+		return -1;
+	}
+	LM_DBG("App-ID %i\n", i_appid);
+	if (get_str_fparam(&s_commandcode, msg, (fparam_t*)commandcode) < 0) {
+		LM_ERR("failed to get Command-Code\n");
+		return -1;
+	}
+	if (str2int(&s_commandcode, &i_commandcode) != 0) {
+		LM_ERR("Invalid Command-Code (%.*s)\n", s_commandcode.len, s_commandcode.s);
+		return -1;
+	}
+	LM_DBG("Command-Code %i\n", i_commandcode);
+	if (get_str_fparam(&s_commandcode, msg, (fparam_t*)commandcode) < 0) {
+		LM_ERR("failed to get Command-Code\n");
+		return -1;
+	}
+
+	session = cdpb.AAACreateSession(0);
+
+	req = cdpb.AAACreateRequest(i_appid, i_commandcode, Flag_Proxyable, session);
+	if (!req) goto error1;
+
+	if (addAVPsfromJSON(req, &s_message)) {
+		LM_ERR("Failed to parse JSON Request\n");
+		return -1;
+	}
+
+
+	if (peer && (s_peer.len > 0)) {
+		if (async) {
+			cdpb.AAASendMessageToPeer(req, &s_peer, (void*) async_cdp_diameter_callback, req);
+			LM_DBG("Successfully sent async diameter\n");
+			return 0;
+		} else {
+			resp = cdpb.AAASendRecvMessageToPeer(req, &s_peer);
+			LM_DBG("Successfully sent diameter\n");
+			if (AAAmsg2json(resp, &responsejson) == 1) {
+				return 1;
+			} else {
+				LM_ERR("Failed to convert response to JSON\n");
+				return -1;
+			}
+		}
+	} else {
+		if (async) {
+			cdpb.AAASendMessage(req, (void*) async_cdp_diameter_callback, req);
+			LM_DBG("Successfully sent async diameter\n");
+			return 0;
+		} else {
+			resp = cdpb.AAASendRecvMessage(req);
+			LM_DBG("Successfully sent diameter\n");
+			if (AAAmsg2json(resp, &responsejson) == 1) {
+				return 1;
+			} else {
+				LM_ERR("Failed to convert response to JSON\n");
+				return -1;
+			}
+		}
+	}
+error1:
+	//Only free UAR IFF it has not been passed to CDP
+	if (req) cdpb.AAAFreeMessage(&req);
+	LM_ERR("Error occurred trying to send request\n");
+	return -1;
+}
+
+

+ 41 - 0
src/modules/ims_diameter_server/ims_diameter_server.h

@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright (C) 2016-2017 ng-voice GmbH, Carsten Bock, [email protected]
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ */
+
+
+#ifndef IMS_DIAMETERSERVER_MOD_H
+#define	IMS_DIAMETERSERVER_MOD_H
+
+/** callback functions */
+
+struct cdp_binds cdpb;
+cdp_avp_bind_t *cdp_avp;
+
+struct AAAMessage;
+
+AAAMessage *request;
+str responsejson;
+str requestjson;
+
+AAAMessage* callback_cdp_request(AAAMessage *request, void *param);
+
+#endif	/* IMS_DIAMETERSERVER_MOD_H */
+

+ 90 - 0
src/modules/ims_diameter_server/sem.h

@@ -0,0 +1,90 @@
+/*
+ * $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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ *
+ *
+ * History:
+ * --------
+ *  2011-02-02  initial version (jason.penton)
+ */
+
+#ifndef __SEM_H
+#define __SEM_H
+
+	#include <semaphore.h>
+
+	typedef sem_t gen_sem_t;
+
+	/**
+	 * Create a new unnamed semaphore and initialize it
+	 * @param value - 0 if it should be pre-locked, 1 if not, or how many locks until block
+	 * @return
+	 */
+    #define sem_new(sem_ptr,value)\
+	do {\
+		sem_ptr=shm_malloc(sizeof(gen_sem_t));\
+		if (!sem_ptr){\
+			LM_ERR("Error allocating %lx bytes of shm!\n",sizeof(gen_sem_t));\
+		}	\
+		if (sem_init(sem_ptr, 1, value)<0) {\
+			LM_ERR("Error > %s\n",strerror(errno));\
+		}\
+	} while(0)
+	
+    #define sem_free(sem)\
+	do {\
+		if (sem) {\
+			sem_destroy(sem);\
+			shm_free(sem);\
+			sem=0;\
+		}\
+	} while(0)
+	
+	
+	#define sem_get(sem) sem_wait(sem)
+	#define sem_tryget(sem) sem_trywait(sem)
+	#define sem_timedget(sem,abs_timeout) sem_trywait(sem,abs_timeout)
+	
+	#define sem_release(sem) sem_post(sem)
+
+#endif