changeset 638:9448cba86673

Improved usability of dbg_interactive
author Sebastien Decugis <sdecugis@nict.go.jp>
date Fri, 17 Dec 2010 18:41:19 +0900
parents 22e8fac3b2d6
children 95a784729cac
files doc/dbg_interactive.py.sample extensions/dbg_interactive/dbg_interactive.i extensions/dbg_interactive/dictionary.i extensions/dbg_interactive/dispatch.i extensions/dbg_interactive/messages.i extensions/dbg_interactive/peers.i extensions/dbg_interactive/queues.i extensions/dbg_interactive/sessions.i include/freeDiameter/libfreeDiameter.h libfreeDiameter/messages.c
diffstat 10 files changed, 791 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/doc/dbg_interactive.py.sample	Thu Dec 16 18:56:41 2010 +0900
+++ b/doc/dbg_interactive.py.sample	Fri Dec 17 18:41:19 2010 +0900
@@ -103,7 +103,8 @@
 
 
 ############# Hash ############
-hex(fd_hash("hello world"))	# A typemap is applied to accept binary data
+
+hex(fd_hash("hello world"))	# It accepts binary data
 
 
 ############# Dictionary ############
@@ -121,6 +122,10 @@
 d.dump()
 d.vendors_list()
 
+# Compact invocation also possible:
+v2 = dict_vendor_data(124, "My test vendor 2")
+del v2
+
 # New application
 a = dict_application_data()
 a.application_id = 99
@@ -128,6 +133,9 @@
 my_appl = d.new_obj(DICT_APPLICATION, a, my_vendor)
 del a
 
+a2 = dict_application_data(99, "My test appl 2")
+del a2
+
 # New type (callbacks are not supported yet...)
 t = dict_type_data()
 t.type_base = AVP_TYPE_INTEGER32
@@ -138,6 +146,9 @@
 my_type_os = d.new_obj(DICT_TYPE, t, my_appl)
 del t
 
+t2 = dict_type_data(AVP_TYPE_UNSIGNED32, "u32 type")
+del t2
+
 # Constants
 c = dict_enumval_data()
 c.enum_name = "AVP_VALUE_TROIS"
@@ -150,6 +161,12 @@
 d.new_obj(DICT_ENUMVAL, c, my_type_os)
 del c
 
+c2 = dict_enumval_data("enum 23", 23)  # The constructor only accepts unsigned32, for other values, set them afterwards
+c3 = dict_enumval_data("enum other")
+c3.os = "other value"
+del c2
+del c3
+
 # AVP
 a = dict_avp_data()
 a.avp_code = 234
@@ -166,6 +183,16 @@
 my_avp_os = d.new_obj(DICT_AVP, a, my_type_os)
 del a
 
+a2 = dict_avp_data(235, "no vendor, not mandatory", AVP_TYPE_OCTETSTRING)
+a3 = dict_avp_data(236, "vendor 12, not mandatory", AVP_TYPE_OCTETSTRING, 12)
+a4 = dict_avp_data(237, "vendor 12, mandatory", AVP_TYPE_OCTETSTRING, 12, 1)
+a5 = dict_avp_data(238, "no vendor, mandatory", AVP_TYPE_OCTETSTRING, 0, 1)
+del a2
+del a3
+del a4
+del a5
+
+
 # Command
 c = dict_cmd_data()
 c.cmd_code = 345
@@ -178,6 +205,11 @@
 my_ans = d.new_obj(DICT_COMMAND, c, my_appl)
 del c
 
+c2 = dict_cmd_data(346, "Second-Request", 1) # Default created with PROXIABLE flag.
+c3 = dict_cmd_data(346, "Second-Answer",  0) 
+del c2
+del c3
+
 # Rule
 r = dict_rule_data()
 r.rule_avp = my_avp_int
@@ -194,6 +226,12 @@
 d.dump()
 del d
 
+r2 = dict_rule_data(my_avp_int, RULE_REQUIRED) # min & max are optional parameters, default to -1
+r3 = dict_rule_data(my_avp_int, RULE_REQUIRED, 2, 3) # min is 2, max is 3
+r4 = dict_rule_data(my_avp_int, RULE_FIXED_HEAD) # The r4.rule_order = 1 by default, change afterwards if needed.
+del r2
+del r3
+del r4
 
 ####### Now play with the "real" dictionary
 
@@ -271,10 +309,11 @@
 
 ## AVP
 
-# Create empty (as for messages, pass None or a dictionary object as 1st param, and flags as optional 2nd param)
+# Create empty
 blank_avp = avp()
 del blank_avp
 
+# Create from dictionary definitions
 oh = avp(cvar.fd_g_config.cnf_dict.search ( DICT_AVP, AVP_BY_NAME, "Origin-Host"))	                # Octet String
 vi = avp(cvar.fd_g_config.cnf_dict.search ( DICT_AVP, AVP_BY_NAME, "Vendor-Id"))                        # U32
 vsai = avp(cvar.fd_g_config.cnf_dict.search ( DICT_AVP, AVP_BY_NAME, "Vendor-Specific-Application-Id")) # Grouped
