OpenSIPS 3.1 开发手册(八)-模块间API

https://www.opensips.org/Documentation/Development-Manual

目录

17.  模块API

17.1  TM module

17.2  RR Module

17.3  Dialog 模块

18.  视频教程


17.  模块API

        在OpenSIPS内部,一个模块可能需要访问其它模块的的功能(一个非常常见的实例是模块需要对dialog执行某种操作,因此需要访问dialog模块的部分功能)。OpenSIPS 不是直接访问目标模块,而是大量使用“模块导出”API的概念。
        在OpenSIPS 中通用的方法是目标模块实现一种可加载的API----实际上,转换为填充一个数据结构,携带需要导出的函数指针,以及各种指示行为控制的的其它成员。

        需要操作上述API的模块,首先在mod_init 中调用函数绑定目标模块所提供的API,然后操纵接收到的结构体。

        以下是 OpenSIPS中最常用的模块API。
 

17.1  TM module

17.2  RR Module

        RR ( Record Route )导出的API在modules/rr/api.h里。
         从一般功能的角度来看,RR模块是负责控制请求的Record-Route 部分,继而对后续请求根据Route头域路由请求。RR模块是最简单的模块,它提供非常简单的dialog感知功能,允许在 Record-Route头域里存储信息,然后从后续请求的Route 头域中提取出来。

        首先,你必须绑定RR模块的API,获取后续使用的数据结构。绑定函数是:

/*
Parameters : rrb is the API output to be further used

Returns : 0 in case of success and -1 in case of failure
*/
inline static int load_rr_api( struct rr_binds *rrb );


rr_binds 结构的细节如下:

struct rr_binds {
        add_rr_param_t      add_rr_param;
        check_route_param_t check_route_param;
        is_direction_t      is_direction;
        get_route_param_t   get_route_param;
        register_rrcb_t     register_rrcb;
        get_remote_target_t get_remote_target;
        get_route_set_t     get_route_set;
        /* whether or not the append_fromtag parameter is enabled in the RR module */
        int                 append_fromtag;
        /* the number of routes removed within the loose routing process */
        int*                removed_routes;
        /* the type of routing done, when comparing the previous and the next hop
        Both can be either strict or loose routers, thus here we have 4 different options :
        ROUTING_LL - loose to loose routing
        ROUTING_SL - strict to loose routing
        ROUTING_SS - strict to strict routing
        ROUTING_LS - loose to strict routing
        */
        int*                routing_type;

        loose_route_t       loose_route;
        record_route_t      record_route;
};


以下是API函数及其用法:

/* Adds a parameter to the requests's Record-Route URI. The API supports the use case
   where the Record-Routed header will be further added.
   The function is to be used for marking certain dialogs that can
   be identified from the sequential requests - since the Route
   headers in the sequential requests will also contain our added
   params, which we'll be able to fetch with get_route_param ( see below )

   The function returns 0 on success. Otherwise, -1 is returned.

   Parameters :
     * struct sip_msg* msg - request that will has the parameter
       “param” added to its Record-Route header.
     * str* param - parameter to be added to the Record-Route
       header - it must be in “;name=value” format.
*/
typedef  int (*add_rr_param_t)(struct sip_msg* msg, str* param);


/*    The function checks for the request “msg” if the URI parameters
   of the local Route header (corresponding to the local server)
   matches the given regular expression “re”. It MUST be call
   after the loose_route was done.

   The function returns 0 on success. Otherwise, -1 is returned.

   * struct sip_msg* msg - request that will has the Route
       header parameters checked.
   * regex_t* re - compiled regular expression to be checked
       against the Route header parameters.
*/
int (*check_route_param_t)(struct sip_msg* msg, regex_t* rem);


/*    The function checks the flow direction of the request “msg”. As
   for checking it's used the “ftag” Route header parameter, the
   append_fromtag (see Section 1.4.1, “append_fromtag (integer)”
   module parameter must be enables. Also this must be call only
   after the loose_route is done.

   The function returns 0 if the “dir” is the same with the
   request's flow direction. Otherwise, -1 is returned.

   Meaning of the parameters is as follows:
     * struct sip_msg* msg - request that will have the direction
       checked.
     * int direction - direction to be checked against. It may be
       RR_FLOW_UPSTREAM ( from callee to caller ) or
       RR_FLOW_DOWNSTREAM ( from caller to callee ).
*/
typedef  int (*is_direction_t)(struct sip_msg* msg, int direction);


