Przeglądaj źródła

Merge commit 'origin/rbetancor/drouting'

* commit 'origin/rbetancor/drouting':
  drouting: blacklist functions remove
  drouting: Now the module compiles, but it's not tested
  drouting:
  drouting:
  drouting:
  drouting: Module for more flexible dynamic routing rules
  drouting: Documentation updated
  drouting: Updated all Copyright notices
  drouting: Makefile fixed to compile with OPENSER_MOD_INTERFACE
  drouting: new module to support Dynamic Routing
Raul Alexis Betancor Santana 16 lat temu
rodzic
commit
dd0874121b

+ 11 - 0
modules_k/drouting/Makefile

@@ -0,0 +1,11 @@
+# $Id: Makefile,v 1.1.1.1 2007/05/09 11:25:33 bogdan Exp $
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=drouting.so
+LIBS= 
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+include ../../Makefile.modules

+ 836 - 0
modules_k/drouting/README

@@ -0,0 +1,836 @@
+Dynamic Routing Module
+
+   Voice Sistem SRL
+
+Edited by
+
+Anca-Maria Vamanu
+
+   Copyright © 2005-2008 voice-system.ro
+   Revision History
+   Revision $Revision: 4473 $ $Date: 2008-07-12 00:16:22 +0300 (Sat, 12
+                              Jul 2008) $
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+
+              1.1. Introduction
+              1.2. Features
+              1.3. Performance
+              1.4. Routing Rule Definition
+
+                    1.4.1. Gateway Addresses
+                    1.4.2. Destination/GW lists
+                    1.4.3. Routing Rules
+
+              1.5. Routing Rule Processing
+
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Exported Parameters
+
+              3.1. db_url(str)
+              3.2. drd_table(str)
+              3.3. drr_table(str)
+              3.4. drg_table(str)
+              3.5. drl_table(str)
+              3.6. sort_order (int)
+              3.7. ruri_avp (str)
+              3.8. attrs_avp (str)
+              3.9. define_blacklist (str)
+              3.10. use_domain (int)
+              3.11. drg_user_col (str)
+              3.12. drg_domain_col (str)
+              3.13. drg_grpid_col (str)
+              3.14. fetch_rows (int)
+              3.15. force_dns (int)
+
+        4. Exported Functions
+
+              4.1. do_routing("[groupID]")
+              4.2. use_next_gw()/next_routing()
+              4.3. goes_to_gw([type])
+              4.4. is_from_gw([type])
+              4.5. is_from_gw( type, [flag])
+
+        5. Exported MI Functions
+
+              5.1. dr_reload
+
+        6. Installation
+
+   2. Developer Guide
+
+   List of Tables
+
+   1.1. Definition of table dr_gateways
+   1.2. Sample dr_gateways records
+   1.3. Definition of dr_rules table
+   1.4. Time recurrence attributes
+   1.5. Sample dr_rules records
+
+   List of Examples
+
+   1.1. Set db_url parameter
+   1.2. Set drd_table parameter
+   1.3. Set drr_table parameter
+   1.4. Set drg_table parameter
+   1.5. Set drl_table parameter
+   1.6. Set sort_order parameter
+   1.7. Set ruri_avp parameter
+   1.8. Set attrs_avp parameter
+   1.9. Set define_blacklist parameter
+   1.10. Set use_domain parameter
+   1.11. Set drg_user_col parameter
+   1.12. Set drg_domain_col parameter
+   1.13. Set drg_grpid_col parameter
+   1.14. Set fetch_rows parameter
+   1.15. Set force_dns parameter
+   1.16. do_routing usage
+   1.17. use_next_gw usage
+   1.18. goes_to_gw usage
+   1.19. is_from_gw usage
+   1.20. is_from_gw usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+
+        1.1. Introduction
+        1.2. Features
+        1.3. Performance
+        1.4. Routing Rule Definition
+
+              1.4.1. Gateway Addresses
+              1.4.2. Destination/GW lists
+              1.4.3. Routing Rules
+
+        1.5. Routing Rule Processing
+
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Exported Parameters
+
+        3.1. db_url(str)
+        3.2. drd_table(str)
+        3.3. drr_table(str)
+        3.4. drg_table(str)
+        3.5. drl_table(str)
+        3.6. sort_order (int)
+        3.7. ruri_avp (str)
+        3.8. attrs_avp (str)
+        3.9. define_blacklist (str)
+        3.10. use_domain (int)
+        3.11. drg_user_col (str)
+        3.12. drg_domain_col (str)
+        3.13. drg_grpid_col (str)
+        3.14. fetch_rows (int)
+        3.15. force_dns (int)
+
+   4. Exported Functions
+
+        4.1. do_routing("[groupID]")
+        4.2. use_next_gw()/next_routing()
+        4.3. goes_to_gw([type])
+        4.4. is_from_gw([type])
+        4.5. is_from_gw( type, [flag])
+
+   5. Exported MI Functions
+
+        5.1. dr_reload
+
+   6. Installation
+
+1. Overview
+
+   1.1. Introduction
+   1.2. Features
+   1.3. Performance
+   1.4. Routing Rule Definition
+
+        1.4.1. Gateway Addresses
+        1.4.2. Destination/GW lists
+        1.4.3. Routing Rules
+
+   1.5. Routing Rule Processing
+
+1.1. Introduction
+
+   Dynamic Routing is a module for selecting (based on multiple criteria)
+   the the best gateway/destination to be used for delivering a certain
+   call. Least Cost Routing (LCR) is a special case of dynamic routing -
+   when the rules are ordered based on costs. Dynamic Routing comes with
+   many features regarding routing rule selection:
+     * prefix based
+     * caller/group based
+     * time based
+     * priority based
+
+   , processing :
+     * stripping and prefixing
+     * default rules
+     * inbound and outbound processing
+     * script route triggering
+
+   and failure handling:
+     * serial forking
+     * weight based GW selection
+     * random GW selection
+
+1.2. Features
+
+   The dynamic routing implementation for Kamailio is designed with the
+   following properties:
+     * routing info (destinations, rules, groups) are stored in a database
+       and loaded into memory at start up time; reload at runtime via MI
+       command
+     * load balancing or random selection of the destinations (from a
+       given set)
+     * able to handle large volume of routing info (300K of rules) with
+       minimal speed/time and memory consumption penalties
+     * script integration - Pseudo variables support in functions;
+       scripting route triggering when rules are matched
+     * bidirectional behavior - inbound and outbound processing (strip and
+       prefixing when sending and receiving from a destination/GW)
+     * blacklisting - the module allows definition of backlists based on
+       the destination IPs. This blacklists are to be used to prevent
+       malicious forwarding to GWs (based on DNS lookups) when the script
+       logic does none-GE forwarding (like foreign domains).
+
+1.3. Performance
+
+   There were several tests performed regarding the performance of the
+   module when dealing with a large number of routing rules.
+
+   The tests were performed with a set of 383000 rules and to values were
+   measured:
+     * time to load from DB
+     * used shared memory
+
+   The time to load was varying between 4 seconds and 8 seconds, depending
+   of the caching of the DB client - the first load was the slowest (as
+   the DB query hits the disk drive); the following are faster as data is
+   already cached in the DB client. So technically speaking, the time to
+   load (without the time to query which is DB type dependent) is ~4
+   seconds
+
+   After loading the data into shared memory ~ 96M of memory were used
+   exclusively for the DR data.
+
+1.4. Routing Rule Definition
+
+   Dynamic routing rules are stored in a database, in four tables:
+     * one for storing the gateway definitions
+     * one for storing the routing rule definitions
+     * one for storing the users mappings over groups
+     * one for storing a list of gateways, so you don't have to enter all
+       the elements every time you need it
+
+1.4.1. Gateway Addresses
+
+   Default name for the table storing gateway addresses is "dr_gateways".
+   Gateway addresses are stored in a separate table because of need to
+   access them independent of Dynamic Routing processing (e.g., adding/
+   removing gateway PRI prefix before/after performing other operation --
+   receiving/relaying to gateway).
+
+   Table 1.1. Definition of table dr_gateways
+   Column name     Type     Default value         Description
+   gwid        integer      auto increment unique identifier for GW
+   type        unsigned int 0              type/class of GW
+   address     varchar(128)                address of the gateway
+   strip       unsigned int 0              no of digits to strip
+   pri_prefix  varchar(255)                PRI prefix of the gateway
+   description varchar(128)                description of the gateway
+
+   Once a rule is matched, the STRIP number of digits are removed from the
+   username part of the RURI and then the PRI prefix has to be added to
+   the request URI before forwarding the call to the gateway.
+
+   Table 1.2. Sample dr_gateways records
+   gwid type     address      strip pri_prefix description
+   1    10   10.10.10.10:5080 0     2222       Gateway 1
+   2    10   10.10.10.10      2     3333       Gateway 2
+   3    20   10.10.10.11      0                Gateway 3
+
+1.4.2. Destination/GW lists
+
+   For each rule, you can set a list of destinations to be used. The list
+   is comma or pipe separated enumeration of the destinations. The module
+   will use (one by one) each destination from the list (in the given
+   order).
+
+   Also the module allows the usage of groups in the destination lists. A
+   group of destinations is delimited by semi-colon char. inside the whole
+   destination list ( like: 2,4;5,78,23;4;7;2 ). The destinations from
+   within a group may be act differently (like load-balancing, random
+   selection, etc), depending of the "sort_order" module parameter - more
+   about this is available under the module paramters section.
+
+1.4.3. Routing Rules
+
+   Default name for the table storing rule definitions is "dr_rules".
+
+   Table 1.3. Definition of dr_rules table
+   Column name     Type     Default            Description
+   ruleid      integer      auto    UID of the rule
+   groupid     varchar(255)         list of routing group IDs
+   prefix      varchar(64)          destination prefix for this route
+   timerec     varchar(255)         time recurrence for this rule
+   priority    integer      0       priority of the rule
+   routeid     integer      0       route block to be execute
+   gwlist      varchar(255)         the list with GWs to be used
+   description varchar(128)         description of this rule
+
+    a. groupid column
+       Each user must be member of only one routing group. It must be
+       specified in user's profile.
+    b. prefix column
+       Destination URI must start with prefix value to match the rule.
+    c. time rec column
+       A date-time expression that defines the time recurrence to match
+       for current rule. Time recurrences are based closely on the
+       specification of recurring intervals of time in the Internet
+       Calendaring and Scheduling Core Object Specification (calendar
+       COS), RFC 2445. The set of attributes used in routing rule
+       specification is subset of time recurrence attributes used by Call
+       Processing Language (CPL). These attributes are (extracted from CPL
+       draft 09):
+       Table 1.4. Time recurrence attributes
+
+     Attribute                            Description
+     dastard    Start of interval (RFC 2445 DATE-TIME)
+     duration   Length of interval (RFC 2445 DURATION)
+     freq       Frequency of recurrence (secondly,minutely,hourly, daily,weekly,
+                monthly, or yearly).
+     until      bound of recurrence (RFC 2445 DATE-TIME)
+     interval   How often the recurrence repeats
+     byday      List of days of the week
+     bymonthday List of days of the month
+     byyearday  List of days of the year
+     byweekno   List of weeks of the year
+     bymonth    List of months of the year
+       The value stored in database has the format of:
+       <dtstart>|<duration>|<freq>|<until>|<interval>|<byday>|<bymonthday>
+       |<byyearday>|<byweekno>|<bymonth>
+       When an attribute is not specified, the corresponding place must be
+       left empty, whenever another attribute that follows in the list has
+       to be specified.
+       Detailed description of time recurrence attributes:
+          + dtstart - specifies the beginning of the first period.
+          + duration - specifies the duration of the period. For a
+            recurring interval, the "duration" parameter MUST be small
+            enough such that subsequent intervals do not overlap. For
+            non-recurring intervals, durations of any positive length are
+            permitted, zero-length duration means "forever".
+            Negative-length durations are not allowed.
+          + freq - takes one of the following values: "daily", to specify
+            repeating periods based on an interval of a day or more;
+            "weekly", to specify repeating periods based on an interval of
+            a week or more; "monthly", to specify repeating periods based
+            on an interval of a month or more; and "yearly", to specify
+            repeating periods based on an interval of a year or more.
+            These values are not case-sensitive.
+          + until - defines an iCalendar COS DATE or DATE-TIME value which
+            bounds the recurrence rule in an inclusive manner. If the
+            value specified by "until" is synchronized with the specified
+            recurrence, this date or date-time becomes the last instance
+            of the recurrence. If not present, the recurrence is
+            considered to repeat forever.
+          + interval - contains a positive integer representing how often
+            the recurrence rule repeats. The default value is "1", meaning
+            every day for a "daily" rule, every week for a "weekly" rule,
+            every month for a "monthly" rule and every year for a "yearly"
+            rule.
+          + interval - contains a positive integer representing how often
+            the recurrence rule repeats. The default value is "1", meaning
+            every day for a "daily" rule, every week for a "weekly" rule,
+            every month for a "monthly" rule and every year for a "yearly"
+            rule.
+          + byday - specifies a comma-separated list of days of the week.
+            "MO" indicates Monday; "TU" indicates Tuesday; "WE" indicates
+            Wednesday; "TH" indicates Thursday; "FR" indicates Friday;
+            "SA" indicates Saturday; "SU" indicates Sunday. These values
+            are not case-sensitive.
+            Each "byday" value can also be preceded by a positive (+n) or
+            negative (-n) integer. If present, this indicates the nth
+            occurrence of the specific day within the "monthly" or
+            "yearly" recurrence. For example, within a "monthly" rule,
+            +1MO (or simply 1MO) represents the first Monday within the
+            month, whereas -1MO represents the last Monday of the month.
+            If an integer modifier is not present, it means all days of
+            this type within the specified frequency. For example, within
+            a "monthly" rule, MO represents all Mondays within the month.
+          + bymonthday - parameter specifies a comma-separated list of
+            days of the month. Valid values are 1 to 31 or -31 to -1. For
+            example, -10 represents the tenth to the last day of the
+            month.
+          + byyearday - specifies a comma-separated list of days of the
+            year. Valid values are 1 to 366 or -366 to -1. For example, -1
+            represents the last day of the year (December 31st) and -306
+            represents the 306th to the last day of the year (March 1st).
+          + byweekno - specifies a comma-separated list of ordinals
+            specifying weeks of the year. Valid values are 1 to 53 or -53
+            to -1.
+          + bymonth - parameter specifies a comma-separated list of months
+            of the year. Valid values are 1 to 12.
+       A recurrence is specified by including the "freq" parameter, which
+       indicates the type of recurrence rule. Parameters other than
+       "dtstart" and "duration" SHOULD NOT be specified unless "freq" is
+       present.
+       If byxxx parameter values are found which are beyond the available
+       scope (ie, bymonthday="30" in February), they are simply ignored.
+       Byxxx parameters modify the recurrence in some manner. Byxxx rule
+       parts for a period of time which is the same or greater than the
+       frequency generally reduce or limit the number of occurrences of
+       the recurrence generated. For example, freq="daily" bymonth="1"
+       reduces the number of recurrence instances from all days (if the
+       "bymonth" parameter is not present) to all days in January. Byxxx
+       parameters for a period of time less than the frequency generally
+       increase or expand the number of occurrences of the recurrence. For
+       example, freq="yearly" bymonth="1,2" increases the number of days
+       within the yearly recurrence set from 1 (if "bymonth" parameter is
+       not present) to 2.
+       If multiple Byxxx parameters are specified, then after evaluating
+       the specified "freq" and "interval" parameters, the Byxxx
+       parameters are applied to the current set of evaluated occurrences
+       in the following order: "bymonth", "byweekno", "byyearday",
+       "bymonthday", "byday"; then "until" is evaluated.
+       Here is an example of evaluating multiple Byxxx parameters.
+       dtstart="19970105T083000" duration="10M" freq="yearly" interval="2"
+       bymonth="1" byday="SU"
+       First, the interval="2" would be applied to freq="yearly" to arrive
+       at "every other year" . Then, bymonth="1" would be applied to
+       arrive at "every January, every other year". Then, byday="SU" would
+       be applied to arrive at "every Sunday in January, every other year,
+       from 8:30 to 8:40 ". The appropriate minutes and hours have been
+       retrieved from the "dtstart" and "duration" parameters.
+    d. priority column
+       If many rules are eligible, choose the one with highest priority.
+    e. routeid column
+       If different than 0, then execute the route with the specified ID.
+       That is, a route which can be used to perform custom operations on
+       message. At this route, no modification is performed at signaling
+       level.
+    f. gwlist column
+       A comma separated list of gateway identifiers corresponding to a
+       row in table "dr_gateways". The first gateway is tried first and if
+       failure the second one, and so one. If no gateway is left a
+       negative response is sent back to the user.
+    g. Routing Rules Examples
+       Table 1.5. Sample dr_rules records
+
+   ruleid group prefix timerec priority routeid gwlist description
+   1 6 0049 20040101T083000|10H|weekly|||MO,TU,WE,TH,FR 5 23 1,2 Rule 1
+   2 8 0049 20040101T083000 0 0 1,2 Rule 2
+   3 7,8,9 0049 20040101T083000 0 0 3 Rule 3
+       (The time recurrence for first rule is:
+       "20040101T083000|10H|weekly|||MO,TU,WE,TH,FR")
+
+1.5. Routing Rule Processing
+
+   The module can be used to find out which is the best gateway to use for
+   new calls terminated to PSTN. The algorithm to select the rule is as
+   follows:
+     * the module discovers the routing group of the originating user.
+       This step is skipped if a routing group is passed from the script
+       as parameter.
+     * once the group is known, in the subset of the rules for this group
+       the module looks for the one that matches the destination based on
+       "prefix" column. The set of rules with the longest prefix is
+       chosen. If no digit from the prefix matches, the default rules are
+       used (rules with no prefix)
+     * within the set of rules is applied the time criteria, and the rule
+       which has the highest priority and matches the time criteria is
+       selected to drive the routing.
+     * Once found the rule, it may contain a route ID to execute. If a
+       certain flag is set, then the processing is stopped after executing
+       the route block.
+     * The rule must contain a gateway chain. The module will execute
+       serial forking for each address in chain. The next address in chain
+       is used only if the previously has failed.
+     * With the right gateway address found, the prefix (PRI) of the
+       gateway is added to the request URI and then the request is
+       forwarded.
+
+   If no rule is found to match the selection criteria an default action
+   must be taken (e.g., error response sent back). If the gateway in the
+   chain has no prefix the request is forwarded without adding any prefix
+   to the request URI.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * a database module.
+
+2.2. External Libraries or Applications
+
+     * none.
+
+3. Exported Parameters
+
+   3.1. db_url(str)
+   3.2. drd_table(str)
+   3.3. drr_table(str)
+   3.4. drg_table(str)
+   3.5. drl_table(str)
+   3.6. sort_order (int)
+   3.7. ruri_avp (str)
+   3.8. attrs_avp (str)
+   3.9. define_blacklist (str)
+   3.10. use_domain (int)
+   3.11. drg_user_col (str)
+   3.12. drg_domain_col (str)
+   3.13. drg_grpid_col (str)
+   3.14. fetch_rows (int)
+   3.15. force_dns (int)
+
+3.1. db_url(str)
+
+   The database url.
+
+   Default value is "NULL".
+
+   Example 1.1. Set db_url parameter
+...
+modparam("drouting", "db_url",
+        "mysql://opensips:opensipsrw@localhost/opensips")
+...
+
+3.2. drd_table(str)
+
+   The name of the db table storing gateway addresses.
+
+   Default value is "dr_gateways".
+
+   Example 1.2. Set drd_table parameter
+...
+modparam("drouting", "drd_table", "dr_gateways")
+...
+
+3.3. drr_table(str)
+
+   The name of the db table storing routing rules.
+
+   Default value is "dr_rules".
+
+   Example 1.3. Set drr_table parameter
+...
+modparam("drouting", "drr_table", "rules")
+...
+
+3.4. drg_table(str)
+
+   The name of the db table storing groups.
+
+   Default value is "dr_groups".
+
+   Example 1.4. Set drg_table parameter
+...
+modparam("drouting", "drg_table", "groups")
+...
+
+3.5. drl_table(str)
+
+   The name of the db table storing definitions of destination lists (to
+   be used directly by the routing rules). You will have a identifier to a
+   group of gateways instead of having all the members of the group as a
+   individual elements. Very useful to reuse a list of gateways in
+   different places.
+
+   Default value is "dr_gw_lists".
+
+   Example 1.5. Set drl_table parameter
+...
+modparam("drouting", "drl_table", "my_gw_lists")
+...
+
+3.6. sort_order (int)
+
+   Defines how the destination list should be processed (ordering of the
+   elements). Possible modes are
+     * 0 - destination groups are ignored and all the destinations are
+       tried in the given order; Ex: list 1,2;3,4,5;6 will lead to usage
+       as 1,2,3,4,5,6
+     * 1 - the destinations from each group are randomly arranged (only
+       the two elements are randomly selected); groups do maintain their
+       order (as given); the resulting list is used (with all the defined
+       destinations). Ex: 1,2;3,4,5;6 -> randomizer -> (A) 2,1;4,3,5;6 ->
+       usage 2,1,4,3,5,6 (B) 1,2;3,5,4;6 -> usage 1,2,3,5,4,6
+     * 2 - from each destination group, only a single destination is
+       randomly selected; groups do maintain their order (as given); Ex:
+       1,2;3,4,5;6 -> randomizer -> (A) 2;4;6 -> usage 2,4,6 (B) 1;5;6 ->
+       usage 1,5,6
+
+   Default value is "0".
+
+   Example 1.6. Set sort_order parameter
+...
+modparam("drouting", "sort_order", 2)
+...
+
+3.7. ruri_avp (str)
+
+   The name of the avp for storing Request URIs to be later used
+   (alternative destiantions for the current one).
+
+   Default value is "NULL".
+
+   Example 1.7. Set ruri_avp parameter
+...
+modparam("drouting", "ruri_avp", '$avp(dr_ruri)')
+modparam("drouting", "ruri_avp", '$avp(i:33)')
+...
+
+3.8. attrs_avp (str)
+
+   The name of the avp for storing the attribute of the current selected
+   destination - once a new destination is selected (via the use_next_gw()
+   function), the AVP will be updated with the attrs of the new used
+   destination.
+
+   Default value is "NULL".
+
+   Example 1.8. Set attrs_avp parameter
+...
+modparam("drouting", "attrs_avp", '$avp(dr_attrs)')
+modparam("drouting", "atrrs_avp", '$avp(i:67)')
+...
+
+3.9. define_blacklist (str)
+
+   Defines a backlist based on a list of GW types - the list will contain
+   the IPs (no port, all protocols) of the GWs with the specified types.
+
+   Multiple instances of this param are allowed.
+
+   Default value is "NULL".
+
+   Example 1.9. Set define_blacklist parameter
+...
+modparam("drouting", "define_blacklist", 'bl_name= 3,5,25,23')
+modparam("drouting", "define_blacklist", 'list= 4,2')
+...
+
+3.10. use_domain (int)
+
+   Flag to configure whether to use domain match when querying database
+   for user's routing group.
+
+   Default value is "1".
+
+   Example 1.10. Set use_domain parameter
+...
+modparam("drouting", "use_domain", 0)
+...
+
+3.11. drg_user_col (str)
+
+   The name of the column in group db table where the username is stored.
+
+   Default value is "username".
+
+   Example 1.11. Set drg_user_col parameter
+...
+modparam("drouting", "drg_user_col", "user")
+...
+
+3.12. drg_domain_col (str)
+
+   The name of the column in group db table where the domain is stored.
+
+   Default value is "domain".
+
+   Example 1.12. Set drg_domain_col parameter
+...
+modparam("drouting", "drg_domain_col", "host")
+...
+
+3.13. drg_grpid_col (str)
+
+   The name of the column in group db table where the group id is stored.
+
+   Default value is "groupid".
+
+   Example 1.13. Set drg_grpid_col parameter
+...
+modparam("drouting", "drg_grpid_col", "grpid")
+...
+
+3.14. fetch_rows (int)
+
+   The number of rows that should be fetched from the result of a query in
+   rules db table.
+
+   Default value is "2000".
+
+   Example 1.14. Set fetch_rows parameter
+...
+modparam("drouting", "fetch_rows", 1500)
+...
+
+3.15. force_dns (int)
+
+   Force DNS resolving of GW/destination names (if not IPs) during
+   startup. If not enabled, the GW name will be blindly used during
+   routing.
+
+   Default value is "1 (enabled)".
+
+   Example 1.15. Set force_dns parameter
+...
+modparam("drouting", "force_dns", 0)
+...
+
+4. Exported Functions
+
+   4.1. do_routing("[groupID]")
+   4.2. use_next_gw()/next_routing()
+   4.3. goes_to_gw([type])
+   4.4. is_from_gw([type])
+   4.5. is_from_gw( type, [flag])
+
+4.1.  do_routing("[groupID]")
+
+   Function to trigger routing of the message according to the rules in
+   the database table and the configured parameters.
+
+   This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+
+   The module can take one optional parameter: the routing group the
+   caller belongs to - this may be a static numerical value or an AVP
+   specification. If none specified, the function will automatically try
+   to query the dr_group table to get this information.
+
+   Example 1.16. do_routing usage
+...
+do_routing();
+...
+do_routing("0");
+...
+do_routing("$avp(i:10)");
+
+4.2.  use_next_gw()/next_routing()
+
+   The function takes the next available destination (set by do_routing,
+   as alternative destinations) and push it into RURI. Note that the
+   function just sets the RURI (nothing more).
+
+   If a new RURI is set, the used destination is removed from the pending
+   set of alternative destinations.
+
+   This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+
+   The function returns true only if a new RURI was set. False is returned
+   is no other alternative destinations are found or in case of internal
+   processing error.
+
+   Example 1.17. use_next_gw usage
+...
+if (use_next_gw()) {
+        t_relay();
+        exit;
+}
+...
+
+4.3.  goes_to_gw([type])
+
+   Function returns true if the destination of the current request
+   (destination URI or Request URI) points (as IP) to one of the gateways.
+   There no DNS lookups done if the domain part of the URI is not an IP.
+
+   This function does not change anything in the message.
+
+   This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+
+   The function can take one optional parameter:
+     * type (optional) - GW/destination type to be checked
+
+   Example 1.18. goes_to_gw usage
+...
+if (goes_to_gw("1")) {
+        sl_send_reply("403","Forbidden");
+        exit;
+}
+...
+
+4.4.  is_from_gw([type])
+
+   The function checks if the sender of the message is a gateway from a
+   certain group.
+
+   This function can be used from REQUEST_ROUTE, FAILURE_ROUTE and
+   ONREPLY_ROUTE
+
+   The function can take one optional parameter:
+     * type (optional) - GW/destination type to be checked
+     * flags - if message is a request and the GW has a STRIP defined,
+       then apply it if GW is source.
+
+   Example 1.19. is_from_gw usage
+...
+if (is_from_gw("1") {
+}
+...
+
+4.5.  is_from_gw( type, [flag])
+
+   The function checks if the sender of the message is a gateway from a
+   certain group.
+
+   This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+
+   The function can take two parameters:
+     * type (mandatory) - GW/destination type to be checked
+     * flags (optional) - if message is a request and the GW has a STRIP
+       defined, then apply it if GW is source.
+
+   Example 1.20. is_from_gw usage
+...
+if (is_from_gw("3","1") {
+}
+...
+
+5. Exported MI Functions
+
+   5.1. dr_reload
+
+5.1.  dr_reload
+
+   Command to reload routing rules from database.
+
+   It takes no parameter.
+
+   MI FIFO Command Format:
+                :dr_reload:fifo_reply
+                _empty_line_
+
+6. Installation
+
+   The module requires 3 table in OpenSIPS database: dr_groups,
+   dr_gateways, dr_rules. The SQL syntax to create them can be found in
+   drouting-create.sql script in the database directories in the
+   opensips/scripts folder. You can also find the complete database
+   documentation on the project webpage,
+   http://www.kamailio.org/docs/db-tables/kamailio-db-devel.html.
+
+Chapter 2. Developer Guide
+
+   The module provides no function to be used by other Kamailio modules.

+ 4 - 0
modules_k/drouting/doc/Makefile

@@ -0,0 +1,4 @@
+docs = drouting.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module

+ 57 - 0
modules_k/drouting/doc/drouting.xml

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+
+<!ENTITY admin SYSTEM "drouting_admin.xml">
+<!ENTITY devel SYSTEM "drouting_devel.xml">
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book>
+	<bookinfo>
+	<title>Dynamic Routing Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+		<author>
+		<firstname></firstname>
+		<surname></surname>
+		<affiliation><orgname>&vsname;</orgname></affiliation>
+		<address>
+			<email>[email protected]</email>
+		<otheraddr>
+			<ulink url="http://www.voice-system.ro">http://www.voice-system.ro</ulink>
+		</otheraddr>
+		</address>
+		</author>
+		<editor>
+		<firstname>Anca-Maria</firstname>
+		<surname>Vamanu</surname>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</editor>
+	</authorgroup>
+	<copyright>
+		<year>2005-2008</year>
+		<holder>&voicesystem;</holder>
+	</copyright>
+	<revhistory>
+		<revision>
+		<revnumber>$Revision: 4473 $</revnumber>
+		<date>$Date: 2008-07-12 00:16:22 +0300 (Sat, 12 Jul 2008) $</date>
+		</revision>
+	</revhistory>
+	</bookinfo>
+	<toc></toc>
+
+	&admin;
+	&devel;
+
+</book>
+
+

+ 1364 - 0
modules_k/drouting/doc/drouting_admin.xml

@@ -0,0 +1,1364 @@
+<!-- Drouting Module User's Guide -->
+
+<chapter>
+	<title>&adminguide;</title>
+	
+	<section>
+	<title>Overview</title>
+	<section>
+	<title>Introduction</title>
+
+	<para>Dynamic Routing is a module for selecting (based on multiple
+	criteria) the the best gateway/destination to be used for delivering a
+	certain call. Least Cost Routing (LCR) is a special case of dynamic
+	routing - when the rules are ordered based on costs. Dynamic Routing 
+	comes with many features regarding routing rule selection:
+	</para>
+	<itemizedlist>
+		<listitem><para>prefix based</para></listitem>
+		<listitem><para>caller/group based</para></listitem>
+		<listitem><para>time based</para></listitem>
+		<listitem><para>priority based</para></listitem>
+	</itemizedlist>
+	<para>
+	, processing :
+	</para>
+	<itemizedlist>
+		<listitem><para>stripping and prefixing</para></listitem>
+		<listitem><para>default rules</para></listitem>
+		<listitem><para>inbound and outbound processing</para></listitem>
+		<listitem><para>script route triggering</para></listitem>
+	</itemizedlist>
+	<para>
+	and failure handling:
+	<itemizedlist>
+		<listitem><para>serial forking</para></listitem>
+		<listitem><para>weight based GW selection</para></listitem>
+		<listitem><para>random GW selection</para></listitem>
+	</itemizedlist>
+	</para>
+</section>
+
+	<section>
+	<title>Features</title>
+	<para>
+	The dynamic routing implementation for &kamailio; is designed with the
+	following properties:
+	</para>
+	<itemizedlist>
+	<listitem>
+	<para>
+	routing info (destinations, rules, groups) are stored in a database and
+	loaded into memory at start up time; reload at runtime via MI command
+	</para>
+	</listitem>
+
+	<listitem>
+	<para>
+	load balancing or random selection of the destinations (from a given set)
+	</para>
+	</listitem>
+
+	<listitem>
+	<para>
+	able to handle large volume of routing info (300K of rules) with minimal
+	speed/time and memory consumption penalties
+	</para>
+	</listitem>
+	
+	<listitem>
+	<para>
+	script integration - Pseudo variables support in functions; scripting
+	route triggering when rules are matched
+	</para>
+	</listitem>
+	
+	<listitem>
+	<para>
+	bidirectional behavior - inbound and outbound processing (strip and 
+	prefixing when sending and receiving from a destination/GW)
+	</para>
+	</listitem>
+
+	<listitem>
+	<para>
+	blacklisting - the module allows definition of backlists based on the
+	destination IPs. This blacklists are to be used to prevent malicious 
+	forwarding to GWs (based on DNS lookups) when the script logic does
+	none-GE forwarding (like foreign domains).
+	</para>
+	</listitem>
+
+	</itemizedlist>
+	</section>
+
+	<section>
+	<title>Performance</title>
+	<para>
+	There were several tests performed regarding the performance of the module
+	when dealing with a large number of routing rules.
+	</para>
+	<para>
+	The tests were performed with a set of 383000 rules and to values were
+	measured:
+	</para>
+	<itemizedlist>
+	<listitem><para>time to load from DB</para></listitem>
+	<listitem><para>used shared memory</para></listitem>
+	</itemizedlist>
+	<para>
+	The time to load was varying between 4 seconds and 8 seconds, depending of
+	the caching of the DB client - the first load was the slowest (as the DB 
+	query hits the disk drive); the following are faster as data is already 
+	cached in the DB client. So technically speaking, the time to load (without
+	the time to query which is DB type dependent) is ~4 seconds
+	</para>
+	<para>
+	After loading the data into shared memory ~ 96M of memory were used 
+	exclusively for the DR data.
+	</para>
+	</section>
+
+
+	<section>
+	<title>Routing Rule Definition</title>
+	<para>
+	Dynamic routing rules are stored in a database, in four tables:
+	</para>
+	<itemizedlist>
+	<listitem>
+	<para>one for storing the gateway definitions
+	</para>
+	</listitem>
+	<listitem>
+	<para>one for storing the routing rule definitions
+	</para>
+	</listitem>
+	<listitem>
+	<para>one for storing the users mappings over groups
+	</para>
+	</listitem>
+	<listitem>
+        <para>one for storing a list of gateways, so you don't have to enter all
+	the elements every time you need it
+        </para>
+        </listitem>
+	</itemizedlist>
+	<section>
+	<title>Gateway Addresses</title>
+	<para>
+	Default name for the table storing gateway addresses is 
+	<quote>dr_gateways</quote>.
+	Gateway addresses are stored in a separate table because of need to
+	access them independent of Dynamic Routing processing (e.g., adding/
+	removing gateway PRI prefix before/after performing other operation
+	-- receiving/relaying to gateway).
+	</para>
+	<para>
+	<table>
+	<title>Definition of table dr_gateways</title>
+	<tgroup cols="4">
+	<thead>
+	<row>
+	<entry>Column name</entry>
+	<entry>Type</entry>
+	<entry>Default value</entry>
+	<entry>Description</entry>
+	</row>
+	</thead>
+	<tbody>
+	<row>
+	<entry>gwid</entry>
+	<entry>integer</entry>
+	<entry>auto increment</entry>
+	<entry>unique identifier for GW</entry>
+	</row>
+	<row>
+	<entry>type</entry>
+	<entry>unsigned int</entry>
+	<entry>0</entry>
+	<entry>type/class of GW</entry>
+	</row>
+	<row>
+	<entry>address</entry>
+	<entry>varchar(128)</entry>
+	<entry></entry>
+	<entry>address of the gateway</entry>
+	</row>
+	<row>
+	<entry>strip</entry>
+	<entry>unsigned int</entry>
+	<entry>0</entry>
+	<entry>no of digits to strip</entry>
+	</row>
+	<row>
+	<entry>pri_prefix</entry>
+	<entry>varchar(255)</entry>
+	<entry></entry>
+	<entry>PRI prefix of the gateway</entry>
+	</row>
+	<row>
+	<entry>description</entry>
+	<entry>varchar(128)</entry>
+	<entry></entry>
+	<entry>description of the gateway</entry>
+	</row>
+	</tbody>
+	</tgroup>
+	</table> 
+	
+	</para>
+	<para>
+	Once a rule is matched, the STRIP number of digits are removed from the
+	username part of the RURI and then the PRI prefix has to be added to the 
+	request URI before forwarding the call to the gateway. 
+	</para>
+
+	<para>
+	<table>
+	<title>Sample dr_gateways records</title>
+	<tgroup cols="6">
+	<thead>
+	<row>
+	<entry>gwid</entry>
+	<entry>type</entry>
+	<entry>address</entry>
+	<entry>strip</entry>
+	<entry>pri_prefix</entry>
+	<entry>description</entry>
+	</row>
+	</thead>
+	<tbody>
+	<row>
+	<entry>1</entry>
+	<entry>10</entry>
+	<entry>10.10.10.10:5080</entry>
+	<entry>0</entry>
+	<entry>2222</entry>
+	<entry>Gateway 1</entry>
+	</row>
+	<row>
+	<entry>2</entry>
+	<entry>10</entry>
+	<entry>10.10.10.10</entry>
+	<entry>2</entry>
+	<entry>3333</entry>
+	<entry>Gateway 2</entry>
+	</row>
+	<row>
+	<entry>3</entry>
+	<entry>20</entry>
+	<entry>10.10.10.11</entry>
+	<entry>0</entry>
+	<entry></entry>
+	<entry>Gateway 3</entry>
+	</row>
+	</tbody>
+	</tgroup>
+	</table> 
+	</para>
+	</section>
+
+
+	<section>
+	<title>Destination/GW lists</title>
+	<para>
+	For each rule, you can set a list of destinations to be used. The list is
+	comma or pipe separated enumeration of the destinations. The module
+	will use (one by one) each destination from the list (in the given order).
+	</para>
+	<para>
+	Also the module allows the usage of groups in the destination lists. A
+	group of destinations is delimited by semi-colon char. inside the whole
+	destination list ( like: 2,4;5,78,23;4;7;2 ). The destinations from 
+	within a group may be act differently (like load-balancing, random 
+	selection, etc), depending of the <quote>sort_order</quote> module 
+	parameter - more about this is available under the module paramters 
+	section.
+	</para>
+	</section>
+
+
+	<section>
+	<title>Routing Rules</title>
+	<para>
+	Default name for the table storing rule definitions is 
+	<quote>dr_rules</quote>.
+	</para>
+
+	<para>
+	<table>
+	<title>Definition of dr_rules table</title>
+	<tgroup cols="4">
+	<thead>
+	<row>
+	<entry>Column name</entry>
+	<entry>Type</entry>
+	<entry>Default</entry>
+	<entry>Description</entry>
+	</row>
+	</thead>
+	<tbody>
+	<row>
+	<entry>ruleid</entry>
+	<entry>integer</entry>
+	<entry>auto</entry>
+	<entry>UID of the rule</entry>
+	</row>
+	<row>
+	<entry>groupid</entry>
+	<entry>varchar(255)</entry>
+	<entry></entry>
+	<entry>list of routing group IDs</entry>
+	</row>
+	<row>
+	<entry>prefix</entry>
+	<entry>varchar(64)</entry>
+	<entry></entry>
+	<entry>destination prefix for this route</entry>
+	</row>
+	<row>
+	<entry>timerec</entry>
+	<entry>varchar(255)</entry>
+	<entry></entry>
+	<entry>time recurrence for this rule</entry>
+	</row>
+	<row>
+	<entry>priority</entry>
+	<entry>integer</entry>
+	<entry>0</entry>
+	<entry>priority of the rule</entry>
+	</row>
+	<row>
+	<entry>routeid</entry>
+	<entry>integer</entry>
+	<entry>0</entry>
+	<entry>route block to be execute</entry>
+	</row>
+	<row>
+	<entry>gwlist</entry>
+	<entry>varchar(255)</entry>
+	<entry></entry>
+	<entry>the list with GWs to be used</entry>
+	</row>
+	<row>
+	<entry>description</entry>
+	<entry>varchar(128)</entry>
+	<entry></entry>
+	<entry>description of this rule</entry>
+	</row>
+	</tbody>
+	</tgroup>
+	</table> 
+	</para>
+
+	<para>
+	<orderedlist numeration="loweralpha">
+	<listitem>
+	<para>
+		<emphasis>groupid column</emphasis> 
+	</para>
+	<para>
+	Each user must be member of only one routing group. It must be 
+	specified in user's profile.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>prefix column</emphasis> 
+	</para>
+	<para>
+	Destination URI must start with prefix value to match the rule.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>time rec column</emphasis> 
+	</para>
+	<para>
+	A date-time expression that defines the time recurrence to match for
+	current rule. Time recurrences are based closely on the specification
+	of recurring intervals of time in the Internet Calendaring and Scheduling
+	Core Object Specification (calendar COS), RFC 2445. The set of attributes
+	used in routing rule specification is subset of time recurrence attributes
+	used by Call Processing Language (CPL). These attributes are (extracted
+	from CPL draft 09):
+	</para>
+
+	<para>
+	<table>
+	<title>Time recurrence attributes</title>
+	<tgroup cols="2">
+	<thead>
+	<row>
+	<entry>Attribute</entry>
+	<entry>Description</entry>
+	</row>
+	</thead>
+	<tbody>
+	<row>
+	<entry>dastard</entry>
+	<entry>Start of interval (RFC 2445 DATE-TIME)</entry>
+	</row>
+	<row>
+	<entry>duration</entry>
+	<entry>Length of interval (RFC 2445 DURATION)</entry>
+	</row>
+	<row>
+	<entry>freq</entry>
+	<entry>Frequency of recurrence (secondly,minutely,hourly, daily,weekly,
+	monthly, or yearly).</entry>
+	</row>
+	<row>
+	<entry>until</entry>
+	<entry>bound of recurrence (RFC 2445 DATE-TIME)</entry>
+	</row>
+	<row>
+	<entry>interval</entry>
+	<entry>How often the recurrence repeats</entry>
+	</row>
+	<row>
+	<entry>byday</entry>
+	<entry>List of days of the week</entry>
+	</row>
+	<row>
+	<entry>bymonthday</entry>
+	<entry>List of days of the month</entry>
+	</row>
+	<row>
+	<entry>byyearday</entry>
+	<entry>List of days of the year</entry>
+	</row>
+	<row>
+	<entry>byweekno</entry>
+	<entry>List of weeks of the year</entry>
+	</row>
+	<row>
+	<entry>bymonth</entry>
+	<entry>List of months of the year</entry>
+	</row>
+	</tbody>
+	</tgroup>
+	</table>
+	</para>
+
+	<para>
+	The value stored in database has the format of:
+	<![CDATA[
+	<dtstart>|<duration>|<freq>|<until>|<interval>|<byday>|<bymonthday>|<byyearday>|<byweekno>|<bymonth>
+	]]>
+	</para>
+	<para>
+	When an attribute is not specified, the corresponding place must be left
+	empty, whenever another attribute that follows in the list has to be
+	specified.
+	</para>
+	<para>
+	Detailed description of time recurrence attributes:
+	<itemizedlist>
+	<listitem>
+	<para>
+		<emphasis>dtstart</emphasis> - specifies the beginning of the first 
+		period.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>duration</emphasis> - specifies the duration of the period.
+		For a recurring interval, the <quote>duration</quote> parameter MUST
+		be small enough such that subsequent intervals do not overlap. 
+		For non-recurring intervals, durations of any positive length are 
+		permitted, zero-length duration means <quote>forever</quote>. 
+		Negative-length durations are not allowed.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>freq</emphasis> - takes one of the following values: 
+		<quote>daily</quote>,
+		to specify repeating periods based on an interval of a day or more;
+		<quote>weekly</quote>, to specify repeating periods based on an 
+		interval of a week or more; <quote>monthly</quote>, to specify 
+		repeating periods based on an interval of a month or more; and 
+		<quote>yearly</quote>, to specify repeating periods based
+		on an interval of a year or more. These values are not case-sensitive.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>until</emphasis> - defines an iCalendar COS DATE or DATE-TIME
+		value which bounds the recurrence rule in an inclusive manner. If the
+		value specified by <quote>until</quote> is synchronized with the 
+		specified 
+		recurrence, this date or date-time becomes the last instance of the 
+		recurrence. If not present, the recurrence is considered to repeat 
+		forever.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>interval</emphasis> - contains a positive integer 
+		representing how often the recurrence rule repeats. The default value
+		is <quote>1</quote>, meaning every day for a <quote>daily</quote> rule,
+		every week for a <quote>weekly</quote>
+		rule, every month for a <quote>monthly</quote> rule and every year for
+		a <quote>yearly</quote> rule.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>interval</emphasis> - contains a positive integer 
+		representing how often the recurrence rule repeats. The default value 
+		is <quote>1</quote>, meaning every day for a <quote>daily</quote> rule,
+		every week for a <quote>weekly</quote> rule, every
+		month for a <quote>monthly</quote> rule and every year for a 
+		<quote>yearly</quote> rule.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>byday</emphasis> - specifies a comma-separated list of days 
+		of the week. <quote>MO</quote> indicates Monday; <quote>TU</quote> 
+		indicates Tuesday; <quote>WE</quote> indicates Wednesday; 
+		<quote>TH</quote> indicates Thursday; <quote>FR</quote> indicates 
+		Friday; <quote>SA</quote> indicates Saturday; <quote>SU</quote> 
+		indicates Sunday. These values are not case-sensitive.
+	</para>
+	<para>
+		Each <quote>byday</quote> value can also be preceded by a positive 
+		(+n) or negative (-n) integer. If present, this indicates the nth 
+		occurrence of the specific day within the <quote>monthly</quote> or 
+		<quote>yearly</quote> recurrence. For example, within a 
+		<quote>monthly</quote> rule, +1MO (or simply 1MO) represents the first 
+		Monday within the month, whereas -1MO represents the last Monday of
+		the month. If an integer modifier is not present, it means all days
+		of this type within the specified frequency. For example, within a 
+		<quote>monthly</quote> rule, MO represents all Mondays within the month.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>bymonthday</emphasis> - parameter specifies a comma-separated
+		list of days of the month. Valid values are 1 to 31 or -31 to -1. For 
+		example, -10 represents the tenth to the last day of the month.
+	</para>
+	</listitem>
+
+	<listitem>
+	<para>
+		<emphasis>byyearday</emphasis> - specifies a comma-separated list of 
+		days of the year. Valid values are 1 to 366 or -366 to -1. For example,
+		-1 represents the last day of the year (December 31st) and -306 
+		represents the 306th to the last day of the year (March 1st).
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>byweekno</emphasis> - specifies a comma-separated list of 
+		ordinals specifying weeks of the year. Valid values are 1 to 53 or 
+		-53 to -1.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>bymonth</emphasis> - parameter specifies a comma-separated
+		list of months of the year. Valid values are 1 to 12.
+	</para>
+	</listitem>
+	</itemizedlist>
+</para>
+<para>
+</para>
+	<para></para>
+	<para>
+		A recurrence is specified by including the <quote>freq</quote> 
+		parameter, which indicates the type of recurrence rule. Parameters 
+		other than <quote>dtstart</quote>
+		and <quote>duration</quote> SHOULD NOT be specified unless 
+		<quote>freq</quote> is present.
+	</para>
+	<para></para>
+	<para>
+		If byxxx parameter values are found which are beyond the available 
+		scope (ie, bymonthday=<quote>30</quote> in February), they are simply
+		ignored. 
+	</para>
+	<sbr></sbr>
+	<para></para>
+	<para>
+		Byxxx parameters modify the recurrence in some manner. Byxxx rule 
+		parts for a period of time which is the same or greater than the 
+		frequency generally reduce or limit the number of occurrences of the 
+		recurrence generated. For example, freq=<quote>daily</quote> 
+		bymonth=<quote>1</quote> reduces the number of
+		recurrence instances from all days (if the <quote>bymonth</quote> 
+		parameter is not present) to all days in January. Byxxx parameters for 
+		a period of time less than the frequency generally increase or expand 
+		the number of occurrences of the recurrence. For example, 
+		freq=<quote>yearly</quote> bymonth=<quote>1,2</quote>
+		increases the number of days within the yearly recurrence set from 1 
+		(if <quote>bymonth</quote> parameter is not present) to 2.
+	</para>
+	<para>
+		If multiple Byxxx parameters are specified, then after evaluating the
+		specified <quote>freq</quote> and <quote>interval</quote> parameters,
+		the Byxxx parameters are 
+		applied to the current set of evaluated occurrences in the following
+		order: <quote>bymonth</quote>, <quote>byweekno</quote>, 
+		<quote>byyearday</quote>, <quote>bymonthday</quote>, 
+		<quote>byday</quote>; then <quote>until</quote> is  evaluated.
+	</para>
+	<para>
+		Here is an example of evaluating multiple Byxxx parameters.
+	</para>
+	<para>
+		dtstart=<quote>19970105T083000</quote> duration=<quote>10M</quote>
+		freq=<quote>yearly</quote> interval=<quote>2</quote> 
+		bymonth=<quote>1</quote> byday=<quote>SU</quote>
+	</para>
+	<para>
+		First, the interval=<quote>2</quote> would be applied to 
+		freq=<quote>yearly</quote> to arrive at <quote>every other year</quote>
+		. Then, bymonth=<quote>1</quote> would be applied to arrive at 
+		<quote>every January, every other year</quote>. Then,
+		byday=<quote>SU</quote> would be applied to arrive at <quote>every 
+		Sunday in January, 
+		every other year, from 8:30 to 8:40 </quote>. The appropriate minutes 
+		and hours have been retrieved from the <quote>dtstart</quote> and 
+		<quote>duration</quote> parameters.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>priority column</emphasis> 
+	</para>
+	<para>
+	If many rules are eligible, choose the one with highest priority.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>routeid column</emphasis> 
+	</para>
+	<para>
+	If different than 0, then execute the route with the specified ID.
+	That is, a route which can be used to perform custom
+	operations on message. At this route, no modification is performed
+	at signaling level.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>gwlist column</emphasis> 
+	</para>
+	<para>
+	A comma separated list of gateway identifiers corresponding to a row in 
+	table <quote>dr_gateways</quote>. The first gateway is tried first and if 
+	failure the second one, and so one. If no gateway is left a negative 
+	response is sent back to the user.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+		<emphasis>Routing Rules Examples</emphasis> 
+	</para>
+	
+	<para>
+	<table align="center" frame= "all" pgwide="1">
+	<title>Sample dr_rules records</title>
+	<tgroup cols="8" align='left' colsep='1' rowsep='1'>
+	<thead>
+	<row>
+	<entry>ruleid</entry>
+	<entry>group</entry>
+	<entry>prefix</entry>
+	<entry>timerec</entry>
+	<entry>priority</entry>
+	<entry>routeid</entry>
+	<entry>gwlist</entry>
+	<entry>description</entry>
+	</row>
+	</thead>
+	<tbody>
+	<row>
+	<entry>1</entry>
+	<entry>6</entry>
+	<entry>0049</entry>
+	<entry>20040101T083000|10H|weekly|||MO,TU,WE,TH,FR</entry>
+	<entry>5</entry>
+	<entry>23</entry>
+	<entry>1,2</entry>
+	<entry>Rule 1</entry>
+	</row>
+	<row>
+	<entry>2</entry>
+	<entry>8</entry>
+	<entry>0049</entry>
+	<entry>20040101T083000</entry>
+	<entry>0</entry>
+	<entry>0</entry>
+	<entry>1,2</entry>
+	<entry>Rule 2</entry>
+	</row>
+	<row>
+	<entry>3</entry>
+	<entry>7,8,9</entry>
+	<entry>0049</entry>
+	<entry>20040101T083000</entry>
+	<entry>0</entry>
+	<entry>0</entry>
+	<entry>3</entry>
+	<entry>Rule 3</entry>
+	</row>
+	</tbody>
+	</tgroup>
+	</table>
+
+	</para>
+	<para>
+	(The time recurrence for first rule is:
+	<quote>20040101T083000|10H|weekly|||MO,TU,WE,TH,FR</quote>)
+	</para>
+
+	</listitem>
+</orderedlist>
+	</para>
+</section>
+
+</section>
+<section>
+	<title>Routing Rule Processing</title>
+	<para>
+	The module can be used to find out which is the best gateway to use for new
+	calls terminated to PSTN. The algorithm to select the rule is as follows:
+	<itemizedlist mark='bullet'>
+	<listitem>
+	<para>
+	the module discovers the routing group of the originating user. This 
+	step is skipped if a routing group is passed from the script as parameter.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+	once the group is known, in the subset of the rules for this group the 
+	module looks for the one that matches the destination based on "prefix"
+	column. The set of rules with the longest prefix is chosen. If no digit 
+	from the prefix matches, the default rules are used (rules with no prefix)
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+	within the set of rules is applied the time criteria, and the rule which
+	has the highest priority and matches the time criteria is selected to drive
+	the routing.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+	Once found the rule, it may contain a route ID to execute. If a certain 
+	flag is set, then the processing is stopped after executing the route
+	block.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+	The rule must contain a gateway chain. The module will execute serial 
+	forking for each address in chain. The next address in chain is used only
+	if the previously has failed.
+	</para>
+	</listitem>
+	<listitem>
+	<para>
+	With the right gateway address found, the prefix (PRI) of the gateway is
+	added to the request URI and then the request is forwarded.
+	</para>
+	</listitem>
+	</itemizedlist>
+	</para>
+	<para>
+	If no rule is found to match the selection criteria an default action must
+	be taken (e.g., error response sent back). If the gateway in the chain has 
+	no prefix the request is forwarded without adding any prefix to the request
+	URI.
+	</para>
+
+</section>
+
+</section>
+
+
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>a database module</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+
+	<section>
+		<title>External Libraries or Applications</title>
+		<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>none</emphasis>.
+			</para>
+			</listitem>
+		</itemizedlist>
+
+		</section>
+	</section>
+	
+	<section>
+	<title>Exported Parameters</title>
+	<section>
+		<title><varname>db_url</varname>(str)</title>
+		<para>
+		The database url.
+		</para>
+		<para>
+		<emphasis>	Default value is <quote>NULL</quote>.	
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>db_url</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "db_url", 
+	"mysql://opensips:opensipsrw@localhost/opensips")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>drd_table</varname>(str)</title>
+		<para>
+		The name of the db table storing gateway addresses.
+		</para>
+		<para>
+		<emphasis>	Default value is <quote>dr_gateways</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>drd_table</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "drd_table", "dr_gateways")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>drr_table</varname>(str)</title>
+		<para>
+		The name of the db table storing routing rules.
+		</para>
+		<para>
+		<emphasis>	Default value is <quote>dr_rules</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>drr_table</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "drr_table", "rules")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>drg_table</varname>(str)</title>
+		<para>
+		The name of the db table storing groups.
+		</para>
+		<para>
+		<emphasis>	Default value is <quote>dr_groups</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>drg_table</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "drg_table", "groups")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>drl_table</varname>(str)</title>
+		<para>
+		The name of the db table storing definitions of destination lists (to 
+		be used directly by the routing rules).
+		You will have a identifier to a group of gateways instead of having all the
+		members of the group as a individual elements.
+		Very useful to reuse a list of gateways in different places.
+		</para>
+		<para>
+		<emphasis>	Default value is <quote>dr_gw_lists</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>drl_table</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "drl_table", "my_gw_lists")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>sort_order</varname> (int)</title>
+		<para>
+		Defines how the destination list should be processed (ordering of
+		the elements). Possible modes are
+		<itemizedlist>
+		<listitem>
+			<emphasis>0</emphasis> - destination groups are ignored and all the
+			destinations are tried in the given order; 
+			Ex: list 1,2;3,4,5;6 will lead to usage as 1,2,3,4,5,6 
+		</listitem>
+		<listitem>
+			<emphasis>1</emphasis> - the destinations from each group are
+			randomly arranged (only the two elements are randomly selected);
+			groups do maintain their order (as given); the resulting list is 
+			used (with all the defined destinations).
+			Ex: 1,2;3,4,5;6 -> randomizer -> 
+			(A) 2,1;4,3,5;6  -> usage 2,1,4,3,5,6
+			(B) 1,2;3,5,4;6  -> usage 1,2,3,5,4,6
+		</listitem>
+		<listitem>
+			<emphasis>2</emphasis> - from each destination group, only a 
+			single destination is randomly selected; groups do maintain their
+			order (as given);
+			Ex: 1,2;3,4,5;6 -> randomizer ->
+			(A) 2;4;6  -> usage 2,4,6
+			(B) 1;5;6  -> usage 1,5,6
+		</listitem>
+		</itemizedlist>
+		</para>
+		<para>
+		<emphasis>Default value is <quote>0</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>sort_order</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "sort_order", 2)
+...
+</programlisting>
+		</example>
+	</section>
+
+<section>
+		<title><varname>ruri_avp</varname> (str)</title>
+		<para>
+		The name of the avp for storing Request URIs to be later used 
+		(alternative destiantions for the current one).
+		</para>
+		<para>
+		<emphasis>Default value is <quote>NULL</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>ruri_avp</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "ruri_avp", '$avp(dr_ruri)')
+modparam("drouting", "ruri_avp", '$avp(i:33)')
+...
+	</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>attrs_avp</varname> (str)</title>
+		<para>
+		The name of the avp for storing the attribute of the current selected
+		destination - once a new destination is selected (via the 
+		use_next_gw() function), the AVP will be updated with the attrs of the
+		new used destination.
+		</para>
+		<para>
+		<emphasis>Default value is <quote>NULL</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>attrs_avp</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "attrs_avp", '$avp(dr_attrs)')
+modparam("drouting", "atrrs_avp", '$avp(i:67)')
+...
+	</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>define_blacklist</varname> (str)</title>
+		<para>
+		Defines a backlist based on a list of GW types - the list will contain
+		the IPs (no port, all protocols) of the GWs with the specified types.
+		</para>
+		<para>
+		Multiple instances of this param are allowed.
+		</para>
+		<para>
+		<emphasis>Default value is <quote>NULL</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>define_blacklist</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "define_blacklist", 'bl_name= 3,5,25,23')
+modparam("drouting", "define_blacklist", 'list= 4,2')
+...
+	</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>use_domain</varname> (int)</title>
+		<para>
+			Flag to configure whether to use domain match when querying
+			database for user's routing group.
+		</para>
+		<para>
+		<emphasis>Default value is <quote>1</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>use_domain</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "use_domain", 0)
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>drg_user_col</varname> (str)</title>
+		<para>
+		The name of the column in group db table where the username is stored.
+		</para>
+		<para>
+		<emphasis>Default value is <quote>username</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>drg_user_col</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "drg_user_col", "user")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>drg_domain_col</varname> (str)</title>
+		<para>
+		The name of the column in group db table where the domain is stored.
+		</para>
+		<para>
+		<emphasis>Default value is <quote>domain</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>drg_domain_col</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "drg_domain_col", "host")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>drg_grpid_col</varname> (str)</title>
+		<para>
+			The name of the column in group db table where the
+			group id is stored.
+		</para>
+		<para>
+		<emphasis>Default value is <quote>groupid</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>drg_grpid_col</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "drg_grpid_col", "grpid")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>fetch_rows</varname> (int)</title>
+		<para>
+		</para>
+		The number of rows that should be fetched from the result of a
+		query in rules db table.
+		<para>
+		<emphasis>Default value is <quote>2000</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>fetch_rows</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "fetch_rows", 1500)
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>force_dns</varname> (int)</title>
+		<para>
+		Force DNS resolving of GW/destination names (if not IPs) during 
+		startup. If not enabled, the GW name will be blindly used during 
+		routing.
+		</para>
+		<para>
+		<emphasis>Default value is <quote>1 (enabled)</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>force_dns</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("drouting", "force_dns", 0)
+...
+	</programlisting>
+		</example>
+	</section>
+
+</section>
+
+<section>
+	<title>Exported Functions</title>
+	<section>
+		<title>
+		<function moreinfo="none">do_routing("[groupID]")</function>
+		</title>
+		<para>
+		Function to trigger routing of the message according to the 
+		rules in the database table and the configured parameters.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+		</para>
+		<para>
+		The module can take one optional parameter: the routing group the 
+		caller belongs to - this may be a static numerical value or an AVP
+		specification. If none specified, the function will automatically
+		try to query the dr_group table to get this information.
+		</para>
+		<example>
+		<title><function>do_routing</function> usage</title>
+		<programlisting format="linespecific">
+...
+do_routing();
+...
+do_routing("0");
+...
+do_routing("$avp(i:10)");
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title>
+		<function moreinfo="none">use_next_gw()/next_routing()</function>
+		</title>
+		<para>
+		The function takes the next available destination (set by do_routing, 
+		as alternative destinations) and push it into RURI. Note that the 
+		function just sets the RURI (nothing more).
+		</para>
+		<para>
+		If a new RURI is set, the used destination is removed from the 
+		pending set of alternative destinations.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+		</para>
+		<para>
+		The function returns true only if a new RURI was set. False
+		is returned is no other alternative destinations are found or in case
+		of internal processing error.
+		</para>
+		<example>
+		<title><function>use_next_gw</function> usage</title>
+		<programlisting format="linespecific">
+...
+if (use_next_gw()) {
+	t_relay();
+	exit;
+}
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title>
+		<function moreinfo="none">goes_to_gw([type])</function>
+		</title>
+		<para>
+		Function returns true if the destination of the current request 
+		(destination URI or Request URI) points (as IP) to one of the gateways.
+		There no DNS lookups done if the domain part of the URI is not an IP.
+		</para>
+		<para>
+		This function does not change anything in the message.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+		</para>
+		<para>
+			The function can take one optional parameter:
+			<itemizedlist>
+				<listitem>
+					<emphasis>type</emphasis> (optional) - GW/destination 
+					type to be checked
+				</listitem>
+			</itemizedlist>
+		</para>
+		<example>
+		<title><function>goes_to_gw</function> usage</title>
+		<programlisting format="linespecific">
+...
+if (goes_to_gw("1")) {
+	sl_send_reply("403","Forbidden");
+	exit;
+}
+...
+</programlisting>
+		</example>
+	</section>
+
+
+	<section>
+		<title>
+		<function moreinfo="none">is_from_gw([type])</function>
+		</title>
+		<para>
+		The function checks if the sender of the message is a gateway
+		from a certain group.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE, FAILURE_ROUTE and 
+		ONREPLY_ROUTE
+		</para>
+		<para>
+			The function can take one optional parameter:
+			<itemizedlist>
+				<listitem>
+					<emphasis>type</emphasis> (optional) - GW/destination type
+					to be checked
+				</listitem>
+				<listitem>
+					<emphasis>flags</emphasis> - if message is a request and
+					the GW has a STRIP defined, then apply it if GW is source.
+				</listitem>
+			</itemizedlist>
+		</para>
+		<example>
+		<title><function>is_from_gw</function> usage</title>
+		<programlisting format="linespecific">
+...
+if (is_from_gw("1") {
+}
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title>
+		<function moreinfo="none">is_from_gw( type, [flag])</function>
+		</title>
+		<para>
+		The function checks if the sender of the message is a gateway
+		from a certain group.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+		</para>
+		<para>
+			The function can take two parameters:
+			<itemizedlist>
+				<listitem>
+					<emphasis>type</emphasis> (mandatory) - GW/destination
+					type to be checked
+				</listitem>
+				<listitem>
+					<emphasis>flags</emphasis> (optional) - if message is a 
+					request and the GW has a STRIP defined, then apply it 
+					if GW is source.
+				</listitem>
+			</itemizedlist>
+		</para>
+		<example>
+		<title><function>is_from_gw</function> usage</title>
+		<programlisting format="linespecific">
+...
+if (is_from_gw("3","1") {
+}
+...
+</programlisting>
+		</example>
+	</section>
+</section>
+
+
+
+<section>
+	<title>Exported MI Functions</title>
+	<section>
+		<title>
+		<function moreinfo="none">dr_reload</function>
+		</title>
+		<para>
+		Command to reload routing rules from database.
+		</para>
+		<para>
+			It takes no parameter.
+		</para>
+
+		<para>
+		MI FIFO Command Format:
+		</para>
+		<programlisting  format="linespecific">
+		:dr_reload:fifo_reply
+		_empty_line_
+		</programlisting>
+	</section>
+</section>
+
+<section>
+	<title>Installation</title>
+	<para>
+	The module requires 3 table in OpenSIPS database: dr_groups,
+	dr_gateways, dr_rules. The SQL syntax to create them can be
+	found in drouting-create.sql script in the database directories
+	in the opensips/scripts folder. You can also find the complete
+	database documentation on the project webpage, &kamailiodbdocslink;.
+	</para>
+</section>
+
+</chapter>
+

+ 11 - 0
modules_k/drouting/doc/drouting_devel.xml

@@ -0,0 +1,11 @@
+<!-- Drouting Module Developer's Guide -->
+
+<chapter>
+
+	<title>&develguide;</title>
+	<para>
+		The module provides no function to be used
+		by other &kamailio; modules.
+	</para>
+</chapter>
+

+ 577 - 0
modules_k/drouting/dr_load.c

@@ -0,0 +1,577 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005-2009 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * History:
+ * ---------
+ *  2005-02-20  first version (cristian)
+ *  2005-02-27  ported to 0.9.0 (bogdan)
+ */
+
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+
+
+#include "../../dprint.h"
+//#include "../../db/db.h"
+#include "../../mem/shm_mem.h"
+
+#include "dr_load.h"
+#include "routing.h"
+#include "prefix_tree.h"
+#include "dr_time.h"
+#include "parse.h"
+
+
+#define DST_ID_DRD_COL   "gwid"
+#define ADDRESS_DRD_COL  "address"
+#define STRIP_DRD_COL    "strip"
+#define PREFIX_DRD_COL   "pri_prefix"
+#define TYPE_DRD_COL     "type"
+#define ATTRS_DRD_COL    "attrs"
+static str dst_id_drd_col = str_init(DST_ID_DRD_COL);
+static str address_drd_col = str_init(ADDRESS_DRD_COL);
+static str strip_drd_col = str_init(STRIP_DRD_COL);
+static str prefix_drd_col = str_init(PREFIX_DRD_COL);
+static str type_drd_col = str_init(TYPE_DRD_COL);
+static str attrs_drd_col = str_init(ATTRS_DRD_COL);
+
+#define RULE_ID_DRR_COL   "ruleid"
+#define GROUP_DRR_COL     "groupid"
+#define PREFIX_DRR_COL    "prefix"
+#define TIME_DRR_COL      "timerec"
+#define PRIORITY_DRR_COL  "priority"
+#define ROUTEID_DRR_COL   "routeid"
+#define DSTLIST_DRR_COL   "gwlist"
+static str rule_id_drr_col = str_init(RULE_ID_DRR_COL);
+static str group_drr_col = str_init(GROUP_DRR_COL);
+static str prefix_drr_col = str_init(PREFIX_DRR_COL);
+static str time_drr_col = str_init(TIME_DRR_COL);
+static str priority_drr_col = str_init(PRIORITY_DRR_COL);
+static str routeid_drr_col = str_init(ROUTEID_DRR_COL);
+static str dstlist_drr_col = str_init(DSTLIST_DRR_COL);
+
+#define ID_DRL_COL     "id"
+#define GWLIST_DRL_CAL "gwlist"
+static str id_drl_col = str_init(ID_DRL_COL);
+static str gwlist_drl_col = str_init(GWLIST_DRL_CAL);
+
+struct dr_gwl_tmp {
+	unsigned int id;
+	char *gwlist;
+	struct dr_gwl_tmp *next;
+};
+
+
+static struct dr_gwl_tmp* dr_gw_lists = NULL;
+
+#define check_val( _val, _type, _not_null, _is_empty_str) \
+	do{\
+		if ((_val)->type!=_type) { \
+			LM_ERR("bad colum type\n");\
+			goto error;\
+		} \
+		if (_not_null && (_val)->nul) { \
+			LM_ERR("nul column\n");\
+			goto error;\
+		} \
+		if (_is_empty_str && VAL_STRING(_val)==0) { \
+			LM_ERR("empty str column\n");\
+			goto error;\
+		} \
+	}while(0)
+
+
+#define TR_SEPARATOR '|'
+
+#define load_TR_value( _p,_s, _tr, _func, _err, _done) \
+	do{ \
+		_s = strchr(_p, (int)TR_SEPARATOR); \
+		if (_s) \
+			*_s = 0; \
+		/*DBG("----parsing tr param <%s>\n",_p);*/\
+		if(_s != _p) {\
+			if( _func( _tr, _p)) {\
+				if (_s) *_s = TR_SEPARATOR; \
+				goto _err; \
+			} \
+		} \
+		if (_s) { \
+			*_s = TR_SEPARATOR; \
+			_p = _s+1;\
+			if ( *(_p)==0 ) \
+				goto _done; \
+		} else {\
+			goto _done; \
+		}\
+	} while(0)
+
+extern int dr_fetch_rows;
+
+
+static int add_tmp_gw_list(unsigned int id, char *list)
+{
+	struct dr_gwl_tmp *tmp;
+	unsigned int list_len;
+
+	list_len = strlen(list) + 1;
+	tmp = (struct dr_gwl_tmp*)pkg_malloc(sizeof(struct dr_gwl_tmp) + list_len);
+	if (tmp==NULL) {
+		LM_ERR("no more pkg mem\n");
+		return -1;
+	}
+	tmp->id = id;
+	tmp->gwlist = (char*)(tmp+1);
+	memcpy(tmp->gwlist, list, list_len);
+
+	tmp->next = dr_gw_lists;
+	dr_gw_lists = tmp;
+	return 0;
+}
+
+static char* get_tmp_gw_list(unsigned int id)
+{
+	struct dr_gwl_tmp *tmp;
+
+	for( tmp=dr_gw_lists ; tmp ; tmp=tmp->next )
+		if (tmp->id == id) return tmp->gwlist;
+	return NULL;
+}
+
+static void free_tmp_gw_list(void)
+{
+	struct dr_gwl_tmp *tmp, *tmp1;
+
+	for( tmp=dr_gw_lists ; tmp ; ) {
+		tmp1 = tmp;
+		tmp = tmp->next;
+		pkg_free(tmp1);
+	}
+	dr_gw_lists = NULL;
+}
+
+
+static inline tmrec_t* parse_time_def(char *time_str)
+{
+	tmrec_t *time_rec;
+	char *p,*s;
+
+	p = time_str;
+	time_rec = 0;
+
+	time_rec = (tmrec_t*)shm_malloc(sizeof(tmrec_t));
+	if (time_rec==0) {
+		LM_ERR("no more pkg mem\n");
+		goto error;
+	}
+	memset( time_rec, 0, sizeof(tmrec_t));
+
+	/* empty definition? */
+	if ( time_str==0 || *time_str==0 )
+		goto done;
+
+	load_TR_value( p, s, time_rec, tr_parse_dtstart, parse_error, done);
+	load_TR_value( p, s, time_rec, tr_parse_duration, parse_error, done);
+	load_TR_value( p, s, time_rec, tr_parse_freq, parse_error, done);
+	load_TR_value( p, s, time_rec, tr_parse_until, parse_error, done);
+	load_TR_value( p, s, time_rec, tr_parse_interval, parse_error, done);
+	load_TR_value( p, s, time_rec, tr_parse_byday, parse_error, done);
+	load_TR_value( p, s, time_rec, tr_parse_bymday, parse_error, done);
+	load_TR_value( p, s, time_rec, tr_parse_byyday, parse_error, done);
+	load_TR_value( p, s, time_rec, tr_parse_byweekno, parse_error, done);
+	load_TR_value( p, s, time_rec, tr_parse_bymonth, parse_error, done);
+
+	/* success */
+done:
+	return time_rec;
+parse_error:
+	LM_ERR("parse error in <%s> around position %i\n",
+		time_str, (int)(long)(p-time_str));
+error:
+	if (time_rec)
+		tmrec_free( time_rec );
+	return 0;
+}
+
+
+static int add_rule(rt_data_t *rdata, char *grplst, str *prefix, rt_info_t *rule)
+{
+	long int t;
+	char *tmp;
+	char *ep;
+	int n;
+
+	tmp=grplst;
+	n=0;
+	/* parse the grplst */
+	while(tmp && (*tmp!=0)) {
+		errno = 0;
+		t = strtol(tmp, &ep, 10);
+		if (ep == tmp) {
+			LM_ERR("bad grp id '%c' (%d)[%s]\n",
+				*ep, (int)(ep-grplst), grplst);
+			goto error;
+		}
+		if ((!IS_SPACE(*ep)) && (*ep != SEP) && (*ep != SEP1) && (*ep!=0)) {
+			LM_ERR("bad char %c (%d) [%s]\n",
+					*ep, (int)(ep-grplst), grplst);
+			goto error;
+		}
+		if (errno == ERANGE && (t== LONG_MAX || t== LONG_MIN)) {
+			LM_ERR("out of bounds\n");
+			goto error;
+		}
+		n++;
+		/* add rule -> has prefix? */
+		if (prefix->len) {
+			/* add the routing rule */
+			if ( add_prefix(rdata->pt, prefix, rule, (unsigned int)t)!=0 ) {
+				LM_ERR("failed to add prefix route\n");
+					goto error;
+			}
+		} else {
+			if ( add_rt_info( &rdata->noprefix, rule, (unsigned int)t)!=0 ) {
+				LM_ERR("failed to add prefixless route\n");
+					goto error;
+			}
+		}
+		/* keep parsing */
+		if(IS_SPACE(*ep))
+			EAT_SPACE(ep);
+		if(ep && (*ep == SEP || *ep == SEP1))
+			ep++;
+		tmp = ep;
+	}
+
+	if(n==0) {
+		LM_ERR("no id in grp list [%s]\n",
+			grplst);
+		goto error;
+	}
+
+	return 0;
+error:
+	return -1;
+}
+
+
+rt_data_t* dr_load_routing_info( db_func_t *dr_dbf, db1_con_t* db_hdl,
+							str *drd_table, str *drl_table, str* drr_table )
+{
+	int    int_vals[4];
+	char * str_vals[4];
+	str tmp;
+	db_key_t columns[7];
+	db1_res_t* res;
+	db_row_t* row;
+	rt_info_t *ri;
+	rt_data_t *rdata;
+	tmrec_t   *time_rec;
+	unsigned int id;
+	str s_id;
+	int i,n;
+
+	res = 0;
+	ri = 0;
+	rdata = 0;
+
+	/* init new data structure */
+	if ( (rdata=build_rt_data())==0 ) {
+		LM_ERR("failed to build rdata\n");
+		goto error;
+	}
+
+	/* read the destinations */
+	if (dr_dbf->use_table( db_hdl, drd_table) < 0) {
+		LM_ERR("cannot select table \"%.*s\"\n", drd_table->len,drd_table->s);
+		goto error;
+	}
+
+	columns[0] = &dst_id_drd_col;
+	columns[1] = &address_drd_col;
+	columns[2] = &strip_drd_col;
+	columns[3] = &prefix_drd_col;
+	columns[4] = &type_drd_col;
+	columns[5] = &attrs_drd_col;
+
+	if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) {
+		if ( dr_dbf->query( db_hdl, 0, 0, 0, columns, 0, 6, 0, 0 ) < 0) {
+			LM_ERR("DB query failed\n");
+			goto error;
+		}
+		if(dr_dbf->fetch_result(db_hdl, &res, dr_fetch_rows)<0) {
+			LM_ERR("Error fetching rows\n");
+			goto error;
+		}
+	} else {
+		if ( dr_dbf->query( db_hdl, 0, 0, 0, columns, 0, 6, 0, &res) < 0) {
+			LM_ERR("DB query failed\n");
+			goto error;
+		}
+	}
+
+	if (RES_ROW_N(res) == 0) {
+		LM_WARN("table \"%.*s\" empty\n", drd_table->len,drd_table->s );
+	}
+	LM_DBG("%d records found in %.*s\n",
+		RES_ROW_N(res), drd_table->len,drd_table->s);
+	n = 0;
+	do {
+		for(i=0; i < RES_ROW_N(res); i++) {
+			row = RES_ROWS(res) + i;
+			/* DST_ID column */
+			check_val( ROW_VALUES(row), DB1_INT, 1, 0);
+			int_vals[0] = VAL_INT   (ROW_VALUES(row));
+			/* ADDRESS column */
+			check_val( ROW_VALUES(row)+1, DB1_STRING, 1, 1);
+			str_vals[0] = (char*)VAL_STRING(ROW_VALUES(row)+1);
+			/* STRIP column */
+			check_val( ROW_VALUES(row)+2, DB1_INT, 1, 0);
+			int_vals[1] = VAL_INT   (ROW_VALUES(row)+2);
+			/* PREFIX column */
+			check_val( ROW_VALUES(row)+3, DB1_STRING, 0, 0);
+			str_vals[1] = (char*)VAL_STRING(ROW_VALUES(row)+3);
+			/* TYPE column */
+			check_val( ROW_VALUES(row)+4, DB1_INT, 1, 0);
+			int_vals[2] = VAL_INT(ROW_VALUES(row)+4);
+			/* ATTRS column */
+			check_val( ROW_VALUES(row)+5, DB1_STRING, 0, 0);
+			str_vals[2] = (char*)VAL_STRING(ROW_VALUES(row)+5);
+
+			/* add the destinaton definition in */
+			if ( add_dst( rdata, int_vals[0], str_vals[0], int_vals[1],
+					str_vals[1], int_vals[2], str_vals[2])<0 ) {
+				LM_ERR("failed to add destination id %d -> skipping\n",
+					int_vals[0]);
+				continue;
+			}
+			n++;
+		}
+		if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) {
+			if(dr_dbf->fetch_result(db_hdl, &res, dr_fetch_rows)<0) {
+				LM_ERR( "fetching rows (1)\n");
+				goto error;
+			}
+		} else {
+			break;
+		}
+	} while(RES_ROW_N(res)>0);
+
+	dr_dbf->free_result(db_hdl, res);
+	res = 0;
+
+	if (n==0) {
+		LM_WARN("no valid "
+			"destinations set -> ignoring the routing rules\n");
+		return rdata;
+	}
+
+	/* read the gw lists, if any */
+	if (dr_dbf->use_table( db_hdl, drl_table) < 0) {
+		LM_ERR("cannot select table \"%.*s\"\n", drl_table->len,drl_table->s);
+		goto error;
+	}
+
+	columns[0] = &id_drl_col;
+	columns[1] = &gwlist_drl_col;
+
+	if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) {
+		if ( dr_dbf->query( db_hdl, 0, 0, 0, columns, 0, 2, 0, 0 ) < 0) {
+			LM_ERR("DB query failed\n");
+			goto error;
+		}
+		if(dr_dbf->fetch_result(db_hdl, &res, dr_fetch_rows)<0) {
+			LM_ERR("Error fetching rows\n");
+			goto error;
+		}
+	} else {
+		if ( dr_dbf->query( db_hdl, 0, 0, 0, columns, 0, 2, 0, &res) < 0) {
+			LM_ERR("DB query failed\n");
+			goto error;
+		}
+	}
+
+	if (RES_ROW_N(res) == 0) {
+		LM_DBG("table \"%.*s\" empty\n", drl_table->len,drl_table->s );
+	} else {
+		LM_DBG("%d records found in %.*s\n",
+			RES_ROW_N(res), drl_table->len,drl_table->s);
+		do {
+			for(i=0; i < RES_ROW_N(res); i++) {
+				row = RES_ROWS(res) + i;
+				/* ID column */
+				check_val( ROW_VALUES(row), DB1_INT, 1, 0);
+				int_vals[0] = VAL_INT   (ROW_VALUES(row));
+				/* GWLIST column */
+				check_val( ROW_VALUES(row)+1, DB1_STRING, 1, 1);
+				str_vals[0] = (char*)VAL_STRING(ROW_VALUES(row)+1);
+
+				if (add_tmp_gw_list(int_vals[0], str_vals[0])!=0) {
+					LM_ERR("failed to add temporary GW list\n");
+					goto error;
+				}
+			}
+			if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) {
+				if(dr_dbf->fetch_result(db_hdl, &res, dr_fetch_rows)<0) {
+					LM_ERR( "fetching rows (1)\n");
+					goto error;
+				}
+			} else {
+				break;
+			}
+		} while(RES_ROW_N(res)>0);
+	}
+	dr_dbf->free_result(db_hdl, res);
+	res = 0;
+
+	/* read the routing rules */
+	if (dr_dbf->use_table( db_hdl, drr_table) < 0) {
+		LM_ERR("cannot select table \"%.*s\"\n", drr_table->len, drr_table->s);
+		goto error;
+	}
+
+	columns[0] = &rule_id_drr_col;
+	columns[1] = &group_drr_col;
+	columns[2] = &prefix_drr_col;
+	columns[3] = &time_drr_col;
+	columns[4] = &priority_drr_col;
+	columns[5] = &routeid_drr_col;
+	columns[6] = &dstlist_drr_col;
+
+	if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) {
+		if ( dr_dbf->query( db_hdl, 0, 0, 0, columns, 0, 7, 0, 0) < 0) {
+			LM_ERR("DB query failed\n");
+			goto error;
+		}
+		if(dr_dbf->fetch_result(db_hdl, &res, dr_fetch_rows)<0) {
+			LM_ERR("Error fetching rows\n");
+			goto error;
+		}
+	} else {
+		if ( dr_dbf->query( db_hdl, 0, 0, 0, columns, 0, 7, 0, &res) < 0) {
+			LM_ERR("DB query failed\n");
+			goto error;
+		}
+	}
+
+	if (RES_ROW_N(res) == 0) {
+		LM_WARN("table \"%.*s\" is empty\n", drr_table->len, drr_table->s);
+	}
+
+	LM_DBG("%d records found in %.*s\n", RES_ROW_N(res),
+		drr_table->len, drr_table->s);
+
+	n = 0;
+	do {
+		for(i=0; i < RES_ROW_N(res); i++) {
+			row = RES_ROWS(res) + i;
+			/* RULE_ID column */
+			check_val( ROW_VALUES(row), DB1_INT, 1, 0);
+			int_vals[0] = VAL_INT (ROW_VALUES(row));
+			/* GROUP column */
+			check_val( ROW_VALUES(row)+1, DB1_STRING, 1, 1);
+			str_vals[0] = (char*)VAL_STRING(ROW_VALUES(row)+1);
+			/* PREFIX column - it may be null or empty */
+			check_val( ROW_VALUES(row)+2, DB1_STRING, 0, 0);
+			if ((ROW_VALUES(row)+2)->nul || VAL_STRING(ROW_VALUES(row)+2)==0){
+				tmp.s = NULL;
+				tmp.len = 0;
+			} else {
+				str_vals[1] = (char*)VAL_STRING(ROW_VALUES(row)+2);
+				tmp.s = str_vals[1];
+				tmp.len = strlen(str_vals[1]);
+			}
+			/* TIME column */
+			check_val( ROW_VALUES(row)+3, DB1_STRING, 1, 1);
+			str_vals[2] = (char*)VAL_STRING(ROW_VALUES(row)+3);
+			/* PRIORITY column */
+			check_val( ROW_VALUES(row)+4, DB1_INT, 1, 0);
+			int_vals[2] = VAL_INT   (ROW_VALUES(row)+4);
+			/* ROUTE_ID column */
+			check_val( ROW_VALUES(row)+5, DB1_INT, 1, 0);
+			int_vals[3] = VAL_INT   (ROW_VALUES(row)+5);
+			/* DSTLIST column */
+			check_val( ROW_VALUES(row)+6, DB1_STRING, 1, 1);
+			str_vals[3] = (char*)VAL_STRING(ROW_VALUES(row)+6);
+			/* parse the time definition */
+			if ((time_rec=parse_time_def(str_vals[2]))==0) {
+				LM_ERR("bad time definition <%s> for rule id %d -> skipping\n",
+					str_vals[2], int_vals[0]);
+				continue;
+			}
+			/* is gw_list a list or a list id? */
+			if (str_vals[3][0]=='#') {
+				s_id.s = str_vals[3]+1;
+				s_id.len = strlen(s_id.s);
+				if ( str2int( &s_id, &id)!=0 ||
+				(str_vals[3]=get_tmp_gw_list(id))==NULL ) {
+					LM_ERR("invalid reference to a GW list <%s> -> skipping\n",
+						str_vals[3]);
+					continue;
+				}
+			}
+			/* build the routing rule */
+			if ((ri = build_rt_info( int_vals[2], time_rec, int_vals[3],
+					str_vals[3], rdata->pgw_l))== 0 ) {
+				LM_ERR("failed to add routing info for rule id %d -> "
+					"skipping\n", int_vals[0]);
+				tmrec_free( time_rec );
+				continue;
+			}
+			/* add the rule */
+			if (add_rule( rdata, str_vals[0], &tmp, ri)!=0) {
+				LM_ERR("failed to add rule id %d -> skipping\n", int_vals[0]);
+				free_rt_info( ri );
+				continue;
+			}
+			n++;
+		}
+		if (DB_CAPABILITY(*dr_dbf, DB_CAP_FETCH)) {
+			if(dr_dbf->fetch_result(db_hdl, &res, dr_fetch_rows)<0) {
+				LM_ERR( "fetching rows (1)\n");
+				goto error;
+			}
+		} else {
+			break;
+		}
+	} while(RES_ROW_N(res)>0);
+
+	dr_dbf->free_result(db_hdl, res);
+	res = 0;
+
+	free_tmp_gw_list();
+
+	if (n==0) {
+		LM_WARN("no valid routing rules -> discarding all destinations\n");
+		free_rt_data( rdata, 0 );
+	}
+
+	return rdata;
+error:
+	if (res)
+		dr_dbf->free_result(db_hdl, res);
+	if (rdata)
+		free_rt_data( rdata, 1 );
+	return 0;
+}