@@ -288,10 +327,15 @@
 oh.setval(val)
 vsai.add_child(vi) # call as add_child(vi, 1) to add the new AVP at the beginning, default is at the end
 
+# It is possible to initialize the AVP with a blank value as follow:
+blank_with_value = avp(None, AVPFL_SET_BLANK_VALUE)
+# it enables this without doing the setval call:
+blank_with_value.header().avp_value.u32 = 12
+
 
 ## Messages
 
-# Create empty
+# Create empt (as for avps, pass None or a dictionary object as 1st param, and flags as optional 2nd param)y
 a_msg = msg()
 a_msg.dump()
 del a_msg
@@ -357,7 +401,8 @@
 oh_hdr = oh.header()
 hex(oh_hdr.avp_flags)
 oh_hdr.avp_vendor
-oh_hdr.avp_value.os.dump()  # The initial avp value must be set with setval(), but then this accessor is allowed.
+oh_hdr.avp_value.os.as_str() 
+
 
 # Get or set the routing data
 rd = rt_data()
@@ -389,21 +434,206 @@
 gavp.children()
 
 
+# Send a message:
+mydwr.send()
+
+# Optionaly, a callback can be registered when a message is sent, with an optional object.
+# This callback takes the answer message as parameter and should return None or a message. (cf. fd_msg_send)
+def send_callback(msg, obj):
+    print "Received answer:"
+    msg.dump()
+    print "Associated data:"
+    obj
+    return None
+
+mydwr.send(send_callback, some_object)
+
+
+# Set a result code in an answer message.
+dwa = mydwr.create_answer()
+dwa.rescode_set()   # This adds the DIAMETER_SUCCESS result code
+dwa.rescode_set("DIAMETER_LIMITED_SUCCESS" )   # This adds a different result code
+dwa.rescode_set("DIAMETER_LIMITED_SUCCESS", "Something went not so well" )   # This adds a different result code + specified Error-Message
+dwa.rescode_set("DIAMETER_INVALID_AVP", None, faulty_avp )   # This adds a Failed-AVP
+dwa.rescode_set("DIAMETER_SUCCESS", None, None, 1 )   # This adds origin information (see fd_msg_rescode_set's type_id for more info)
+
+# Set the origin to local host
+mydwr.add_origin()  # adds Origin-Host & Origin-Realm
+mydwr.add_origin(1) # adds Origin-State-Id in addition.
+
+
+############# DISPATCH (aka. server application) ############
+
+# As for sessions, only one dispatch handler can be registered in this extension at the moment.
+# The callback for the handler has the following syntax:
+def dispatch_cb_model(inmsg, inavp, insession):
+   print "Callback trigged on message: "
+   inmsg.dump()
+   # inavp is None or the AVP that trigged the callback, depending on how it was registered.
+   if inavp:
+     print "From the following AVP:"
+     inavp.dump()
+   else:
+     print "No AVP"
+   # Session is provided only if a Session-Id is in the message
+   if insession:
+     print "The session is: ", insession.getsid()
+   else:
+     print "No session"
+   # Now, for the return value.
+   # This callback must return 3 elements:
+   # - an integer which is interpreted as an error code (errno.h)
+   # - a message or None, depending on the next item
+   # - an enum disp_action value, with the same meaning as in C (see libfreeDiameter.h)
+   del inmsg
+   return [ 0, None, DISP_ACT_CONT ]
+
+
+### Example use: rebuild the server-side of test_app.fdx in python
+
+# The following block defines the dictionary objects from the test_app.fdx application that we use on the remote peer
+gdict = cvar.fd_g_config.cnf_dict
+d_si = gdict.search ( DICT_AVP, AVP_BY_NAME, "Session-Id" )
+d_oh  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Origin-Host" )
+d_or  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Origin-Realm" )
+d_dh  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Destination-Host" )
+d_dr  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Destination-Realm" )
+d_rc  = gdict.search ( DICT_AVP, AVP_BY_NAME, "Result-Code" )
+d_vnd = gdict.new_obj(DICT_VENDOR, 	dict_vendor_data(999999, 	"app_test_py vendor") )
+d_app = gdict.new_obj(DICT_APPLICATION, dict_application_data(0xffffff, "app_test_py appli"), d_vnd)
+d_req = gdict.new_obj(DICT_COMMAND, 	dict_cmd_data(0xfffffe, "Test_py-Request", 1), d_app)
+d_ans = gdict.new_obj(DICT_COMMAND, 	dict_cmd_data(0xfffffe, "Test_py-Answer",  0), d_app)
+d_avp = gdict.new_obj(DICT_AVP, 	dict_avp_data(0xffffff, "app_test_py avp", AVP_TYPE_INTEGER32, 999999 ))
+gdict.new_obj(DICT_RULE, dict_rule_data(d_si, RULE_FIXED_HEAD, 1, 1), d_req)
+gdict.new_obj(DICT_RULE, dict_rule_data(d_si, RULE_FIXED_HEAD, 1, 1), d_ans)
+gdict.new_obj(DICT_RULE, dict_rule_data(d_avp, RULE_REQUIRED, 1, 1), d_req)
+gdict.new_obj(DICT_RULE, dict_rule_data(d_avp, RULE_REQUIRED, 1, 1), d_ans)
+gdict.new_obj(DICT_RULE, dict_rule_data(d_oh, RULE_REQUIRED, 1, 1), d_req)
+gdict.new_obj(DICT_RULE, dict_rule_data(d_oh, RULE_REQUIRED, 1, 1), d_ans)
+gdict.new_obj(DICT_RULE, dict_rule_data(d_or, RULE_REQUIRED, 1, 1), d_req)
+gdict.new_obj(DICT_RULE, dict_rule_data(d_or, RULE_REQUIRED, 1, 1), d_ans)
+gdict.new_obj(DICT_RULE, dict_rule_data(d_dr, RULE_REQUIRED, 1, 1), d_req)
+gdict.new_obj(DICT_RULE, dict_rule_data(d_dh, RULE_OPTIONAL, 0, 1), d_req)
+gdict.new_obj(DICT_RULE, dict_rule_data(d_rc, RULE_REQUIRED, 1, 1), d_ans)
+
+# Now, create the Test_app server callback:
+def test_app_cb(inmsg, inavp, insession):
+   tval = inmsg.search(d_avp).header().avp_value.u32
+   print "Py ECHO Test message from '%s' with test value %x, replying..." % (inmsg.search(d_oh).header().avp_value.os.as_str(), tval)
+   answ = inmsg.create_answer()
+   answ.rescode_set()
+   answ.add_origin()
+   ta = avp(d_avp, AVPFL_SET_BLANK_VALUE)
+   ta.header().avp_value.u32 = tval
+   answ.add_child(ta)
+   return [ 0, answ, DISP_ACT_SEND ]
+
+# Register the callback for dispatch thread:
+hdl = disp_hdl(test_app_cb, DISP_HOW_CC, disp_when(d_app, d_req))  # disp_when() takes 0 to 4 arguments as follow: (app=NULL, cmd=NULL, avp=NULL, val=NULL)
+
+# Don't forget to register the application in the daemon for CER/CEA capabilities.
+fd_disp_app_support ( d_app, d_vnd, 1, 0 )
+
+
+###  For the fun, the client part of the test_app:
+
+def receive_answer(ans, testval):
+   try:
+     tval = ans.search(d_avp).header().avp_value.u32
+   except:
+     print "Error in receive_answer: no Test-AVP included"
+     tval = 0
+   try:
+     print "Py RECV %x (expected: %x) Status: %d From: '%s'" % (tval, testval, ans.search(d_rc).header().avp_value.u32, ans.search(d_oh).header().avp_value.os.as_str())
+   except:
+     print "Error in receive_answer: Result-Code or Origin-Host are missing"
+   del ans
+   return None
+
+import random
+
+def send_query(destrealm="localdomain"):
+   qry = msg(d_req)
+   sess = session()
+   tv = random.randint(1, 1<<32)
+   # Session-Id
+   a = avp(d_si, AVPFL_SET_BLANK_VALUE)
+   a.header().avp_value.os = sess.getsid()
+   qry.add_child(a)
+   # Destination-Realm
+   a = avp(d_dr, AVPFL_SET_BLANK_VALUE)
+   a.header().avp_value.os = destrealm
+   qry.add_child(a)
+   # Origin-Host, Origin-Realm
+   qry.add_origin()
+   # Test-AVP
+   a = avp(d_avp, AVPFL_SET_BLANK_VALUE)
+   a.header().avp_value.u32 = tv
+   qry.add_child(a)
+   print "Py SEND %x to '%s'" % (tv, destrealm)
+   qry.send(receive_answer, tv)
+
+send_query()
+
+
+############# FIFO queues ############
+
+myqueue = fifo()
+
+# enqueue any object
+myqueue.post(3)
+myqueue.post("blah")
+myqueue.post( [ 3, 2 ] )
+
+# Simple get (blocks when the queue is empty)
+myqueue.get()
+
+# Try get: returns the next object, or None if the queue is empty
+myqueue.tryget()
+
+# timed get: like get, but returns None after x seconds
+myqueue.timedget(3)
+
+# Show the number of items in the queue
+myqueue.length()
+
+del myqueue
 
 
 
