Index: doc/dbg_interactive.py.sample
===================================================================
--- doc/dbg_interactive.py.sample	(revision 635)
+++ doc/dbg_interactive.py.sample	(revision 636)
@@ -95,5 +95,5 @@
 v.vendor_id = 123
 v.vendor_name = "My test vendor"
-r, my_vendor = fd_dict_new(d, DICT_VENDOR, v, None)
+my_vendor = d.new_obj(DICT_VENDOR, v)
 del v
 d.dump()
@@ -104,5 +104,5 @@
 a.application_id = 99
 a.application_name = "My test appl"
-r, my_appl = fd_dict_new(d, DICT_APPLICATION, a, my_vendor)
+my_appl = d.new_obj(DICT_APPLICATION, a, my_vendor)
 del a
 
@@ -111,8 +111,8 @@
 t.type_base = AVP_TYPE_INTEGER32
 t.type_name = "My integer AVP"
-r, my_type_int = fd_dict_new(d, DICT_TYPE, t, my_appl)
+my_type_int = d.new_obj(DICT_TYPE, t, my_appl)
 t.type_base = AVP_TYPE_OCTETSTRING
 t.type_name = "My binary buffer AVP"
-r, my_type_os = fd_dict_new(d, DICT_TYPE, t, my_appl)
+my_type_os = d.new_obj(DICT_TYPE, t, my_appl)
 del t
 
@@ -121,10 +121,10 @@
 c.enum_name = "AVP_VALUE_TROIS"
 c.enum_value.i32 = 3
-fd_dict_new(d, DICT_ENUMVAL, c, my_type_int)
+d.new_obj(DICT_ENUMVAL, c, my_type_int)
 
 c.enum_name = "A_BUFFER_CONSTANT"
 c.enum_value.os = "This is a very long AVP value that we prefer to represent as a constant"
 c.enum_value.os.dump()
-fd_dict_new(d, DICT_ENUMVAL, c, my_type_os)
+d.new_obj(DICT_ENUMVAL, c, my_type_os)
 del c
 
@@ -135,5 +135,5 @@
 a.avp_flag_mask = AVP_FLAG_MANDATORY
 a.avp_basetype = AVP_TYPE_INTEGER32
-r, my_avp_int = fd_dict_new(d, DICT_AVP, a, my_type_int)
+my_avp_int = d.new_obj(DICT_AVP, a, my_type_int)
 
 a.avp_vendor = 123
@@ -142,5 +142,5 @@
 a.avp_flag_val = AVP_FLAG_VENDOR
 a.avp_basetype = AVP_TYPE_OCTETSTRING
-r, my_avp_os = fd_dict_new(d, DICT_AVP, a, my_type_os)
+my_avp_os = d.new_obj(DICT_AVP, a, my_type_os)
 del a
 
@@ -151,22 +151,22 @@
 c.cmd_flag_mask = CMD_FLAG_REQUEST + CMD_FLAG_PROXIABLE
 c.cmd_flag_val = CMD_FLAG_REQUEST + CMD_FLAG_PROXIABLE
-r, my_req = fd_dict_new(d, DICT_COMMAND, c, my_appl)
+my_req = d.new_obj(DICT_COMMAND, c, my_appl)
 c.cmd_name = "My-Python-Answer"
 c.cmd_flag_val = CMD_FLAG_PROXIABLE
-r, my_ans = fd_dict_new(d, DICT_COMMAND, c, my_appl)
+my_ans = d.new_obj(DICT_COMMAND, c, my_appl)
 del c
 
 # Rule
-rd = dict_rule_data()
-rd.rule_avp = my_avp_int
-rd.rule_position = RULE_REQUIRED
-rd.rule_min = -1
-rd.rule_max = -1
-r, my_rule1 = fd_dict_new(d, DICT_RULE, rd, my_req)
-r, my_rule2 = fd_dict_new(d, DICT_RULE, rd, my_ans)
-rd.rule_avp = my_avp_os
-r, my_rule3 = fd_dict_new(d, DICT_RULE, rd, my_req)
-r, my_rule4 = fd_dict_new(d, DICT_RULE, rd, my_ans)
-del rd
+r = dict_rule_data()
+r.rule_avp = my_avp_int
+r.rule_position = RULE_REQUIRED
+r.rule_min = -1
+r.rule_max = -1
+d.new_obj(DICT_RULE, r, my_req)
+d.new_obj(DICT_RULE, r, my_ans)
+r.rule_avp = my_avp_os
+d.new_obj(DICT_RULE, r, my_req)
+d.new_obj(DICT_RULE, r, my_ans)
+del r
 
 d.dump()