/*
   The function search in to the “msg”'s Route header parameters
   the parameter called “name” and returns its value into “val”.
   It must be call only after the loose_route is done.

   The function returns 0 if parameter was found (even if it has
   no value). Otherwise, -1 is returned.

   Meaning of the parameters is as follows:
     * struct sip_msg* msg - request that will have the Route
       header parameter searched.
     * str *name - contains the Route header parameter to be
       serached.
     * str *val - returns the value of the searched Route header
       parameter if found. It might be empty string if the
       parameter had no value.
*/
typedef  int (*get_route_param_t)(struct sip_msg*, str*, str*);


/*
   The function register a new callback (along with its
   parameter). The callback will be called when a loose route will
   succesfully be performed for the local address.

   The function returns 0 on success. Otherwise, -1 is returned.

    Meaning of the parameters is as follows:
     * rr_cb_t func - callback function to be registered.
     * void *param - parameter to be passed to the callback
       function.
     * short prior - parameter to set the priority. The callbacks
        will be executed in order from small to big priority - thus
        to be used for ordering callbacks that depend on each other.

*/
typedef int (*register_rrcb_t)( rr_cb_t func, void *param, short prior);


/* Function to be registered as callback within the RR API :
     * struct sip_msg* req - request that is currently being processed
     * str *rr_param - the parameters in our server's Route header
     * str *param - the custom parameter provided at the callback registration
*/
typedef void (rr_cb_t) (struct sip_msg* req, str *rr_param, void *param);


/*
   Function used to fetch the far-end remote target for the current message.
   Depending on the type routing done ( see the '''routing_type''' API member )
   the remote target can be either in the initial Request URI, in the current
   Request-URI or in the last route header. The API function take care to
   correctly identify which scenario is correct.
   The API function MUST be called after loose_route() was called.

   The function returns the str pointer with the remote target, or NULL in case of error.

   Meaning of the parameters is as follows:
        -* struct sip_msg* msg - request that the remote target will be extracted from
*/
typedef  str* (*get_remote_target_t)(struct sip_msg* msg);


/*
    Function used to fetch the route set from the current SIP message.
    The function takes into account the actual loose_route() done, and properly discards
    the proxy's own Route headers from the SIP message. Thus, the function must be called
    after loose_route() was done.

    The function will return an array of str structures, or NULL in case of error. The
    nr_routes parameter will indicate the size of the returned array

    Meaning of the parameters is as follows:
        -* struct sip_msg* msg - request that the remote target will be extracted from
        -* int* nr_routes - the size of the returned array
*/
typedef  str* (*get_route_set_t)(struct sip_msg*,int *nr_routes);


/*
    Function to be used when for routing a request according to the Route headers present
    in it and to the type of Routing ( loose vs strict ) that needs to be used.

    The function will return 0 in case of success ( request is succesfully routed ). Otherwise,
    -1 is returned.

   Meaning of the parameters is as follows:
        -* struct sip_msg* msg - request to be routed

*/
typedef  int (*loose_route_t)(struct sip_msg* msg);


/*
    Function to be used when record-routing an initial request. The function will add
    one or two Record-Route headers , depending if there are any interface changes and
    if r2 is enabled. Also, if any parameters are provided, they will be added to all the
    Record-Route headers that the function internally adds.

    Returns 0 in case of success. Otherwise, -1 will be returned.

    Meaning of the parameters is as follows:
        -* struct sip_msg* msg - request to be record routed
        -* str* params - parameters to be added to the Record-Route headers
*/
typedef  int (*record_route_t)(struct sip_msg* msg, str* params);


        下面是一个其它模块绑定RR API的示例,注册一个回调函数,然后检查请求的方向,并检查某个特定的参数。

...
#include "../rr/api.h"
...
struct rr_binds my_rrb;
...
...
int mod_init(void) {
        ...
        ...
        /* load the RR API */
        if (load_rr_api( &my_rrb )!=0) {
            LM_ERR("can't load RR API\n");
            goto error;
        }

        if (!my_rrb.append_fromtag) {
            LM_ERR("The append_fromtag parameter is not set, but we need it for detecting the direction of requests \n");
            goto error;
        }
        ...
        ...
        /* register a RR callback */
        if (my_rrb.register_rrcb(my_callback,0,0))!=0) {
            LM_ERR("can't register RR callback\n");
            goto error;
        }
        ...
        ...
}