+############# PEERS ############
 
+# Get the list of peers defined in the system 
+# (well, we are supposed actually to readlock fd_g_peers_rw before doing this, but it should be fine most of the time)
+peers = cvar.fd_g_peers.enum_as("struct peer_hdr *")
+for p in peers:
+   print "Peer:", p.info.pi_diamid
+
+
+# Create a new peer
+np = peer_info()
+np.pi_diamid = "nas.localdomain"
+np.config.pic_flags.pro4 = 1   # 1 for TCP, for some reason PI_P4_TCP is not defined
 
 
 
 
-######################### old stuff (need update) ######################
+# Add this peer into the framework.
+np.add()
+
+# It is possible to specify a callback for when the connection completes or fails to this peer.
+# The prototype is as follow:
+def add_cb(peer):
+    if peer:
+        if peer.runtime.pir_state == STATE_OPEN:
+	   print "Connection to peer '%s' completed" % (peer.pi_diamid)
+	   # can find more information in peer.runtime.*
+	else:
+	   print "Connection to peer '%s' failed (state:%d)" % (peer.pi_diamid, peer.runtime.pir_state)
+    else:
+        print "The peer has been destroyed before it completed the connection."
+
+# Then add the peer simply like this:
+np.add(add_cb)
 
 
-# Create a new peer_info structure and add the peer to the framework.
-mypeer = peer_info()
-mypeer.pi_diamid = "nas.testbed.aaa"
-mypeer.config.pic_flags.pro4 = 1   # 1 for TCP, for some reason PI_P4_TCP is not defined
-fd_peer_add(mypeer, "python", None, None)
-del mypeer
-
--- a/extensions/dbg_interactive/dbg_interactive.i	Thu Dec 16 18:56:41 2010 +0900
+++ b/extensions/dbg_interactive/dbg_interactive.i	Fri Dec 17 18:41:19 2010 +0900
@@ -128,6 +128,11 @@
 }
 
 
