Mercurial > hg > freeDiameter
view doc/dbg_interactive.py.sample @ 1308:19d01728f26f
Updated changelog
author | Sebastien Decugis <sdecugis@freediameter.net> |
---|---|
date | Sun, 18 Sep 2016 20:43:43 +0800 |
parents | e5010975da35 |
children |
line wrap: on
line source
# 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()