void my_callback(struct sip_msg* msg,str* rr_param,void *param)
{
        str name = str_init("ftag");
        str val;

        LM_INFO("Received a new sequential request from %s\n",
        my_rrb.is_direction( msg, RR_FLOW_UPSTREAM)?"callee":"caller");

        if (my_rrb.get_route_param(msg,&name,&val) == 0) {
                LM_INFO("We have the ftag parameter with value [%.*s]\n",val.len,val.s);
        }
}

17.3  Dialog 模块

        Dialog模块导出的API在modules/dialog/dlg_load.h文件中。
        dialog 模块为OpenSIPS 提供感知dialog的能力。它的功能是跟踪当前的dialog并提供与之相关的信息(比如说当前活跃dialog数量)。

        除了跟踪之外,dialog还提供其它功能,比如说对每个dialog标志属性(通过dialog持久化数据),dialog分析及终止(超时或外部触发的终止)。

        首先,你需要绑定dialog模块的API并获取后续操作的数据结构。绑定函数是:

/*
Parameters : dlgb is the API output to be further used
Returns the 0 in case of success and -1 in case of failure
*/
static inline int load_dlg_api( struct dlg_binds *dlgb );


dlg_binds 结构体定义如下:

struct dlg_binds {
        register_dlgcb_f     register_dlgcb;
        create_dlg_f         create_dlg;
        get_dlg_f            get_dlg;
        add_profiles_f       add_profiles;
        search_dlg_profile_f search_profile;
        set_dlg_profile_f    set_profile;
        unset_dlg_profile_f  unset_profile;
        get_profile_size_f   get_profile_size;
        store_dlg_value_f    store_dlg_value;
        fetch_dlg_value_f    fetch_dlg_value;
        terminate_dlg_f      terminate_dlg;

        match_dialog_f       match_dialog;
        validate_dialog_f    validate_dialog;
        fix_route_dialog_f   fix_route_dialog;
};


以下是API及用法说明 :

/*
To be used to register a new dialog based callback.
The function returns 0 on success. Otherwise, -1 is returned.

Parameters :
        * struct dlg_cell *dlg : the dialog that the registered callback belongs to
        * int cb_types : The type of registered callback ( can be a bitmask of multiple callback types). The options are as follows :
                        * DLGCB_LOADED - callback will get called when a new dialog is loaded into memory at startup ( from a database ) or at runtime ( from a database via external MI call or via the binary replication interface ). This callback must be registered alone, and the dlg_cell* provided at registration time must be NULL ( since it's a global cb type, not associated to any particular dialog )
                        * DLGCB_CREATED - callback will get called when a new dialog is created. The callback will be called when the dialog is fully initialized ( create_dialog() was called either from script or from the API and the Transaction associated to the initial invite is fully initialised as well ). This callback must be registered alone, and the dlg_cell* provided at registration time must be NULL ( since it's a global cb type, not associated to any particular dialog )
                        * DLGCB_FAILED - callback will get called when a particular dialog fails to establish ( 3xx, 4xx, 5xx or 6xx reply received and relayed ). This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_CONFIRMED - callback will get called when a particular dialog gets established ( 2xx reply received ). This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_REQ_WITHIN - callback will get called when a sequential request is matched as belonging to a particular dialog ( either via loose_route() or match_dialog() calling from the script). This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_TERMINATED - callback will get called when a dialog gets terminated. Reasons here are BYE routing or external Termination ( MI, API, etc ). This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_EXPIRED - callback will get called when a dialog lives past it's assigned timeout ( see the $DLG_timeout script pvar ). This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_EARLY - callback will get called when the first provisional reply ( 1xx ) is received for the registered dialog. This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_RESPONSE_FWDED - callback will get called when a reply is forwarded during the initial state of the dialog setup ( usually provisional 180, 183, etc ). This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_RESPONSE_WITHIN - callback will get called for all replies forwarded by OpenSIPS for sequential requests belonging to the current dialog. This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_MI_CONTEXT - callback will get called when the 'dlg_list_ctx' MI function is called. Useful when modules binded to the dialog module API want to append nodes to the dlg_list_ctx MI response tree. This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_DESTROY - callback will get called when the dialog is getting ready to be destroyed. At the time of the callback calling, the dialog is unlinked from the main hash, but it is not freed yet. This is a per dialog callback, so the first dlg parameter MUST be provided.
                        * DLGCB_SAVED - callback will get called when the dialog information is synchronized to the database ( either initial insertion, updating various fields in the DB or removing the dialog from the DB ). This is a per dialog callback, so the first dlg parameter MUST be provided.
        func - the actual callback function that will be executed.
        param - parameter that will be sent to the callback when called
        free_func - function to free the callback parameter at destroy time.
*/
typedef int (*register_dlgcb_f)(struct dlg_cell* dlg, int cb_types,
                dialog_cb func, void *param, param_free_cb free_func);