+/* Forward declaration for the peers module */
+%{
+static void fd_add_cb(struct peer_info *peer, void *data);
+%}
+
 /*********************************************************
  Now, create wrappers for (almost) all objects from fD API 
  *********************************************************/
--- a/extensions/dbg_interactive/dictionary.i	Thu Dec 16 18:56:41 2010 +0900
+++ b/extensions/dbg_interactive/dictionary.i	Fri Dec 17 18:41:19 2010 +0900
@@ -241,4 +241,171 @@
 			fd_log_debug("%02.2X", $self->data[i]);
 		fd_log_debug("] '%.*s%s'\n", n, $self->data, n == LEN_MAX ? "..." : "");
 	}
+	%cstring_output_allocate_size(char ** outbuffer, size_t * outlen, free(*$1));
+	void as_str ( char ** outbuffer, size_t * outlen ) {
+		char * b;
+		if (!$self->len) {
+			*outlen = 0;
+			*outbuffer = NULL;
+			return;
+		}
+		b = malloc($self->len);
+		if (!b) {
+			DI_ERROR_MALLOC;
+			return;
+		}
+		memcpy(b, $self->data, $self->len);
+		*outlen = $self->len;
+		*outbuffer = b;
+	}
 }
+
+
+/* Allow constructors with parameters for the dict_*_data */
+%extend dict_vendor_data {
+	dict_vendor_data(uint32_t id = 0, char * name = NULL) {
+		struct dict_vendor_data * d = (struct dict_vendor_data *)calloc(1, sizeof(struct dict_vendor_data));
+		if (!d) {
+			DI_ERROR_MALLOC;
+			return NULL;
+		}
+		d->vendor_id = id;
+		if (name) {
+			d->vendor_name = strdup(name);
+			if (!d->vendor_name) {
+				DI_ERROR_MALLOC;
+				free(d);
+				return NULL;
+			}
+		}
+		return d;
+	}
+}
+
+%extend dict_application_data {
+	dict_application_data(uint32_t id = 0, char * name = NULL) {
+		struct dict_application_data * d = (struct dict_application_data *)calloc(1, sizeof(struct dict_application_data));
+		if (!d) {
+			DI_ERROR_MALLOC;
+			return NULL;
+		}
+		d->application_id = id;
+		if (name) {
+			d->application_name = strdup(name);
+			if (!d->application_name) {
+				DI_ERROR_MALLOC;
+				free(d);
+				return NULL;
+			}
+		}
+		return d;
+	}
+}
+
+%extend dict_type_data {
+	dict_type_data(enum dict_avp_basetype base = 0, char * name = NULL) {
+		struct dict_type_data * d = (struct dict_type_data *)calloc(1, sizeof(struct dict_type_data));
+		if (!d) {
+			DI_ERROR_MALLOC;
+			return NULL;
+		}
+		d->type_base = base;
+		if (name) {
+			d->type_name = strdup(name);
+			if (!d->type_name) {
+				DI_ERROR_MALLOC;
+				free(d);
+				return NULL;
+			}
+		}
+		return d;
+	}
+}
+
+%extend dict_enumval_data {
+	dict_enumval_data(char * name = NULL, uint32_t v = 0) {
+		struct dict_enumval_data * d = (struct dict_enumval_data *)calloc(1, sizeof(struct dict_enumval_data));
+		if (!d) {
+			DI_ERROR_MALLOC;
+			return NULL;
+		}
+		if (name) {
+			d->enum_name = strdup(name);
+			if (!d->enum_name) {
+				DI_ERROR_MALLOC;
+				free(d);
+				return NULL;
+			}
+		}
+		d->enum_value.u32 = v;
+		return d;
+	}
+}
+
+%extend dict_avp_data {
+	dict_avp_data(uint32_t code = 0, char * name = NULL, enum dict_avp_basetype basetype = 0, uint32_t vendor = 0, int mandatory=0) {
+		struct dict_avp_data * d = (struct dict_avp_data *)calloc(1, sizeof(struct dict_avp_data));
+		if (!d) {
+			DI_ERROR_MALLOC;
+			return NULL;
+		}
+		if (name) {
+			d->avp_name = strdup(name);
+			if (!d->avp_name) {
+				DI_ERROR_MALLOC;
+				free(d);
+				return NULL;
+			}
+		}
+		d->avp_code = code;
+		d->avp_basetype = basetype;
+		d->avp_vendor = vendor;
+		if (vendor) {
+			d->avp_flag_val |= AVP_FLAG_VENDOR;
+			d->avp_flag_mask |= AVP_FLAG_VENDOR;
+		}
+		d->avp_flag_mask |= AVP_FLAG_MANDATORY;
+		if (mandatory)
+			d->avp_flag_val |= AVP_FLAG_MANDATORY;
+		return d;
+	}
+}
+
+%extend dict_cmd_data {
+	dict_cmd_data(uint32_t code = 0, char * name = NULL, int request = 1) {
+		struct dict_cmd_data * d = (struct dict_cmd_data *)calloc(1, sizeof(struct dict_cmd_data));
+		if (!d) {
+			DI_ERROR_MALLOC;
+			return NULL;
+		}
+		if (name) {
+			d->cmd_name = strdup(name);
+			if (!d->cmd_name) {
+				DI_ERROR_MALLOC;
+				free(d);
+				return NULL;
+			}
+		}
+		d->cmd_code = code;
+		d->cmd_flag_mask = CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE;
+		d->cmd_flag_val = CMD_FLAG_PROXIABLE | ( request ? CMD_FLAG_REQUEST : 0 );
+		return d;
+	}
+}
+
+%extend dict_rule_data {
+	dict_rule_data(struct dict_object *avp = NULL, enum rule_position pos = 0, int min = -1, int max = -1 ) {
+		struct dict_rule_data * d = (struct dict_rule_data *)calloc(1, sizeof(struct dict_rule_data));
+		if (!d) {
+			DI_ERROR_MALLOC;
+			return NULL;
+		}
+		d->rule_avp = avp;
+		d->rule_position = pos;
+		d->rule_order = 1;
+		d->rule_min = min;
+		d->rule_max = max;
+		return d;
+	}
+}
+
--- a/extensions/dbg_interactive/dispatch.i	Thu Dec 16 18:56:41 2010 +0900
+++ b/extensions/dbg_interactive/dispatch.i	Fri Dec 17 18:41:19 2010 +0900
@@ -35,3 +35,120 @@
 
 /* Do not include this directly, use dbg_interactive.i instead */
 