@@ -178,21 +178,20 @@
 gdict = cvar.fd_g_config.cnf_dict
 
-r, appl = fd_dict_search ( gdict, DICT_APPLICATION, APPLICATION_BY_ID, 3, -1 )
-obj.dump()
-r, avp = fd_dict_search( gdict, DICT_AVP, AVP_BY_NAME, "Origin-Host", -1)
-obj.dump()
-r, errcmd = fd_dict_get_error_cmd( gdict )
-
-data = dict_avp_data()
-fd_dict_getval(avp, data)
-print data.avp_code
-del data
-
-r, t = fd_dict_gettype(appl)
+appl = gdict.search ( DICT_APPLICATION, APPLICATION_BY_ID, 3 )
+appl.dump()
+avp = gdict.search ( DICT_AVP, AVP_BY_NAME, "Origin-Host")
+avp.dump()
+errcmd = gdict.error_cmd()
+
+v = avp.getval()
+print v.avp_code
+del v
+
+t = avp.gettype()
+print t
 del t
 
-r, dict = fd_dict_getdict(avp)
+dict = avp.getdict()
 del dict
-
 
 
@@ -203,4 +202,5 @@
     print "Cleaning up python state for session:", sid
     print "Received state:", state
+    del state
 
 hdl = session_handler(my_cleanup)
@@ -208,8 +208,6 @@
 del hdl
 
+# Session
 hdl = session_handler(my_cleanup)
-
-
-# Session
 s1 = session()
 s1.getsid()
@@ -217,29 +215,31 @@
 r,s3,isnew = fd_sess_fromsid("this.is.a.full.session.id")
 s4 = session("host.id", "opt.part")
-
 s4.settimeout(30) # the python wrapper takes a number of seconds as parameter for simplicity
 s4.dump()
 
-
-# state
-mystate = [ 34, "clah", [ 32, 12 ] ]
-
-s4.store(hdl, mystate)
-
-
-
-## TODO : debug the following (segfault)
-
-def my_cleanup(state,sid):
-    print "Cleaning up python state for session:", sid
-    print "Received state:", state
-
-hdl = session_handler(my_cleanup)
-s4 = session("host.id", "opt.part")
-mystate = [ 34, "clah", [ 32, 12 ] ]
-s4.store(hdl, mystate)
-del hdl
-
-
+# states
+mystate = [ 34, "blah", [ 32, 12 ] ]
+s1.store(hdl, mystate)
+del mystate
+gotstate = s1.retrieve(hdl)
+print gotstate
+del gotstate
+
+
+############# Routing ############
+
+rd = rt_data()
+
+rd.add("p1.testbed.aaa", "testbed.aaa")
+rd.add("p2.testbed.aaa", "testbed.aaa")
+rd.add("p3.testbed.aaa", "testbed.aaa")
+rd.add("p4.testbed.aaa", "testbed.aaa")
+
+rd.remove("p2.testbed.aaa")
+
+rd.error("p3.testbed.aaa", "relay.testbed.aaa", 3002)
+
+list = rd.extract(-1)
+list[0].dump()
 
 
@@ -250,20 +250,4 @@
 ######################### old stuff (need update) ######################
 
