Initial commit
Change-Id: I6a4444e3c193dae437cd7929f4c39aba7b749efa
diff --git a/doc/dbg_interactive.py.sample b/doc/dbg_interactive.py.sample
new file mode 100644
index 0000000..8f1ffe8
--- /dev/null
+++ b/doc/dbg_interactive.py.sample
@@ -0,0 +1,783 @@
+# Example file for the dbg_interactive.fdx extension.
+#
+# This extension provides an interactive python interpreter console that allows
+# interacting with freeDiameter framework.
+#
+# The adaptation layer between Python and C is provided by SWIG (http://swig.org).
+# You may refer to SWIG documentation for more information on how the wrapper is generated and used.
+# The name of the module wrapping freeDiameter framework is: _fDpy
+#
+# Similar to all freeDiameter extensions, an optional filename can be specified in the
+# main freeDiameter.conf configuration file for the dbg_interactive.fdx extension.
+# If such file is provided, it will be passed to the python interpreter as a python script
+# to execute. Otherwise, the interpreter will be interactive.
+#
+# SWIG deals with structures as follow:
+# Given the structure:
+# struct foo { int a; }
+# The following functions are available to python (their C equivalent processing is given in [ ]):
+# s = new_foo() [ s = calloc(1, sizeof(struct foo)) ]
+# foo_a_set(s, 2) [ s->a = 2 ]
+# foo_a_get(s) [ returns s->a value ]
+# delete_foo(s) [ free(s) ]
+#
+# In addition, thanks to the proxy (aka shadow) class, we can also do the more user-friendly:
+# s = foo()
+# s.a = 2
+# s.a
+# del s
+#
+
+# The remaining of this file gives some examples of how to use the python interpreter.
+# Note that at the moment not 100% of the framework is usable.
+# You may have to extend some classes or write some typemaps in the source code
+# of the extension to do what you want.
+
+
+############# Compilation-time constants (from freeDiameter-host.h) ############
+
+# Display current version
+print "%s %d.%d.%d" % (FD_PROJECT_NAME, FD_PROJECT_VERSION_MAJOR, FD_PROJECT_VERSION_MINOR, FD_PROJECT_VERSION_REV)
+
+
+############# Debug ############
+
+# Change the global debug level of the framework (cvar contains all global variables)
+cvar.fd_g_debug_lvl = 1
+
+
+# Turn on debug for a specific function (if framework compiled with DEBUG support)
+cvar.fd_debug_one_function = "gc_th_fct"
+
+
+# Print messages to freeDiameter's debug facility
+# Note: the python version does not support printf-like argument list. The formating should be done in python.
+# See SWIG documentation about varargs functions for more information.
+fd_log(FD_LOG_NOTICE, "3 + 4 = %d" % (7))
+
+
+# Display some framework state information
+conf = fd_conf_dump();
+print conf;
+
+fd_peer_dump_list(0)
+fd_servers_dump(0)
+fd_ext_dump(0)
+
+
+############# Global variables ############
+
+# Display the local Diameter Identity:
+print "Local Diameter Identity:", cvar.fd_g_config.cnf_diamid
+
+# Display realm, using the low-level functions (skip proxy classe definitions):
+print "Realm:", _fDpy.fd_config_cnf_diamrlm_get(_fDpy.cvar.fd_g_config)
+
+
+
+############# Lists ############
+
+# Note: we use different names from the C API here, for usability.
+l1 = fd_list() # Will be our sentinel
+l2 = fd_list()
+l3 = fd_list()
+l1.isempty()
+l1.insert_next(l2) # l1 -> l2
+l1.isempty()
+l1.insert_prev(l3) # l1 -> l2 -> l3 (circular list)
+l1.dump()
+l3.detach() # l1 -> l2
+l4=fd_list()
+l5=fd_list()
+l3.insert_next(l4) # l3 -> l4
+l3.insert_next(l5) # l3 -> l5 -> l4
+l1.concat(l3) # l1 -> l2 -> l5 -> l4
+
+elements = l1.enum_as() # default: enumerates as fd_list. Warning: this a copy, changing the python list has no effect on the underlying fd_list.
+for li in elements:
+ li.dump()
+
+del elements
+del l2
+del l3
+del l4
+del l5
+l1.isempty() # The destructor has an implicit fd_list_unlink call
+del l1
+
+
+############# Hash ############
+
+hex(fd_os_hash("hello world")) # It accepts binary data
+
+
+############# Dictionary ############
+
+##### Create a dedicated dictionary for our tests
+d = dictionary()
+d.dump()
+
+# New vendor
+v = dict_vendor_data()
+v.vendor_id = 123
+v.vendor_name = "My test vendor"
+my_vendor = d.new_obj(DICT_VENDOR, v)
+del v
+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
+a.application_name = "My test appl"
+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
+t.type_name = "My integer AVP"
+my_type_int = d.new_obj(DICT_TYPE, t, my_appl)
+t.type_base = AVP_TYPE_OCTETSTRING
+t.type_name = "My binary buffer AVP"
+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"
+c.enum_value.i32 = 3
+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()
+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
+a.avp_name = "my integer avp"
+a.avp_flag_mask = AVP_FLAG_MANDATORY
+a.avp_basetype = AVP_TYPE_INTEGER32
+my_avp_int = d.new_obj(DICT_AVP, a, my_type_int)
+
+a.avp_vendor = 123
+a.avp_name = "my OS avp"
+a.avp_flag_mask = AVP_FLAG_MANDATORY + AVP_FLAG_VENDOR
+a.avp_flag_val = AVP_FLAG_VENDOR
+a.avp_basetype = AVP_TYPE_OCTETSTRING
+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
+c.cmd_name = "My-Python-Request"
+c.cmd_flag_mask = CMD_FLAG_REQUEST + CMD_FLAG_PROXIABLE
+c.cmd_flag_val = CMD_FLAG_REQUEST + CMD_FLAG_PROXIABLE
+my_req = d.new_obj(DICT_COMMAND, c, my_appl)
+c.cmd_name = "My-Python-Answer"
+c.cmd_flag_val = CMD_FLAG_PROXIABLE
+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
+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
+
+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
+
+d.dump()
+del d
+
+####### Now play with the "real" dictionary
+
+gdict = cvar.fd_g_config.cnf_dict
+
+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
+
+dict = avp.getdict()
+del dict
+
+
+############# Sessions ############
+
+# handler
+def my_cleanup(state,sid):
+ print "Cleaning up python state for session:", sid
+ print "Received state:", state
+ del state
+
+hdl = session_handler(my_cleanup)
+hdl.dump()
+del hdl
+
+# Session
+hdl = session_handler(my_cleanup)
+s1 = session()
+s1.getsid()
+s2 = session("this.is.a.full.session.id")
+r,s3,isnew = fd_sess_fromsid("this.is.a.full.session.id") # use this call if "isnew" is really needed...
+s4 = session("host.id", "optional.part")
+s4.settimeout(30) # the python wrapper takes a number of seconds as parameter for simplicity
+s4.dump()
+
+# 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)
+for c in list.enum_as("struct rtd_candidate *"):
+ print "%s (%s): %s" % (c.diamid, c.realm, c.score)
+
+del rd
+
+
+# A rt_fwd callback has the following prototype:
+def my_rtfwd_cb(msg):
+ print "Forwarding the following message:"
+ msg.dump()
+ return [ 0, msg ] # return None instead of msg to stop forwarding.
+
+fwdhdl = fd_rt_fwd_hdl( my_rtfwd_cb, RT_FWD_REQ )
+
+
+# A rt_out cb has the following prototype:
+def my_rtout_cb(msg, list):
+ print "Sending out the following message:"
+ msg.dump()
+ print "The possible candidates are:"
+ for c in list.enum_as("struct rtd_candidate *"):
+ print "%s (%s): %s" % (c.diamid, c.realm, c.score)
+ return 0 # returns an error code (standard errno values)
+
+outhdl = fd_rt_out_hdl( my_rtout_cb ) # a priority can be specified as 2nd parameter, default is 0.
+
+
+
+
+
+############# Messages, AVPs ############
+
+## AVP
+
+# 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
+
+# Set values
+val = avp_value()
+val.u32 = 123
+vi.setval(None) # this cleans a previous value (usually not needed)
+vi.setval(val)
+val.os = "my.origin.host"
+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 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
+
+# It is also possible to pass MSGFL_* flags in second parameter (ALLOC_ETEID is default)
+msg_no_eid = msg(None, 0)
+msg_no_eid.dump()
+del msg_no_eid
+
+# Create from dictionary
+dwr_dict = cvar.fd_g_config.cnf_dict.search ( DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request" )
+dwr = msg(dwr_dict)
+dwr.dump()
+
+# Create msg from a binary buffer (then you should call parse_dict and parse_rules methods)
+dwr2 = msg("\x01\x00\x00\x14\x80\x00\x01\x18\x00\x00\x00\x00\x00\x00\x00\x00\x1b\xf0\x00\x01")
+
+# Create answer from request (optional parameters: dictionary to use, and flags):
+dwr3 = msg(cvar.fd_g_config.cnf_dict.search ( DICT_COMMAND, CMD_BY_NAME, "Device-Watchdog-Request" ))
+dwa3 = dwr3.create_answer()
+dwr3cpy = dwa3.get_query()
+
+
+## Other functions with AVPs & messages
+
+# Add the AVPs in the message
+dwr.add_child(oh)
+oh.add_next(vsai) # equivalent to add_child on the parent
+
+# Create a network byte buffer from the message
+dwr.bufferize()
+
+# Get first child AVP (fast)
+avp = dwr.first_child()
+
+# then:
+avp = avp.get_next() # when last AVP, returns None
+
+
+# Get all 1st level children (slower) -- warning, changes to the python list will not be reflected on the underlying message. read-only use.
+dwr.children()
+# example use:
+for a in dwr.children():
+ a.dump(0) # 0 means: dump only this object, do not walk the tree
+
+
+# Search the first AVP of a given type
+oh_dict = cvar.fd_g_config.cnf_dict.search( DICT_AVP, AVP_BY_NAME, "Origin-Host")
+oh = dwr.search( oh_dict )
+
+# After adding AVPs, the length in the message header is outdated, refresh as follow:
+dwr.update_length()
+
+# Get dictionary model for a message or avp
+dwr.model()
+oh.model().dump()
+
+# Retrieve the header of messages & avp:
+dwr_hdr = dwr.header()
+dwr_hdr.msg_version
+dwr_hdr.msg_hbhid
+
+oh_hdr = oh.header()
+hex(oh_hdr.avp_flags)
+oh_hdr.avp_vendor
+oh_hdr.avp_value.os.as_str()
+
+
+# Get or set the routing data
+rd = rt_data()
+dwr.set_rtd(rd)
+rd = dwr.get_rtd()
+
+# Test if message is routable
+dwr.is_routable()
+
+# Which peer the message was received from (when received from network)
+dwr.source()
+
+# The session corresponding to this message (returns None when no Session-Id AVP is included)
+dwr.get_session()
+
+
+# Parse a buffer
+buf = "\x01\x00\x00@\x80\x00\x01\x18\x00\x00\x00\x00\x00\x00\x00\x00N\x10\x00\x00\x00\x00\x01\x08@\x00\x00\x16my.origin.host\x00\x00\x00\x00\x01\x04@\x00\x00\x14\x00\x00\x01\n@\x00\x00\x0c\x00\x00\x00{"
+mydwr = msg(buf)
+# Resolve objects in the dictionary. Return value is None or a struct pei_error in case of problem.
+mydwr.parse_dict() # if not using the fD global dict, pass it as parameter
+err = mydwr.parse_rules()
+err.pei_errcode
+
+
+# Grouped AVPs are browsed with same methods as messages:
+gavp = dwr.children()[1]
+gavp.first_child().dump()
+gavp.children()
+
+
+# Send a message:
+mydwr = msg(buf)
+mydwr.send()
+
+# Optionally, a callback can be registered when a request 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 = msg(buf)
+mydwr.send(send_callback, some_object)
+
+# Again optionally, a time limit can be specified in this case as follow:
+mydwr.send(send_callback, some_object, 10)
+# In that case, if no answer / error is received after 10 seconds (the value specified),
+# the callback is called with the request as parameter.
+# Testing for timeout case is done by using msg.is_request()
+def send_callback(msg, obj):
+ if (msg.is_request()):
+ print "Request timed out without answer:"
+ else:
+ print "Received answer:"
+ msg.dump()
+ print "Associated data:"
+ obj
+ return None
+
+
+# Set a result code in an answer message.
+mydwr = msg(buf)
+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()
+
+
+## Variants:
+# All the previous calls are suitable to queue Python objects.
+# In order to interact with objects queued / poped by C counterpart,
+# a second parameter must be passed to specify the object type,
+# as follow:
+ev = fd_event()
+ev.code = FDEV_DUMP_EXT
+cvar.fd_g_config.cnf_main_ev.post(ev, "struct fd_event *")
+
+# Similarly, for *get, we can specify the structure that was queued:
+myqueue.get("struct fd_event *")
+myqueue.tryget("struct fd_event *")
+myqueue.timedget(3, "struct fd_event *")
+
+del myqueue
+
+
+############# HOOKS ############
+
+def my_hook_cb(type, msg, peer, other, oldpmd):
+ print "callback type ", type, " called: ", msg, other, oldpmd
+ return "this is the new pmd"
+
+# Create a wrapped fd_hook_data_hdl:
+datahdl = fd_hook_data_hdl()
+
+# Register the hook callback:
+hdl = fd_hook_hdl(1 << HOOK_MESSAGE_SENT, my_hook_cb, datahdl)
+
+
+
+
+############# PEERS ############
+
+# Get the list of peers defined in the system
+# (we are supposed to readlock fd_g_peers_rw before accessing this list)
+cvar.fd_g_peers_rw.rdlock()
+peers = cvar.fd_g_peers.enum_as("struct peer_hdr *")
+cvar.fd_g_peers_rw.unlock()
+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 = PI_P4_TCP
+
+
+# Add this peer into the framework.
+np.add()
+
+# It is possible to specify a callback for when the connection completes or fails with 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 like this:
+np.add(add_cb)
+
+
+# Search a peer by its diameter id (returns a peer_hdr object if found) -- similar to fd_peer_getbyid
+p = peer_search("nas.domain.aaa")
+
+
+## Validation callback (see fd_peer_validate_register documentation)
+
+# cb2 prototype:
+def my_validate_cb2(pinfo):
+ print "Cb2 callback trigged for peer %s" % (pinfo.pi_diamid)
+ # Usually, this would be used only to check some TLS properties,
+ # which is not really possible yet through the python interpreter...
+ return 0 # return an error code if the peer is not validated
+
+# cb prototype:
+def my_validate_cb(pinfo):
+ print "Validate callback trigged for peer %s" % (pinfo.pi_diamid)
+ # If the peer is not allowed to connect:
+ #return -1
+ # If the peer is authorized:
+ #return 1
+ # In addition, if IPsec is allowed,
+ #pinfo.config.pic_flags.sec = PI_SEC_NONE
+ # If no decision has been made:
+ #return 0
+ # If the peer is temporarily authorized but a second callback must be called after TLS negociation:
+ return my_validate_cb2
+
+# Register the callback, it will be called on new incoming connections.
+peer_validate_register(my_validate_cb)
+
+
+
+############# ENDPOINTS ############
+
+ep = fd_endpoint("129.168.168.192")
+
+# with port:
+ep = fd_endpoint("129.168.168.192", 3868)
+
+# With different flags:
+ep = fd_endpoint("129.168.168.192", 3868, EP_FL_PRIMARY)
+
+# Add IP information for the peer
+np = peer_info()
+ep.add_merge(np.pi_endpoints)
+fd_ep_dump(0, np.pi_endpoints)
+
+
+
+############# POSIX functions wrappers ############
+
+# The interface also provides wrappers around base POSIX
+# synchronization functions:
+
+m = pthread_mutex_t()
+m.lock()
+m.unlock()
+
+c = pthread_cond_t()
+c.signal()
+c.broadcast()
+c.wait(m)
+c.timedwait(m, 5) # it takes a relative time
+
+r = pthread_rwlock_t()
+r.rdlock()
+r.unlock()
+r.wrlock()