+/****** DISPATCH *********/
+
+
+%{
+/* store the python callback function here */
+static PyObject * py_dispatch_cb = NULL;
+static int        py_dispatch_cb_n = 0;
+/* call it (will be called from a different thread than the interpreter, when message arrives) */
+static int call_the_python_dispatch_callback(struct msg **msg, struct avp *avp, struct session *session, enum disp_action *action) {
+	PyObject *PyMsg, *PyAvp, *PySess;
+	PyObject *result = NULL;
+	int ret = 0;
+	
+	if (!py_dispatch_cb)
+		return ENOTSUP;
+	
+	SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+	/* Convert the arguments */
+	PyMsg  = SWIG_NewPointerObj((void *)*msg,     SWIGTYPE_p_msg,     0 );
+	PyAvp  = SWIG_NewPointerObj((void *) avp,     SWIGTYPE_p_avp,     0 );
+	PySess = SWIG_NewPointerObj((void *) session, SWIGTYPE_p_session, 0 );
+	
+	/* Call the function */
+	result = PyEval_CallFunction(py_dispatch_cb, "(OOO)", PyMsg, PyAvp, PySess);
+	
+	/* The result is supposedly composed of: [ ret, *msg, *action ] */
+	if ((result == NULL) || (!PyList_Check(result)) || (PyList_Size(result) != 3)) {
+		fd_log_debug("Error: The Python callback did not return [ ret, msg, action ].\n");
+		ret = EINVAL;
+		goto out;
+	}
+	
+	/* Convert the return values */
+	if (!SWIG_IsOK(SWIG_AsVal_int(PyList_GetItem(result, 0), &ret))) {
+		fd_log_debug("Error: Cannot convert the first return value to integer.\n");
+		ret = EINVAL;
+		goto out;
+	}
+	if (ret) {
+		TRACE_DEBUG(INFO, "The Python callback returned the error code %d (%s)\n", ret, strerror(ret));
+		goto out;
+	}
+	
+	if (!SWIG_IsOK(SWIG_ConvertPtr(PyList_GetItem(result, 1), (void *)msg, SWIGTYPE_p_msg, SWIG_POINTER_DISOWN))) {
+		fd_log_debug("Error: Cannot convert the second return value to message.\n");
+		ret = EINVAL;
+		goto out;
+	}
+	
+	if (!SWIG_IsOK(SWIG_AsVal_int(PyList_GetItem(result, 2), (int *)action))) {
+		fd_log_debug("Error: Cannot convert the third return value to integer.\n");
+		ret = EINVAL;
+		goto out;
+	}
+	
+	TRACE_DEBUG(FULL, "Python callback return: *action = %d\n", *action);
+out:	
+	Py_XDECREF(result);
+	
+	SWIG_PYTHON_THREAD_END_BLOCK;
+	return ret;
+}
+%}
+
+struct disp_hdl {
+};
+
+%nodefaultctor disp_hdl;
+%extend disp_hdl {
+	disp_hdl(PyObject * PyCb, enum disp_how how, struct disp_when * when) {
+		struct disp_hdl * hdl = NULL;
+		int ret;
+		if (py_dispatch_cb && (py_dispatch_cb != PyCb)) {
+			DI_ERROR(EINVAL, PyExc_SyntaxError, "Only one dispatch callback is supported at the moment in this extension\n.");
+			return NULL;
+		}
+		py_dispatch_cb = PyCb;
+		py_dispatch_cb_n += 1;
+		Py_XINCREF(py_dispatch_cb);
+		
+		ret = fd_disp_register ( call_the_python_dispatch_callback, how, when, &hdl );
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+			return NULL;
+		}
+		return hdl;
+	}
+	~disp_hdl() {
+		struct disp_hdl * hdl = self;
+		int ret = fd_disp_unregister(&hdl);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+		/* Now free the callback */
+		Py_XDECREF(py_dispatch_cb);
+		py_dispatch_cb_n -= 1;
+		if (!py_dispatch_cb_n)
+			py_dispatch_cb = NULL;
+		return;
+	}
+}
+
+
+%extend disp_when {
+	disp_when(struct dict_object * app = NULL, struct dict_object * command = NULL, struct dict_object * avp = NULL, struct dict_object * value = NULL) {
+      		struct disp_when * w = (struct disp_when *)calloc(1, sizeof(struct disp_when));
+		if (!w) {
+			DI_ERROR_MALLOC;
+			return NULL;
+		}
+		w->app = app;
+		w->command = command;
+		w->avp = avp;
+		w->value = value;
+		return w;
+	}
+}
--- a/extensions/dbg_interactive/messages.i	Thu Dec 16 18:56:41 2010 +0900
+++ b/extensions/dbg_interactive/messages.i	Fri Dec 17 18:41:19 2010 +0900
@@ -37,6 +37,49 @@
 
 /****** MESSAGES *********/
 