-# Routing data
-prtd = new_rt_data_pptr()
-fd_rtd_init(prtd)
-fd_rtd_candidate_add(rt_data_pptr_value(prtd), "p1.testbed.aaa", "testbed.aaa")
-fd_rtd_candidate_add(rt_data_pptr_value(prtd), "p2.testbed.aaa", "testbed.aaa")
-fd_rtd_candidate_add(rt_data_pptr_value(prtd), "p3.testbed.aaa", "testbed.aaa")
-fd_rtd_candidate_del(rt_data_pptr_value(prtd), "p2.testbed.aaa", 0)
-pcands = new_fd_list_pptr()
-fd_rtd_candidate_extract(rt_data_pptr_value(prtd), pcands, 0)
-li = fd_list_pptr_value(pcands)
-li = fd_list_next_get(li)
-c = fd_list_to_rtd_candidate(li)
-rtd_candidate_diamid_get(c)
-li = fd_list_next_get(li)
-c = fd_list_to_rtd_candidate(li)
-rtd_candidate_diamid_get(c)
 
 
Index: extensions/dbg_interactive/dbg_interactive.i
===================================================================
--- extensions/dbg_interactive/dbg_interactive.i	(revision 635)
+++ extensions/dbg_interactive/dbg_interactive.i	(revision 636)
@@ -51,7 +51,8 @@
 %include <typemaps.i>
 
-/* Some functions are not available through the wrapper, or accessed differently */
+/* Some functions are not available through the wrapper */
 %ignore fd_lib_init;
 %ignore fd_lib_fini;
+/* -- the following functions are better accessed differently, but we leave their definitions just in case
 %ignore fd_dict_init;
 %ignore fd_dict_fini;
@@ -64,5 +65,11 @@
 %ignore fd_sess_state_store_internal;
 %ignore fd_sess_state_retrieve_internal;
-
+%ignore fd_rtd_init;
+%ignore fd_rtd_free;
+%ignore fd_rtd_candidate_add;
+%ignore fd_rtd_candidate_del;
+%ignore fd_rtd_candidate_extract;
+%ignore fd_rtd_error_add;
+*/
 
 /* Inline functions seems to give problems to SWIG -- just remove the inline definition */
@@ -75,4 +82,45 @@
 
 
+/* Create a generic error handling mechanism so that functions can provoke an exception */
+%{
+/* This is not thread-safe etc. but it should work /most of the time/. */
+static int wrapper_errno;
+static PyObject* wrapper_errno_py;
+static char * wrapper_error_txt; /* if NULL, use strerror(errno) */
+#define DI_ERROR(code, pycode, str) {	\
+	fd_log_debug("[dbg_interactive] ERROR: %s: %s\n", __PRETTY_FUNCTION__, str ? str : strerror(code)); \
+	wrapper_errno = code;		\
+	wrapper_errno_py = pycode;	\
+	wrapper_error_txt = str;	\
+}
+
+#define DI_ERROR_MALLOC	\
+	 DI_ERROR(ENOMEM, PyExc_MemoryError, NULL)
+
+%}
+
+%exception {
+	/* reset the errno */
+	wrapper_errno = 0;
+	/* Call the function  -- it will use DI_ERROR macro in case of error */
+	$action
+	/* Now, test for error */
+	if (wrapper_errno) {
+		char * str = wrapper_error_txt ? wrapper_error_txt : strerror(wrapper_errno);
+		PyObject * exc = wrapper_errno_py;
+		if (!exc) {
+			switch (wrapper_errno) {
+				case ENOMEM: exc = PyExc_MemoryError; break;
+				case EINVAL: exc = PyExc_ValueError; break;
+				default: exc = PyExc_RuntimeError;
+			}
+		}
+		SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+		PyErr_SetString(exc, str);
+		SWIG_fail;
+		SWIG_PYTHON_THREAD_END_BLOCK;
+	}
+}
+
 
 /***********************************
@@ -80,8 +128,6 @@
  ***********************************/
 
-/* for fd_hash */
 %apply (char *STRING, size_t LENGTH) { ( char * string, size_t len ) };
 
-/* for dictionary functions */
 %typemap(in, numinputs=0,noblock=1) SWIGTYPE ** OUTPUT (void *temp = NULL) {
 	$1 = (void *)&temp;
@@ -90,12 +136,20 @@
 	%append_output(SWIG_NewPointerObj(*$1, $*1_descriptor, 0));
 }