+ 39 - 0
modules_k/drouting/dr_load.h

@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005-2009 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * History:
+ * ---------
+ *  2005-02-20  first version (cristian)
+ *  2005-02-27  ported to 0.9.0 (bogdan)
+ */
+
+
+#ifndef _DR_LOAD_
+#define _DR_LOAD_
+
+#include "../../str.h"
+#include "../../lib/srdb1/db.h"
+#include "routing.h"
+
+rt_data_t* dr_load_routing_info( db_func_t *dr_dbf, db1_con_t* db_hdl,
+							str *drd_table, str *drl_table, str* str_table);
+
+#endif

+ 1164 - 0
modules_k/drouting/dr_time.c

@@ -0,0 +1,1164 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005-2009 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * History:
+ * ---------
+ *  2005-02-27  ported to 0.9.0 (bogdan)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "../../mem/shm_mem.h"
+#include "dr_time.h"
+
+
+/************************ imported from "utils.h"  ***************************/
+
+static inline int strz2int(char *_bp)
+{
+	int _v;
+	char *_p;
+	if(!_bp)
+		return 0;
+	_v = 0;
+	_p = _bp;
+	while(*_p && *_p>='0' && *_p<='9')
+	{
+		_v += *_p - '0';
+		_p++;
+	}
+	return _v;
+}
+
+
+static inline char* trim(char* _s)
+{
+	int len;
+	char* end;
+
+	     /* Null pointer, there is nothing to do */
+	if (!_s) return _s;
+
+	     /* Remove spaces and tabs from the beginning of string */
+	while ((*_s == ' ') || (*_s == '\t')) _s++;
+
+	len = strlen(_s);
+
+        end = _s + len - 1;
+
+	     /* Remove trailing spaces and tabs */
+	while ((*end == ' ') || (*end == '\t')) end--;
+	if (end != (_s + len - 1)) {
+		*(end+1) = '\0';
+	}
+
+	return _s;
+}
+
+
+
+
+/************************ imported from "ac_tm.c"  ***************************/
+
+/* #define USE_YWEEK_U		// Sunday system
+ * #define USE_YWEEK_V		// ISO 8601
+ */
+#ifndef USE_YWEEK_U
+#ifndef USE_YWEEK_V
+#ifndef USE_YWEEK_W
+#define USE_YWEEK_W		/* Monday system */
+#endif
+#endif
+#endif
+
+#ifdef USE_YWEEK_U
+#define SUN_WEEK(t)	(int)(((t)->tm_yday + 7 - \
+				((t)->tm_wday)) / 7)
+#else
+#define MON_WEEK(t)	(int)(((t)->tm_yday + 7 - \
+				((t)->tm_wday ? (t)->tm_wday - 1 : 6)) / 7)
+#endif
+
+#define ac_get_wday_yr(t) (int)((t)->tm_yday/7)
+#define ac_get_wday_mr(t) (int)(((t)->tm_mday-1)/7)
+
+ac_tm_p ac_tm_new(void)
+{
+	ac_tm_p _atp = NULL;
+	_atp = (ac_tm_p)shm_malloc(sizeof(ac_tm_t));
+	if(!_atp)
+		return NULL;
+	memset(_atp, 0, sizeof(ac_tm_t));
+	
+	return _atp;
+}
+
+int ac_tm_fill(ac_tm_p _atp, struct tm* _tm)
+{
+	if(!_atp || !_tm)
+		return -1;
+	_atp->t.tm_sec = _tm->tm_sec;       /* seconds */
+	_atp->t.tm_min = _tm->tm_min;       /* minutes */
+	_atp->t.tm_hour = _tm->tm_hour;     /* hours */
+	_atp->t.tm_mday = _tm->tm_mday;     /* day of the month */
+	_atp->t.tm_mon = _tm->tm_mon;       /* month */
+	_atp->t.tm_year = _tm->tm_year;     /* year */
+	_atp->t.tm_wday = _tm->tm_wday;     /* day of the week */
+	_atp->t.tm_yday = _tm->tm_yday;     /* day in the year */
+	_atp->t.tm_isdst = _tm->tm_isdst;   /* daylight saving time */
+	
+	_atp->mweek = ac_get_mweek(_tm);
+	_atp->yweek = ac_get_yweek(_tm);
+	_atp->ywday = ac_get_wday_yr(_tm);
+	_atp->mwday = ac_get_wday_mr(_tm);
+	return 0;
+}
+
+int ac_tm_set_time(ac_tm_p _atp, time_t _t)
+{
+	if(!_atp)
+		return -1;
+	_atp->time = _t;
+	return ac_tm_fill(_atp, localtime(&_t));
+}
+
+int ac_get_mweek(struct tm* _tm)
+{
+	if(!_tm)
+		return -1;
+#ifdef USE_YWEEK_U
+	return ((_tm->tm_mday-1)/7 + (7-_tm->tm_wday+(_tm->tm_mday-1)%7)/7);
+#else
+	return ((_tm->tm_mday-1)/7 + (7-(6+_tm->tm_wday)%7+(_tm->tm_mday-1)%7)/7);
+#endif
+}
+
+int ac_get_yweek(struct tm* _tm)
+{
+	int week = -1;
+#ifdef USE_YWEEK_V
+	int days;
+#endif
+	
+	if(!_tm)
+		return -1;
+	
+#ifdef USE_YWEEK_U
+	week = SUN_WEEK(_tm);
+#else
+	week = MON_WEEK(_tm);
+#endif
+
+#ifdef USE_YWEEK_V
+	days = ((_tm->tm_yday + 7 - (_tm->tm_wday ? _tm->tm_wday-1 : 6)) % 7);
+
+	if(days >= 4) 
+		week++;
+	else 
+		if(week == 0) 
+			week = 53;
+#endif
+	return week;
+}
+
+int ac_get_wkst(void)
+{
+#ifdef USE_YWEEK_U
+	return 0;
+#else
+	return 1;
+#endif
+}
+
+int ac_tm_reset(ac_tm_p _atp)
+{
+	if(!_atp)
+		return -1;
+	memset(_atp, 0, sizeof(ac_tm_t));
+	return 0;
+}
+
+int ac_tm_free(ac_tm_p _atp)
+{
+	if(!_atp)
+		return -1;
+	if(_atp->mv)
+		shm_free(_atp->mv);
+	shm_free(_atp);
+	return 0;
+}
+
+ac_maxval_p ac_get_maxval(ac_tm_p _atp)
+{
+	struct tm _tm;
+	int _v;
+	ac_maxval_p _amp = NULL;
+
+	if(!_atp)
+		return NULL;
+	_amp = (ac_maxval_p)shm_malloc(sizeof(ac_maxval_t));
+	if(!_amp)
+		return NULL;
+	
+	/* the number of the days in the year */
+	_amp->yday = 365 + is_leap_year(_atp->t.tm_year+1900);
+
+	/* the number of the days in the month */
+	switch(_atp->t.tm_mon)
+	{
+		case 1:
+			if(_amp->yday == 366)
+				_amp->mday = 29;
+			else
+				_amp->mday = 28;
+		break;
+		case 3: case 5: case 8: case 10:
+			_amp->mday = 30;
+		break;
+		default:
+			_amp->mday = 31;
+	}
+	
+	/* maximum occurrences of a week day in the year */
+	memset(&_tm, 0, sizeof(struct tm));
+	_tm.tm_year = _atp->t.tm_year;
+	_tm.tm_mon = 11;
+	_tm.tm_mday = 31;
+	mktime(&_tm);
+	_v = 0;
+	if(_atp->t.tm_wday > _tm.tm_wday)
+		_v = _atp->t.tm_wday - _tm.tm_wday + 1;
+	else
+		_v = _tm.tm_wday - _atp->t.tm_wday;
+	_amp->ywday = (int)((_tm.tm_yday-_v)/7) + 1;
+	
+	/* maximum number of weeks in the year */
+	_amp->yweek = ac_get_yweek(&_tm) + 1;
+	
+	/* maximum number of the week day in the month */
+	_amp->mwday=(int)((_amp->mday-1-(_amp->mday-_atp->t.tm_mday)%7)/7)+1;
+	
+	/* maximum number of weeks in the month */
+	_v = (_atp->t.tm_wday + (_amp->mday - _atp->t.tm_mday)%7)%7;
+#ifdef USE_YWEEK_U
+	_amp->mweek = (int)((_amp->mday-1)/7+(7-_v+(_amp->mday-1)%7)/7)+1;
+#else
+	_amp->mweek = (int)((_amp->mday-1)/7+(7-(6+_v)%7+(_amp->mday-1)%7)/7)+1;
+#endif
+
+	_atp->mv = _amp;
+	return _amp;
+}
+
+
+
+
+
+/************************ imported from "tmrec.c"  ***************************/
+
+#define _D(c) ((c) -'0')
+
+tr_byxxx_p tr_byxxx_new(void)
+{
+	tr_byxxx_p _bxp = NULL;
+	_bxp = (tr_byxxx_p)shm_malloc(sizeof(tr_byxxx_t));
+	if(!_bxp)
+		return NULL;
+	memset(_bxp, 0, sizeof(tr_byxxx_t));
+	return _bxp;
+}
+
+int tr_byxxx_init(tr_byxxx_p _bxp, int _nr)
+{
+	if(!_bxp)
+		return -1;
+	_bxp->nr = _nr;
+	_bxp->xxx = (int*)shm_malloc(_nr*sizeof(int));
+	if(!_bxp->xxx)
+		return -1;
+	_bxp->req = (int*)shm_malloc(_nr*sizeof(int));
+	if(!_bxp->req)
+	{
+		shm_free(_bxp->xxx);
+		return -1;
+	}
+	
+	memset(_bxp->xxx, 0, _nr*sizeof(int));
+	memset(_bxp->req, 0, _nr*sizeof(int));
+	
+	return 0;
+}
+
+
+int tr_byxxx_free(tr_byxxx_p _bxp)
+{
+	if(!_bxp)
+		return -1;
+	if(_bxp->xxx)
+		shm_free(_bxp->xxx);
+	if(_bxp->req)
+		shm_free(_bxp->req);
+	shm_free(_bxp);
+	return 0;
+}
+
+tmrec_p tmrec_new(void)
+{
+	tmrec_p _trp = NULL;
+	_trp = (tmrec_p)shm_malloc(sizeof(tmrec_t));
+	if(!_trp)
+		return NULL;
+	memset(_trp, 0, sizeof(tmrec_t));
+	localtime_r(&_trp->dtstart,&(_trp->ts));
+	return _trp;
+}
+
+int tmrec_free(tmrec_p _trp)
+{
+	if(!_trp)
+		return -1;
+	
+	tr_byxxx_free(_trp->byday);
+	tr_byxxx_free(_trp->bymday);
+	tr_byxxx_free(_trp->byyday);
+	tr_byxxx_free(_trp->bymonth);
+	tr_byxxx_free(_trp->byweekno);
+
+	shm_free(_trp);
+	return 0;
+}
+
+int tr_parse_dtstart(tmrec_p _trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->dtstart = ic_parse_datetime(_in, &(_trp->ts));
+	return (_trp->dtstart==0)?-1:0;
+}
+
+int tr_parse_dtend(tmrec_p _trp, char *_in)
+{
+	struct tm _tm;
+	if(!_trp || !_in)
+		return -1;
+	_trp->dtend = ic_parse_datetime(_in,&_tm);
+	return (_trp->dtend==0)?-1:0;
+}
+
+int tr_parse_duration(tmrec_p _trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->duration = ic_parse_duration(_in);
+	return 0;
+}
+
+int tr_parse_until(tmrec_p _trp, char *_in)
+{
+	struct tm _tm;
+	if(!_trp || !_in)
+		return -1;
+	_trp->until = ic_parse_datetime(_in, &_tm);
+	return 0;
+}
+
+int tr_parse_freq(tmrec_p _trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	if(strlen(_in)<5)
+	{
+		_trp->freq = FREQ_NOFREQ;
+		return 0;
+	}
+	if(!strcasecmp(_in, "daily"))
+	{
+		_trp->freq = FREQ_DAILY;
+		return 0;
+	}
+	if(!strcasecmp(_in, "weekly"))
+	{
+		_trp->freq = FREQ_WEEKLY;
+		return 0;
+	}
+	if(!strcasecmp(_in, "monthly"))
+	{
+		_trp->freq = FREQ_MONTHLY;
+		return 0;
+	}
+	if(!strcasecmp(_in, "yearly"))
+	{
+		_trp->freq = FREQ_YEARLY;
+		return 0;
+	}
+
+	_trp->freq = FREQ_NOFREQ;
+	return 0;
+}
+
+int tr_parse_interval(tmrec_p _trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->interval = strz2int(_in);
+	return 0;
+}
+
+int tr_parse_byday(tmrec_p _trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->byday = ic_parse_byday(_in); 
+	return 0;
+}
+
+int tr_parse_bymday(tmrec_p _trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->bymday = ic_parse_byxxx(_in); 
+	return 0;
+}
+
+int tr_parse_byyday(tmrec_p _trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->byyday = ic_parse_byxxx(_in); 
+	return 0;
+}
+
+int tr_parse_bymonth(tmrec_p _trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->bymonth = ic_parse_byxxx(_in); 
+	return 0;
+}
+
+int tr_parse_byweekno(tmrec_p _trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->byweekno = ic_parse_byxxx(_in); 
+	return 0;
+}
+
+int tr_parse_wkst(tmrec_p _trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->wkst = ic_parse_wkst(_in);
+	return 0;
+}
+
+
+time_t ic_parse_datetime(char *_in, struct tm *_tm)
+{
+	if(!_in || !_tm || strlen(_in)!=15)
+		return 0;
+	
+	memset(_tm, 0, sizeof(struct tm));
+	_tm->tm_year = _D(_in[0])*1000 + _D(_in[1])*100 
+			+ _D(_in[2])*10 + _D(_in[3]) - 1900;
+	_tm->tm_mon = _D(_in[4])*10 + _D(_in[5]) - 1;
+	_tm->tm_mday = _D(_in[6])*10 + _D(_in[7]);
+	_tm->tm_hour = _D(_in[9])*10 + _D(_in[10]);
+	_tm->tm_min = _D(_in[11])*10 + _D(_in[12]);
+	_tm->tm_sec = _D(_in[13])*10 + _D(_in[14]);
+	_tm->tm_isdst = -1 /*daylight*/;
+	return mktime(_tm);
+}
+
+time_t ic_parse_duration(char *_in)
+{
+	time_t _t, _ft;
+	char *_p;
+	int _fl;
+	
+	if(!_in || strlen(_in)<2)
+		return 0;
+	
+	if(*_in == 'P' || *_in=='p')
+	{
+		_p = _in+1;
+		_fl = 1;
+	} else {
+		_p = _in;
+		_fl = 0;
+	}
+	
+	_t = _ft = 0;
+	
+	while(*_p)
+	{
+		switch(*_p)
+		{
+			case '0': case '1': case '2':
+			case '3': case '4': case '5':
+			case '6': case '7': case '8':
+			case '9':
+				_t = _t*10 + *_p - '0';
+			break;
+			
+			case 'w':
+			case 'W':
+				if(!_fl)
+				{
+					LM_ERR("week duration not allowed"
+						" here (%d) [%s]\n", (int)(_p-_in), _in);
+					return 0;
+				}
+				_ft += _t*7*24*3600;
+				_t = 0;
+			break;
+			case 'd':
+			case 'D':
+				if(!_fl)
+				{
+					LM_ERR("day duration not allowed"
+						" here (%d) [%s]\n", (int)(_p-_in), _in);
+					return 0;
+				}
+				_ft += _t*24*3600;
+				_t = 0;
+			break;
+			case 'h':
+			case 'H':
+				if(_fl)
+				{
+					LM_ERR("hour duration not allowed"
+						" here (%d) [%s]\n", (int)(_p-_in), _in);
+					return 0;
+				}
+				_ft += _t*3600;
+				_t = 0;
+			break;
+			case 'm':
+			case 'M':
+				if(_fl)
+				{
+					LM_ERR("minute duration not allowed"
+						" here (%d) [%s]\n", (int)(_p-_in), _in);
+					return 0;
+				}
+				_ft += _t*60;
+				_t = 0;
+			break;
+			case 's':
+			case 'S':
+				if(_fl)
+				{
+					LM_ERR("second duration not allowed"
+						" here (%d) [%s]\n", (int)(_p-_in), _in);
+					return 0;
+				}
+				_ft += _t;
+				_t = 0;
+			break;
+			case 't':
+			case 'T':
+				if(!_fl)
+				{
+					LM_ERR("'T' not allowed"
+						" here (%d) [%s]\n", (int)(_p-_in), _in);
+					return 0;
+				}
+				_fl = 0;
+			break;
+			default:
+				LM_ERR("bad character here (%d) [%s]\n",
+					(int)(_p-_in), _in);
+				return 0;
+		}
+		_p++;
+	}
+
+	return _ft;
+}
+
+tr_byxxx_p ic_parse_byday(char *_in)
+{
+	tr_byxxx_p _bxp = NULL;
+	int _nr, _s, _v;
+	char *_p;
+
+	if(!_in)
+		return NULL;
+	_bxp = tr_byxxx_new();
+	if(!_bxp)
+		return NULL;
+	_p = _in;
+	_nr = 1;
+	while(*_p)
+	{
+		if(*_p == ',')
+			_nr++;
+		_p++;
+	}
+	if(tr_byxxx_init(_bxp, _nr) < 0)
+	{
+		tr_byxxx_free(_bxp);
+		return NULL;
+	}
+	_p = _in;
+	_nr = _v = 0;
+	_s = 1;
+	while(*_p && _nr < _bxp->nr)
+	{
+		switch(*_p)
+		{
+			case '0': case '1': case '2':
+			case '3': case '4': case '5':
+			case '6': case '7': case '8':
+			case '9':
+				_v = _v*10 + *_p - '0';
+			break;
+			
+			case 's':
+			case 'S':
+				_p++;
+				switch(*_p)
+				{
+					case 'a':
+					case 'A':
+						_bxp->xxx[_nr] = WDAY_SA;
+						_bxp->req[_nr] = _s*_v;
+					break;
+					case 'u':
+					case 'U':
+						_bxp->xxx[_nr] = WDAY_SU;
+						_bxp->req[_nr] = _s*_v;
+					break;
+					default:
+						goto error;
+				}
+				_s = 1;
+				_v = 0;
+			break;
+			case 'm':
+			case 'M':
+				_p++;
+				if(*_p!='o' && *_p!='O')
+					goto error;
+				_bxp->xxx[_nr] = WDAY_MO;
+				_bxp->req[_nr] = _s*_v;
+				_s = 1;
+				_v = 0;
+			break;
+			case 't':
+			case 'T':
+				_p++;
+				switch(*_p)
+				{
+					case 'h':
+					case 'H':
+						_bxp->xxx[_nr] = WDAY_TH;
+						_bxp->req[_nr] = _s*_v;
+					break;
+					case 'u':
+					case 'U':
+						_bxp->xxx[_nr] = WDAY_TU;
+						_bxp->req[_nr] = _s*_v;
+					break;
+					default:
+						goto error;
+				}
+				_s = 1;
+				_v = 0;
+			break;
+			case 'w':
+			case 'W':
+				_p++;
+				if(*_p!='e' && *_p!='E')
+					goto error;
+				_bxp->xxx[_nr] = WDAY_WE;
+				_bxp->req[_nr] = _s*_v;
+				_s = 1;
+				_v = 0;
+			break;
+			case 'f':
+			case 'F':
+				_p++;
+				if(*_p!='r' && *_p!='R')
+					goto error;
+				_bxp->xxx[_nr] = WDAY_FR;
+				_bxp->req[_nr] = _s*_v;
+				_s = 1;
+				_v = 0;
+			break;
+			case '-':
+				_s = -1;
+			break;
+			case '+':
+			case ' ':
+			case '\t':
+			break;
+			case ',':
+				_nr++;
+			break;
+			default:
+				goto error;
+		}
+		_p++;
+	}
+
+	return _bxp;
+
+error:
+	tr_byxxx_free(_bxp);
+	return NULL;
+}
+
+tr_byxxx_p ic_parse_byxxx(char *_in)
+{
+	tr_byxxx_p _bxp = NULL;
+	int _nr, _s, _v;
+	char *_p;
+
+	if(!_in)
+		return NULL;
+	_bxp = tr_byxxx_new();
+	if(!_bxp)
+		return NULL;
+	_p = _in;
+	_nr = 1;
+	while(*_p)
+	{
+		if(*_p == ',')
+			_nr++;
+		_p++;
+	}
+	if(tr_byxxx_init(_bxp, _nr) < 0)
+	{
+		tr_byxxx_free(_bxp);
+		return NULL;
+	}
+	_p = _in;
+	_nr = _v = 0;
+	_s = 1;
+	while(*_p && _nr < _bxp->nr)
+	{
+		switch(*_p)
+		{
+			case '0': case '1': case '2':
+			case '3': case '4': case '5':
+			case '6': case '7': case '8':
+			case '9':
+				_v = _v*10 + *_p - '0';
+			break;
+			
+			case '-':
+				_s = -1;
+			break;
+			case '+':
+			case ' ':
+			case '\t':
+			break;
+			case ',':
+				_bxp->xxx[_nr] = _v;
+				_bxp->req[_nr] = _s;
+				_s = 1;
+				_v = 0;
+				_nr++;
+			break;
+			default:
+				goto error;
+		}
+		_p++;
+	}
+	if(_nr < _bxp->nr)
+	{
+		_bxp->xxx[_nr] = _v;
+		_bxp->req[_nr] = _s;
+	}
+	return _bxp;
+
+error:
+	tr_byxxx_free(_bxp);
+	return NULL;
+}
+
+int ic_parse_wkst(char *_in)
+{
+	if(!_in || strlen(_in)!=2)
+		goto error;
+	
+	switch(_in[0])
+	{
+		case 's':
+		case 'S':
+			switch(_in[1])
+			{
+				case 'a':
+				case 'A':
+					return WDAY_SA;
+				case 'u':
+				case 'U':
+					return WDAY_SU;
+				default:
+					goto error;
+			}
+		case 'm':
+		case 'M':
+			if(_in[1]!='o' && _in[1]!='O')
+				goto error;
+			return WDAY_MO;
+		case 't':
+		case 'T':
+			switch(_in[1])
+			{
+				case 'h':
+				case 'H':
+					return WDAY_TH;
+				case 'u':
+				case 'U':
+					return WDAY_TU;
+				default:
+					goto error;
+			}
+		case 'w':
+		case 'W':
+			if(_in[1]!='e' && _in[1]!='E')
+				goto error;
+			return WDAY_WE;
+		case 'f':
+		case 'F':
+			if(_in[1]!='r' && _in[1]!='R')
+				goto error;
+			return WDAY_FR;
+		break;
+		default:
+			goto error;
+	}
+	
+error:
+#ifdef USE_YWEEK_U
+	return WDAY_SU;
+#else
+	return WDAY_MO;
+#endif
+}
+
+
+
+
+
+
+/*********************** imported from "checktr.c"  **************************/
+
+#define REC_ERR    -1
+#define REC_MATCH   0
+#define REC_NOMATCH 1
+
+#define _IS_SET(x) (((x)>0)?1:0)
+
+/*** local headers ***/
+int get_min_interval(tmrec_p);
+int check_min_unit(tmrec_p, ac_tm_p, tr_res_p);
+int check_freq_interval(tmrec_p _trp, ac_tm_p _atp);
+int check_byxxx(tmrec_p, ac_tm_p);
+
+/**
+ *
+ * return 0/REC_MATCH - the time falls in
+ *       -1/REC_ERR - error
+ *        1/REC_NOMATCH - the time falls out
+ */
+int check_tmrec(tmrec_p _trp, ac_tm_p _atp, tr_res_p _tsw)
+{
+	if(!_trp || !_atp)
+		return REC_ERR;
+
+	/* it is before start date */
+	if(_atp->time < _trp->dtstart)
+		return REC_NOMATCH;
+
+	/* no duration or end -> for ever */
+	if (!_IS_SET(_trp->duration) && !_IS_SET(_trp->dtend))
+		return REC_MATCH;
+
+	/* compute the duration of the recurrence interval */
+	if(!_IS_SET(_trp->duration))
+		_trp->duration = _trp->dtend - _trp->dtstart;
+	
+	if(_atp->time <= _trp->dtstart+_trp->duration)
+	{
+		if(_tsw)
+		{
+			if(_tsw->flag & TSW_RSET)
+			{
+				if(_tsw->rest>_trp->dtstart+_trp->duration-_atp->time)
+					_tsw->rest = _trp->dtstart+_trp->duration - _atp->time;
+			}
+			else
+			{
+				_tsw->flag |= TSW_RSET;
+				_tsw->rest = _trp->dtstart+_trp->duration - _atp->time;
+			}
+		}
+		return REC_MATCH;
+	}
+	
+	/* after the bound of recurrence */
+	if(_IS_SET(_trp->until) && _atp->time >= _trp->until + _trp->duration)
+		return REC_NOMATCH;
+	
+	/* check if the instance of recurrence matches the 'interval' */
+	if(check_freq_interval(_trp, _atp)!=REC_MATCH)
+		return REC_NOMATCH;
+
+	if(check_min_unit(_trp, _atp, _tsw)!=REC_MATCH)
+		return REC_NOMATCH;
+
+	if(check_byxxx(_trp, _atp)!=REC_MATCH)
+		return REC_NOMATCH;
+
+	return REC_MATCH;
+}
+
+
+int check_freq_interval(tmrec_p _trp, ac_tm_p _atp)
+{
+	int _t0, _t1;
+	struct tm _tm;
+	if(!_trp || !_atp)
+		return REC_ERR;
+	
+	if(!_IS_SET(_trp->freq))
+		return REC_NOMATCH;
+	
+	if(!_IS_SET(_trp->interval) || _trp->interval==1)
+		return REC_MATCH;
+	
+	switch(_trp->freq)
+	{
+		case FREQ_DAILY:
+		case FREQ_WEEKLY:
+			memset(&_tm, 0, sizeof(struct tm));
+			_tm.tm_year = _trp->ts.tm_year;
+			_tm.tm_mon = _trp->ts.tm_mon;
+			_tm.tm_mday = _trp->ts.tm_mday;
+			_t0 = (int)mktime(&_tm);
+			memset(&_tm, 0, sizeof(struct tm));
+			_tm.tm_year = _atp->t.tm_year;
+			_tm.tm_mon = _atp->t.tm_mon;
+			_tm.tm_mday = _atp->t.tm_mday;
+			_t1 = (int)mktime(&_tm);
+			if(_trp->freq == FREQ_DAILY)
+				return (((_t1-_t0)/(24*3600))%_trp->interval==0)?
+					REC_MATCH:REC_NOMATCH;
+#ifdef USE_YWEEK_U
+			_t0 -= _trp->ts.tm_wday*24*3600;
+			_t1 -= _atp->t.tm_wday*24*3600;
+#else
+			_t0 -= ((_trp->ts.tm_wday+6)%7)*24*3600;
+			_t1 -= ((_atp->t.tm_wday+6)%7)*24*3600;
+#endif
+			return (((_t1-_t0)/(7*24*3600))%_trp->interval==0)?
+					REC_MATCH:REC_NOMATCH;
+		case FREQ_MONTHLY:
+			_t0 = (_atp->t.tm_year-_trp->ts.tm_year)*12
+					+ _atp->t.tm_mon-_trp->ts.tm_mon;
+			return (_t0%_trp->interval==0)?REC_MATCH:REC_NOMATCH;
+		case FREQ_YEARLY:
+			return ((_atp->t.tm_year-_trp->ts.tm_year)%_trp->interval==0)?
+					REC_MATCH:REC_NOMATCH;
+	}
+	
+	return REC_NOMATCH;
+}
+
+int get_min_interval(tmrec_p _trp)
+{
+	if(!_trp)
+		return FREQ_NOFREQ;
+	
+	if(_trp->freq == FREQ_DAILY || _trp->byday || _trp->bymday || _trp->byyday)
+		return FREQ_DAILY;
+	if(_trp->freq == FREQ_WEEKLY || _trp->byweekno) 
+		return FREQ_WEEKLY;
+	if(_trp->freq == FREQ_MONTHLY || _trp->bymonth)
+		return FREQ_MONTHLY;
+	if(_trp->freq == FREQ_YEARLY)
+		return FREQ_YEARLY;
+	
+	return FREQ_NOFREQ;
+}
+
+int check_min_unit(tmrec_p _trp, ac_tm_p _atp, tr_res_p _tsw)
+{
+	int _v0, _v1;
+	if(!_trp || !_atp)
+		return REC_ERR;
+	switch(get_min_interval(_trp))
+	{
+		case FREQ_DAILY:
+		break;
+		case FREQ_WEEKLY:
+			if(_trp->ts.tm_wday != _atp->t.tm_wday)
+				return REC_NOMATCH;
+		break;
+		case FREQ_MONTHLY:
+			if(_trp->ts.tm_mday != _atp->t.tm_mday)
+				return REC_NOMATCH;
+		break;
+		case FREQ_YEARLY:
+			if(_trp->ts.tm_mon != _atp->t.tm_mon 
+					|| _trp->ts.tm_mday != _atp->t.tm_mday)
+				return REC_NOMATCH;
+		break;
+		default:
+			return REC_NOMATCH;
+	}
+	_v0 = _trp->ts.tm_hour*3600 + _trp->ts.tm_min*60 + _trp->ts.tm_sec;
+	_v1 = _atp->t.tm_hour*3600 + _atp->t.tm_min*60 + _atp->t.tm_sec;
+	if(_v1 >= _v0 && _v1 < _v0 + _trp->duration)
+	{
+		if(_tsw)
+		{
+			if(_tsw->flag & TSW_RSET)
+			{
+				if(_tsw->rest>_v0+_trp->duration-_v1)
+					_tsw->rest = _v0 + _trp->duration - _v1;
+			}
+			else
+			{
+				_tsw->flag |= TSW_RSET;
+				_tsw->rest = _v0 + _trp->duration - _v1;
+			}
+		}
+		return REC_MATCH;
+	}
+	
+	return REC_NOMATCH;
+}
+
+int check_byxxx(tmrec_p _trp, ac_tm_p _atp)
+{
+	int i;
+	ac_maxval_p _amp = NULL;
+	if(!_trp || !_atp)
+		return REC_ERR;
+	if(!_trp->byday && !_trp->bymday && !_trp->byyday && !_trp->bymonth 
+			&& !_trp->byweekno)
+		return REC_MATCH;
+	
+	_amp = ac_get_maxval(_atp);
+	if(!_amp)
+		return REC_NOMATCH;
+	
+	if(_trp->bymonth)
+	{
+		for(i=0; i<_trp->bymonth->nr; i++)
+		{
+			if(_atp->t.tm_mon == 
+					(_trp->bymonth->xxx[i]*_trp->bymonth->req[i]+12)%12)
+				break;
+		}
+		if(i>=_trp->bymonth->nr)
+			return REC_NOMATCH;
+	}
+	if(_trp->freq==FREQ_YEARLY && _trp->byweekno)
+	{
+		for(i=0; i<_trp->byweekno->nr; i++)
+		{
+			if(_atp->yweek == (_trp->byweekno->xxx[i]*_trp->byweekno->req[i]+
+							_amp->yweek)%_amp->yweek)
+				break;
+		}
+		if(i>=_trp->byweekno->nr)
+			return REC_NOMATCH;
+	}
+	if(_trp->byyday)
+	{
+		for(i=0; i<_trp->byyday->nr; i++)
+		{
+			if(_atp->t.tm_yday == (_trp->byyday->xxx[i]*_trp->byyday->req[i]+
+						_amp->yday)%_amp->yday)
+				break;
+		}
+		if(i>=_trp->byyday->nr)
+			return REC_NOMATCH;
+	}
+	if(_trp->bymday)
+	{
+		for(i=0; i<_trp->bymday->nr; i++)
+		{
+#ifdef EXTRA_DEBUG
+			LM_DBG("%d == %d\n", _atp->t.tm_mday,
+				(_trp->bymday->xxx[i]*_trp->bymday->req[i]+
+				_amp->mday)%_amp->mday + ((_trp->bymday->req[i]<0)?1:0));
+#endif
+			if(_atp->t.tm_mday == (_trp->bymday->xxx[i]*_trp->bymday->req[i]+
+						_amp->mday)%_amp->mday + (_trp->bymday->req[i]<0)?1:0)
+				break;
+		}
+		if(i>=_trp->bymday->nr)
+			return REC_NOMATCH;
+	}
+	if(_trp->byday)
+	{
+		for(i=0; i<_trp->byday->nr; i++)
+		{
+			if(_trp->freq==FREQ_YEARLY)
+			{
+#ifdef EXTRA_DEBUG
+				LM_DBG("%d==%d && %d==%d\n", _atp->t.tm_wday,
+					_trp->byday->xxx[i], _atp->ywday+1, 
+					(_trp->byday->req[i]+_amp->ywday)%_amp->ywday);
+#endif
+				if(_atp->t.tm_wday == _trp->byday->xxx[i] &&
+						_atp->ywday+1 == (_trp->byday->req[i]+_amp->ywday)%
+						_amp->ywday)
+					break;
+			}
+			else
+			{
+				if(_trp->freq==FREQ_MONTHLY)
+				{
+#ifdef EXTRA_DEBUG
+					LM_DBG("%d==%d && %d==%d\n", _atp->t.tm_wday,
+						_trp->byday->xxx[i], _atp->mwday+1, 
+						(_trp->byday->req[i]+_amp->mwday)%_amp->mwday);
+#endif
+					if(_atp->t.tm_wday == _trp->byday->xxx[i] &&
+							_atp->mwday+1==(_trp->byday->req[i]+
+							_amp->mwday)%_amp->mwday)
+						break;
+				}
+				else
+				{
+					if(_atp->t.tm_wday == _trp->byday->xxx[i])
+						break;
+				}
+			}
+		}
+		if(i>=_trp->byday->nr)
+			return REC_NOMATCH;
+	}
+
+	return REC_MATCH;
+}
+
+