+%{
+struct anscb_py_layer {
+	PyObject * cb;
+	PyObject * data;
+};
+
+/* If a python callback was provided, it is received in cbdata */
+static void anscb_python(void *cbdata, struct msg ** msg) {
+	/* The python callback is received in cbdata */
+	PyObject * result, *PyMsg;
+	struct anscb_py_layer * l = cbdata;
+	
+	if (!l) {
+		fd_log_debug("Internal error! Python callback disappeared...\n");
+		return;
+	}
+	
+	SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+	
+	if (!msg || !*msg) {
+		PyMsg = Py_None;
+	} else {
+		PyMsg = SWIG_NewPointerObj((void *)*msg,     SWIGTYPE_p_msg,     0 );
+	}
+	
+	result = PyEval_CallFunction(l->cb, "(OO)", PyMsg, l->data);
+	Py_XDECREF(l->cb);
+	Py_XDECREF(l->data);
+	free(l);
+	
+	/* The callback is supposed to return a message or NULL */
+	if (!SWIG_IsOK(SWIG_ConvertPtr(result, (void *)msg, SWIGTYPE_p_msg, SWIG_POINTER_DISOWN))) {
+		fd_log_debug("Error: Cannot convert the return value to message.\n");
+		*msg = NULL;
+	}
+	
+	Py_XDECREF(result);
+	
+	SWIG_PYTHON_THREAD_END_BLOCK;
+	
+}
+%}
+
 struct msg {
 };
 