-%apply SWIGTYPE ** OUTPUT { struct dict_object ** ref };
-%apply SWIGTYPE ** OUTPUT { struct dict_object ** obj };
-%apply SWIGTYPE ** OUTPUT { struct dict_object ** result };
-%apply SWIGTYPE ** OUTPUT { struct dictionary  ** dict }; /* this is for fd_dict_getdict, not fd_dict_init (use constructor) */
+
 %apply int * OUTPUT { enum dict_object_type * type };
 %apply (char *STRING, size_t LENGTH) { (char * sid, size_t len) };
 %apply SWIGTYPE ** OUTPUT { struct session ** session };
 %apply int * OUTPUT { int * new };
+
+/* Callbacks defined in python */
+%typemap(in) PyObject *PyCb {
+	if (!PyCallable_Check($input)) {
+		PyErr_SetString(PyExc_TypeError, "Need a callable object!");
+		SWIG_fail;
+	}
+	$1 = $input;
+}
+
+
 
 /*********************************************************
@@ -121,6 +175,5 @@
 		li = (struct fd_list *) malloc(sizeof(struct fd_list));
 		if (!li) {
-			fd_log_debug("Out of memory!\n");
-			PyErr_SetString(PyExc_MemoryError,"Not enough memory");
+			DI_ERROR_MALLOC;
 			return NULL;
 		}
@@ -153,6 +206,5 @@
 		int ret = fd_dict_init(&r);
 		if (ret != 0) {
-			fd_log_debug("Error: %s\n", strerror(ret));
-			PyErr_SetString(PyExc_MemoryError,"Not enough memory");
+			DI_ERROR(ret, NULL, NULL);
 			return NULL;
 		}
@@ -160,25 +212,18 @@
 	}
 	~dictionary() {
-		if (self) {
-			struct dictionary *d = self;
-			int ret = fd_dict_fini(&d);
-			if (ret != 0) {
-				fd_log_debug("Error: %s\n", strerror(ret));
-			}
-			return;
-		}
+		struct dictionary *d = self;
+		int ret = fd_dict_fini(&d);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+		return;
 	}
 	void dump() {
-		if ($self) {
-			fd_dict_dump($self);
-		}
+		fd_dict_dump($self);
 	}
 	PyObject * vendors_list() {
 		uint32_t *list = NULL, *li;
 		PyObject * ret;
-		if (!$self) {
-			PyErr_SetString(PyExc_SyntaxError,"dict_object cannot be created directly. Use fd_dict_new.");
-			return NULL;
-		}
+		SWIG_PYTHON_THREAD_BEGIN_BLOCK;
 		ret = PyList_New(0);
 		list = fd_dict_get_vendorid_list($self);
@@ -187,7 +232,52 @@
 		}
 		free(list);
+		SWIG_PYTHON_THREAD_END_BLOCK;
 		return ret;
 	}
-	
+	struct dict_object * new_obj(enum dict_object_type type, void * data, struct dict_object * parent = NULL) {
+		struct dict_object * obj = NULL;
+		int ret = fd_dict_new($self, type, data, parent, &obj);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+			return NULL;
+		}
+		return obj;
+	}
+	struct dict_object * search(enum dict_object_type type, int criteria, int what_by_val) {
+		struct dict_object * obj = NULL;
+		int ret = fd_dict_search ( $self, type, criteria, &what_by_val, &obj, ENOENT );
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+			return NULL;
+		}
+		return obj;
+	}
+	struct dict_object * search(enum dict_object_type type, int criteria, char * what_by_string) {
+		struct dict_object * obj = NULL;
+		int ret = fd_dict_search ( $self, type, criteria, what_by_string, &obj, ENOENT );
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+			return NULL;
+		}
+		return obj;
+	}
+	struct dict_object * search(enum dict_object_type type, int criteria, void * what) {
+		struct dict_object * obj = NULL;
+		int ret = fd_dict_search ( $self, type, criteria, what, &obj, ENOENT );
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+			return NULL;
+		}
+		return obj;
+	}
+	struct dict_object * error_cmd() {
+		struct dict_object * obj = NULL;
+		int ret = fd_dict_get_error_cmd ( $self, &obj );
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+			return NULL;
+		}
+		return obj;
+	}
 }
 
@@ -197,32 +287,78 @@
 %extend dict_object {
 	dict_object() {
-		fd_log_debug("Error: dict_object cannot be created directly. Use fd_dict_new\n");
-		PyErr_SetString(PyExc_SyntaxError,"dict_object cannot be created directly. Use fd_dict_new.");
+		DI_ERROR(EINVAL, PyExc_SyntaxError, "dict_object cannot be created directly. Use fd_dict_new().");
 		return NULL;
 	}
 	~dict_object() {
-		fd_log_debug("Error: dict_object cannot be destroyed directly. Destroy the parent dictionary.\n");
+		DI_ERROR(EINVAL, PyExc_SyntaxError, "dict_object cannot be destroyed directly. Destroy the parent dictionary.");
 		return;
 	}
 	void dump() {
-		if ($self) {
-			fd_dict_dump_object($self);
-		}
-	}
-}
-
-/* overload the search function to allow passing integers & string criteria directly */
-%rename(fd_dict_search) fd_dict_search_int;
-%inline %{
-int fd_dict_search_int ( struct dictionary * dict, enum dict_object_type type, int criteria, int what_by_val, struct dict_object ** result, int retval ) {
-	return fd_dict_search ( dict, type, criteria, &what_by_val, result, retval );
-}
-%}
-%rename(fd_dict_search) fd_dict_search_string;
-%inline %{
-int fd_dict_search_string ( struct dictionary * dict, enum dict_object_type type, int criteria, char * what_by_string, struct dict_object ** result, int retval ) {
-	return fd_dict_search ( dict, type, criteria, what_by_string, result, retval );
-}
-%}
+		fd_dict_dump_object($self);
+	}
+	enum dict_object_type gettype() {
+		enum dict_object_type t;
+		int ret = fd_dict_gettype ( $self, &t);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+			return 0;
+		}
+		return t;
+	}
+	struct dictionary * getdict() {
+		struct dictionary *d;
+		int ret = fd_dict_getdict ( $self, &d );
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+			return NULL;
+		}
+		return d;
+	}
+	/* Since casting the pointer requires intelligence, we do it here instead of giving it to SWIG */
+	PyObject * getval() {
+		/* first, get the type */
+		enum dict_object_type t;
+		int ret = fd_dict_gettype ( $self, &t);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+			return NULL;
+		}
+		switch (t) {
+%define %GETVAL_CASE(TYPE,STRUCT)
+			case TYPE: {
+				PyObject * v = NULL;
+				struct STRUCT * data = NULL;
+				data = malloc(sizeof(struct STRUCT));
+				if (!data) {
+					DI_ERROR_MALLOC;
+					return NULL;
+				}
+				ret = fd_dict_getval($self, data);
+				if (ret != 0) {
+					DI_ERROR(ret, NULL, NULL);
+					free(data);
+					return NULL;
+				}
+				SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+				v = SWIG_NewPointerObj((void *)data, SWIGTYPE_p_##STRUCT, SWIG_POINTER_OWN );
+				Py_XINCREF(v);
+				SWIG_PYTHON_THREAD_END_BLOCK;
+				return v;
+			} break
+%enddef
+			%GETVAL_CASE( DICT_VENDOR, 	dict_vendor_data );
+			%GETVAL_CASE( DICT_APPLICATION, dict_application_data );
+			%GETVAL_CASE( DICT_TYPE, 	dict_type_data );
+			%GETVAL_CASE( DICT_ENUMVAL, 	dict_enumval_data );
+			%GETVAL_CASE( DICT_AVP, 	dict_avp_data );
+			%GETVAL_CASE( DICT_COMMAND, 	dict_cmd_data );
+			%GETVAL_CASE( DICT_RULE, 	dict_rule_data );
+			default:
+				DI_ERROR(EINVAL, PyExc_SystemError, "Internal error: Got invalid object type");
+		}
+		return NULL;
+	}
+}
+
 
 /* The following wrapper leaks memory each time an union avp_value is assigned an octet string.
@@ -244,6 +380,5 @@
 		$self->os.data = malloc(LENGTH);
 		if (!$self->os.data) {
-			fd_log_debug("Out of memory!\n");
-			PyErr_SetString(PyExc_MemoryError,"Not enough memory");
+			DI_ERROR_MALLOC;
 			return;
 		}
@@ -255,6 +390,5 @@
 		$self->os.data = malloc(os->len);
 		if (!$self->os.data) {
-			fd_log_debug("Out of memory!\n");
-			PyErr_SetString(PyExc_MemoryError,"Not enough memory");
+			DI_ERROR_MALLOC;
 			return;
 		}
@@ -266,14 +400,12 @@
 %extend avp_value_os {
 	void dump() {
-		if ($self) {
-			%#define LEN_MAX 20
-			int i, n=LEN_MAX;
-			if ($self->len < LEN_MAX)
-				n = $self->len;
-			fd_log_debug("l:%u, v:[", $self->len);
-			for (i=0; i < n; i++)
-				fd_log_debug("%02.2X", $self->data[i]);
-			fd_log_debug("] '%.*s%s'\n", n, $self->data, n == LEN_MAX ? "..." : "");
-		}
+		%#define LEN_MAX 20
+		int i, n=LEN_MAX;
+		if ($self->len < LEN_MAX)
+			n = $self->len;
+		fd_log_debug("l:%u, v:[", $self->len);
+		for (i=0; i < n; i++)
+			fd_log_debug("%02.2X", $self->data[i]);
+		fd_log_debug("] '%.*s%s'\n", n, $self->data, n == LEN_MAX ? "..." : "");
 	}
 }
@@ -282,6 +414,7 @@
 
 %{
-/* At the moment, only 1 callback is supported... */
+/* store the python callback function here */
 static PyObject * py_cleanup_cb = NULL;