/* callback function prototype */
typedef void (dialog_cb) (struct dlg_cell* dlg, int type,
                struct dlg_cb_params * params);
/* function to free the callback param */
typedef void (param_free_cb) (void *param);


/*
        Creates the dialog structure for the current initial INVITE message and will keep track of the call for the rest of it's lifetime.
        Returns 0 in case of success. Otherwise, -1 is returned.
        Parameters :
                msg : the initial INVITE sip message
                flags : flags altering the behavior of the create dialog. Options here are :
                        * DLG_FLAG_BYEONTIMEOUT - dialog will be terminated from the middle, by OpenSIPS, when the dialog lifetime is exceeded.
                        * DLG_FLAG_PING_CALLER - ping the caller with OPTIONS messages to detect if the call is still up
                        * DLG_FLAG_PING_CALLEE - ping the callee with OPTIONS messages to detect if the call is still up
*/
typedef int (*create_dlg_f)(struct sip_msg *req,int flags);


/*
        Returns the current dialog pointer. In case of no created dialog or other internall errors, NULL is returned.
*/
typedef struct dlg_cell *(*get_dlg_f) (void);


/*
        Parses and creates the provided profile definitions.
        Returns 0 in case of success. Otherwise, -1 is returned.
        Parameters :
                profiles - the NULL terminated string containing one or multiple profile definitions ( sepparated by ';' )
                has_value - whether our profies will contain values with counters, or they will be just stand-alone counters
*/
typedef int (*add_profiles_f)(char* profiles, unsigned int has_value);


/*
        Looks up and returns the profile definition associated to the provided name. In case the profile is not found, or of internal error, NULL is returned.
        Parameters :
                name : str containing the name of a single profile definition
*/
typedef struct dlg_profile_table* (*search_dlg_profile_f)(str *name);


/*
        Sets the current dialog to belong in the provided profile definition.
        Returns 0 in case of success, and -1 otherwise.
        Parameters :
                msg : the SIP message currently being processed
                value : the value the dialog will be associated with within the provided profile
                profile : the main profile the dialog will be linked to
                is_replicated : whether or not this dialog was originated on the current machine, or we received it via replication mechanisms. Controls whether the cachedb counters should be increased for the current dialog profile or not.
*/
typedef int (*set_dlg_profile_f)(struct sip_msg *msg, str *value,
                        struct dlg_profile_table *profile, char is_replicated);


/*
        The opposite of the set_dlg_profile_f API function.
        Returns 0 in case of success, and -1 otherwise.
        Parameters :
                msg : the SIP message currently being processed
                value : the value the dialog will be de-associated with within the provided profile
                profile : the main profile the dialog will be un-linked from

*/
typedef int (*unset_dlg_profile_f)(struct sip_msg *msg, str *value,
                         struct dlg_profile_table *profile);


/*
        Returns the number of dialogs belonging to the current profile.
        Parameters :
                profile : the profile definition
                value : the value to filter the profile. Can be missing, the size of all individual values within a profile will be returned.
*/
typedef unsigned int (*get_profile_size_f)(struct dlg_profile_table *profile,
                                                                                str *value);


/*
        Stores an opaque key-value mapping inside the dialog structure, which can be fetched at a later time based on the current dialog. If the key already exists, it will be overwritten.
        Returns 0 in case of success. Otherwise, -1 is returned.
        Parameters :
                dlg : the dialog pointer to link the key to
                name : name of the key to store within the provided dialog
                val : the value to be mapped to the provided key
*/
typedef int (*store_dlg_value_f)(struct dlg_cell *dlg,
                str *name, str *val);