@@ -74,6 +117,34 @@
 			DI_ERROR(ret, NULL, NULL);
 		}
 	}
+	
+	/* SEND THE MESSAGE */
+	%delobject send; /* when this has been called, the msg must not be freed anymore */
+	void send(PyObject * PyCb = NULL, PyObject * data = NULL) {
+		int ret;
+		struct msg * m = $self;
+		struct anscb_py_layer * l = NULL;
+		
+		if (PyCb) {
+			l = malloc(sizeof(struct anscb_py_layer));
+			if (!l) {
+				DI_ERROR_MALLOC;
+				return;
+			}
+
+			Py_XINCREF(PyCb);
+			Py_XINCREF(data);
+			l->cb = PyCb;
+			l->data = data;
+		}
+		
+		ret = fd_msg_send(&m, PyCb ? anscb_python : NULL, l);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+	}
+	
+	/* Create an answer */
 	%delobject create_answer; /* when this has been called, the original msg should not be freed anymore */
 	struct msg * create_answer(struct dictionary * dict = NULL, int flags = 0) {
 		/* if dict is not provided, attempt to get it from the request model */
@@ -312,6 +383,23 @@
 			DI_ERROR(ret, NULL, NULL);
 		}
 	}
+	
+	/* Set the result code */
+	void rescode_set(char * rescode = "DIAMETER_SUCCESS", char * errormsg = NULL, struct avp * optavp = NULL, int type_id = 0) {
+		int ret = fd_msg_rescode_set( $self, rescode, errormsg, optavp, type_id );
+		if (ret) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+	}
+	
+	/* Add the origin */
+	void add_origin(int osi = 0) {
+		int ret = fd_msg_add_origin( $self, osi );
+		if (ret) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+	}
+	
 }
 
 struct avp {
--- a/extensions/dbg_interactive/peers.i	Thu Dec 16 18:56:41 2010 +0900
+++ b/extensions/dbg_interactive/peers.i	Fri Dec 17 18:41:19 2010 +0900
@@ -35,3 +35,55 @@
 
 /* Do not include this directly, use dbg_interactive.i instead */
 
+/****** PEERS *********/
+
+%{
+static void fd_add_cb(struct peer_info *peer, void *data) {
+	/* Callback called when the peer connection completes (or fails) */
+	PyObject *PyPeer, *PyFunc;
+	PyObject *result = NULL;
+	
+	if (!data) {
+		TRACE_DEBUG(INFO, "Internal error: missing callback\n");
+		return;
+	}
+	PyFunc = data;
+	
+	SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+	
+	/* Convert the argument */
+	PyPeer  = SWIG_NewPointerObj((void *)peer,     SWIGTYPE_p_peer_info,     0 );
+	
+	/* Call the function */
+	result = PyEval_CallFunction(PyFunc, "(O)", PyPeer);
+	
+	Py_XDECREF(result);
+	Py_XDECREF(PyFunc);
+	
+	SWIG_PYTHON_THREAD_END_BLOCK;
+	return;
+}
+%}
+
+%extend peer_info {
+	/* Wrapper around fd_peer_add to allow calling the python callback */
+	void add(PyObject * PyCb=NULL) {
+		int ret;
+		
+		if (PyCb) {
+			Py_XINCREF(PyCb);
+			ret = fd_peer_add ( $self, "dbg_interactive", fd_add_cb, PyCb );
+		} else {
+			ret = fd_peer_add ( $self, "dbg_interactive", NULL, NULL );
+		}
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+	}
+
+	/* Add an endpoint */
+	void add_endpoint(char * endpoint) {
+		fd_log_debug("What is the best way in python to pass an endpoint? (ip + port)");
+	}
+
+}
--- a/extensions/dbg_interactive/queues.i	Thu Dec 16 18:56:41 2010 +0900
+++ b/extensions/dbg_interactive/queues.i	Fri Dec 17 18:41:19 2010 +0900
@@ -35,3 +35,111 @@
 
 /* Do not include this directly, use dbg_interactive.i instead */
 
+/****** FIFO QUEUES *********/
+
+struct fifo {
+};
+
+%extend fifo {
+	fifo() {
+		struct fifo * q = NULL;
+		int ret = fd_fifo_new(&q);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+			return NULL;
+		}
+		return q;
+	}
+	~fifo() {
+		struct fifo *q = self;
+		fd_fifo_del(&q);
+	}
+	
+	/* Move all elements to another queue */
+	void move(struct fifo * to) {
+		int ret = fd_fifo_move($self, to, NULL);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+	}
+	
+	/* Get the length of the queue (nb elements) */
+	int length() {
+		int l;
+		int ret = fd_fifo_length ( $self, &l );
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+		return l;
+	}
+
+	/* Is the threashold function useful here? TODO... */
+	
+	/* Post an item */
+	void post(PyObject * item) {
+		int ret;
+		PyObject * i = item;
+		
+		Py_XINCREF(i);
+		ret = fd_fifo_post($self, &i);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+	}
+	
+	/* Get (blocking) */
+	PyObject * get() {
+		int ret;
+		PyObject * i = NULL;
+		
+		ret = fd_fifo_get($self, &i);
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+		
+		return i;
+	}
+	
+	/* TryGet (non-blocking, returns None on empty queue) */
+	PyObject * tryget() {
+		int ret;
+		PyObject * i = NULL;
+		
+		ret = fd_fifo_tryget($self, &i);
+		if (ret == EWOULDBLOCK) {
+			Py_XINCREF(Py_None);
+			return Py_None;
+		}
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+		
+		return i;
+	}
+	
+	/* TimedGet (blocking for a while) */
+	PyObject * timedget(long seconds) {
+		int ret;
+		PyObject * i = NULL;
+		struct timespec ts;
+		
+		clock_gettime(CLOCK_REALTIME, &ts);
+		ts.tv_sec += seconds;
+		
+		ret = fd_fifo_timedget($self, &i, &ts);
+		if (ret == ETIMEDOUT) {
+			Py_XINCREF(Py_None);
+			return Py_None;
+		}
+		if (ret != 0) {
+			DI_ERROR(ret, NULL, NULL);
+		}
+		
+		return i;
+	}
+	
+}		
+	
+	
+	
+	
--- a/extensions/dbg_interactive/sessions.i	Thu Dec 16 18:56:41 2010 +0900
+++ b/extensions/dbg_interactive/sessions.i	Fri Dec 17 18:41:19 2010 +0900
@@ -58,11 +58,8 @@
 struct session_handler {
 };
 