+/* call it (might be called from a different thread than the interpreter, when session times out) */
 static void call_the_python_cleanup_callback(session_state * state, char * sid) {
 	PyObject *result;
@@ -290,7 +423,8 @@
 	
 	/* Call the function */
+	SWIG_PYTHON_THREAD_BEGIN_BLOCK;
 	result = PyEval_CallFunction(py_cleanup_cb, "(Os)", state, sid);
-	
 	Py_XDECREF(result);
+	SWIG_PYTHON_THREAD_END_BLOCK;
 	return;
 }
@@ -302,27 +436,20 @@
 %extend session_handler {
 	session_handler() {
-		fd_log_debug("Error: a cleanup callback parameter is required.\n");
-		PyErr_SetString(PyExc_SyntaxError,"Error: a cleanup callback parameter is required.\n");
+		DI_ERROR(EINVAL, PyExc_SyntaxError, "a cleanup callback parameter is required.");
 		return NULL;
 	}
-	session_handler(PyObject * PyCleanupCb) {
+	session_handler(PyObject * PyCb) {
 		struct session_handler * hdl = NULL;
 		int ret;
 		if (py_cleanup_cb) {
-			fd_log_debug("dbg_interactive supports only 1 session handler in python at the moment\n");
-			PyErr_SetString(PyExc_SyntaxError,"dbg_interactive supports only 1 session handler in python at the moment\n");
-			return NULL;
-		}
-		if (!PyCallable_Check(PyCleanupCb)) {
-			PyErr_SetString(PyExc_TypeError, "Need a callable object!");
-			return NULL;
-		}
-		py_cleanup_cb = PyCleanupCb;
+			DI_ERROR(EINVAL, PyExc_SyntaxError, "Only one session handler at a time is supported at the moment in this extension\n.");
+			return NULL;
+		}
+		py_cleanup_cb = PyCb;
 		Py_XINCREF(py_cleanup_cb);
 		
 		ret = fd_sess_handler_create_internal ( &hdl, call_the_python_cleanup_callback );
 		if (ret != 0) {
-			fd_log_debug("Error: %s\n", strerror(ret));
-			PyErr_SetString(PyExc_MemoryError,"Not enough memory");
+			DI_ERROR(ret, NULL, NULL);
 			return NULL;
 		}
@@ -330,17 +457,16 @@
 	}
 	~session_handler() {
-		if (self) {
-			struct session_handler * hdl = self;
-			int ret = fd_sess_handler_destroy(&hdl);
-			if (ret != 0) {
-				fd_log_debug("Error: %s\n", strerror(ret));
-			}
-			return;
-		}
+		struct session_handler * hdl = self;
+		int ret = fd_sess_handler_destroy(&hdl);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+		/* Now free the callback */
+		Py_XDECREF(py_cleanup_cb);
+		py_cleanup_cb = NULL;
+		return;
 	}
 	void dump() {
-		if ($self) {
-			fd_sess_dump_hdl(0, $self);
-		}
+		fd_sess_dump_hdl(0, $self);
 	}
 }