+ 174 - 0
modules_k/drouting/dr_time.h

@@ -0,0 +1,174 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005-2009 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * History:
+ * ---------
+ *  2005-02-27  ported to 0.9.0 (bogdan)
+ */
+
+
+#ifndef _DR_TIME_H_
+#define _DR_TIME_H_
+
+
+/************************ imported from "ac_tm.h"  ***************************/
+
+#include <time.h>
+
+
+/* USE_YWEEK_U	-- Sunday system - see strftime %U
+ * USE_YWEEK_V	-- ISO 8601 - see strftime %V
+ * USE_YWEEK_W	-- Monday system - see strftime %W
+*/
+
+#ifndef USE_YWEEK_U
+# ifndef USE_YWEEK_V
+#  ifndef USE_YWEEK_W
+#   define USE_YWEEK_W
+#  endif
+# endif
+#endif
+
+#define is_leap_year(yyyy) ((((yyyy)%400))?(((yyyy)%100)?(((yyyy)%4)?0:1):0):1)
+
+
+typedef struct _ac_maxval
+{
+	int yweek;
+	int yday;
+	int ywday;
+	int mweek;
+	int mday;
+	int mwday;
+} ac_maxval_t, *ac_maxval_p;
+
+typedef struct _ac_tm
+{
+	time_t time;
+	struct tm t;
+	int mweek;
+	int yweek;
+	int ywday;
+	int mwday;
+	ac_maxval_p mv;
+} ac_tm_t, *ac_tm_p;
+
+ac_tm_p ac_tm_new();
+
+int ac_tm_set_time(ac_tm_p, time_t);
+
+int ac_tm_reset(ac_tm_p);
+int ac_tm_free(ac_tm_p);
+
+int ac_get_mweek(struct tm*);
+int ac_get_yweek(struct tm*);
+ac_maxval_p ac_get_maxval(ac_tm_p);
+int ac_get_wkst();
+
+int ac_print(ac_tm_p);
+
+
+
+
+/************************ imported from "tmrec.h"  ***************************/
+
+
+#define FREQ_NOFREQ  0
+#define FREQ_YEARLY  1
+#define FREQ_MONTHLY 2
+#define FREQ_WEEKLY  3
+#define FREQ_DAILY   4
+
+#define WDAY_SU 0
+#define WDAY_MO 1
+#define WDAY_TU 2
+#define WDAY_WE 3
+#define WDAY_TH 4
+#define WDAY_FR 5
+#define WDAY_SA 6
+#define WDAY_NU 7
+
+#define TSW_TSET	1
+#define TSW_RSET	2
+
+typedef struct _tr_byxxx
+{
+	int nr;
+	int *xxx;
+	int *req;
+} tr_byxxx_t, *tr_byxxx_p;
+
+typedef struct _tmrec
+{
+	time_t dtstart;
+	struct tm ts;
+	time_t dtend;
+	time_t duration;
+	time_t until;
+	int freq;
+	int interval;
+	tr_byxxx_p byday;
+	tr_byxxx_p bymday;
+	tr_byxxx_p byyday;
+	tr_byxxx_p bymonth;
+	tr_byxxx_p byweekno;
+	int wkst;
+} tmrec_t, *tmrec_p;
+
+typedef struct _tr_res
+{
+	int flag;
+	time_t rest;
+} tr_res_t, *tr_res_p;
+
+tr_byxxx_p tr_byxxx_new();
+int tr_byxxx_init(tr_byxxx_p, int);
+int tr_byxxx_free(tr_byxxx_p);
+
+tmrec_p tmrec_new();
+int tmrec_free(tmrec_p);
+
+int tr_parse_dtstart(tmrec_p, char*);
+int tr_parse_dtend(tmrec_p, char*);
+int tr_parse_duration(tmrec_p, char*);
+int tr_parse_until(tmrec_p, char*);
+int tr_parse_freq(tmrec_p, char*);
+int tr_parse_interval(tmrec_p, char*);
+int tr_parse_byday(tmrec_p, char*);
+int tr_parse_bymday(tmrec_p, char*);
+int tr_parse_byyday(tmrec_p, char*);
+int tr_parse_bymonth(tmrec_p, char*);
+int tr_parse_byweekno(tmrec_p, char*);
+int tr_parse_wkst(tmrec_p, char*);
+
+int tr_print(tmrec_p);
+time_t ic_parse_datetime(char*,struct tm*);
+time_t ic_parse_duration(char*);
+
+tr_byxxx_p ic_parse_byday(char*);
+tr_byxxx_p ic_parse_byxxx(char*);
+int ic_parse_wkst(char*);
+
+int check_tmrec(tmrec_p, ac_tm_p, tr_res_p);
+
+
+#endif
+