+%nodefaultctor session_handler;
 %extend session_handler {
-	session_handler() {
-		DI_ERROR(EINVAL, PyExc_SyntaxError, "a cleanup callback parameter is required.");
-		return NULL;
-	}
 	session_handler(PyObject * PyCb) {
 		struct session_handler * hdl = NULL;
 		int ret;
@@ -96,6 +93,7 @@
 	}
 }
 
+
 struct session {
 };
 
--- a/include/freeDiameter/libfreeDiameter.h	Thu Dec 16 18:56:41 2010 +0900
+++ b/include/freeDiameter/libfreeDiameter.h	Fri Dec 17 18:41:19 2010 +0900
@@ -1876,6 +1876,9 @@
 };
 
 /* Some flags used in the functions bellow */
+#define AVPFL_SET_BLANK_VALUE	0x01	/* When creating an AVP, initialize its value to a blank area */
+#define AVPFL_MAX		AVPFL_SET_BLANK_VALUE	/* The biggest valid flag value */
+	
 #define MSGFL_ALLOC_ETEID	0x01	/* When creating a message, a new end-to-end ID is allocated and set in the message */
 #define MSGFL_ANSW_ERROR	0x02	/* When creating an answer message, set the 'E' bit and use the generic error ABNF instead of command-specific ABNF */
 #define MSGFL_ANSW_NOSID	0x04	/* When creating an answer message, do not add the Session-Id even if present in request */
@@ -1889,7 +1892,7 @@
  *
  * PARAMETERS:
  *  model 	: Pointer to a DICT_AVP dictionary object describing the avp to create, or NULL.
- *  flags	: Flags to use in creation (not used yet, should be 0).
+ *  flags	: Flags to use in creation (AVPFL_*).
  *  avp 	: Upon success, pointer to the new avp is stored here.
  *
  * DESCRIPTION: 
--- a/libfreeDiameter/messages.c	Thu Dec 16 18:56:41 2010 +0900
+++ b/libfreeDiameter/messages.c	Fri Dec 17 18:41:19 2010 +0900
@@ -139,6 +139,7 @@
 
 
 /* Macro to validate a MSGFL_ value */
+#define CHECK_AVPFL(_fl) ( ((_fl) & (- (AVPFL_MAX << 1) )) == 0 )
 #define CHECK_MSGFL(_fl) ( ((_fl) & (- (MSGFL_MAX << 1) )) == 0 )
 
 
@@ -199,7 +200,7 @@
 	TRACE_ENTRY("%p %x %p", model, flags, avp);
 	
 	/* Check the parameters */
-	CHECK_PARAMS(  avp && CHECK_MSGFL(flags)  );
+	CHECK_PARAMS(  avp && CHECK_AVPFL(flags)  );
 	
 	if (model) {
 		enum dict_object_type 	 dicttype;
@@ -224,6 +225,10 @@
 		new->avp_public.avp_vendor  = dictdata.avp_vendor;
 	}
 	
+	if (flags & AVPFL_SET_BLANK_VALUE) {
+		new->avp_public.avp_value = &new->avp_storage;
+	}
+	
 	/* The new object is ready, return */
 	*avp = new;
 	return 0;
"Welcome to our mercurial repository"