@@ -356,6 +482,5 @@
 		ret = fd_sess_new(&s, fd_g_config->cnf_diamid, "dbg_interactive", sizeof("dbg_interactive"));
 		if (ret != 0) {
-			fd_log_debug("Error: %s\n", strerror(ret));
-			PyErr_SetString(PyExc_MemoryError,"Not enough memory");
+			DI_ERROR(ret, NULL, NULL);
 			return NULL;
 		}
@@ -367,6 +492,5 @@
 		ret = fd_sess_new(&s, diamid, STRING, LENGTH);
 		if (ret != 0) {
-			fd_log_debug("Error: %s\n", strerror(ret));
-			PyErr_SetString(PyExc_MemoryError,"Not enough memory");
+			DI_ERROR(ret, NULL, NULL);
 			return NULL;
 		}
@@ -378,9 +502,9 @@
 		ret = fd_sess_fromsid(STRING, LENGTH, &s, &n);
 		if (ret != 0) {
-			fd_log_debug("Error: %s\n", strerror(ret));
-			PyErr_SetString(PyExc_MemoryError,"Not enough memory");
+			DI_ERROR(ret, NULL, NULL);
 			return NULL;
 		}
 		/* When defining n as OUTPUT parameter, we get something strange... Use fd_sess_fromsid if you need it */