/*
        Fetch a previously stored value within the provided dialog.
        Returns 0 in case of success. Otherwise, -1 is returned.
        Parameters :
                dlg : the dialog pointer to fetch the key from
                name : the name of the key to fetch
                val : output parameter, the value of the key will be stored here
                val_has_buf - whether we have a buffer allocated for fetching the key's value or not. If 0, the dialog module will return a static buffer that it reuses for all key values.
*/
typedef int (*fetch_dlg_value_f)(struct dlg_cell *dlg,
                str *name, str *val, int val_has_buf);


/*
        Terminates an ongoing dialog ( by sending BYE messages both ways )
        Returns 0 in case of success. Otherwise, -1 is returned.
        Parameters :
                h_entry : The hash bucket ID for the dialog that we want to terminate
                h_id : The ID of the dialog element within our hash bucket
                reason : An opaque string describing the reason for terminating the dialog. Can be later fetched from the script by the user
*/
typedef int (*terminate_dlg_f)(unsigned int h_entry, unsigned int h_id,str *reason);


        以下是一个简单的示例,看看其它模块是如何绑定Dialog API并执行几个dialog相关的操作的。

...
#include "../dialog/dlg_load.h"
...
...
struct dlg_binds my_dlgb;
...
...
int mod_init(void) {
        ...
        ...
        /* load the dialog API */
        if (load_dlg_api(&my_dlgb)!=0) {
                LM_ERR("failed to find dialog API - is dialog module loaded?\n");
                goto error;
        }

        /* make sure we get notified of all upcoming created dialogs */
        if (my_dlgb.register_dlgcb(NULL,DLGCB_CREATED,new_created_dialog_cb,NULL,NULL) != 0 ) {
                LM_ERR("Failed to register initial dlg callback \n");
                goto error;
        }

        ...
        ...
}

void new_created_dialog_cb(struct dlg_cell *did, int type,
                struct dlg_cb_params * params)
{
        time_t curtime;
        str create_time_key = str_init("created_at");
        str create_time;

        time(&curtime);
        LM_INFO("Dialog was created ! \n");

        if ( my_dlgb->register_dlgcb(did,
        DLGCB_CONFIRMED, dialog_confirmed_cb, NULL, NULL) != 0) {
                LM_ERR("Failed to register CB for dialog establishment \n");
                return;
        }

        if ( my_dlgb->register_dlgcb(did,
        DLGCB_REQ_WITHIN, dialog_sequential_cb, NULL, NULL) != 0) {
                LM_ERR("Failed to register CB for dialog sequential requests \n");
                return;
        }

        create_time.s = ctime(&curtime);
        create_time.len = strlen(create_time.s);

        if ( my_dlgb.store_dlg_value(did,&create_time_key,&create_time) != 0) {
                LM_ERR("Failed to store our create string key \n");
                return;
        }

}

void dialog_confirmed_cb(struct dlg_cell *did, int type,
                struct dlg_cb_params * params)
{
        str create_time_key = str_init("created_at");
        str create_time;

        if ( my_dlgb.fetch_dlg_value(did,&create_time_key,&create_time,0) != 0 ) {
                LM_ERR("Failed to fetch our create string key \n");
                return;
        }

        LM_INFO("The dialog was created at %.*s and is now established \n",create_time.len,create_time.s);
}

void dialog_sequential_cb(struct dlg_cell *did, int type,
                struct dlg_cb_params * params)
{
        struct sip_msg *msg = msg;
        str term_reason = "we HATE options from callee :D";

        if (msg->first_line.u.request.method_value == METHOD_OPTIONS && params->dir == DLG_DIR_UPSTREAM) {
                LM_INFO("Received OPTIONS sequential from callee. Terminating DLG because %.*s\n",
                term_reason.len,term_reason.s);

                if (my_dlgb.terminate_dlg(did->h_entry,did->h_id,&term_reason) != 0) {
                        LM_ERR("Failed to terminate the dialog \n");
                }
        }
}

18.  视频教程

        这里有完整的视频教程(包括7段视频,每段大约1到2小时),完整地介绍了OpenSIPS 的开发知识,视频中还有一些代码实例。

猜你喜欢

转载自blog.csdn.net/yetyongjin/article/details/106619212
3.1