+ 1083 - 0
modules_k/drouting/drouting.c

@@ -0,0 +1,1083 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005-2009 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+
+ * History:
+ * ---------
+ *  2005-02-20  first version (cristian)
+ *  2005-02-27  ported to 0.9.0 (bogdan)
+ */
+
+#include "stdlib.h"
+#include "stdio.h"
+#include "assert.h"
+#include <unistd.h>
+
+#include "../../sr_module.h"
+#include "../../str.h"
+#include "../../dprint.h"
+#include "../../usr_avp.h"
+#include "../../lib/srdb1/db.h"
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include "../../locking.h"
+#include "../../action.h"
+#include "../../error.h"
+#include "../../ut.h"
+#include "../../resolve.h"
+#include "../../parser/parse_from.h"
+#include "../../parser/parse_uri.h"
+#include "../../mi/mi.h"
+
+#include "dr_load.h"
+#include "prefix_tree.h"
+#include "routing.h"
+
+
+/*** DB relatede stuff ***/
+/* parameters  */
+static str db_url = {NULL,0};
+static str drg_table = str_init("dr_groups");
+static str drd_table = str_init("dr_gateways");
+static str drr_table = str_init("dr_rules");
+static str drl_table = str_init("dr_gw_lists");
+/* DRG use domain */
+static int use_domain = 1;
+/**
+ * - 0 - normal order
+ * - 1 - random order, full set
+ * - 2 - random order, one per set
+ */
+static int sort_order = 0;
+int dr_fetch_rows = 1000;
+int dr_force_dns = 1;
+
+/* DRG table columns */
+static str drg_user_col = str_init("username");
+static str drg_domain_col = str_init("domain");
+static str drg_grpid_col = str_init("groupid");
+/* variables */
+static db1_con_t  *db_hdl=0;     /* DB handler */
+static db_func_t dr_dbf;        /* DB functions */
+
+/* current dr data - pointer to a pointer in shm */
+static rt_data_t **rdata = 0;
+
+/* AVP used to store serial RURIs */
+static struct _ruri_avp{
+	unsigned short type; /* AVP ID */
+	int_str name; /* AVP name*/
+}ruri_avp = { 0, {.n=(int)0xad346b2f} };
+static str ruri_avp_spec = {0,0};
+
+/* AVP used to store serial ATTRs */
+static struct _attrs_avp{
+	unsigned short type; /* AVP ID */
+	int_str name; /* AVP name*/
+}attrs_avp = { 0, {.n=(int)0xad346b30} };
+static str attrs_avp_spec = {0,0};
+
+/* statistic data */
+int tree_size = 0;
+int inode = 0;
+int unode = 0;
+
+/* lock, ref counter and flag used for reloading the date */
+static gen_lock_t *ref_lock = 0;
+static int* data_refcnt = 0;
+static int* reload_flag = 0;
+
+static int dr_init(void);
+static int dr_child_init(int rank);
+static int dr_exit(void);
+
+static int fixup_do_routing(void** param, int param_no);
+static int fixup_from_gw(void** param, int param_no);
+
+static int do_routing(struct sip_msg* msg, dr_group_t *drg);
+static int do_routing_0(struct sip_msg* msg, char* str1, char* str2);
+static int do_routing_1(struct sip_msg* msg, char* str1, char* str2);
+static int use_next_gw(struct sip_msg* msg);
+static int is_from_gw_0(struct sip_msg* msg, char* str1, char* str2);
+static int is_from_gw_1(struct sip_msg* msg, char* str1, char* str2);
+static int is_from_gw_2(struct sip_msg* msg, char* str1, char* str2);
+static int goes_to_gw_0(struct sip_msg* msg, char* f1, char* f2);
+static int goes_to_gw_1(struct sip_msg* msg, char* f1, char* f2);
+
+static struct mi_root* dr_reload_cmd(struct mi_root *cmd_tree, void *param);
+
+#define RELOAD_MI_CMD  "dr_reload"
+
+
+MODULE_VERSION
+
+/*
+ * Exported functions
+ */
+static cmd_export_t cmds[] = {
+	{"do_routing",  (cmd_function)do_routing_0,   0,  0, 0,
+		REQUEST_ROUTE|FAILURE_ROUTE},
+	{"do_routing",  (cmd_function)do_routing_1,   1,  fixup_do_routing, 0,
+		REQUEST_ROUTE|FAILURE_ROUTE},
+	{"use_next_gw",  (cmd_function)use_next_gw,   0,  0, 0,
+		REQUEST_ROUTE|FAILURE_ROUTE},
+	{"next_routing",  (cmd_function)use_next_gw, 0,  0, 0,
+		FAILURE_ROUTE},
+	{"is_from_gw",  (cmd_function)is_from_gw_0,   0,  0, 0,
+		REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
+	{"is_from_gw",  (cmd_function)is_from_gw_1,   1,  fixup_from_gw, 0,
+		REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
+	{"is_from_gw",  (cmd_function)is_from_gw_2,   2,  fixup_from_gw, 0,
+		REQUEST_ROUTE},
+	{"goes_to_gw",  (cmd_function)goes_to_gw_0,   0,  0, 0,
+		REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
+	{"goes_to_gw",  (cmd_function)goes_to_gw_1,   1,  fixup_from_gw, 0,
+		REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+
+/*
+ * Exported parameters
+ */
+static param_export_t params[] = {
+	{"db_url",          STR_PARAM, &db_url.s        },
+	{"drd_table",       STR_PARAM, &drd_table.s     },
+	{"drr_table",       STR_PARAM, &drr_table.s     },
+	{"drg_table",       STR_PARAM, &drg_table.s     },
+	{"drl_table",       STR_PARAM, &drl_table.s     },
+	{"use_domain",      INT_PARAM, &use_domain      },
+	{"drg_user_col",    STR_PARAM, &drg_user_col.s  },
+	{"drg_domain_col",  STR_PARAM, &drg_domain_col.s},
+	{"drg_grpid_col",   STR_PARAM, &drg_grpid_col.s },
+	{"ruri_avp",        STR_PARAM, &ruri_avp_spec.s },
+	{"attrs_avp",       STR_PARAM, &attrs_avp_spec.s},
+	{"sort_order",      INT_PARAM, &sort_order      },
+	{"fetch_rows",      INT_PARAM, &dr_fetch_rows   },
+	{"force_dns",       INT_PARAM, &dr_force_dns    },
+	{0, 0, 0}
+};
+
+
+/*
+ * Exported MI functions
+ */
+static mi_export_t mi_cmds[] = {
+	{ RELOAD_MI_CMD, dr_reload_cmd, MI_NO_INPUT_FLAG, 0, 0 },
+	{ 0, 0, 0, 0, 0}
+};
+
+
+
+struct module_exports exports = {
+	"drouting",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,            /* Exported functions */
+	params,          /* Exported parameters */
+	0,               /* exported statistics */
+	mi_cmds,         /* exported MI functions */
+	0,               /* exported pseudo-variables */
+	0,               /* additional processes */
+	dr_init,         /* Module initialization function */
+	(response_function) 0,
+	(destroy_function) dr_exit,
+	(child_init_function) dr_child_init /* per-child init function */
+};
+
+
+/**
+ * Rewrite Request-URI
+ */
+static inline int rewrite_ruri(struct sip_msg* _m, char* _s)
+{
+   struct action act;
+   struct run_act_ctx ra_ctx;
+
+   memset(&act, '\0', sizeof(act));
+   act.type = SET_URI_T;
+   act.val[0].type = STRING_ST;
+   act.val[0].u.string = _s;
+   init_run_actions_ctx(&ra_ctx);
+   if (do_action(&ra_ctx, &act, _m) < 0)
+   {
+      LM_ERR("do_action failed\n");
+      return -1;
+   }
+   return 0;
+}
+
+static inline int dr_reload_data( void )
+{
+	rt_data_t *new_data;
+	rt_data_t *old_data;
+
+	new_data = dr_load_routing_info( &dr_dbf, db_hdl,
+		&drd_table, &drl_table, &drr_table);
+	if ( new_data==0 ) {
+		LM_CRIT("failed to load routing info\n");
+		return -1;
+	}
+
+	/* block access to data for all readers */
+	lock_get( ref_lock );
+	*reload_flag = 1;
+	lock_release( ref_lock );
+
+	/* wait for all readers to finish - it's a kind of busy waitting but
+	 * it's not critical;
+	 * at this point, data_refcnt can only be decremented */
+	while (*data_refcnt) {
+		usleep(10);
+	}
+
+	/* no more activ readers -> do the swapping */
+	old_data = *rdata;
+	*rdata = new_data;
+
+	/* release the readers */
+	*reload_flag = 0;
+
+	/* destroy old data */
+	if (old_data)
+		free_rt_data( old_data, 1 );
+
+	return 0;
+}
+
+
+
+static int dr_init(void)
+{
+	pv_spec_t avp_spec;
+
+	LM_INFO("Dynamic-Routing - initializing\n");
+
+	/* check the module params */
+	if (db_url.s==NULL || db_url.s[0]==0) {
+		LM_CRIT("mandatory parameter \"DB_URL\" found empty\n");
+		goto error;
+	}
+	db_url.len = strlen(db_url.s);
+
+	drd_table.len = strlen(drd_table.s);
+	if (drd_table.s[0]==0) {
+		LM_CRIT("mandatory parameter \"DRD_TABLE\" found empty\n");
+		goto error;
+	}
+
+	drr_table.len = strlen(drr_table.s);
+	if (drr_table.s[0]==0) {
+		LM_CRIT("mandatory parameter \"DRR_TABLE\" found empty\n");
+		goto error;
+	}
+
+	drg_table.len = strlen(drg_table.s);
+	if (drg_table.s[0]==0) {
+		LM_CRIT("mandatory parameter \"DRG_TABLE\"  found empty\n");
+		goto error;
+	}
+
+	drl_table.len = strlen(drl_table.s);
+	if (drl_table.s[0]==0) {
+		LM_CRIT("mandatory parameter \"DRL_TABLE\"  found empty\n");
+		goto error;
+	}
+
+	drg_user_col.len = strlen(drg_user_col.s);
+	drg_domain_col.len = strlen(drg_domain_col.s);
+	drg_grpid_col.len = strlen(drg_grpid_col.s);
+
+	/* fix AVP spec */
+	if (ruri_avp_spec.s) {
+		ruri_avp_spec.len = strlen(ruri_avp_spec.s);
+
+		if (pv_parse_spec( &ruri_avp_spec, &avp_spec)==0
+		|| avp_spec.type!=PVT_AVP) {
+			LM_ERR("malformed or non AVP [%.*s] for RURI AVP definition\n",
+				ruri_avp_spec.len, ruri_avp_spec.s);
+			return E_CFG;
+		}
+
+		if( pv_get_avp_name(0, &(avp_spec.pvp), &(ruri_avp.name),
+		&(ruri_avp.type) )!=0) {
+			LM_ERR("[%.*s]- invalid AVP definition for RURI AVP\n",
+				ruri_avp_spec.len, ruri_avp_spec.s);
+			return E_CFG;
+		}
+	}
+	if (attrs_avp_spec.s) {
+		attrs_avp_spec.len = strlen(attrs_avp_spec.s);
+
+		if (pv_parse_spec( &attrs_avp_spec, &avp_spec)==0
+		|| avp_spec.type!=PVT_AVP) {
+			LM_ERR("malformed or non AVP [%.*s] for ATTRS AVP definition\n",
+				attrs_avp_spec.len, attrs_avp_spec.s);
+			return E_CFG;
+		}
+
+		if( pv_get_avp_name(0, &(avp_spec.pvp), &(attrs_avp.name),
+		&(attrs_avp.type) )!=0) {
+			LM_ERR("[%.*s]- invalid AVP definition for ATTRS AVP\n",
+				attrs_avp_spec.len, attrs_avp_spec.s);
+			return E_CFG;
+		}
+	}
+
+	/* data pointer in shm */
+	rdata = (rt_data_t**)shm_malloc( sizeof(rt_data_t*) );
+	if (rdata==0) {
+		LM_CRIT("failed to get shm mem for data ptr\n");
+		goto error;
+	}
+	*rdata = 0;
+
+	/* create & init lock */
+	if ( (ref_lock=lock_alloc())==0) {
+		LM_CRIT("failed to alloc ref_lock\n");
+		goto error;
+	}
+	if (lock_init(ref_lock)==0 ) {
+		LM_CRIT("failed to init ref_lock\n");
+		goto error;
+	}
+	data_refcnt = (int*)shm_malloc(sizeof(int));
+	reload_flag = (int*)shm_malloc(sizeof(int));
+	if(!data_refcnt || !reload_flag)
+	{
+		LM_ERR("no more shared memory\n");
+		goto error;
+	}
+	*data_refcnt = 0;
+	*reload_flag = 0;
+
+	/* bind to the mysql module */
+	if (db_bind_mod( &db_url, &dr_dbf  )) {
+		LM_CRIT("cannot bind to database module! "
+			"Did you forget to load a database module ?\n");
+		goto error;
+	}
+
+	if (!DB_CAPABILITY( dr_dbf, DB_CAP_QUERY)) {
+		LM_CRIT( "database modules does not "
+			"provide QUERY functions needed by DRounting module\n");
+		return -1;
+	}
+
+	return 0;
+error:
+	if (ref_lock) {
+		lock_destroy( ref_lock );
+		lock_dealloc( ref_lock );
+		ref_lock = 0;
+	}
+	if (db_hdl) {
+		dr_dbf.close(db_hdl);
+		db_hdl = 0;
+	}
+	if (rdata) {
+		shm_free(rdata);
+		rdata = 0;
+	}
+	return -1;
+}
+
+
+
+static int dr_child_init(int rank)
+{
+	/* only workers needs DB connection */
+	if (rank==PROC_MAIN || rank==PROC_TCP_MAIN)
+		return 0;
+
+	/* init DB connection */
+	if ( (db_hdl=dr_dbf.init(&db_url))==0 ) {
+		LM_CRIT("cannot initialize database connection\n");
+		return -1;
+	}
+
+	/* child 1 load the routing info */
+	if ( (rank==1) && dr_reload_data()!=0 ) {
+		LM_CRIT("failed to load routing data\n");
+		return -1;
+	}
+
+	/* set GROUP table for workers */
+	if (dr_dbf.use_table( db_hdl, &drg_table) < 0) {
+		LM_ERR("cannot select table \"%.*s\"\n", drg_table.len, drg_table.s);
+		return -1;
+	}
+	srand(getpid()+time(0)+rank);
+	return 0;
+}
+
+
+static int dr_exit(void)
+{
+	/* close DB connection */
+	if (db_hdl) {
+		dr_dbf.close(db_hdl);
+		db_hdl = 0;
+	}
+
+	/* destroy data */
+	if ( rdata) {
+		if (*rdata)
+			free_rt_data( *rdata, 1 );
+		shm_free( rdata );
+		rdata = 0;
+	}
+
+	/* destroy lock */
+	if (ref_lock) {
+		lock_destroy( ref_lock );
+		lock_dealloc( ref_lock );
+		ref_lock = 0;
+	}
+	
+	if(reload_flag)
+		shm_free(reload_flag);
+	if(data_refcnt)
+		shm_free(data_refcnt);
+
+	return 0;
+}
+
+
+
+static struct mi_root* dr_reload_cmd(struct mi_root *cmd_tree, void *param)
+{
+	int n;
+
+	LM_INFO("\"%s\" MI command received!\n",RELOAD_MI_CMD);
+
+	if ( (n=dr_reload_data())!=0 ) {
+		LM_CRIT("failed to load routing data\n");
+		goto error;
+	}
+
+	return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+error:
+	return init_mi_tree( 500, "Failed to reload",16);
+}
+
+
+
+static inline int get_group_id(struct sip_uri *uri)
+{
+	db_key_t keys_ret[1];
+	db_key_t keys_cmp[2];
+	db_val_t vals_cmp[2];
+	db1_res_t* res;
+	int n;
+
+
+	/* user */
+	keys_cmp[0] = &drg_user_col;
+	vals_cmp[0].type = DB1_STR;
+	vals_cmp[0].nul  = 0;
+	vals_cmp[0].val.str_val = uri->user;
+	n = 1;
+
+	if (use_domain) {
+		keys_cmp[1] = &drg_domain_col;
+		vals_cmp[1].type = DB1_STR;
+		vals_cmp[1].nul  = 0;
+		vals_cmp[1].val.str_val = uri->host;
+		n++;
+	}
+
+	keys_ret[0] = &drg_grpid_col;
+	res = 0;
+
+	if ( dr_dbf.query(db_hdl,keys_cmp,0,vals_cmp,keys_ret,n,1,0,&res)<0 ) {
+		LM_ERR("DB query failed\n");
+		goto error;
+	}
+
+	if (RES_ROW_N(res) == 0) {
+		LM_ERR("no group for user "
+			"\"%.*s\"@\"%.*s\"\n", uri->user.len, uri->user.s,
+			uri->host.len, uri->host.s);
+		goto error;
+	}
+	if (res->rows[0].values[0].nul || res->rows[0].values[0].type!=DB1_INT) {
+		LM_ERR("null or non-integer group_id\n");
+		goto error;
+	}
+	n = res->rows[0].values[0].val.int_val;
+
+	dr_dbf.free_result(db_hdl, res);
+	return n;
+error:
+	if (res)
+		dr_dbf.free_result(db_hdl, res);
+	return -1;
+}
+
+
+
+static inline str* build_ruri(struct sip_uri *uri, int strip, str *pri,
+																str *hostport)
+{
+	static str uri_str;
+	char *p;
+
+	if (uri->user.len<=strip) {
+		LM_ERR("stripping %d makes "
+			"username <%.*s> null\n",strip,uri->user.len,uri->user.s);
+		return 0;
+	}
+
+	uri_str.len = 4 /*sip:*/ + uri->user.len - strip +pri->len +
+		(uri->passwd.s?(uri->passwd.len+1):0) + 1/*@*/ + hostport->len +
+		(uri->params.s?(uri->params.len+1):0) +
+		(uri->headers.s?(uri->headers.len+1):0);
+
+	if ( (uri_str.s=(char*)pkg_malloc( uri_str.len + 1))==0) {
+		LM_ERR("no more pkg mem\n");
+		return 0;
+	}
+
+	p = uri_str.s;
+	*(p++)='s';
+	*(p++)='i';
+	*(p++)='p';
+	*(p++)=':';
+	if (pri->len) {
+		memcpy(p, pri->s, pri->len);
+		p += pri->len;
+	}
+	memcpy(p, uri->user.s+strip, uri->user.len-strip);
+	p += uri->user.len-strip;
+	if (uri->passwd.len) {
+		*(p++)=':';
+		memcpy(p, uri->passwd.s, uri->passwd.len);
+		p += uri->passwd.len;
+	}
+	*(p++)='@';
+	memcpy(p, hostport->s, hostport->len);
+	p += hostport->len;
+	if (uri->params.len) {
+		*(p++)=';';
+		memcpy(p, uri->params.s, uri->params.len);
+		p += uri->params.len;
+	}
+	if (uri->headers.len) {
+		*(p++)='?';
+		memcpy(p, uri->headers.s, uri->headers.len);
+		p += uri->headers.len;
+	}
+	*p = 0;
+
+	if (p-uri_str.s!=uri_str.len) {
+		LM_CRIT("difference between allocated(%d)"
+			" and written(%d)\n",uri_str.len,(int)(long)(p-uri_str.s));
+		return 0;
+	}
+	return &uri_str;
+}
+
+
+static int do_routing_0(struct sip_msg* msg, char* str1, char* str2)
+{
+	return do_routing(msg, NULL);
+}
+
+static int do_routing_1(struct sip_msg* msg, char* str1, char* str2)
+{
+	return do_routing(msg, (dr_group_t*)str1);
+}
+
+
+static int use_next_gw(struct sip_msg* msg)
+{
+	struct usr_avp *avp;
+	int_str val;
+
+	/* search for the first RURI AVP containing a string */
+	do {
+		avp = search_first_avp(ruri_avp.type, ruri_avp.name, &val, 0);
+	}while (avp && (avp->flags&AVP_VAL_STR)==0 );
+
+	if (!avp) return -1;
+
+	if (rewrite_ruri(msg, &val)==-1) {
+		LM_ERR("failed to rewite RURI\n");
+		return -1;
+	}
+	destroy_avp(avp);
+	LM_DBG("new RURI set to <%.*s>\n", val.s.len,val.s.s);
+
+	/* remove the old attrs */
+	avp = NULL;
+	do {
+		if (avp) destroy_avp(avp);
+		avp = search_first_avp(attrs_avp.type, attrs_avp.name, NULL, 0);
+	}while (avp && (avp->flags&AVP_VAL_STR)==0 );
+	if (avp) destroy_avp(avp);
+
+	return 1;
+}
+
+
+static int do_routing(struct sip_msg* msg, dr_group_t *drg)
+{
+	struct to_body  *from;
+	struct sip_uri  uri;
+	rt_info_t      *rt_info;
+	int    grp_id;
+	int    i, j, l, t;
+	str    *ruri;
+	int_str val;
+	struct usr_avp *avp;
+#define DR_MAX_GWLIST	32
+	static int local_gwlist[DR_MAX_GWLIST];
+	int gwlist_size;
+	int ret;
+
+	ret = -1;
+
+	if ( (*rdata)==0 || (*rdata)->pgw_l==0 ) {
+		LM_DBG("empty ruting table\n");
+		goto error1;
+	}
+
+	/* get the username from FROM_HDR */
+	if (parse_from_header(msg)!=0) {
+		LM_ERR("unable to parse from hdr\n");
+		goto error1;
+	}
+	from = (struct to_body*)msg->from->parsed;
+	/* parse uri */
+	if (parse_uri( from->uri.s, from->uri.len, &uri)!=0) {
+		LM_ERR("unable to parse from uri\n");
+		goto error1;
+	}
+
+	/* get user's routing group */
+	if(drg==NULL)
+	{
+		grp_id = get_group_id( &uri );
+		if (grp_id<0) {
+			LM_ERR("failed to get group id\n");
+			goto error1;
+		}
+	} else {
+		if(drg->type==0)
+			grp_id = (int)drg->u.grp_id;
+		else if(drg->type==1) {
+			grp_id = 0; /* call get avp here */
+			if((avp=search_first_avp( drg->u.avp_id.type,
+			drg->u.avp_id.name, &val, 0))==NULL||(avp->flags&AVP_VAL_STR)) {
+				LM_ERR( "failed to get group id\n");
+				goto error1;
+			}
+			grp_id = val.n;
+		} else
+			grp_id = 0; 
+	}
+	LM_DBG("using dr group %d\n",grp_id);
+
+	/* get the number */
+	ruri = GET_RURI(msg);
+	/* parse ruri */
+	if (parse_uri( ruri->s, ruri->len, &uri)!=0) {
+		LM_ERR("unable to parse RURI\n");
+		goto error1;
+	}
+
+	/* ref the data for reading */
+again:
+	lock_get( ref_lock );
+	/* if reload must be done, do un ugly busy waiting 
+	 * until reload is finished */
+	if (*reload_flag) {
+		lock_release( ref_lock );
+		usleep(5);
+		goto again;
+	}
+	*data_refcnt = *data_refcnt + 1;
+	lock_release( ref_lock );
+
+	/* search a prefix */
+	rt_info = get_prefix( (*rdata)->pt, &uri.user , (unsigned int)grp_id);
+	if (rt_info==0) {
+		LM_DBG("no matching for prefix \"%.*s\"\n",
+			uri.user.len, uri.user.s);
+		/* try prefixless rules */
+		rt_info = check_rt( &(*rdata)->noprefix, (unsigned int)grp_id);
+		if (rt_info==0) {
+			LM_DBG("no prefixless matching for "
+				"grp %d\n", grp_id);
+			goto error2;
+		}
+	}
+
+	if (rt_info->route_idx>0 && rt_info->route_idx<RT_NO) {
+		ret = run_top_route(main_rt.rlist[rt_info->route_idx], msg );
+		if (ret<1) {
+			/* drop the action */
+			LM_DBG("script route %d drops routing "
+				"by %d\n", rt_info->route_idx, ret);
+			goto error2;
+		}
+		ret = -1;
+	}
+
+	gwlist_size
+		= (rt_info->pgwa_len>DR_MAX_GWLIST)?DR_MAX_GWLIST:rt_info->pgwa_len;
+	
+	/* set gw order */
+	if(sort_order>=1&&gwlist_size>1)
+	{
+		j = 0;
+		t = 0;
+		while(j<gwlist_size)
+		{
+			/* identify the group: [j..i) */
+			for(i=j+1; i<gwlist_size; i++)
+				if(rt_info->pgwl[j].grpid!=rt_info->pgwl[i].grpid)
+					break;
+			if(i-j==1)
+			{
+				local_gwlist[t++] = j;
+				/*LM_DBG("selected gw[%d]=%d\n",
+					j, local_gwlist[j]);*/
+			} else {
+				if(i-j==2)
+				{
+					local_gwlist[t++]   = j + rand()%2;
+					if(sort_order==1)
+					{
+						local_gwlist[t++] = j + (local_gwlist[j]-j+1)%2;
+						/*LM_DBG("selected gw[%d]=%d"
+						 *  " gw[%d]=%d\n", j, local_gwlist[j], j+1,
+						 *  local_gwlist[j+1]);*/
+					}
+				} else {
+					local_gwlist[t++]   = j + rand()%(i-j);
+					if(sort_order==1)
+					{
+						do{
+							local_gwlist[t] = j + rand()%(i-j);
+						}while(local_gwlist[t]==local_gwlist[t-1]);
+						t++;
+
+						/*
+						LM_DBG("selected gw[%d]=%d"
+							" gw[%d]=%d.\n",
+							j, local_gwlist[j], j+1, local_gwlist[j+1]); */
+						/* add the rest in this group */
+						for(l=j; l<i; l++)
+						{
+							if(l==local_gwlist[j] || l==local_gwlist[j+1])
+								continue;
+							local_gwlist[t++] = l;
+							/* LM_DBG("selected gw[%d]=%d.\n",
+								j+k, local_gwlist[t]); */
+						}
+					}
+				}
+			}
+			/* next group starts from i */
+			j=i;
+		}
+	} else {
+		for(i=0; i<gwlist_size; i++)
+			local_gwlist[i] = i;
+		t = i;
+	}
+
+	/* do some cleanup first */
+	destroy_avps( ruri_avp.type, ruri_avp.name, 1);
+
+	/* push gwlist into avps in reverse order */
+	for( j=t-1 ; j>=1 ; j-- ) {
+		/* build uri*/
+		ruri = build_ruri(&uri, rt_info->pgwl[local_gwlist[j]].pgw->strip,
+				&rt_info->pgwl[local_gwlist[j]].pgw->pri,
+				&rt_info->pgwl[local_gwlist[j]].pgw->ip);
+		if (ruri==0) {
+			LM_ERR("failed to build avp ruri\n");
+			goto error2;
+		}
+		LM_DBG("adding gw [%d] as avp \"%.*s\"\n",
+			local_gwlist[j], ruri->len, ruri->s);
+		/* add ruri avp */
+		val.s = *ruri;
+		if (add_avp( AVP_VAL_STR|(ruri_avp.type),ruri_avp.name, val)!=0 ) {
+			LM_ERR("failed to insert ruri avp\n");
+			pkg_free(ruri->s);
+			goto error2;
+		}
+		pkg_free(ruri->s);
+		/* add attrs avp */
+		val.s = rt_info->pgwl[local_gwlist[j]].pgw->attrs;
+		LM_DBG("setting attr [%.*s] as avp\n",val.s.len,val.s.s);
+		if (add_avp( AVP_VAL_STR|(attrs_avp.type),attrs_avp.name, val)!=0 ) {
+			LM_ERR("failed to insert attrs avp\n");
+			goto error2;
+		}
+	}
+
+	/* use first GW in RURI */
+	ruri = build_ruri(&uri, rt_info->pgwl[local_gwlist[0]].pgw->strip,
+			&rt_info->pgwl[local_gwlist[0]].pgw->pri,
+			&rt_info->pgwl[local_gwlist[0]].pgw->ip);
+
+	/* add attrs avp */
+	val.s = rt_info->pgwl[local_gwlist[0]].pgw->attrs;
+	LM_DBG("setting attr [%.*s] as for ruri\n",val.s.len,val.s.s);
+	if (add_avp( AVP_VAL_STR|(attrs_avp.type),attrs_avp.name, val)!=0 ) {
+		LM_ERR("failed to insert attrs avp\n");
+		goto error2;
+	}
+
+	/* we are done reading -> unref the data */
+	lock_get( ref_lock );
+	*data_refcnt = *data_refcnt - 1;
+	lock_release( ref_lock );
+
+	/* what hev we get here?? */
+	if (ruri==0) {
+		LM_ERR("failed to build ruri\n");
+		goto error1;
+	}
+	LM_DBG("setting the gw [%d] as ruri \"%.*s\"\n",
+			local_gwlist[0], ruri->len, ruri->s);
+	if (msg->new_uri.s)
+		pkg_free(msg->new_uri.s);
+	msg->new_uri = *ruri;
+	msg->parsed_uri_ok = 0;
+
+	return 1;
+error2:
+	/* we are done reading -> unref the data */
+	lock_get( ref_lock );
+	*data_refcnt = *data_refcnt - 1;
+	lock_release( ref_lock );
+error1:
+	return ret;
+}
+
+
+static int fixup_do_routing(void** param, int param_no)
+{
+	char *s;
+	dr_group_t *drg;
+	pv_spec_t avp_spec;
+	str r;
+
+	s = (char*)*param;
+
+	if (param_no==1)
+	{
+		drg = (dr_group_t*)pkg_malloc(sizeof(dr_group_t));
+		if(drg==NULL)
+		{
+			LM_ERR( "no more memory\n");
+			return E_OUT_OF_MEM;
+		}
+		memset(drg, 0, sizeof(dr_group_t));
+
+		if ( s==NULL || s[0]==0 ) {
+			LM_CRIT("empty group id definition");
+			return E_CFG;
+		}
+
+		if (s[0]=='$') {
+			/* param is a PV (AVP only supported) */
+			r.s = s;
+			r.len = strlen(s);
+			if (pv_parse_spec( &r, &avp_spec)==0
+			|| avp_spec.type!=PVT_AVP) {
+				LM_ERR("malformed or non AVP %s AVP definition\n", s);
+				return E_CFG;
+			}
+
+			if( pv_get_avp_name(0, &(avp_spec.pvp), &(drg->u.avp_id.name),
+			&(drg->u.avp_id.type) )!=0) {
+				LM_ERR("[%s]- invalid AVP definition\n", s);
+				return E_CFG;
+			}
+			drg->type = 1;
+			/* do not free the param as the AVP spec may point inside 
+			   this string*/
+		} else {
+			while(s && *s) {
+				if(*s<'0' || *s>'9') {
+					LM_ERR( "bad number\n");
+					return E_UNSPEC;
+				}
+				drg->u.grp_id = (drg->u.grp_id)*10+(*s-'0');
+				s++;
+			}
+			pkg_free(*param);
+		}
+		*param = (void*)drg;
+	}
+
+	return 0;
+}
+
+
+static int fixup_from_gw( void** param, int param_no)
+{
+	unsigned long type;
+	int err;
+
+	if (param_no == 1 || param_no == 2) {
+		type = str2s(*param, strlen(*param), &err);
+		if (err == 0) {
+			pkg_free(*param);
+			*param = (void *)type;
+			return 0;
+		} else {
+			LM_ERR( "bad number <%s>\n",
+				(char *)(*param));
+			return E_CFG;
+		}
+	}
+	return 0;
+}
+
+static int strip_username(struct sip_msg* msg, int strip)
+{
+	struct action act;
+   struct run_act_ctx ra_ctx;
+ 
+	act.type = STRIP_T;
+	act.val[0].type = NUMBER_ST;
+	act.val[0].u.number = strip;
+	act.next = 0;
+
+   init_run_actions_ctx(&ra_ctx);
+   if (do_action(&ra_ctx, &act, msg) < 0)
+	{
+		LM_ERR( "Error in do_action\n");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int is_from_gw_0(struct sip_msg* msg, char* str, char* str2)
+{
+	pgw_addr_t *pgwa = NULL;
+
+	if(rdata==NULL || *rdata==NULL || msg==NULL)
+		return -1;
+	
+	pgwa = (*rdata)->pgw_addr_l;
+	while(pgwa) {
+		if( (pgwa->port==0 || pgwa->port==msg->rcv.src_port) &&
+		ip_addr_cmp(&pgwa->ip, &msg->rcv.src_ip))
+			return 1;
+		pgwa = pgwa->next;
+	}
+	return -1;
+}
+
+
+static int is_from_gw_1(struct sip_msg* msg, char* str, char* str2)
+{
+	pgw_addr_t *pgwa = NULL;
+	int type = (int)(long)str;
+
+	if(rdata==NULL || *rdata==NULL || msg==NULL)
+		return -1;
+	
+	pgwa = (*rdata)->pgw_addr_l;
+	while(pgwa) {
+		if( type==pgwa->type && 
+		(pgwa->port==0 || pgwa->port==msg->rcv.src_port) &&
+		ip_addr_cmp(&pgwa->ip, &msg->rcv.src_ip) )
+			return 1;
+		pgwa = pgwa->next;
+	}
+	return -1;
+}
+
+static int is_from_gw_2(struct sip_msg* msg, char* str1, char* str2)
+{
+	pgw_addr_t *pgwa = NULL;
+	int type = (int)(long)str1;
+	int flags = (int)(long)str2;
+
+	if(rdata==NULL || *rdata==NULL || msg==NULL)
+		return -1;
+	
+	pgwa = (*rdata)->pgw_addr_l;
+	while(pgwa) {
+		if( type==pgwa->type &&
+		(pgwa->port==0 || pgwa->port==msg->rcv.src_port) &&
+		ip_addr_cmp(&pgwa->ip, &msg->rcv.src_ip) ) {
+			if (flags!=0 && pgwa->strip>0)
+				strip_username(msg, pgwa->strip);
+			return 1;
+		}
+		pgwa = pgwa->next;
+	}
+	return -1;
+}
+
+
+static int goes_to_gw_1(struct sip_msg* msg, char* _type, char* _f2)
+{
+	pgw_addr_t *pgwa = NULL;
+	struct sip_uri puri;
+	struct ip_addr *ip;
+	str *uri;
+	int type;
+
+	if(rdata==NULL || *rdata==NULL || msg==NULL)
+		return -1;
+
+	uri = GET_NEXT_HOP(msg);
+	type = (int)(long)_type;
+
+	if (parse_uri(uri->s, uri->len, &puri)<0){
+		LM_ERR("bad uri <%.*s>\n", uri->len, uri->s);
+		return -1;
+	}
+
+	if ( ((ip=str2ip(&puri.host))!=0)
+#ifdef USE_IPV6
+	|| ((ip=str2ip6(&puri.host))!=0)
+#endif
+	){
+		pgwa = (*rdata)->pgw_addr_l;
+		while(pgwa) {
+			if( (type<0 || type==pgwa->type) && ip_addr_cmp(&pgwa->ip, ip))
+				return 1;
+			pgwa = pgwa->next;
+		}
+	}
+
+	return -1;
+}
+
+
+static int goes_to_gw_0(struct sip_msg* msg, char* _type, char* _f2)
+{
+	return goes_to_gw_1(msg, (char*)(long)-1, _f2);
+}
+

+ 43 - 0
modules_k/drouting/parse.h

@@ -0,0 +1,43 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005-2009 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * History:
+ * ---------
+ *  2005-07-27  first version (bogdan)
+ */
+
+
+#ifndef dr_parse_h_
+#define dr_parse_h_
+
+
+#define SEP '|'
+#define SEP1 ','
+#define SEP_GRP ';'
+
+#define IS_SPACE(s)\
+	((s)==' ' || (s)=='\t' || (s)=='\r' || (s)=='\n')
+
+#define EAT_SPACE(s)\
+	while((s) && IS_SPACE(*(s))) (s)++
+
+
+#endif

+ 320 - 0
modules_k/drouting/prefix_tree.c

@@ -0,0 +1,320 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005-2009 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * History:
+ * ---------
+ *  2005-02-20  first version (cristian)
+ *  2005-02-27  ported to 0.9.0 (bogdan)
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "../../str.h"
+#include "../../mem/shm_mem.h"
+
+#include "prefix_tree.h"
+#include "routing.h"
+#include "dr_time.h"
+
+extern int inode;
+extern int unode;
+
+
+
+static inline int 
+check_time(
+		tmrec_t *time_rec
+		)
+{
+	ac_tm_t att;
+
+	/* shortcut: if there is no dstart, timerec is valid */
+	if (time_rec->dtstart==0)
+		return 1;
+
+	memset( &att, 0, sizeof(att));
+
+	/* set current time */
+	if ( ac_tm_set_time( &att, time(0) ) )
+		return 0;
+
+	/* does the recv_time match the specified interval?  */
+	if (check_tmrec( time_rec, &att, 0)!=0)
+		return 0;
+
+	return 1;
+}
+
+
+static inline rt_info_t*
+internal_check_rt(
+		ptree_node_t *ptn,
+		unsigned int rgid
+		)
+{
+	int i;
+	int rg_pos=0;
+	rg_entry_t* rg=NULL;
+	rt_info_wrp_t* rtlw=NULL;
+
+	if((NULL==ptn) || (NULL==ptn->rg))
+		goto err_exit;
+	rg_pos = ptn->rg_pos;
+	rg=ptn->rg;
+	for(i=0;(i<rg_pos) && (rg[i].rgid!=rgid);i++);
+	if(i<rg_pos) {
+		LM_DBG("found rgid %d (rule list %p)\n", 
+				rgid, rg[i].rtlw);
+		rtlw=rg[i].rtlw;
+		while(rtlw!=NULL) {
+			if(check_time(rtlw->rtl->time_rec))
+				goto ok_exit;
+			rtlw=rtlw->next;
+		}
+	}
+err_exit:
+	return NULL;
+
+ok_exit:
+	return rtlw?rtlw->rtl:0;
+}
+
+
+rt_info_t* 
+check_rt(
+	ptree_node_t *ptn,
+	unsigned int rgid
+	)
+{
+	return internal_check_rt( ptn, rgid);
+}
+
+
+rt_info_t*
+get_prefix(
+	ptree_t *ptree,
+	str* prefix,
+	unsigned int rgid
+	)
+{
+	rt_info_t *rt = NULL;
+	char *tmp=NULL;
+	char local=0;
+	int idx=0;
+
+	if(NULL == ptree)
+		goto err_exit;
+	if(NULL == prefix)
+		goto err_exit;
+	tmp = prefix->s;
+	/* go the tree down to the last digit in the 
+	 * prefix string or down to a leaf */
+	while(tmp< (prefix->s+prefix->len)) {
+		if(NULL == tmp)
+			goto err_exit;
+		local=*tmp;
+		if( !IS_DECIMAL_DIGIT(local) ) {
+			/* unknown character in the prefix string */
+			goto err_exit;
+		}
+		if( tmp == (prefix->s+prefix->len-1) ) {
+			/* last digit in the prefix string */
+			break;
+		}
+		idx = local -'0';
+		if( NULL == ptree->ptnode[idx].next) {
+			/* this is a leaf */
+			break;
+		}
+		ptree = ptree->ptnode[idx].next;
+		tmp++;
+	}
+	/* go in the tree up to the root trying to match the
+	 * prefix */
+	while(ptree !=NULL ) {
+		if(NULL == tmp)
+			goto err_exit;
+		/* is it a real node or an intermediate one */
+		idx = *tmp-'0';
+		if(NULL != ptree->ptnode[idx].rg) {
+			/* real node; check the constraints on the routing info*/
+			if( NULL != (rt = internal_check_rt( &(ptree->ptnode[idx]), rgid)))
+				break;
+		}
+		tmp--;
+		ptree = ptree->bp;
+	}
+	return rt;
+
+err_exit:
+	return NULL;
+}
+
+
+pgw_t* 
+get_pgw(
+		pgw_t* pgw_l,
+		long id
+		)
+{
+	if(NULL==pgw_l)
+		goto err_exit;
+	while(NULL != pgw_l) {
+		if(id == pgw_l->id) {
+			return pgw_l;
+		}
+		pgw_l = pgw_l->next;
+	}
+err_exit:
+	return NULL;
+}
+
+
+int 
+add_prefix(
+	ptree_t *ptree,
+	str* prefix,
+	rt_info_t *r,
+	unsigned int rg
+	) 
+{
+	char* tmp=NULL;
+	int res = 0;
+	if(NULL==ptree)
+		goto err_exit;
+	tmp = prefix->s;
+	while(tmp < (prefix->s+prefix->len)) {
+		if(NULL == tmp)
+			goto err_exit;
+		if( !IS_DECIMAL_DIGIT(*tmp) ) {
+			/* unknown character in the prefix string */
+			goto err_exit;
+		}
+		if( tmp == (prefix->s+prefix->len-1) ) {
+			/* last digit in the prefix string */
+			LM_DBG("adding info %p, %d at: "
+				"%p (%d)\n", r, rg, &(ptree->ptnode[*tmp-'0']), *tmp-'0');
+			res = add_rt_info(&(ptree->ptnode[*tmp-'0']), r,rg);
+			if(res < 0 )
+				goto err_exit;
+			unode++;
+			res = 1;
+			goto ok_exit;
+		}
+		/* process the current digit in the prefix */
+		if(NULL == ptree->ptnode[*tmp - '0'].next) {
+			/* allocate new node */
+			INIT_PTREE_NODE(ptree, ptree->ptnode[*tmp - '0'].next);
+			inode+=10;
+#if 0
+			printf("new tree node: %p (bp: %p)\n", 
+					ptree->ptnode[*tmp - '0'].next,
+					ptree->ptnode[*tmp - '0'].next->bp
+					);
+#endif
+		}
+		ptree = ptree->ptnode[*tmp-'0'].next;
+		tmp++; 
+	}
+
+ok_exit:
+	return 0;
+
+err_exit:
+	return -1;
+}
+
+int 
+del_tree(
+		ptree_t* t
+		)
+{
+	int i,j;
+	if(NULL == t)
+		goto exit;
+	/* delete all the children */
+	for(i=0; i< PTREE_CHILDREN; i++) {
+		/* shm_free the rg array of rt_info */
+		if(NULL!=t->ptnode[i].rg) {
+			for(j=0;j<t->ptnode[i].rg_pos;j++) {
+				/* if non intermediate delete the routing info */
+				if(t->ptnode[i].rg[j].rtlw !=NULL)
+					del_rt_list(t->ptnode[i].rg[j].rtlw);
+			}
+			shm_free(t->ptnode[i].rg);
+		}
+		/* if non leaf delete all the children */
+		if(t->ptnode[i].next != NULL)
+			del_tree(t->ptnode[i].next);
+	}
+	shm_free(t);
+exit:
+	return 0;
+}
+
+void
+del_rt_list(
+		rt_info_wrp_t *rwl
+		)
+{
+	rt_info_wrp_t* t=rwl;
+	while(rwl!=NULL) {
+		t=rwl;
+		rwl=rwl->next;
+		if ( (--t->rtl->ref_cnt)==0)
+			free_rt_info(t->rtl);
+		shm_free(t);
+	}
+}
+
+void
+free_rt_info(
+		rt_info_t *rl
+		)
+{
+	if(NULL == rl)
+		return;
+	if(NULL!=rl->pgwl)
+		shm_free(rl->pgwl);
+	if(NULL!=rl->time_rec)
+		tmrec_free(rl->time_rec);
+	shm_free(rl);
+	return;
+}
+
+void
+print_rt(
+		rt_info_t*rt
+		)
+{
+	int i=0;
+	if(NULL==rt)
+		return;
+	printf("priority:%d list of gw:\n", rt->priority);
+	for(i=0;i<rt->pgwa_len;i++)
+		if(NULL!=rt->pgwl[i].pgw) 
+			printf("  id:%ld pri:%.*s ip:%.*s \n",
+				rt->pgwl[i].pgw->id, 
+				rt->pgwl[i].pgw->pri.len, rt->pgwl[i].pgw->pri.s,
+				rt->pgwl[i].pgw->ip.len, rt->pgwl[i].pgw->ip.s);
+}

+ 179 - 0
modules_k/drouting/prefix_tree.h

@@ -0,0 +1,179 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005-2009 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * History:
+ * ---------
+ *  2005-02-20  first version (cristian)
+ *  2005-02-27  ported to 0.9.0 (bogdan)
+ */
+
+
+
+#ifndef prefix_tree_h
+#define prefix_tree_h
+
+#include "../../str.h"
+#include "../../ip_addr.h"
+#include "dr_time.h"
+
+#define PTREE_CHILDREN 10
+#define IS_DECIMAL_DIGIT(d) \
+	(((d)>='0') && ((d)<= '9'))
+
+extern int tree_size;
+
+#define INIT_PTREE_NODE(p, n) \
+do {\
+	(n) = (ptree_t*)shm_malloc(sizeof(ptree_t));\
+	if(NULL == (n))\
+		goto err_exit;\
+	tree_size+=sizeof(ptree_t);\
+	memset((n), 0, sizeof(ptree_t));\
+	(n)->bp=(p);\
+}while(0);
+
+
+/* list of PSTN gw */
+typedef struct pgw_addr_ {
+	struct ip_addr ip;
+	unsigned short port;
+	int type;
+	int strip;
+	struct pgw_addr_ *next;
+}pgw_addr_t;
+
+/* list of PSTN gw */
+typedef struct pgw_ {
+	/* id matching the one in db */
+	long id;
+	str pri;
+	int strip;
+	str ip;
+	int type;
+	str attrs;
+	struct pgw_ *next;
+}pgw_t;
+
+/**/
+typedef struct pgw_list_ {
+	pgw_t *pgw;
+	int    grpid;
+}pgw_list_t;
+
+/* element containing routing information */
+typedef struct rt_info_ {
+	unsigned int priority;
+	tmrec_t *time_rec;
+	/* array of pointers into the PSTN gw list */
+	pgw_list_t *pgwl;
+	/* length of the PSTN gw array */
+	unsigned short pgwa_len;
+	/* how many list link this element */
+	unsigned short ref_cnt;
+	/* script route to be executed */
+	int route_idx;
+} rt_info_t;
+
+typedef struct rt_info_wrp_ {
+	rt_info_t     *rtl;
+	struct rt_info_wrp_  *next;
+} rt_info_wrp_t;
+
+typedef struct rg_entry_ {
+	unsigned int rgid;
+	rt_info_wrp_t *rtlw;
+} rg_entry_t;
+
+typedef struct ptree_node_ {
+	unsigned int rg_len;
+	unsigned int rg_pos;
+	rg_entry_t *rg;
+	struct ptree_ *next;
+} ptree_node_t;
+
+typedef struct ptree_ {
+	/* backpointer */
+	struct ptree_ *bp;
+	ptree_node_t ptnode[PTREE_CHILDREN];
+} ptree_t;
+
+void 
+print_interim(
+		int,
+		int,
+		ptree_t*
+		);
+
+int
+del_tree(
+	ptree_t *
+	);
+
+int
+add_prefix(
+	ptree_t*,
+	/* prefix */
+	str*,
+	rt_info_t *,
+	unsigned int
+	);
+
+rt_info_t*
+get_prefix(
+	ptree_t *ptree,
+	str* prefix,
+	unsigned int rgid
+	);
+
+int
+add_rt_info(
+	ptree_node_t*, 
+	rt_info_t*,
+	unsigned int
+	);
+
+pgw_t*
+get_pgw(
+	pgw_t*,
+	long
+	);
+
+void
+del_rt_list(
+	rt_info_wrp_t *rl
+	);
+
+void print_rt(
+	rt_info_t*
+	);
+
+void
+free_rt_info(
+	rt_info_t*
+	);
+
+rt_info_t*
+check_rt(
+	ptree_node_t *ptn,
+	unsigned int rgid
+	);
+
+#endif

+ 466 - 0
modules_k/drouting/routing.c

@@ -0,0 +1,466 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005-2009 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * History:
+ * ---------
+ *  2005-02-20  first version (cristian)
+ *  2005-02-27  ported to 0.9.0 (bogdan)
+ */
+
+
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "../../str.h"
+#include "../../resolve.h"
+#include "../../mem/shm_mem.h"
+#include "../../parser/parse_uri.h"
+
+#include "routing.h"
+#include "prefix_tree.h"
+#include "dr_time.h"
+#include "parse.h"
+
+#define IDX_SIZE 32
+
+extern int dr_force_dns;
+
+rt_data_t*
+build_rt_data( void )
+{
+	rt_data_t *rdata;
+
+	if( NULL==(rdata=shm_malloc(sizeof(rt_data_t)))) {
+		LM_ERR("no more shm mem\n");
+		goto err_exit;
+	}
+	memset(rdata, 0, sizeof(rt_data_t));
+
+	INIT_PTREE_NODE(NULL, rdata->pt);
+
+	return rdata;
+err_exit:
+	return 0;
+}
+
+
+rt_info_t*
+build_rt_info(
+	int priority,
+	tmrec_t *trec,
+	/* script routing table index */
+	int route_idx,
+	/* list of destinations indexes */
+	char* dstlst,
+	pgw_t* pgw_l
+	) 
+{
+	char *tmp=NULL;
+	char *ep=NULL;
+	rt_info_t* rt = NULL;
+	int *idx = NULL, *t_idx=NULL;
+	int n=0, idx_size=0,i, grp_idx=0;
+	long t=0;
+	pgw_t *pgw=NULL;
+
+	if(NULL == (rt = (rt_info_t*)shm_malloc(sizeof(rt_info_t)))) {
+		LM_ERR("no more shm mem(1)\n");
+		goto err_exit;
+	}
+	memset(rt, 0, sizeof(rt_info_t));
+
+	idx_size = IDX_SIZE;
+	if( NULL == (idx = (int*)shm_malloc(2*idx_size*sizeof(int)))) {
+		LM_ERR("no more shm mem(2)\n");
+		goto err_exit;
+	}
+	memset(idx, 0, 2*idx_size*sizeof(int));
+
+	rt->priority = priority;
+	rt->time_rec = trec;
+	rt->route_idx = route_idx;
+	tmp=dstlst;
+	n=0;
+	/* parse the dstlst */
+	while(tmp && (*tmp!=0)) {
+		errno = 0;
+		t = strtol(tmp, &ep, 10);
+		if (ep == tmp) {
+			LM_ERR("bad id '%c' (%d)[%s]\n",
+					*ep, (int)(ep-dstlst), dstlst);
+			goto err_exit;
+		}
+		if ((!IS_SPACE(*ep)) && (*ep != SEP) && (*ep != SEP1)
+				&& (*ep != SEP_GRP) && (*ep!=0)) {
+			LM_ERR("bad char %c (%d) [%s]\n",
+					*ep, (int)(ep-dstlst), dstlst);
+			goto err_exit;
+		}
+		if (errno == ERANGE && (t== LONG_MAX || t== LONG_MIN)) {
+			LM_ERR("out of bounds\n");
+			goto err_exit;
+		}
+		idx[2*n]=t;
+		idx[2*n+1]=grp_idx;
+		if(*ep == SEP_GRP)
+			grp_idx++;
+		n++;
+		/* reallocate the array which keeps the parsed indexes */
+		if(n>=idx_size){
+			if(NULL==((t_idx)=(int*)shm_malloc((idx_size*2*2)*sizeof(int)))) {
+				LM_ERR("out of shm\n");
+				goto err_exit;
+			}
+			memset(t_idx+(2*idx_size), 0, 2*idx_size*sizeof(int));
+			memcpy(t_idx, idx, 2*idx_size*sizeof(int));
+			shm_free(idx);
+			idx_size*=2;
+			idx=t_idx;
+		}
+		if(IS_SPACE(*ep))
+			EAT_SPACE(ep);
+		if(ep && (*ep == SEP || *ep == SEP1 || *ep == SEP_GRP))
+			ep++;
+		tmp = ep;
+	}
+	if(n==0) {
+		LM_ERR("invalid n\n");
+		goto err_exit;
+	}
+	/* create the pgwl */
+	rt->pgwa_len = n;
+	if(NULL ==
+		(rt->pgwl=(pgw_list_t*)shm_malloc(rt->pgwa_len*sizeof(pgw_list_t)))) {
+		goto err_exit;
+	}
+	memset(rt->pgwl, 0, rt->pgwa_len*sizeof(pgw_list_t));
+	/* translate GW ids to GW pointers */
+	for(i=0;i<n; i++){
+		if ( NULL == (pgw = get_pgw(pgw_l, idx[2*i]))) {
+			LM_ERR("invalid GW id %d\n",
+				idx[2*i]);
+			goto err_exit;
+		}
+		rt->pgwl[i].pgw=pgw;
+		rt->pgwl[i].grpid=idx[2*i+1];
+		/* LM_DBG("added to gwlist [%d/%d/%p]\n",
+				idx[2*i], idx[2*i+1], pgw); */
+	}
+
+	shm_free(idx);
+	return rt;
+
+err_exit:
+	if(NULL!=idx)
+		shm_free(idx);
+	if((NULL != rt) && 
+		(NULL!=rt->pgwl))
+		shm_free(rt->pgwl); 
+	if(NULL!=rt)
+		shm_free(rt);
+	return NULL;
+}
+
+int
+add_rt_info(
+	ptree_node_t *pn,
+	rt_info_t* r,
+	unsigned int rgid
+	)
+{
+	rg_entry_t    *trg=NULL;
+	rt_info_wrp_t *rtl_wrp=NULL;
+	rt_info_wrp_t *rtlw=NULL;
+	int i=0;
+
+	if((NULL == pn) || (NULL == r))
+		goto err_exit;
+
+	if (NULL == (rtl_wrp = (rt_info_wrp_t*)shm_malloc(sizeof(rt_info_wrp_t)))) {
+		LM_ERR("no more shm mem\n");
+		goto err_exit;
+	}
+	memset( rtl_wrp, 0, sizeof(rt_info_wrp_t));
+	rtl_wrp->rtl = r;
+
+	if(NULL==pn->rg) {
+		/* allocate the routing groups array */
+		pn->rg_len = RG_INIT_LEN;
+		if(NULL == (pn->rg = (rg_entry_t*)shm_malloc(
+						pn->rg_len*sizeof(rg_entry_t)))) {
+			/* recover the old pointer to be able to shm_free mem */
+			goto err_exit;
+		}
+		memset( pn->rg, 0, pn->rg_len*sizeof(rg_entry_t));
+		pn->rg_pos=0;
+	}
+	/* search for the rgid up to the rg_pos */
+	for(i=0; (i<pn->rg_pos) && (pn->rg[i].rgid!=rgid); i++);
+	if((i==pn->rg_len-1)&&(pn->rg[i].rgid!=rgid)) {
+		/* realloc & copy the old rg */
+		trg = pn->rg;
+		if(NULL == (pn->rg = (rg_entry_t*)shm_malloc(
+						2*pn->rg_len*sizeof(rg_entry_t)))) {
+			/* recover the old pointer to be able to shm_free mem */
+			pn->rg = trg;
+			goto err_exit;
+		}
+		memset(pn->rg+pn->rg_len, 0, pn->rg_len*sizeof(rg_entry_t));
+		memcpy(pn->rg, trg, pn->rg_len*sizeof(rg_entry_t));
+		pn->rg_len*=2;
+		shm_free( trg );
+	}
+	/* insert into list */
+	r->ref_cnt++;
+	if(NULL==pn->rg[i].rtlw){
+		pn->rg[i].rtlw = rtl_wrp;
+		pn->rg[i].rgid = rgid;
+		pn->rg_pos++;
+		goto ok_exit;
+	}
+	if( r->priority > pn->rg[i].rtlw->rtl->priority) {
+		/* change the head of the list */
+		rtl_wrp->next = pn->rg[i].rtlw;
+		pn->rg[i].rtlw = rtl_wrp;
+		goto ok_exit;
+	}
+	rtlw = pn->rg[i].rtlw;
+	while( rtlw->next !=NULL) {
+		if(r->priority > rtlw->next->rtl->priority) {
+			rtl_wrp->next = rtlw->next;
+			rtlw->next = rtl_wrp;
+			goto ok_exit;
+		}
+		rtlw = rtlw->next;
+	}
+	/* the smallest priority is linked at the end */
+	rtl_wrp->next=NULL;
+	rtlw->next=rtl_wrp;
+ok_exit:
+	return 0;
+
+err_exit:
+	if (rtl_wrp) shm_free(rtl_wrp);
+	return -1;
+}
+
+int
+add_dst(
+	rt_data_t *r,
+	/* id */
+	int id,
+	/* ip address */ 
+	char* ip,
+	/* strip len */
+	int strip,
+	/* pri prefix */
+	char* pri,
+	/* dst type*/
+	int type,
+	/* dst attrs*/
+	char* attrs
+	)
+{
+	pgw_t *pgw=NULL, *tmp=NULL;
+	pgw_addr_t *tmpa=NULL;
+	struct hostent* he;
+	struct sip_uri uri;
+	struct ip_addr ipa;
+	int l_ip,l_pri,l_attrs;
+#define GWABUF_MAX_SIZE	512
+	char gwabuf[GWABUF_MAX_SIZE];
+	str gwas;
+
+	if (NULL==r || NULL==ip) {
+		LM_ERR("invalid parametres\n");
+		goto err_exit;
+	}
+
+	l_ip = strlen(ip);
+	l_pri = pri?strlen(pri):0;
+	l_attrs = attrs?strlen(attrs):0;
+
+	pgw = (pgw_t*)shm_malloc(sizeof(pgw_t) + l_ip + l_pri + l_attrs);
+	if (NULL==pgw) {
+		LM_ERR("no more shm mem (%u)\n",
+			(unsigned int)(sizeof(pgw_t)+l_ip+l_pri +l_attrs));
+		goto err_exit;
+	}
+	memset(pgw,0,sizeof(pgw_t));
+
+	pgw->ip.len= l_ip;
+	pgw->ip.s = (char*)(pgw+1);
+	memcpy(pgw->ip.s, ip, l_ip);
+
+	if (pri) {
+		pgw->pri.len = l_pri;
+		pgw->pri.s = ((char*)(pgw+1))+l_ip;
+		memcpy(pgw->pri.s, pri, l_pri);
+	}
+	if (attrs) {
+		pgw->attrs.len = l_attrs;
+		pgw->attrs.s = ((char*)(pgw+1))+l_ip+l_pri;
+		memcpy(pgw->attrs.s, attrs, l_attrs);
+	}
+	pgw->id = id;
+	pgw->strip = strip;
+	pgw->type = type;
+
+	/* add address in the list */
+	if(pgw->ip.len<5 || (strncasecmp("sip:", ip, 4)
+			&&strncasecmp("sips:", ip, 5)))
+	{
+		if(pgw->ip.len+4>=GWABUF_MAX_SIZE) {
+			LM_ERR("GW address (%d) longer "
+				"than %d\n",pgw->ip.len+4,GWABUF_MAX_SIZE);
+			goto err_exit;
+		}
+		memcpy(gwabuf, "sip:", 4);
+		memcpy(gwabuf+4, ip, pgw->ip.len);
+		gwas.s = gwabuf;
+		gwas.len = 4+pgw->ip.len;
+	} else {
+		gwas.s = ip;
+		gwas.len = pgw->ip.len;
+	}
+
+	memset(&uri, 0, sizeof(struct sip_uri));
+	if(parse_uri(gwas.s, gwas.len, &uri)!=0) {
+		LM_ERR("invalid uri <%.*s>\n",
+			gwas.len, gwas.s);
+		goto err_exit;
+	}
+	/* note we discard the port discovered by the resolve function - we are
+	interested only in the port that was actually configured. */
+	if ( (he=sip_resolvehost( &uri.host, NULL, (char*)(void*)&uri.proto)==0 )) {
+		if(dr_force_dns)
+		{
+			LM_ERR("cannot resolve <%.*s>\n",
+				uri.host.len, uri.host.s);
+			goto err_exit;
+		} else {
+			LM_DBG("cannot resolve <%.*s> - won't be used"
+					" by is_from_gw()\n", uri.host.len, uri.host.s);
+			goto done;
+		}
+	}
+	hostent2ip_addr(&ipa, he, 0);
+	tmpa = r->pgw_addr_l;
+	while(tmpa) {
+		if(tmpa->type==type && uri.port_no==tmpa->port
+		&& ip_addr_cmp(&ipa, &tmpa->ip)) {
+			LM_DBG("gw ip addr [%s]:%d loaded\n",
+				ip_addr2a(&ipa), uri.port_no);
+			goto done;
+		}
+		tmpa = tmpa->next;
+	}
+	
+	LM_DBG("new gw ip addr [%s]\n", ip);
+	tmpa = (pgw_addr_t*)shm_malloc(sizeof(pgw_addr_t));
+	if(tmpa==NULL) {
+		LM_ERR("no more shm mem (%u)\n",
+			(unsigned int)sizeof(pgw_addr_t));
+		goto err_exit;
+	}
+	memset(tmpa, 0, sizeof(pgw_addr_t));
+	memcpy(&tmpa->ip, &ipa, sizeof(struct ip_addr));
+	tmpa->port = uri.port_no;
+	tmpa->type = type;
+	tmpa->strip = strip;
+	tmpa->next = r->pgw_addr_l;
+	r->pgw_addr_l = tmpa;
+
+done:
+	if(NULL==r->pgw_l)
+		r->pgw_l = pgw;
+	else {
+		tmp = r->pgw_l;
+		while(NULL != tmp->next)
+			tmp = tmp->next;
+		tmp->next = pgw;
+	}
+	return 0;
+
+err_exit:
+	if(NULL!=pgw)
+		shm_free(pgw);
+	return -1;
+}
+
+void
+del_pgw_list(
+		pgw_t *pgw_l
+		)
+{
+	pgw_t *t;
+	while(NULL!=pgw_l){
+		t = pgw_l;
+		pgw_l=pgw_l->next;
+		shm_free(t);
+	}
+}
+
+void
+del_pgw_addr_list(
+		pgw_addr_t *pgw_addr_l
+		)
+{
+	pgw_addr_t *t;
+	while(NULL!=pgw_addr_l){
+		t = pgw_addr_l;
+		pgw_addr_l=pgw_addr_l->next;
+		shm_free(t);
+	}
+}
+
+void 
+free_rt_data(
+		rt_data_t* rt_data,
+		int all
+		)
+{
+	int j;
+	if(NULL!=rt_data) {
+		/* del GW list */
+		del_pgw_list(rt_data->pgw_l);
+		rt_data->pgw_l = 0 ;
+		/* del GW addr list */
+		del_pgw_addr_list(rt_data->pgw_addr_l);
+		rt_data->pgw_addr_l =0;
+		/* del prefix tree */
+		del_tree(rt_data->pt);
+		/* del prefixless rules */
+		if(NULL!=rt_data->noprefix.rg) {
+			for(j=0;j<rt_data->noprefix.rg_pos;j++) {
+				if(rt_data->noprefix.rg[j].rtlw !=NULL) {
+					del_rt_list(rt_data->noprefix.rg[j].rtlw);
+					rt_data->noprefix.rg[j].rtlw = 0;
+				}
+			}
+			shm_free(rt_data->noprefix.rg);
+			rt_data->noprefix.rg = 0;
+		}
+		/* del top level */
+		if (all) shm_free(rt_data);
+	}
+}

+ 120 - 0
modules_k/drouting/routing.h

@@ -0,0 +1,120 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005-2009 Voice Sistem SRL
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * History:
+ * ---------
+ *  2005-02-20  first version (cristian)
+ *  2005-02-27  ported to 0.9.0 (bogdan)
+ */
+
+
+#ifndef routing_h
+#define routing_h
+
+#include "../../str.h"
+#include "../../usr_avp.h"
+#include "prefix_tree.h"
+#include "dr_time.h"
+
+#define RG_HASH_SIZE
+#define RG_INIT_LEN 4;
+
+/* the buckets for the rt_data rg_hash */
+typedef struct hb_ {
+	int rgid;
+	ptree_t *pt;
+	struct hb_*next;
+} hb_t;
+
+/* routing data is comprised of:
+	- a list of PSTN gw
+	- a hash over routing groups containing 
+	pointers to the coresponding prefix trees
+*/
+typedef struct rt_data_ {
+	/* list of PSTN gw */
+	pgw_t *pgw_l;
+	/* list of IP addr for PSTN gw */
+	pgw_addr_t *pgw_addr_l;
+	/* default routing list for prefixless rules */
+	ptree_node_t noprefix;
+	/* hash table with routing prefixes */
+	ptree_t *pt;
+}rt_data_t;
+
+typedef struct _dr_group {
+	/* 0 - use grp ; 1 - use AVP */
+	int type;
+	union {
+		unsigned int grp_id;
+		struct _avp_id{
+			int_str name;
+			unsigned short type;
+		}avp_id;
+	}u;
+} dr_group_t;
+
+/* init new rt_data structure */
+rt_data_t*
+build_rt_data( void );
+
+
+/* add a PSTN gw in the list */
+int
+add_dst(
+	rt_data_t*,
+	/* id */
+	int ,
+	/* ip address */ 
+	char*,
+	/* strip len */
+	int,
+	/* pri prefix */
+	char*,
+	/* dst type*/
+	int,
+	/* dst attrs*/
+	char*
+	);
+
+/* build a routing info list element */
+rt_info_t*
+build_rt_info(
+	int priority,
+	tmrec_t* time,
+	/* ser routing table id */
+	int route_id,
+	/* list of destinations indexes */
+	char* dstlst,
+	pgw_t* pgw_l
+);
+
+void
+del_pgw_list(
+		pgw_t *pgw_l
+		);
+
+void 
+free_rt_data(
+		rt_data_t*,
+		int
+		);
+#endif