+		#if 0
 		if (n) {
 			fd_log_debug("A new session has been created\n");
@@ -388,25 +512,22 @@
 			fd_log_debug("A session with same id already existed\n");
 		}
+		#endif /* 0 */
+		
 		return s;
 	}
 	~session() {
-		if (self) {
-			struct session * s = self;
-			int ret = fd_sess_reclaim(&s);
-			if (ret != 0) {
-				fd_log_debug("Error: %s\n", strerror(ret));
-			}
-			return;
-		}
+		struct session * s = self;
+		int ret = fd_sess_reclaim(&s);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+		return;
 	}
 	char * getsid() {
 		int ret;
 		char * sid = NULL;
-		if (!$self)
-			return NULL;
 		ret = fd_sess_getsid( $self, &sid);
 		if (ret != 0) {
-			fd_log_debug("Error: %s\n", strerror(ret));
-			PyErr_SetString(PyExc_MemoryError,"Problem...");
+			DI_ERROR(ret, NULL, NULL);
 			return NULL;
 		}
@@ -420,21 +541,17 @@
 		ret = fd_sess_settimeout( $self, &timeout );
 		if (ret != 0) {
-			fd_log_debug("Error: %s\n", strerror(ret));
-			PyErr_SetString(PyExc_MemoryError,"Problem...");
+			DI_ERROR(ret, NULL, NULL);
 		}
 	}
 	void dump() {
-		if ($self) {
-			fd_sess_dump(0, $self);
-		}
+		fd_sess_dump(0, $self);
 	}
 	void store(struct session_handler * handler, PyObject * state) {
 		int ret;
 		void * store = state;
-		Py_INCREF(state);
+		Py_XINCREF(state);
 		ret = fd_sess_state_store_internal(handler, $self, (void *) &store);
 		if (ret != 0) {
-			fd_log_debug("Error: %s\n", strerror(ret));
-			PyErr_SetString(PyExc_MemoryError,"Problem...");
+			DI_ERROR(ret, NULL, NULL);
 		}
 	}
@@ -444,18 +561,158 @@
 		ret = fd_sess_state_retrieve_internal(handler, $self, (void *) &state);
 		if (ret != 0) {
-			fd_log_debug("Error: %s\n", strerror(ret));
-			PyErr_SetString(PyExc_MemoryError,"Problem...");
+			DI_ERROR(ret, NULL, NULL);
+			return NULL;
 		}
 		if (state == NULL) {
+			Py_INCREF(Py_None);
 			return Py_None;
 		}
-		Py_DECREF(state);
 		return state;
 	}
 }	
 
+/****** ROUTING *********/
+
+struct rt_data {
+};
+
+%extend rt_data {
+	rt_data() {
+		struct rt_data * r = NULL;
+		int ret = fd_rtd_init(&r);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+			return NULL;
+		}
+		return r;
+	}
+	~rt_data() {
+		struct rt_data *r = self;
+		fd_rtd_free(&r);
+	}
+	void add(char * peerid, char * realm) {
+		int ret = fd_rtd_candidate_add($self, peerid, realm);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+	}
+	void remove(char * STRING, size_t LENGTH) {
+		fd_rtd_candidate_del($self, STRING, LENGTH);
+	}
+	void error(char * dest, char * STRING, size_t LENGTH, uint32_t rcode) {
+		int ret =  fd_rtd_error_add($self, dest, (uint8_t *)STRING, LENGTH, rcode);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+	}
+	struct fd_list * extract_li(int score = 0) {
+		struct fd_list * li = NULL;
+		fd_rtd_candidate_extract($self, &li, score);
+		return li;
+	}
+	PyObject * extract(int score = 0) {
+		struct fd_list * list = NULL, *li;
+		PyObject * rl;
+		fd_rtd_candidate_extract($self, &list, score);
+		rl = PyList_New(0);
+		SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+		for (li = list->next; li != list; li = li->next) {
+			PyList_Append(rl, SWIG_NewPointerObj((void *)li, SWIGTYPE_p_rtd_candidate, 0 ));
+		}
+		Py_XINCREF(rl);
+		SWIG_PYTHON_THREAD_END_BLOCK;
+		
+		return rl;
+	}
+}
+
+%extend rtd_candidate {
+	void dump() {
+		fd_log_debug("candidate %p\n", $self);
+		fd_log_debug("  id : %s\n",  $self->diamid);
+		fd_log_debug("  rlm: %s\n", $self->realm);
+		fd_log_debug("  sc : %d\n", $self->score);
+	}
+}
+
 
 
 /****** MESSAGES *********/
+
+struct msg {
+};
+
+%extend msg {
+	msg() {
+		DI_ERROR(EINVAL, PyExc_SyntaxError, "A DICT_COMMAND object parameter (or None) is required.");
+		return NULL;
+	}
+	msg(struct dict_object * model, int flags = MSGFL_ALLOC_ETEID) {
+		struct msg * m = NULL;
+		int ret = fd_msg_new( model, flags, &m);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+		return m;
+	}
+	~msg() {
+		int ret = fd_msg_free($self);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+	}
+	%newobject create_answer;
+	struct msg * create_answer(struct dictionary * dict = NULL, int flags = 0) {
+		/* if dict is not provided, attempt to get it from the request model */
+		struct dictionary * d = dict;
+		struct msg * m = $self;
+		int ret;
+		if (!d) {
+			struct dict_object * mo = NULL;
+			ret = fd_msg_model($self, &mo);
+			if (ret != 0) {
+				DI_ERROR(ret, NULL, "Cannot guess the dictionary to use, please provide it as parameter.");
+				return NULL;
+			}
+			ret = fd_dict_getdict ( mo, &d );
+			if (ret != 0) {
+				DI_ERROR(ret, NULL, "Cannot guess the dictionary to use, please provide it as parameter.");
+				return NULL;
+			}
+		}
+		ret = fd_msg_new_answer_from_req(d, &m, flags);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, "Cannot guess the dictionary to use, please provide it as parameter.");
+			return NULL;
+		}
+		return m;
+	}
+}
+
+struct avp {
+};
+
+%extend avp {
+	avp() {
+		DI_ERROR(EINVAL, PyExc_SyntaxError, "A DICT_AVP object parameter (or None) is required.");
+		return NULL;
+	}
+	avp(struct dict_object * model, int flags = 0) {
+		struct avp * a = NULL;
+		int ret = fd_msg_avp_new( model, flags, &a);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+		return a;
+	}
+	~avp() {
+		int ret = fd_msg_free($self);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+	}
+}
+	
+
 
 
