Ver Fonte

Merge branch 'cr'

* merge carrierroute module from kamailio trunk, rev5652
* cr: (165 commits)
  - small improvement in error log message
  - a bit more additional logging for config load errors
  - change functionality in cr_route, cr_prime_route to not store information
  - log a better error message if carrier or domain table is empty at start
  - don't log failed route lookups (failure route case) with ERROR, we should
  - add some explanations of the flag and mask matching to the msg
  - add example for new domain_name table
  - small change related to previous commit, use the same order as in the error msg
  - small change to one INFO log, also output domain and carrier IDs
  - fix small error related to non-numerical matching: don't skip them when
  - fix a bunch of doxygen errors (mostly in modules, some in the core)
  - fix some obselete informations about the user strings,
  - fix default_tree parameter assignment from config file
  - increment DB scheme version number, forgotten during the previous commit
  - fix a compile warning related to size_t
  - add support for non-digit matching to trie utility functions
  - log error when a invalid dice_max value is found
  - finish refactoring of carrierroute module
  - bug fix: return '-1' if use_table failed in load_user_carrier
  - fix problem in call-id parsing (related to carrierroute)
  ...
Henning Westerholt há 16 anos atrás
pai
commit
b1807e7d20

+ 27 - 0
modules/carrierroute/Makefile

@@ -0,0 +1,27 @@
+# $Id: Makefile,v 1.6 2003/05/23 15:43:43 andrei Exp $
+#
+# carrierroute Makefile
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+
+auto_gen=
+NAME=carrierroute.so
+
+BUILDER = $(shell which confuse-config)
+
+ifeq ($(BUILDER),)
+	CONFUSEDEFS=-I$(LOCALBASE)/include -I/usr/local/include -I/opt/include \
+		-I/usr/sfw/include
+	CONFUSELIBS=-L$(LOCALBASE)/lib -L/usr/local/lib -L/usr/sfw/lib \
+		-L/opt/lib -lconfuse
+else
+		CONFUSEDEFS = $(shell confuse-config --cflags)
+		CONFUSELIBS = $(shell confuse-config --libs)
+endif
+
+DEFS+=$(CONFUSEDEFS)
+LIBS=$(CONFUSELIBS)
+
+include ../../Makefile.modules

+ 1325 - 0
modules/carrierroute/README

@@ -0,0 +1,1325 @@
+carrierroute
+
+Jonas Appel
+
+   1&1 Internet AG
+
+Hardy Kahl
+
+   1&1 Internet AG
+
+Henning Westerholt
+
+   1&1 Internet AG
+
+   Copyright © 2007 1&1 Internet AG
+   Revision History
+   Revision $Revision: 4872 $ $Date: 2008-09-09 17:39:38 +0200
+                              (Di, 09 Sep 2008) $
+     __________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1.1. Overview
+        1.2. Dependencies
+
+              1.2.1. Kamailio Modules
+              1.2.2. External Libraries or Applications
+
+        1.3. Exported Parameters
+
+              1.3.1. subscriber_table (string)
+              1.3.2. subscriber_user_col (string)
+              1.3.3. subscriber_domain_col (string)
+              1.3.4. subscriber_carrier_col (string)
+              1.3.5. config_source (string)
+              1.3.6. config_file (string)
+              1.3.7. default_tree (string)
+              1.3.8. use_domain (int)
+              1.3.9. fallback_default (int)
+              1.3.10. fetch_rows (integer)
+              1.3.11. match_mode (integer)
+
+        1.4. Exported Functions
+
+              1.4.1. cr_user_carrier(user, domain, dstavp)
+              1.4.2. cr_route(carrier, domain, prefix_matching,
+                      rewrite_user, hash_source, descavp)
+
+              1.4.3. cr_prime_route(carrier, domain,
+                      prefix_matching, rewrite_user, hash_source,
+                      descavp)
+
+              1.4.4. cr_next_domain(carrier, domain,
+                      prefix_matching, host, reply_code, dstavp)
+
+        1.5. MI Commands
+
+              1.5.1. cr_reload_routes
+              1.5.2. cr_dump_routes
+              1.5.3. cr_replace_host
+              1.5.4. cr_deactivate_host
+              1.5.5. cr_activate_host
+              1.5.6. cr_add_host
+              1.5.7. cr_delete_host
+
+        1.6. Configuration examples
+        1.7. Installation and Running
+
+              1.7.1. Database setup
+              1.7.2. Database examples
+              1.7.3. User specific routing
+
+   2. Module parameter for database access.
+
+        2.1. db_url (String)
+        2.2. carrierroute_table (String)
+        2.3. carrierroute_id_col (string)
+        2.4. carrierroute_carrier_col (string)
+        2.5. carrierroute_domain_col (string)
+        2.6. carrierroute_scan_prefix_col (string)
+        2.7. carrierroute_flags_col (string)
+        2.8. carrierroute_mask_col (string)
+        2.9. carrierroute_prob_col (string)
+        2.10. carrierroute_strip_col (string)
+        2.11. carrierroute_rewrite_host_col (string)
+        2.12. carrierroute_rewrite_prefix_col (string)
+        2.13. carrierroute_rewrite_suffix_col (string)
+        2.14. carrierroute_description_col (string)
+        2.15. carrierfailureroute_table (String)
+        2.16. carrierfailureroute_id_col (string)
+        2.17. carrierfailureroute_carrier_col (string)
+        2.18. carrierfailureroute_domain_col (string)
+        2.19. carrierfailureroute_scan_prefix_col (string)
+        2.20. carrierfailureroute_host_name_col (string)
+        2.21. carrierfailureroute_reply_code_col (string)
+        2.22. carrierfailureroute_flags_col (string)
+        2.23. carrierfailureroute_mask_col (string)
+        2.24. carrierfailureroute_next_domain_col (string)
+        2.25. carrierfailureroute_description_col (string)
+        2.26. carrier_name_table (String)
+        2.27. carrier_name_id_col (string)
+        2.28. carrier_name_carrier_col (string)
+        2.29. domain_name_table (String)
+        2.30. domain_name_id_col (string)
+        2.31. domain_name_domain_col (string)
+
+   List of Examples
+
+   1.1. Set subscriber_table parameter
+   1.2. Set subscriber_user_col parameter
+   1.3. Set subscriber_domain_col parameter
+   1.4. Set subscriber_carrier_col parameter
+   1.5. Set config_source parameter
+   1.6. Set config_file parameter
+   1.7. Set default_tree parameter
+   1.8. Set use_domain parameter
+   1.9. Set fallback_default parameter
+   1.10. Set fetch_rows parameter
+   1.11. Set match_mode parameter
+   1.12. cr_replace_host usage
+   1.13. cr_deactivate_host usage
+   1.14. cr_activate_host usage
+   1.15. cr_add_host usage
+   1.16. cr_delete_host usage
+   1.17. Configuration example - Routing to default tree
+   1.18. Configuration example - Routing to user tree
+   1.19. Configuration example - module configuration
+   1.20. Example database content - carrierroute table
+   1.21. Example database content - simple carrierfailureroute
+          table
+
+   1.22. Example database content - more complex
+          carrierfailureroute table
+
+   1.23. Example database content - carrier_name table
+   1.24. Example database content - domain_name table
+   1.25. Necessary extensions for the user table
+   2.1. Set db_url parameter
+   2.2. Set carrierroute_table parameter
+   2.3. Set carrierroute_id_col parameter
+   2.4. Set carrierroute_carrier_col parameter
+   2.5. Set carrierroute_domain_col parameter
+   2.6. Set carrierroute_scan_prefix_col parameter
+   2.7. Set carrierroute_flags_col parameter
+   2.8. Set carrierroute_mask_col parameter
+   2.9. Set carrierroute_prob_col parameter
+   2.10. Set carrierroute_strip_col parameter
+   2.11. Set carrierroute_rewrite_host_col parameter
+   2.12. Set carrierroute_rewrite_prefix_col parameter
+   2.13. Set carrierroute_rewrite_suffix_col parameter
+   2.14. Set carrierroute_description_col parameter
+   2.15. Set carrierfailureroute_table parameter
+   2.16. Set carrierfailureroute_id_col parameter
+   2.17. Set carrierfailureroute_carrier_col parameter
+   2.18. Set carrierfailureroute_domain_col parameter
+   2.19. Set carrierfailureroute_scan_prefix_col parameter
+   2.20. Set carrierfailureroute_host_name_col parameter
+   2.21. Set carrierfailureroute_reply_code_col parameter
+   2.22. Set carrierfailureroute_flags_col parameter
+   2.23. Set carrierfailureroute_mask_col parameter
+   2.24. Set carrierfailureroute_next_domain_col parameter
+   2.25. Set carrierfailureroute_description_col parameter
+   2.26. Set carrier_name_table parameter
+   2.27. Set carrier_name_id_col parameter
+   2.28. Set carrier_name_carrier_col parameter
+   2.29. Set domain_name_table parameter
+   2.30. Set domain_name_id_col parameter
+   2.31. Set domain_name_domain_col parameter
+
+Chapter 1. Admin Guide
+
+1.1. Overview
+
+   A module which provides routing, balancing and blacklisting
+   capabilities.
+
+   The module provides routing, balancing and blacklisting
+   capabilities. It reads routing entries from a database source
+   or from a config file at Kamailio startup. It can uses one
+   routing tree (for one carrier), or if needed for every user a
+   different routing tree (unique for each carrier) for number
+   prefix based routing. It supports several route tree domains,
+   e.g. for failback routes or different routing rules for VoIP
+   and PSTN targets.
+
+   Based on the tree, the module decides which number prefixes are
+   forwarded to which gateway. It can also distribute the traffic
+   by ratio parameters. Furthermore, the requests can be
+   distributed by a hash funcion to predictable destinations. The
+   hash source is configurable, two different hash functions are
+   available.
+
+   This modules scales up to more than a few million users, and is
+   able to handle more than several hundred thousand routing table
+   entries. We recieved reports of some setups that used more than
+   a million routing table entries. It also supports a large
+   number of carriers and domains which can be efficiently looked
+   up in most of the cases (see below for more informations). In
+   load balancing scenarios the usage of the config file mode is
+   recommended, to avoid the additional complexity that the
+   database driven routing creates.
+
+   Routing tables can be reloaded and edited (in config file mode)
+   with the MI interface, the config file is updated according the
+   changes. This is not implemented for the db interface, because
+   its easier to do the changes directly on the db. But the reload
+   and dump functions works of course here too.
+
+   Some module functionality is not fully available in the config
+   file mode, as it is not possible to specify all information
+   that can be stored in the database tables in the config file.
+   Further information about these limitations is given in later
+   sections. For user based routing or LCR you should use the
+   database mode.
+
+   In database mode, this module supports names and IDs for the
+   carriers and domains. When using IDs for the routing functions,
+   efficient binary search is used to find the needed data
+   structures. If you are using constant strings as parameter,
+   these will be converted to IDs during the fixup procedure.
+   However, if you are using AVPs as parameter and they contain
+   strings, this cannot be converted to IDs during the fixup
+   procedure. In that case linear search is performed to find the
+   needed data structures. So from a performance point of view it
+   is better to pass only IDs in AVPs to the routing functions.
+
+   Basically this module could be used as an replacement for the
+   lcr and the dispatcher module, if you have certain performance,
+   flexibility and/or integration requirements that these modules
+   don't handle properly. But for small installations it probably
+   make more sense to use the lcr and dispatcher module.
+
+   If you want to use this module in failure routes, then you need
+   to call "append_branch()" after rewriting the request URI in
+   order to relay the message to the new target. Its also
+   supportes the usage of database derived failure routing
+   descisions with the carrierfailureroute table.
+
+1.2. Dependencies
+
+1.2.1. Kamailio Modules
+
+   The following module must be loaded before this module:
+     * a database module, when a database is used as configuration
+       data source. Only SQL based databases are supported, as
+       this module needs the capability to issue raw queries. Its
+       not possible to use the dbtext or db_berkeley module at the
+       moment.
+     * The tm module, when you want to use the $T_reply_code
+       pseudo-variable in the "cr_next_domain" function.
+
+1.2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed
+   before running Kamailio with this module loaded:
+     * libconfuse, a configuration file parser library. (
+       http://www.nongnu.org/confuse/ )
+
+1.3. Exported Parameters
+
+1.3.1. subscriber_table (string)
+
+   The name of the table containing the subscribers
+
+   Default value is "subscriber".
+
+   Example 1.1. Set subscriber_table parameter
+...
+modparam("carrierroute", "subscriber_table", "subscriber")
+...
+
+1.3.2. subscriber_user_col (string)
+
+   The name of the column in the subscriber table containing the
+   usernames.
+
+   Default value is "username".
+
+   Example 1.2. Set subscriber_user_col parameter
+...
+modparam("carrierroute", "subscriber_user_col", "username")
+...
+
+1.3.3. subscriber_domain_col (string)
+
+   The name of the column in the subscriber table containing the
+   domain of the subscriber.
+
+   Default value is "domain".
+
+   Example 1.3. Set subscriber_domain_col parameter
+...
+modparam("carrierroute", "subscriber_domain_col", "domain")
+...
+
+1.3.4. subscriber_carrier_col (string)
+
+   The name of the column in the subscriber table containing the
+   carrier id of the subscriber.
+
+   Default value is "cr_preferred_carrier".
+
+   Example 1.4. Set subscriber_carrier_col parameter
+...
+modparam("carrierroute", "subscriber_carrier_col", "cr_preferred_carrier
+")
+...
+
+1.3.5. config_source (string)
+
+   Specifies whether the module loads its config data from a file
+   or from a database. Possible values are file or db.
+
+   Default value is "file".
+
+   Example 1.5. Set config_source parameter
+...
+modparam("carrierroute", "config_source", "file")
+...
+
+1.3.6. config_file (string)
+
+   Specifies the path to the config file.
+
+   Default value is "/etc/kamailio/carrierroute.conf".
+
+   Example 1.6. Set config_file parameter
+...
+modparam("carrierroute", "config_file", "/etc/kamailio/carrierroute.conf
+")
+...
+
+1.3.7. default_tree (string)
+
+   The name of the carrier tree used per default (if the current
+   subscriber has no preferred tree)
+
+   Default value is "default".
+
+   Example 1.7. Set default_tree parameter
+...
+modparam("carrierroute", "default_tree", "default")
+...
+
+1.3.8. use_domain (int)
+
+   When using tree lookup per user, this parameter specifies
+   whether to use the domain part for user matching or not.
+
+   Default value is "0".
+
+   Example 1.8. Set use_domain parameter
+...
+modparam("carrierroute", "use_domain", 0)
+...
+
+1.3.9. fallback_default (int)
+
+   This parameter defines the behaviour when using user-based tree
+   lookup. If the user has a non-existing tree set and
+   fallback_default is set to 1, the default tree is used.
+   Otherwise, cr_user_rewrite_uri returns an error.
+
+   Default value is "1".
+
+   Example 1.9. Set fallback_default parameter
+...
+modparam("carrierroute", "fallback_default", 1)
+...
+
+1.3.10. fetch_rows (integer)
+
+   The number of the rows to be fetched at once from database when
+   loading the routing data. This value can be used to tune the
+   load time at startup. For 1MB of private memory (default) it
+   should be below 3750. The database driver must support the
+   fetch_result() capability.
+
+   Default value is "2000".
+
+   Example 1.10. Set fetch_rows parameter
+...
+modparam("carrierroute", "fetch_rows", 3000)
+...
+
+1.3.11. match_mode (integer)
+
+   The number of individual characters that are used for matching.
+   Valid values are 10 or 128. When you specifiy 10, only digits
+   will be used for matching, this operation mode is equivalent to
+   the old behaviour. When configured with 128, all standard ascii
+   chars are available for matching. Please be aware that memory
+   requirements for storing the routing tree in shared memory will
+   also increase by a factor of 12.8.
+
+   Default value is "10".
+
+   Example 1.11. Set match_mode parameter
+...
+modparam("carrierroute", "match_mode", 10)
+...
+
+1.4. Exported Functions
+
+   Previous versions of carrierroute had some more function. All
+   the old semantics can be achieved by using the few new
+   functions like this:
+cr_rewrite_uri(domain, hash_source)
+-> cr_route("default", domain, "$rU", "$rU", hash_source)
+
+cr_prime_balance_uri(domain, hash_source)
+-> cr_prime_route("default", domain, "$rU", "$rU", hash_source)
+
+cr_rewrite_by_to(domain, hash_source)
+-> cr_route("default", domain, "$tU", "$rU", hash_source)
+
+cr_prime_balance_by_to(domain, hash_source)
+-> cr_prime_route("default", domain, "$tU", "$rU", hash_source)
+
+cr_rewrite_by_from(domain, hash_source)
+-> cr_route("default", domain, "$fU", "$rU", hash_source)
+
+cr_prime_balance_by_from(domain, hash_source)
+-> cr_prime_route("default", domain, "$fU", "$rU", hash_source)
+
+cr_user_rewrite_uri(uri, domain)
+-> cr_user_carrier(user, domain, "$avp(tree_avp)")
+-> cr_route("$avp(tree_avp)", domain, "$rU", "$rU", "call_id")
+
+cr_tree_rewrite_uri(tree, domain)
+-> cr_route(tree, domain, "$rU", "$rU", "call_id")
+
+1.4.1.  cr_user_carrier(user, domain, dstavp)
+
+   This function loads the carrier and stores it in an AVP. It
+   cannot be used in the config file mode, as it needs a mapping
+   of the given user to a certain carrier. The is derived from a
+   database entry belonging to the user parameter. This mapping
+   must be available in the table that is specified in the
+   "subscriber_table" variable. This data is not cached in memory,
+   that means for every execution of this function a database
+   query will be done.
+
+   Meaning of the parameters is as follows:
+     * user - Name of the user for the carrier tree lookup.
+       Additional to a string any pseudo-variable could be used as
+       input.
+     * domain - Name of the routing domain to be used. Additional
+       to a string any pseudo-variable could be used as input.
+     * dstavp - Name of the AVP where to store the carrier id.
+
+1.4.2.  cr_route(carrier, domain, prefix_matching, rewrite_user,
+hash_source, descavp)
+
+   This function searches for the longest match for the user given
+   in prefix_matching at the given domain in the given carrier
+   tree. The Request URI is rewritten using rewrite_user and the
+   given hash source and algorithm. Returns -1 if there is no data
+   found or an empty rewrite host on the longest match is found.
+   On sucess also the description is stored in the given AVP (if
+   obmitted, nothing is stored in an AVP). This is useful if you
+   need some additional informations that belongs to each gw, like
+   the destination or the number of channels.
+
+   This function is only usable with rewrite_user and
+   prefix_matching containing a valid string. This string needs to
+   be numerical if the match_mode parameter is set to 10. It uses
+   the standard CRC32 algorithm to calculate the hash values.
+
+   If flags and masks values are specified in the routing rule,
+   they will be compared by this function to the message flags.
+   Specify a flag and mask value of "0" to match to all possible
+   message flags (this is the default value). If flags and mask
+   are not zero, and no match to the message flags is possible, no
+   routing will be done. The calculation of the hash and the
+   load-balancing is done after the flags matching.
+
+   Meaning of the parameters is as follows:
+     * carrier - The routing tree to be used. Additional to a
+       string any pseudo-variable could be used as input.
+     * domain - Name of the routing domain to be used. Additional
+       to a string any pseudo-variable could be used as input.
+     * prefix_matching - User name to be used for prefix matching
+       in the routing tree. Additional to a string any
+       pseudo-variable could be used as input.
+     * rewrite_user - The user name to be used for applying the
+       rewriting rule. Usually this is the user part of the
+       request URI. Additional to a string any pseudo-variable
+       could be used as input.
+     * hash_source - The hash values of the destination set must
+       be a contiguous range starting at 1, limited by the
+       configuration parameter max_targets. Possible values for
+       hash_source are: call_id, from_uri, from_user, to_uri and
+       to_user.
+     * decsavp - Name of the AVP where to store the description.
+       This parameter is optional.
+
+1.4.3.  cr_prime_route(carrier, domain, prefix_matching,
+rewrite_user, hash_source, descavp)
+
+   This function searches for the longest match for the user given
+   in prefix_matching at the given domain in the given carrier
+   tree. The Request URI is rewritten using rewrite_user and the
+   given hash source and algorithm. Returns -1 if there is no data
+   found or an empty rewrite host on the longest match is found.
+   On success also the description is stored in the given AVP (if
+   obmitted, nothing is stored in an AVP). This is useful if you
+   need some additional informations that belongs to each gw, like
+   the destination or the number of channels. This function is
+   only usable with rewrite_user and prefix_matching containing a
+   valid string. This string needs to be numerical if the
+   match_mode parameter is set to 10. It uses the prime hash
+   algorithm to calculate the hash values.
+
+   Meaning of the parameters is as follows:
+     * carrier - The routing tree to be used. Additional to a
+       string any pseudo-variable could be used as input.
+     * domain - Name of the routing domain to be used. Additional
+       to a string any pseudo-variable could be used as input.
+     * prefix_matching - User name to be used for prefix matching
+       in the routing tree. Additional to a string any
+       pseudo-variable could be used as input.
+     * rewrite_user - The user name to be used for applying the
+       rewriting rule. Usually this is the user part of the
+       request URI. Additional to a string any pseudo-variable
+       could be used as input.
+     * hash_source - The hash values of the destination set must
+       be a contiguous range starting at 1, limited by the
+       configuration parameter max_targets. Possible values for
+       hash_source are: call_id, from_uri, from_user, to_uri and
+       to_user.
+     * descavp - Name of the AVP where to store the description.
+       This parameter is optional.
+
+1.4.4.  cr_next_domain(carrier, domain, prefix_matching, host,
+reply_code, dstavp)
+
+   This function searches for the longest match for the user given
+   in prefix_matching at the given domain in the given carrier
+   failure tree. It tries to find a next domain matching the given
+   host, reply_code and the message flags. The matching is done in
+   this order: host, reply_code and then flags. The more wildcards
+   in reply_code and the more bits used in flags, the lower the
+   priority. Returns -1 if there is no data found or an empty
+   next_domain on the longest match is found. Otherwise the next
+   domain is stored in the given AVP. This function is only usable
+   with rewrite_user and prefix_matching containing a valid
+   string. This string needs to be numerical if the match_mode
+   parameter is set to 10.
+
+   Meaning of the parameters is as follows:
+     * carrier - The routing tree to be used. Additional to a
+       string any pseudo-variable could be used as input.
+     * domain - Name of the routing domain to be used. Additional
+       to a string any pseudo-variable could be used as input.
+     * prefix_matching - User name to be used for prefix matching
+       in the routing tree. Additional to a string any
+       pseudo-variable could be used as input.
+     * host - The host name to be used for failure route rule
+       matching. Usually this is the last tried routing
+       destination stored in an avp by cr_route. Additional to a
+       string any pseudo-variable could be used as input.
+     * reply_code - The reply code to be used for failure route
+       rule matching. Additional to a string any pseudo-variable
+       could be used as input.
+     * dstavp - Name of the AVP where to store the next routing
+       domain.
+
+1.5. MI Commands
+
+   All commands understand the "-?" parameter to print a short
+   help message. The options have to be quoted as one string to be
+   passed to MI interface. Each option except host and new host
+   can be wildcarded by * (but only * and not things like "-d
+   prox*").
+
+1.5.1. cr_reload_routes
+
+   This command reloads the routing data from the data source.
+
+   Important: When new domains have been added, a restart of the
+   server must be done, because the mapping of the ids used in the
+   config script cannot be updated at runtime at the moment. So a
+   reload could result in a wrong routing behaviour, because the
+   ids used in the script could differ from the one used
+   internally from the server. Modifying of already existing
+   domains is no problem.
+
+1.5.2. cr_dump_routes
+
+   This command prints the route rules on the command line.
+
+1.5.3. cr_replace_host
+
+   This command can replace the rewrite_host of a route rule, it
+   is only usable in file mode. Following options are possible:
+     * -d - the domain containing the host
+     * -p - the prefix containing the host
+     * -h - the host to be replaced
+     * -t - the new host
+
+   Use the "null" prefix to specify an empty prefix.
+
+   Example 1.12. cr_replace_host usage
+...
+kamctl fifo cr_replace_host "-d proxy -p 49 -h proxy1 -t proxy2"
+...
+
+1.5.4. cr_deactivate_host
+
+   This command deactivates the specified host, i.e. it sets its
+   status to 0. It is only usable in file mode. Following options
+   are possible:
+     * -d - the domain containing the host
+     * -p - the prefix containing the host
+     * -h - the host to be deactivated
+     * -t - the new host used as backup
+
+   When -t (new_host) is specified, the portion of traffic for the
+   deactivated host is routed to the host given by -t. This is
+   indicated in the output of dump_routes. The backup route is
+   deactivated if the host is activated again.
+
+   Use the "null" prefix to specify an empty prefix.
+
+   Example 1.13. cr_deactivate_host usage
+...
+kamctl fifo cr_deactivate_host "-d proxy -p 49 -h proxy1"
+...
+
+1.5.5. cr_activate_host
+
+   This command activates the specified host, i.e. it sets its
+   status to 1. It is only usable in file mode. Following options
+   are possible:
+     * -d - the domain containing the host
+     * -p - the prefix containing the host
+     * -h - the host to be activated
+
+   Use the "null" prefix to specify an empty prefix.
+
+   Example 1.14. cr_activate_host usage
+...
+kamctl fifo cr_activate_host "-d proxy -p 49 -h proxy1"
+...
+
+1.5.6. cr_add_host
+
+   This command adds a route rule, it is only usable in file mode.
+   Following options are possible:
+     * -d - the domain containing the host
+     * -p - the prefix containing the host
+     * -h - the host to be added
+     * -w - the weight of the rule
+     * -P - an optional rewrite prefix
+     * -S - an optional rewrite suffix
+     * -i - an optional hash index
+     * -s - an optional strip value
+
+   Use the "null" prefix to specify an empty prefix.
+
+   Example 1.15. cr_add_host usage
+...
+kamctl fifo cr_add_host "-d proxy -p 49 -h proxy1 -w 0.25"
+...
+
+1.5.7. cr_delete_host
+
+   This command delete the specified hosts or rules, i.e. remove
+   them from the route tree. It is only usable in file mode.
+   Following options are possible:
+     * -d - the domain containing the host
+     * -p - the prefix containing the host
+     * -h - the host to be added
+     * -w - the weight of the rule
+     * -P - an optional rewrite prefix
+     * -S - an optional rewrite suffix
+     * -i - an optional hash index
+     * -s - an optional strip value
+
+   Use the "null" prefix to specify an empty prefix.
+
+   Example 1.16. cr_delete_host usage
+...
+kamctl fifo cr_delete_host "-d proxy -p 49 -h proxy1 -w 0.25"
+...
+
+1.6. Configuration examples
+
+   Example 1.17. Configuration example - Routing to default tree
+...
+route {
+        # route calls based on hash over callid
+        # choose route domain 0 of the default carrier
+
+        if(!cr_route("default", "0", "$rU", "$rU", "call_id", "crc32")){
+                sl_send_reply("403", "Not allowed");
+        } else {
+                # In case of failure, re-route the request
+                t_on_failure("1");
+                # Relay the request to the gateway
+                t_relay();
+        }
+}
+
+failure_route[1] {
+        # In case of failure, send it to an alternative route:
+        if (t_check_status("408|5[0-9][0-9]")) {
+                #choose route domain 1 of the default carrier
+        if(!cr_route("default", "1", "$rU", "$rU", "call_id", "crc32")){
+                        t_reply("403", "Not allowed");
+                } else {
+                        t_on_failure("2");
+                        t_relay();
+                }
+        }
+}
+
+failure_route[2] {
+        # further processing
+}
+
+
+   Example 1.18. Configuration example - Routing to user tree
+...
+route[1] {
+        cr_user_carrier("$fU", "$fd", "$avp(s:carrier)");
+
+        # just an example domain
+        $avp(s:domain)="start";
+        if (!cr_route("$avp(s:carrier)", "$avp(s:domain)", "$rU", "$rU",
+                        "call_id")) {
+                xlog("L_ERR", "cr_route failed\n");
+                exit;
+        }
+        # if you store also the port as part of the rewrite host,
+        # otherwise you can just use $rd later
+        $avp(s:host)= $rd+":"+$rp;
+        t_on_failure("1");
+                if (!t_relay()) {
+                        sl_reply_error();
+        };
+}
+
+failure_route[1] {
+        revert_uri();
+        if (!cr_next_domain("$avp(s:carrier)", "$avp(s:domain)", "$rU",
+                        "$avp(s:host)", "$T_reply_code", "$avp(s:domain)
+")) {
+                xlog("L_ERR", "cr_next_domain failed\n");
+                exit;
+        }
+        if (!cr_route("$avp(s:carrier)", "$avp(s:domain)", "$rU", "$rU",
+                        "call_id")) {
+                xlog("L_ERR", "cr_route failed\n");
+                exit;
+        }
+        $avp(s:host)= $rd+":"+$rp;
+        t_on_failure("1");
+        append_branch();
+        if (!t_relay()) {
+                xlog("L_ERR", "t_relay failed\n");
+                exit;
+        };
+}
+...
+
+   Example 1.19. Configuration example - module configuration
+
+   The following config file specifies within the default carrier
+   two domains, each with an prefix that contains two hosts. It is
+   not possible to specify another carrier if you use the config
+   file as data source.
+
+   All traffic will be equally distributed between the hosts, both
+   are active. The hash algorithm will working over the [1,2] set,
+   messages hashed to one will go to the first host, the other to
+   the second one. Don't use a hash index value of zero. If you
+   ommit the hash completly, the module gives them a autogenerated
+   value, starting from one.
+
+   Use the "NULL" prefix to specify an empty prefix in the config
+   file. Please note that the prefix is matched against the
+   request URI (or to URI), if they did not contain a valid
+   (numerical) URI, no match is possible. So for loadbalancing
+   purposes e.g. for your registrars, you should use an empty
+   prefix.
+...
+domain proxy {
+   prefix 49 {
+     max_targets = 2
+      target proxy1.localdomain {
+         prob = 0.500000
+         hash_index = 1
+         status = 1
+         comment = "test target 1"
+      }
+      target proxy2.localdomain {
+         prob = 0.500000
+         hash_index = 2
+         status = 1
+         comment = "test target 2"
+      }
+   }
+}
+
+domain register {
+   prefix NULL {
+     max_targets = 2
+      target register1.localdomain {
+         prob = 0.500000
+         hash_index = 1
+         status = 1
+         comment = "test target 1"
+      }
+      target register2.localdomain {
+         prob = 0.500000
+         hash_index = 2
+         status = 1
+         comment = "test target 2"
+      }
+   }
+}
+...
+
+1.7. Installation and Running
+
+1.7.1. Database setup
+
+   Before running Kamailio with carrierroute, you have to setup
+   the database table where the module will store the routing
+   data. For that, if the table was not created by the
+   installation script or you choose to install everything by
+   yourself you can use the carrierroute-create.sql SQL script in
+   the database directories in the kamailio/scripts folder as
+   template. Database and table name can be set with module
+   parameters so they can be changed, but the name of the columns
+   must be as they are in the SQL script. You can also find the
+   complete database documentation on the project webpage,
+   http://www.kamailio.org/docs/db-tables/kamailio-db-devel.html.
+   The flags and mask columns have the same function as in the
+   carrierfailureroute table. A zero value in the flags and mask
+   column means that any message flags will match this rule.
+
+   For a minimal configuration either use the config file given
+   above, or insert some data into the tables of the module.
+
+1.7.2. Database examples
+
+   Example 1.20. Example database content - carrierroute table
+...
++----+---------+--------+-------------+-------+------+---------------+
+| id | carrier | domain | scan_prefix | flags | prob | rewrite_host  |
++----+---------+--------+-------------+-------+------+---------------+
+| 1  |       1 |      0 | 49          |     0 |  0.5 | de-1.carrier1 |
+| 2  |       1 |      0 | 49          |     0 |  0.5 | de-2.carrier1 |
+| 3  |       1 |      0 | 49          |    16 |    1 | de-3.carrier1 |
+| 4  |       1 |      0 |             |     0 |    1 | gw.carrier1-1 |
+| 5  |       1 |      1 | 49          |     0 |    1 | gw.carrier1-1 |
+| 6  |       1 |      2 |             |     0 |    1 | gw.carrier1-2 |
+| 7  |       1 |      3 |             |     0 |    1 | gw.carrier1-3 |
+| 8  |       2 |      0 | 49          |     0 |  0.5 | de-1.carrier2 |
+| 9  |       2 |      0 | 49          |     0 |  0.5 | de-2.carrier2 |
+| 10 |       2 |      0 |             |     0 |    1 | gw.carrier2   |
+| 11 |       2 |      1 | 49          |     0 |    1 | gw.carrier2   |
+| 12 |       3 |      8 | 49          |     0 |    1 | de-gw.default |
+| 13 |       3 |      8 |             |     0 |    1 | gw.default    |
++----+---------+--------+-------------+-------+------+---------------+
+...
+
+   This table contains three routes to two gateways for the "49"
+   prefix, and a default route for other prefixes over carrier 2
+   and carrier 1. The gateways for the default carrier will be
+   used for functions that don't support the user specific carrier
+   lookup. The routing rules for carrier 1 and carrier 2 for the
+   "49" prefix contains a additional rule with the domain 1, that
+   can be used for example as fallback if the gateways in domain 0
+   are not reachable. Two more fallback rules (domain 2 and 3) for
+   carrier 1 are also supplied to support the functionality of the
+   carrierfailureroute table example that is provided in the next
+   section.
+
+   This table provides also a "carrier 1" routing rule for the
+   "49" prefix, that is only choosen if some message flags are
+   set. If this flags are not set, the other two rules are used.
+   The "strip", "mask" and "comment" colums are omitted for
+   brevity.
+
+   Example 1.21. Example database content - simple
+   carrierfailureroute table
+...
++----+---------+--------+---------------+------------+-------------+
+| id | carrier | domain | host_name     | reply_code | next_domain |
++----+---------+--------+---------------+------------+-------------+
+|  1 |       1 | 0      | gw.carrier1-2 | ...        | 3           |
+|  2 |       1 | 0      | gw.carrier1-3 | ...        | 2           |
++----+---------+--------+---------------+------------+-------------+
+...
+
+   This table contains two failure routes for the "gw.carrier1-1"
+   and "-2" gateways. For any (failure) reply code the respective
+   next domain is choosen. After that no more failure routes are
+   available, an error will be returned from the "cr_next_domain"
+   function. Not all table colums are show here for brevity.
+
+   For each failure route domain and carrier that is added to the
+   carrierfailureroute table there must be at least one
+   corresponding entry in the carrierroute table, otherwise the
+   module will not load the routing data.
+
+   Example 1.22. Example database content - more complex
+   carrierfailureroute table
+...
++----+---------+-----------+------------+--------+-----+-------------+
+| id | domain  | host_name | reply_code | flags | mask | next_domain |
++----+---------+-----------+------------+-------+------+-------------+
+|  1 |      99 |           | 408        |    16 |   16 |             |
+|  2 |      99 | gw1       | 404        |     0 |    0 | 100         |
+|  3 |      99 | gw2       | 50.        |     0 |    0 | 100         |
+|  4 |      99 |           | 404        |  2048 | 2112 | 101         |
++----+---------+-----------+------------+-------+------+-------------+
+...
+
+   This table contains four failure routes that shows the usage of
+   more advanced features. The first route matches to a 408, and
+   to some flag for example that indicates that ringing has
+   happened. If this flag is set, there will be no further
+   forwarding, because next_domain is empty. In the second and
+   third routes are certain gateway errors matched, if this errors
+   have occured, then the next domain will be choosen. The last
+   route does forwarding according some flags, e.g. the customer
+   came from a certain carrier, and has call-forwarding
+   deactivated. In order to use the routing that is specified
+   above, a matching carrierroute table must be provided, that
+   holds domain entries for this routing rules. Not all table
+   colums are show here for brevity.
+
+   Example 1.23. Example database content - carrier_name table
+...
++----+----------+
+| id | carrier  |
++----+----------+
+|  1 | carrier1 |
+|  2 | carrier2 |
+|  3 | default  |
++----+----------+
+...
+
+   This table contains the mapping of the carrier id to actual
+   names.
+
+   Example 1.24. Example database content - domain_name table
+...
++----+----------+
+| id | carrier  |
++----+----------+
+|  1 | domain1  |
+|  2 | domain2  |
+|  3 | domain3  |
++----+----------+
+...
+
+   This table contains the mapping of the domain id to actual
+   names.
+
+1.7.3. User specific routing
+
+   For a functional routing the "cr_preferred_carrier" column must
+   be added to the subscriber table (or to the table and column
+   that you specified as modul parameter) to choose the actual
+   carrier for the users.
+
+   Example 1.25. Necessary extensions for the user table
+
+   Suggested changes:
+...
+ALTER TABLE subscriber ADD cr_preferred_carrier int(10) default NULL;
+...
+
+Chapter 2. Module parameter for database access.
+
+2.1. db_url (String)
+
+   URL to the database containing the data.
+
+   Default value is
+   "mysql://openserro:openserro@localhost/openser".
+
+   Example 2.1. Set db_url parameter
+...
+modparam("carrierroute", "db_url", "dbdriver://username:password@dbhost/
+dbname")
+...
+
+2.2. carrierroute_table (String)
+
+   Name of the carrierroute table for the carrierroute module.
+
+   Default value is "carrierroute".
+
+   Example 2.2. Set carrierroute_table parameter
+...
+modparam("carrierroute", "carrierroute_table", "carrierroute")
+...
+
+2.3. carrierroute_id_col (string)
+
+   Name of the column contains the unique identifier of a route.
+
+   Example 2.3. Set carrierroute_id_col parameter
+...
+modparam("carrierroute", "carrierroute_id_col", "id")
+...
+
+2.4. carrierroute_carrier_col (string)
+
+   This column contains the carrier id.
+
+   Example 2.4. Set carrierroute_carrier_col parameter
+...
+modparam("carrierroute", "carrierroute_carrier_col", "carrier")
+...
+
+2.5. carrierroute_domain_col (string)
+
+   This column contains the routing domain id. You can define
+   several routing domains to have different routing rules. Maybe
+   you use domain 0 for normal routing and domain 1 if domain 0
+   failed.
+
+   Example 2.5. Set carrierroute_domain_col parameter
+...
+modparam("carrierroute", "carrierroute_domain_col", "domain")
+...
+
+2.6. carrierroute_scan_prefix_col (string)
+
+   Name of column contains the scan prefixes. Scan prefixes define
+   the matching portion of a phone number, e.g. when we have the
+   scan prefixes 49721 and 49, the called number is 49721913740,
+   it matches 49721, because the longest match is taken. If no
+   prefix matches, the number is not routed. To prevent this, an
+   empty prefix value of could be added.
+
+   Example 2.6. Set carrierroute_scan_prefix_col parameter
+...
+modparam("carrierroute", "carrierroute_scan_prefix_col", "scan_prefix")
+...
+
+2.7. carrierroute_flags_col (string)
+
+   This column contains the flags used for rule matching.
+
+   Example 2.7. Set carrierroute_flags_col parameter
+...
+modparam("carrierroute", "carrierroute_flags_col", "flags")
+...
+
+2.8. carrierroute_mask_col (string)
+
+   This column contains the mask that is applied to the message
+   flags before rule matching.
+
+   Example 2.8. Set carrierroute_mask_col parameter
+...
+modparam("carrierroute", "carrierroute_mask_col", "mask")
+...
+
+2.9. carrierroute_prob_col (string)
+
+   Name of column contains the probability. The probability value
+   is used to distribute the traffic between several gateways.
+   Let's say 70 % of the traffic shall be routed to gateway A, the
+   other 30 % shall be routed to gateway B, we define a rule for
+   gateway A with a prob value of 0.7 and a rule for gateway B
+   with a prob value of 0.3. If all probabilities for a given
+   prefix, tree and domain don't add to 100%, the prefix values
+   will be adjusted according the given prob values. E.g. if three
+   hosts with prob values of 0.5, 0.5 and 0.4 are defined, the
+   resulting probabilities are 35.714, 35.714 and 28.571%. But its
+   better to choose meaningful values in the first place because
+   of clarity.
+
+   Example 2.9. Set carrierroute_prob_col parameter
+...
+modparam("carrierroute", "carrierroute_prob_col", "prob")
+...
+
+2.10. carrierroute_strip_col (string)
+
+   Name of the column contains the number of digits to be stripped
+   of the userpart of an URI before prepending rewrite_prefix.
+
+   Example 2.10. Set carrierroute_strip_col parameter
+...
+modparam("carrierroute", "carrierroute_strip_col", "strip")
+...
+
+2.11. carrierroute_rewrite_host_col (string)
+
+   Name of column contains the rewrite prefixes. Here you can
+   define a rewrite prefix for the localpart of the SIP URI. An
+   empty field represents a blacklist entry, anything else is put
+   as domain part into the Request URI of the SIP message.
+
+   Example 2.11. Set carrierroute_rewrite_host_col parameter
+...
+modparam("carrierroute", "carrierroute_rewrite_host_col", "rewrite_host"
+)
+...
+
+2.12. carrierroute_rewrite_prefix_col (string)
+
+   Name of column contains the rewrite prefixes. Here you can
+   define a rewrite prefix for the localpart of the SIP URI.
+
+   Example 2.12. Set carrierroute_rewrite_prefix_col parameter
+...
+modparam("carrierroute", "carrierroute_rewrite_prefix_col", "rewrite_pre
+fix")
+...
+
+2.13. carrierroute_rewrite_suffix_col (string)
+
+   Name of column contains the rewrite suffixes. Here you can
+   define a rewrite suffix for the localpart of the SIP URI.
+
+   Example 2.13. Set carrierroute_rewrite_suffix_col parameter
+...
+modparam("carrierroute", "carrierroute_rewrite_suffix_col", "rewrite_suf
+fix")
+...
+
+2.14. carrierroute_description_col (string)
+
+   A comment for the route entry, useful for larger routing
+   tables. The comment is also displayed by the fifo cmd
+   "cr_dump_routes".
+
+   Example 2.14. Set carrierroute_description_col parameter
+...
+modparam("carrierroute", "carrierroute_description_col", "description")
+...
+
+2.15. carrierfailureroute_table (String)
+
+   Name of the carrierfailureroute table for the carrierroute
+   module.
+
+   Default value is "carrierfailureroute".
+
+   Example 2.15. Set carrierfailureroute_table parameter
+...
+modparam("carrierroute", "carrierfailureroute_table", "carrierfailurerou
+te")
+...
+
+2.16. carrierfailureroute_id_col (string)
+
+   This column contains the unique identifier of a failure route.
+
+   Example 2.16. Set carrierfailureroute_id_col parameter
+...
+modparam("carrierroute", "carrierfailureroute_id_col", "id")
+...
+
+2.17. carrierfailureroute_carrier_col (string)
+
+   This column contains the carrier id.
+
+   Example 2.17. Set carrierfailureroute_carrier_col parameter
+...
+modparam("carrierroute", "carrierfailureroute_carrier_col", "carrier")
+...
+
+2.18. carrierfailureroute_domain_col (string)
+
+   This column contains the routing domain id. You can define
+   several routing domains to have different routing rules. Maybe
+   you use domain 0 for normal routing and domain 1 if domain 0
+   failed.
+
+   Example 2.18. Set carrierfailureroute_domain_col parameter
+...
+modparam("carrierroute", "carrierfailureroute_domain_col", "domain")
+...
+
+2.19. carrierfailureroute_scan_prefix_col (string)
+
+   Name of column contains the the scan prefixes. Scan prexies
+   define the matching portion of a phone number, e.g. we have the
+   scan prefixes 49721 and 49, the called number is 49721913740,
+   it matches 49721, because the longest match is taken. If no
+   prefix matches, the number is not failure routed. To prevent
+   this, an empty prefix value of could be added.
+
+   Example 2.19. Set carrierfailureroute_scan_prefix_col parameter
+...
+modparam("carrierroute", "carrierfailureroute_scan_prefix_col", "scan_pr
+efix")
+...
+
+2.20. carrierfailureroute_host_name_col (string)
+
+   Name of the column containing the host name of the last routing
+   destination, using for rules matching.
+
+   Example 2.20. Set carrierfailureroute_host_name_col parameter
+...
+modparam("carrierroute", "carrierfailureroute_host_name_col", "host_name
+")
+...
+
+2.21. carrierfailureroute_reply_code_col (string)
+
+   This column contains the reply code used for rule matching.
+
+   Example 2.21. Set carrierfailureroute_reply_code_col parameter
+...
+modparam("carrierroute", "carrierfailureroute_reply_code_col", "reply_co
+de")
+...
+
+2.22. carrierfailureroute_flags_col (string)
+
+   This column contains the flags used for rule matching.
+
+   Example 2.22. Set carrierfailureroute_flags_col parameter
+...
+modparam("carrierroute", "carrierfailureroute_flags_col", "flags")
+...
+
+2.23. carrierfailureroute_mask_col (string)
+
+   This column contains the mask that is applied to the message
+   flags before rule matching.
+
+   Example 2.23. Set carrierfailureroute_mask_col parameter
+...
+modparam("carrierroute", "carrierfailureroute_mask_col", "mask")
+...
+
+2.24. carrierfailureroute_next_domain_col (string)
+
+   This column contains the route domain id that should be used
+   for the next routing attempt.
+
+   Example 2.24. Set carrierfailureroute_next_domain_col parameter
+...
+modparam("carrierroute", "carrierfailureroute_next_domain_col", "next_do
+main")
+...
+
+2.25. carrierfailureroute_description_col (string)
+
+   A comment for the route entry, useful for larger routing
+   tables.
+
+   Example 2.25. Set carrierfailureroute_description_col parameter
+...
+modparam("carrierroute", "carrierfailureroute_description_col", "descrip
+tion")
+...
+
+2.26. carrier_name_table (String)
+
+   Name of the carrier_name table for the carrierroute module.
+
+   Default value is "carrier_name".
+
+   Example 2.26. Set carrier_name_table parameter
+...
+modparam("carrierroute", "carrier_name_table", "carrier_name")
+...
+
+2.27. carrier_name_id_col (string)
+
+   Name of the column containing the unique identifier of a
+   carrier.
+
+   Example 2.27. Set carrier_name_id_col parameter
+...
+modparam("carrierroute", "carrier_name_id_col", "id")
+...
+
+2.28. carrier_name_carrier_col (string)
+
+   This column contains the carrier name.
+
+   Example 2.28. Set carrier_name_carrier_col parameter
+...
+modparam("carrierroute", "carrier_name_carrier_col", "carrier")
+...
+
+2.29. domain_name_table (String)
+
+   Name of the domain_name table for the carrierroute module.
+
+   Default value is "domain_name".
+
+   Example 2.29. Set domain_name_table parameter
+...
+modparam("carrierroute", "domain_name_table", "domain_name")
+...
+
+2.30. domain_name_id_col (string)
+
+   Name of the column containing the unique identifier of a
+   domain.
+
+   Example 2.30. Set domain_name_id_col parameter
+...
+modparam("carrierroute", "domain_name_id_col", "id")
+...
+
+2.31. domain_name_domain_col (string)
+
+   This column contains the domain name.
+
+   Example 2.31. Set domain_name_domain_col parameter
+...
+modparam("carrierroute", "domain_name_domain_col", "domain")
+...

+ 246 - 0
modules/carrierroute/carrierroute.c

@@ -0,0 +1,246 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file carrierroute.c
+ * \brief Contains the functions exported by the module.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+/*!
+ * \defgroup carrierroute CARRIERROUTE :: The Kamailio carrierroute Module
+ * The module provides routing, balancing and blacklisting capabilities.
+ * It reads routing entries from a database source or from a config file
+ * at Kamailio startup. It can uses one routing tree (for one carrier),
+ * or if needed for every user a different routing tree (unique for each carrier)
+ * for number prefix based routing. It supports several routing domains, e.g.
+ * for failback routes or different routing rules for VoIP and PSTN targets.
+ */
+
+#include "../../sr_module.h"
+#include "../../str.h"
+#include "../../mem/mem.h"
+#include "carrierroute.h"
+#include "cr_fixup.h"
+#include "cr_map.h"
+#include "cr_fifo.h"
+#include "cr_data.h"
+#include "cr_func.h"
+#include "db_carrierroute.h"
+#include <sys/stat.h>
+
+MODULE_VERSION
+
+str carrierroute_db_url = str_init(DEFAULT_RODB_URL);
+str subscriber_table = str_init("subscriber");
+
+static str subscriber_username_col = str_init("username");
+static str subscriber_domain_col = str_init("domain");
+static str cr_preferred_carrier_col = str_init("cr_preferred_carrier");
+
+str * subscriber_columns[SUBSCRIBER_COLUMN_NUM] = {
+	&subscriber_username_col,
+	&subscriber_domain_col,
+	&cr_preferred_carrier_col
+};
+
+char * config_source = "file";
+char * config_file = CFG_DIR"carrierroute.conf";
+
+str default_tree = str_init("default");
+const str CR_EMPTY_PREFIX = str_init("null");
+
+int mode = 0;
+int use_domain = 0;
+int fallback_default = 1;
+int cr_fetch_rows = 2000;
+int cr_match_mode = 10;
+
+
+/************* Declaration of Interface Functions **************************/
+static int mod_init(void);
+static int child_init(int);
+static int mi_child_init(void);
+static void mod_destroy(void);
+
+
+/************* Module Exports **********************************************/
+static cmd_export_t cmds[]={
+	{"cr_user_carrier", (cmd_function)cr_load_user_carrier,  3, cr_load_user_carrier_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{"cr_route",        (cmd_function)cr_route,              5, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{"cr_route",        (cmd_function)cr_route,              6, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{"cr_prime_route",  (cmd_function)cr_route,              5, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{"cr_prime_route",  (cmd_function)cr_route,              6, cr_route_fixup,             0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{"cr_next_domain",  (cmd_function)cr_load_next_domain,   6, cr_load_next_domain_fixup,  0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[]= {
+	carrierroute_DB_URL
+	carrierroute_DB_TABLE
+	carrierfailureroute_DB_TABLE
+	carrier_name_DB_TABLE
+	domain_name_DB_TABLE
+	carrierroute_DB_COLS
+	carrierfailureroute_DB_COLS
+	carrier_name_DB_COLS
+	domain_name_DB_COLS
+	{"subscriber_table",       STR_PARAM, &subscriber_table.s },
+	{"subscriber_user_col",    STR_PARAM, &subscriber_username_col.s },
+	{"subscriber_domain_col",  STR_PARAM, &subscriber_domain_col.s },
+	{"subscriber_carrier_col", STR_PARAM, &cr_preferred_carrier_col.s },
+	{"config_source",          STR_PARAM, &config_source },
+	{"default_tree",           STR_PARAM, &default_tree.s },
+	{"config_file",            STR_PARAM, &config_file },
+	{"use_domain",             INT_PARAM, &use_domain },
+	{"fallback_default",       INT_PARAM, &fallback_default },
+	{"fetch_rows",             INT_PARAM, &cr_fetch_rows },
+	{"match_mode",             INT_PARAM, &cr_match_mode },
+	{0,0,0}
+};
+
+static mi_export_t mi_cmds[] = {
+	{ "cr_reload_routes",   reload_fifo,     MI_NO_INPUT_FLAG, 0,  mi_child_init },
+	{ "cr_dump_routes",     dump_fifo,       MI_NO_INPUT_FLAG, 0,  0 },
+	{ "cr_replace_host",    replace_host,    0,                0,  0 },
+	{ "cr_deactivate_host", deactivate_host, 0,                0,  0 },
+	{ "cr_activate_host",   activate_host,   0,                0,  0 },
+	{ "cr_add_host",        add_host,        0,                0,  0 },
+	{ "cr_delete_host",     delete_host,     0,                0,  0 },
+	{ 0, 0, 0, 0, 0}
+};
+
+struct module_exports exports = {
+	"carrierroute",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,       /* Exported functions */
+	params,     /* Export parameters */
+	0,          /* exported statistics */
+	mi_cmds,    /* exported MI functions */
+	0,          /* exported pseudo-variables */
+	0,          /* extra processes */
+	mod_init,   /* Module initialization function */
+	0,          /* Response function */
+	mod_destroy,/* Destroy function */
+	child_init  /* Child initialization function */
+};
+
+
+/************* Interface Functions *****************************************/
+
+/**
+ * Initialises the module, i.e. it binds the necessary API functions
+ * and registers the fifo commands
+ *
+ * @return 0 on success, -1 on failure
+ */
+static int mod_init(void) {
+	struct stat fs;
+
+	subscriber_table.len = strlen(subscriber_table.s);
+	subscriber_username_col.len = strlen(subscriber_username_col.s);
+	subscriber_domain_col.len = strlen(subscriber_domain_col.s);
+	cr_preferred_carrier_col.len = strlen(cr_preferred_carrier_col.s);
+	default_tree.len = strlen(default_tree.s);
+
+	carrierroute_db_vars();
+
+	if (cr_match_mode != 10 && cr_match_mode != 128) {
+		LM_ERR("invalid matching mode %d specific, please use 10 or 128", cr_match_mode);
+		return -1;
+	}
+
+	if (strcmp(config_source, "db") == 0) {
+		mode = CARRIERROUTE_MODE_DB;
+
+		LM_INFO("use database as configuration source");
+		if(carrierroute_db_init() < 0){
+			return -1;
+		}
+		// FIXME, move data initialization into child process
+		if(carrierroute_db_open() < 0){
+			return -1;
+		}
+	}
+	else if (strcmp(config_source, "file") == 0) {
+		mode = CARRIERROUTE_MODE_FILE;
+
+		LM_INFO("use file as configuration source");
+		if(stat(config_file, &fs) != 0){
+			LM_ERR("can't stat config file\n");
+			return -1;
+		}
+		if(fs.st_mode & S_IWOTH){
+			LM_WARN("insecure file permissions, routing data is world writeable");
+		}
+		if( !( fs.st_mode & S_IWOTH) &&
+			!((fs.st_mode & S_IWGRP) && (fs.st_gid == getegid())) &&
+			!((fs.st_mode & S_IWUSR) && (fs.st_uid == geteuid())) ) {
+				LM_ERR("config file %s not writable\n", config_file);
+				return -1;
+		}
+	}
+	else {
+		LM_ERR("invalid config_source parameter: %s\n", config_source);
+		return -1;
+	}
+
+	if (init_route_data() < 0) {
+		LM_ERR("could not init route data\n");
+		return -1;
+	}
+
+	if (reload_route_data() == -1) {
+		LM_ERR("could not prepare route data\n");
+		return -1;
+	}
+
+	if(mode == CARRIERROUTE_MODE_DB){
+		carrierroute_db_close();
+	}
+	return 0;
+}
+
+
+static int child_init(int rank) {
+	if(mode == CARRIERROUTE_MODE_DB){
+		return carrierroute_db_open();
+	}
+	return 0;
+}
+
+
+static int mi_child_init(void) {
+	if(mode == CARRIERROUTE_MODE_DB){
+		return carrierroute_db_open();
+	}
+	return 0;
+}
+
+
+static void mod_destroy(void) {
+	if(mode == CARRIERROUTE_MODE_DB){
+		carrierroute_db_close();
+	}
+	destroy_route_data();
+}

+ 59 - 0
modules/carrierroute/carrierroute.h

@@ -0,0 +1,59 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file carrierroute.h
+ * \brief Some globals.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#ifndef CARRIERROUTE_H
+#define CARRIERROUTE_H
+
+#include "../../str.h"
+
+#define DICE_MAX 1000
+
+#define SUBSCRIBER_COLUMN_NUM 3
+#define SUBSCRIBER_USERNAME_COL 0
+#define SUBSCRIBER_DOMAIN_COL   1
+#define SUBSCRIBER_CARRIER_COL  2
+
+#define CARRIERROUTE_MODE_DB 1
+#define CARRIERROUTE_MODE_FILE 2
+
+extern str subscriber_table;
+extern str * subscriber_columns[];
+extern char * config_source;
+extern char * config_file;
+extern str default_tree;
+
+extern const str CR_EMPTY_PREFIX;
+
+extern int mode;
+extern int use_domain;
+extern int fallback_default;
+extern int cr_fetch_rows;
+extern int cr_match_mode;
+
+#endif

+ 170 - 0
modules/carrierroute/cr_carrier.c

@@ -0,0 +1,170 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_carrier.c
+ * \brief Contains the functions to manage carrier data.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#include <stdlib.h>
+#include "../../mem/shm_mem.h"
+#include "../../ut.h"
+#include "cr_carrier.h"
+#include "cr_domain.h"
+#include "cr_map.h"
+
+
+/**
+ * Create a new carrier_data struct in shared memory and set it up.
+ *
+ * @param carrier_id id of carrier
+ * @param carrier_name pointer to the name of the carrier
+ * @param domains number of domains for that carrier
+ *
+ * @return a pointer to the newly allocated carrier data or NULL on
+ * error, in which case it LOGs an error message.
+ */
+struct carrier_data_t * create_carrier_data(int carrier_id, str *carrier_name, int domains) {
+	struct carrier_data_t * tmp;
+	if ((tmp = shm_malloc(sizeof(struct carrier_data_t))) == NULL) {
+		SHM_MEM_ERROR;
+		return NULL;
+	}
+	memset(tmp, 0, sizeof(struct carrier_data_t));
+	tmp->id = carrier_id;
+	tmp->name = carrier_name;
+	tmp->domain_num = domains;
+	if(domains > 0){
+		if ((tmp->domains = shm_malloc(sizeof(struct domain_data_t *) * domains)) == NULL) {
+			SHM_MEM_ERROR;
+			shm_free(tmp);
+			return NULL;
+		}
+		memset(tmp->domains, 0, sizeof(struct domain_data_t *) * domains);
+	}
+	return tmp;
+}
+
+
+/**
+ * Destroys the given carrier and frees the used memory.
+ *
+ * @param carrier_data the structure to be destroyed.
+ */
+void destroy_carrier_data(struct carrier_data_t *carrier_data) {
+	int i;
+	if (carrier_data) {
+		if (carrier_data->domains != NULL) {
+			for (i=0; i<carrier_data->domain_num; i++) {
+				destroy_domain_data(carrier_data->domains[i]);
+			}
+			shm_free(carrier_data->domains);
+		}
+		shm_free(carrier_data);
+	}
+}
+
+
+/**
+ * Adds a domain_data struct to the given carrier data structure at the given index.
+ * Other etries are moved one position up to make space for the new one.
+ *
+ * @param carrier_data the carrier data struct where domain_data should be inserted
+ * @param domain_data the domain data struct to be inserted
+ * @param index the index where to insert the domain_data structure in the domain array
+ *
+ * @return 0 on success, -1 on failure
+ */
+int add_domain_data(struct carrier_data_t * carrier_data, struct domain_data_t * domain_data, int index) {
+	LM_INFO("adding domain %d '%.*s' to carrier %d '%.*s'", domain_data->id, domain_data->name->len, domain_data->name->s, carrier_data->id, carrier_data->name->len, carrier_data->name->s);
+ 	LM_DBG("domain position %d (domain_num=%d, first_empty_domain=%d)", index, (int) carrier_data->domain_num, (int) carrier_data->first_empty_domain);
+
+	if ((index < 0) || (index > carrier_data->first_empty_domain)) {
+		LM_ERR("got invalid index during binary search\n");
+		return -1;
+	}
+		
+	if (carrier_data->first_empty_domain >= carrier_data->domain_num) {
+		LM_ERR("cannot add new domain '%.*s' into carrier '%.*s' - array already full\n", domain_data->name->len, domain_data->name->s, carrier_data->name->len, carrier_data->name->s);
+		return -1;
+	}
+
+	if (index < carrier_data->first_empty_domain) {
+		/* move other entries one position up */
+		memmove(&carrier_data->domains[index+1], &carrier_data->domains[index], sizeof(struct domain_data_t *)*(carrier_data->first_empty_domain-index));
+	}
+	carrier_data->domains[index] = domain_data;
+	carrier_data->first_empty_domain++;
+
+	return 0;
+}
+
+
+/**
+ * Returns the domain data for the given id by doing a binary search.
+ * @note The domain array must be sorted!
+ *
+ * @param carrier_data carrier data to be searched
+ * @param domain_id the id of desired domain
+ *
+ * @return a pointer to the desired domain data, NULL if not found.
+ */
+struct domain_data_t * get_domain_data(struct carrier_data_t * carrier_data, int domain_id) {
+	struct domain_data_t **ret;
+	struct domain_data_t key;
+	struct domain_data_t *pkey = &key;
+
+	if (!carrier_data) {
+		LM_ERR("NULL pointer in parameter\n");
+		return NULL;
+	}
+	key.id = domain_id;
+	ret = bsearch(&pkey, carrier_data->domains, carrier_data->domain_num, sizeof(carrier_data->domains[0]), compare_domain_data);
+	if (ret) return *ret;
+	return NULL;
+}
+
+
+/**
+ * Compares the IDs of two carrier data structures.
+ * A NULL pointer is always greater than any ID.
+ *
+ * @return -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
+ */
+int compare_carrier_data(const void *v1, const void *v2) {
+  struct carrier_data_t *c1 = *(struct carrier_data_t * const *)v1;
+	struct carrier_data_t *c2 = *(struct carrier_data_t * const *)v2;
+	if (c1 == NULL) {
+		if (c2 == NULL) return 0;
+		else return 1;
+	}
+	else {
+		if (c2 == NULL) return -1;
+		else {
+			if (c1->id < c2->id) return -1;
+			else if (c1->id > c2->id) return 1;
+			else return 0;
+		}
+	}
+}

+ 104 - 0
modules/carrierroute/cr_carrier.h

@@ -0,0 +1,104 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_carrier.h
+ * \brief Contains the functions to manage carrier data.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#ifndef CR_CARRIER_H
+#define CR_CARRIER_H
+
+#include <sys/types.h>
+#include "../../str.h"
+
+
+/**
+ * The struct for a carrier.
+ */
+struct carrier_data_t {
+	int id; /*!< id of the carrier */
+	str * name; /*!< name of the carrier. This points to the name in carrier_map to avoid duplication. */
+	struct domain_data_t ** domains; /*!< array of routing domains */
+	size_t domain_num; /*!< number of routing domains */
+	size_t first_empty_domain; /*!< the index of the first empty entry in domains */
+};
+
+
+/**
+ * Create a new carrier_data struct in shared memory and set it up.
+ *
+ * @param carrier_id id of carrier
+ * @param carrier_name pointer to the name of the carrier
+ * @param domains number of domains for that carrier
+ *
+ * @return a pointer to the newly allocated carrier data or NULL on
+ * error, in which case it LOGs an error message.
+ */
+struct carrier_data_t * create_carrier_data(int carrier_id, str *carrier_name, int domains);
+
+
+/**
+ * Destroys the given carrier and frees the used memory.
+ *
+ * @param carrier_data the structure to be destroyed.
+ */
+void destroy_carrier_data(struct carrier_data_t *carrier_data);
+
+
+/**
+ * Adds a domain_data struct to the given carrier data structure at the given index.
+ * Other etries are moved one position up to make space for the new one.
+ *
+ * @param carrier_data the carrier data struct where domain_data should be inserted
+ * @param domain_data the domain data struct to be inserted
+ * @param index the index where to insert the domain_data structure in the domain array
+ *
+ * @return 0 on success, -1 on failure
+ */
+int add_domain_data(struct carrier_data_t * carrier_data, struct domain_data_t * domain_data, int index);
+
+
+/**
+ * Returns the domain data for the given id by doing a binary search.
+ * @note The domain array must be sorted!
+ *
+ * @param carrier_data carrier data to be searched
+ * @param domain_id the id of desired domain
+ *
+ * @return a pointer to the desired domain data, NULL if not found.
+ */
+struct domain_data_t *get_domain_data(struct carrier_data_t * carrier_data, int domain_id);
+
+
+/**
+ * Compares the IDs of two carrier data structures.
+ * A NULL pointer is always greater than any ID.
+ *
+ * @return -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
+ */
+int compare_carrier_data(const void *v1, const void *v2);
+
+
+#endif

+ 461 - 0
modules/carrierroute/cr_config.c

@@ -0,0 +1,461 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_config.c
+ * \brief Functions for load and save routing data from a config file.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#include <confuse.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "../../mem/shm_mem.h"
+#include "../../mem/mem.h"
+#include "../../ut.h"
+#include "cr_config.h"
+#include "carrierroute.h"
+#include "cr_rule.h"
+#include "cr_domain.h"
+#include "cr_carrier.h"
+
+
+/**
+ * reports errors during config file parsing using LOG macro
+ *
+ * @param cfg points to the current config data structure
+ * @param fmt a format string
+ * @param ap format arguments
+ */
+static void conf_error(cfg_t *cfg, const char * fmt, va_list ap) {
+	// FIXME this don't seems to work reliable, produces strange error messages
+	LM_GEN1(L_ERR, (char *) fmt, ap);
+}
+
+
+/**
+ * Parses the config file
+ *
+ * @return a pointer to the configuration data structure, NULL on failure
+ */
+static cfg_t * parse_config(void) {
+	cfg_t * cfg = NULL;
+
+	cfg_opt_t target_opts[] = {
+	                              CFG_STR("comment", 0, CFGF_NONE),
+	                              CFG_INT("strip", 0, CFGF_NONE),
+	                              CFG_STR("rewrite_prefix", 0, CFGF_NONE),
+	                              CFG_FLOAT("prob", 0, CFGF_NONE),
+	                              CFG_INT("hash_index", 0, CFGF_NONE),
+	                              CFG_STR("rewrite_suffix", 0, CFGF_NONE),
+	                              CFG_INT("status", 1, CFGF_NONE),
+	                              CFG_INT_LIST("backed_up", NULL, CFGF_NONE),
+	                              CFG_INT("backup", -1, CFGF_NONE),
+	                              CFG_END()
+	                          };
+
+	cfg_opt_t prefix_opts[] = {
+	                              CFG_SEC("target", target_opts, CFGF_MULTI | CFGF_TITLE),
+	                              CFG_INT("max_targets", -1, CFGF_NONE),
+	                              CFG_END()
+	                          };
+
+	cfg_opt_t domain_opts[] = {
+	                              CFG_SEC("prefix", prefix_opts, CFGF_MULTI | CFGF_TITLE),
+	                              CFG_END()
+	                          };
+
+	cfg_opt_t opts[] = {
+	                       CFG_SEC("domain", domain_opts, CFGF_MULTI | CFGF_TITLE),
+	                       CFG_END()
+	                   };
+
+	cfg = cfg_init(opts, CFGF_NONE);
+
+	cfg_set_error_function(cfg, conf_error);
+
+	switch (cfg_parse(cfg, config_file)) {
+		case CFG_FILE_ERROR: LM_ERR("file not found: %s\n", config_file);
+			return NULL;
+		case CFG_PARSE_ERROR: LM_ERR("error while parsing %s in line %i, section %s\n",
+			                          cfg->filename, cfg->line, cfg->name);
+			return NULL;
+		case CFG_SUCCESS: break;
+	}
+	return cfg;
+}
+
+
+static int backup_config(void) {
+	FILE * from, * to;
+	char * backup_file, ch;
+	LM_INFO("start configuration backup\n");
+	if((backup_file = pkg_malloc(strlen(config_file) + strlen (".bak") + 1)) == NULL){
+		PKG_MEM_ERROR;
+		return -1;
+	}
+	if(!strcpy(backup_file, config_file)){
+		LM_ERR("can't copy filename\n");
+		goto errout;
+	}
+	if(!strcat(backup_file, ".bak")){
+		LM_ERR("can't attach suffix\n");
+		goto errout;
+	}
+	/* open source file */
+	if ((from = fopen(config_file, "rb"))==NULL) {
+		LM_ERR("Cannot open source file.\n");
+		goto errout;
+	}
+
+	/* open destination file */
+	if ((to = fopen(backup_file, "wb"))==NULL) {
+		LM_ERR("Cannot open destination file.\n");
+		fclose(from);
+		goto errout;
+	}
+
+	/* copy the file */
+	while (!feof(from)) {
+		ch = fgetc(from);
+		if (ferror(from)) {
+			LM_ERR("Error reading source file.\n");
+			goto errout;
+		}
+		if (!feof(from)) fputc(ch, to);
+		if (ferror(to)) {
+			LM_ERR("Error writing destination file.\n");
+			goto errout;
+		}
+	}
+
+	if (fclose(from)==EOF) {
+		LM_ERR("Error closing source file.\n");
+		goto errout;
+	}
+
+	if (fclose(to)==EOF) {
+		LM_ERR("Error closing destination file.\n");
+		goto errout;
+	}
+	LM_NOTICE("backup written to %s\n", backup_file);
+	pkg_free(backup_file);
+	return 0;
+errout:
+	pkg_free(backup_file);
+	return -1;
+}
+
+
+/**
+ * Loads the routing data from the config file given in global
+ * variable config_data and stores it in routing tree rd.
+ *
+ * @param rd Pointer to the route data tree where the routing data
+ * shall be loaded into
+ *
+ * @return 0 means ok, -1 means an error occured
+ *
+ */
+int load_config(struct route_data_t * rd) {
+	cfg_t * cfg = NULL;
+	int m, o, i, j, k,l, status, hash_index, max_targets, strip;
+	cfg_t * d, * p, * t;
+	struct carrier_data_t * tmp_carrier_data;
+	int domain_id;
+	str domain, prefix, rewrite_prefix, rewrite_suffix, rewrite_host, comment;
+	double prob;
+	int * backed_up = NULL;
+	int backed_up_size, backup;
+	backed_up_size = backup = 0;
+
+	if ((cfg = parse_config()) == NULL) {
+		return -1;
+	}
+
+	rd->carrier_num = 1;
+	rd->first_empty_carrier = 0;
+	rd->domain_num = cfg_size(cfg, "domain");
+
+	if ((rd->carriers = shm_malloc(sizeof(struct carrier_data_t *))) == NULL) {
+		SHM_MEM_ERROR;
+		return -1;
+	}
+	memset(rd->carriers, 0, sizeof(struct carrier_data_t *));
+
+	/* Create carrier map */
+	if ((rd->carrier_map = shm_malloc(sizeof(struct name_map_t))) == NULL) {
+		SHM_MEM_ERROR;
+		return -1;
+	}
+	memset(rd->carrier_map, 0, sizeof(struct name_map_t));
+	rd->carrier_map[0].id = 1;
+	rd->carrier_map[0].name.len = default_tree.len;
+	rd->carrier_map[0].name.s = shm_malloc(rd->carrier_map[0].name.len);
+	if (rd->carrier_map[0].name.s == NULL) {
+		SHM_MEM_ERROR;
+		return -1;
+	}
+	memcpy(rd->carrier_map[0].name.s, default_tree.s, rd->carrier_map[0].name.len);
+
+	/* Create domain map */
+	if ((rd->domain_map = shm_malloc(sizeof(struct name_map_t) * rd->domain_num)) == NULL) {
+		SHM_MEM_ERROR;
+		return -1;
+	}
+	memset(rd->domain_map, 0, sizeof(struct name_map_t) * rd->domain_num);
+	for (i=0; i<rd->domain_num; i++) {
+		d = cfg_getnsec(cfg, "domain", i);
+		domain.s = (char *)cfg_title(d);
+		if (domain.s==NULL) domain.s="";
+		domain.len = strlen(domain.s);
+		rd->domain_map[i].id = i+1;
+		rd->domain_map[i].name.len = domain.len;
+		rd->domain_map[i].name.s = shm_malloc(rd->domain_map[i].name.len);
+		if (rd->domain_map[i].name.s == NULL) {
+			SHM_MEM_ERROR;
+			return -1;
+		}
+		memcpy(rd->domain_map[i].name.s, domain.s, rd->domain_map[i].name.len);
+	}
+	/* sort domain map by id for faster access */
+	qsort(rd->domain_map, rd->domain_num, sizeof(rd->domain_map[0]), compare_name_map);
+
+	/* Create and insert carrier data structure */
+	tmp_carrier_data = create_carrier_data(1, &rd->carrier_map[0].name, rd->domain_num);
+	if (tmp_carrier_data == NULL) {
+		LM_ERR("can't create new carrier\n");
+		return -1;
+	}
+	if (add_carrier_data(rd, tmp_carrier_data) < 0) {
+		LM_ERR("couldn't add carrier data\n");
+		destroy_carrier_data(tmp_carrier_data);
+		return -1;
+	}
+
+	/* add all routes */
+	for (i = 0; i < rd->domain_num; i++) {
+		d = cfg_getnsec(cfg, "domain", i);
+		domain.s = (char *)cfg_title(d);
+		if (domain.s==NULL) domain.s="";
+		domain.len = strlen(domain.s);
+		m = cfg_size(d, "prefix");
+
+		LM_INFO("loading domain %.*s\n", domain.len, domain.s);
+		for (j = 0; j < m; j++) {
+			p = cfg_getnsec(d, "prefix", j);
+			prefix.s = (char *)cfg_title(p);
+			if (prefix.s==NULL) prefix.s="";
+			prefix.len = strlen(prefix.s);
+			if (str_strcasecmp(&prefix, &CR_EMPTY_PREFIX) == 0) {
+				prefix.s = "";
+				prefix.len = 0;
+			}
+
+			LM_INFO("loading prefix %.*s\n", prefix.len, prefix.s);
+			max_targets = cfg_getint(p, "max_targets");
+			o = cfg_size(p, "target");
+			for (k = 0; k < o; k++) {
+				t = cfg_getnsec(p, "target", k);
+				rewrite_host.s = (char *)cfg_title(t);
+				if (rewrite_host.s==NULL) rewrite_host.s="";
+				rewrite_host.len = strlen(rewrite_host.s);
+				if (str_strcasecmp(&rewrite_host, &CR_EMPTY_PREFIX) == 0) {
+					rewrite_host.s = "";
+					rewrite_host.len = 0;
+				}
+
+				LM_INFO("loading target %.*s\n", rewrite_host.len, rewrite_host.s);
+				prob = cfg_getfloat(t, "prob");
+				strip = cfg_getint(t, "strip");
+				rewrite_prefix.s = (char *)cfg_getstr(t, "rewrite_prefix");
+				if (rewrite_prefix.s==NULL) rewrite_prefix.s="";
+				rewrite_prefix.len = strlen(rewrite_prefix.s);
+				rewrite_suffix.s = (char *)cfg_getstr(t, "rewrite_suffix");
+				if (rewrite_suffix.s==NULL) rewrite_suffix.s="";
+				rewrite_suffix.len = strlen(rewrite_suffix.s);
+				hash_index = cfg_getint(t, "hash_index");
+				comment.s = (char *)cfg_getstr(t, "comment");
+				if (comment.s==NULL) comment.s="";
+				comment.len = strlen(comment.s);
+				status = cfg_getint(t, "status");
+
+				if ((backed_up_size = cfg_size(t, "backed_up")) > 0) {
+					if ((backed_up = pkg_malloc(sizeof(int) * (backed_up_size + 1))) == NULL) {
+						PKG_MEM_ERROR;
+						return -1;
+					}
+					for (l = 0; l < backed_up_size; l++) {
+						backed_up[l] = cfg_getnint(t, "backed_up", l);
+					}
+					backed_up[backed_up_size] = -1;
+				}
+				backup = cfg_getint(t, "backup");
+
+				domain_id = map_name2id(rd->domain_map, rd->domain_num, &domain);
+				if (domain_id < 0) {
+					LM_ERR("cannot find id for domain '%.*s'", domain.len, domain.s);
+					if (backed_up) {
+						pkg_free(backed_up);
+					}
+					return -1;
+				}
+
+				LM_INFO("adding route for prefix %.*s, to host %.*s, prob %f, backed up: %i, backup: %i\n",
+				    prefix.len, prefix.s, rewrite_host.len, rewrite_host.s, prob, backed_up_size, backup);
+				if (add_route(rd, 1, domain_id, &prefix, 0, 0, max_targets, prob, &rewrite_host,
+				              strip, &rewrite_prefix, &rewrite_suffix, status,
+				              hash_index, backup, backed_up, &comment) < 0) {
+					LM_INFO("Error while adding route\n");
+					if (backed_up) {
+						pkg_free(backed_up);
+					}
+					return -1;
+				}
+				if (backed_up) {
+					pkg_free(backed_up);
+				}
+				backed_up = NULL;
+			}
+		}
+
+	}
+	cfg_free(cfg);
+	return 0;
+}
+
+
+/**
+ * Does the work for save_config, traverses the routing data tree
+ * and writes each rule to file.
+ *
+ * @param node the current prefix tree node
+ * @param outfile the filehandle to which the config data is written
+ *
+ * @return 0 on success, -1 on failure
+ */
+static int save_route_data_recursor(struct dtrie_node_t * node, FILE * outfile) {
+	int i;
+	struct route_flags *rf;
+	struct route_rule * rr;
+	struct route_rule_p_list * rl;
+	str *tmp_str;
+	str null_str = str_init("NULL");
+
+	/* no support for flag lists in route config */
+	rf = (struct route_flags *)(node->data);
+	if (rf && rf->rule_list) {
+		rr = rf->rule_list;
+		tmp_str = (rr->prefix.len ? &rr->prefix : &null_str);
+		fprintf(outfile, "\tprefix %.*s {\n", tmp_str->len, tmp_str->s);
+		fprintf(outfile, "\t\tmax_targets = %i\n\n", rf->max_targets);
+		while (rr) {
+			tmp_str = (rr->host.len ? &rr->host : &null_str);
+			fprintf(outfile, "\t\ttarget %.*s {\n", tmp_str->len, tmp_str->s);
+			fprintf(outfile, "\t\t\tprob = %f\n", rr->orig_prob);
+			fprintf(outfile, "\t\t\thash_index = %i\n", rr->hash_index);
+			fprintf(outfile, "\t\t\tstatus = %i\n", rr->status);
+			if (rr->strip > 0) {
+				fprintf(outfile, "\t\t\tstrip = \"%i\"\n", rr->strip);
+			}
+			if (rr->local_prefix.len) {
+				fprintf(outfile, "\t\t\trewrite_prefix = \"%.*s\"\n", rr->local_prefix.len, rr->local_prefix.s);
+			}
+			if (rr->local_suffix.len) {
+				fprintf(outfile, "\t\t\trewrite_suffix: \"%.*s\"\n", rr->local_suffix.len, rr->local_suffix.s);
+			}
+			if (rr->backup) {
+				fprintf(outfile, "\t\t\tbackup = %i\n", rr->backup->hash_index);
+			}
+			if (rr->backed_up) {
+				rl = rr->backed_up;
+				fprintf(outfile, "\t\t\tbacked_up = {");
+				i=0;
+				while (rl) {
+					if (i>0) {
+						fprintf(outfile, ", ");
+					}
+					fprintf(outfile, "%i", rl->hash_index);
+					rl = rl->next;
+					i++;
+				}
+				fprintf(outfile, "}\n");
+			}
+			if (rr->comment.len) {
+				fprintf(outfile, "\t\t\tcomment = \"%.*s\"\n", rr->comment.len, rr->comment.s);
+			}
+			fprintf(outfile, "\t\t}\n");
+			rr = rr->next;
+		}
+		fprintf(outfile, "\t}\n");
+	}
+	for (i = 0; i < cr_match_mode; i++) {
+		if (node->child[i]) {
+			if (save_route_data_recursor(node->child[i], outfile) < 0) {
+				return -1;
+			}
+		}
+	}
+	return 0;
+}
+
+
+/**
+ * Stores the routing data rd in config_file
+ *
+ * @param rd Pointer to the routing tree which shall be saved to file
+ *
+ * @return 0 means ok, -1 means an error occured
+ */
+int save_config(struct route_data_t * rd) {
+	FILE * outfile;
+	int i,j;
+
+	if(backup_config() < 0){
+		return -1;
+	}
+
+	if ((outfile = fopen(config_file, "w")) == NULL) {
+		LM_ERR("Could not open config file %s\n", config_file);
+		return -1;
+	}
+
+	i = 0;
+	if (rd->carrier_num>=1) {
+		for (j=0; j< rd->carriers[i]->domain_num; j++) {
+			fprintf(outfile, "domain %.*s {\n", rd->carriers[i]->domains[j]->name->len, rd->carriers[i]->domains[j]->name->s);
+			if (save_route_data_recursor(rd->carriers[i]->domains[j]->tree, outfile) < 0) {
+				goto errout;
+			}
+			fprintf(outfile, "}\n\n");
+		}
+	}
+	fclose(outfile);
+	return 0;
+errout:
+	fclose(outfile);
+	LM_ERR("Cannot save config file %s\n", config_file);
+	return -1;
+}

+ 59 - 0
modules/carrierroute/cr_config.h

@@ -0,0 +1,59 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_config.h
+ * \brief Functions for load and save routing data from a config file.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#ifndef CR_CONFIG_H
+#define CR_CONFIG_H
+
+#include "cr_data.h"
+
+
+/**
+ * Loads the routing data from the config file given in global
+ * variable config_data and stores it in routing tree rd.
+ *
+ * @param rd Pointer to the route data tree where the routing data
+ * shall be loaded into
+ *
+ * @return 0 means ok, -1 means an error occured
+ *
+ */
+int load_config(struct route_data_t * rd);
+
+
+/**
+ * Stores the routing data rd in config_file
+ *
+ * @param rd Pointer to the routing tree which shall be saved to file
+ *
+ * @return 0 means ok, -1 means an error occured
+ *
+ */
+int save_config(struct route_data_t * rd);
+
+#endif

+ 665 - 0
modules/carrierroute/cr_data.c

@@ -0,0 +1,665 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_data.c
+ * \brief Contains the functions to manage routing data.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#include <stdlib.h>
+#include "../../mem/shm_mem.h"
+#include "cr_data.h"
+#include "carrierroute.h"
+#include "cr_config.h"
+#include "cr_db.h"
+#include "cr_carrier.h"
+#include "cr_domain.h"
+#include "cr_rule.h"
+
+
+/**
+ * Pointer to the routing data.
+ */
+struct route_data_t ** global_data = NULL;
+
+
+static int carrier_data_fixup(struct route_data_t * rd){
+	int i;
+	str tmp;
+	tmp = default_tree;
+	rd->default_carrier_id = -1;
+	for(i=0; i<rd->carrier_num; i++){
+		if(rd->carriers[i]){
+			if(str_strcmp(rd->carriers[i]->name, &tmp) == 0){
+				rd->default_carrier_id = rd->carriers[i]->id;
+			}
+		}
+	}
+	if(rd->default_carrier_id < 0){
+		LM_ERR("default_carrier not found\n");
+	}
+	return 0;
+}
+
+
+/**
+ * initialises the routing data, initialises the global data pointer
+ *
+ * @return 0 on success, -1 on failure
+ */
+int init_route_data(void) {
+	if (global_data == NULL) {
+		global_data = (struct route_data_t **)
+		              shm_malloc(sizeof(struct route_data_t *));
+		if (global_data == NULL) {
+			SHM_MEM_ERROR;
+			return -1;
+		}
+	}
+	*global_data = NULL;
+	return 0;
+}
+
+
+/**
+ * Frees the routing data
+ */
+void destroy_route_data(void){
+	struct route_data_t * rd = get_data();
+	clear_route_data(rd);
+	if(global_data){
+		*global_data = NULL;
+		shm_free(global_data);
+		global_data = NULL;
+	}
+}
+
+
+/**
+ * Clears the complete routing data.
+ *
+ * @param data route data to be cleared
+ */
+void clear_route_data(struct route_data_t *data) {
+	int i;
+
+	if (data == NULL) {
+		return;
+	}
+	if (data->carriers != NULL) {
+		for (i = 0; i < data->carrier_num; ++i) {
+			if (data->carriers[i] != NULL) {
+				destroy_carrier_data(data->carriers[i]);
+			}
+		}
+		shm_free(data->carriers);
+	}
+	if (data->carrier_map) {
+		for (i = 0; i < data->carrier_num; ++i) {
+			if (data->carrier_map[i].name.s) shm_free(data->carrier_map[i].name.s);
+		}
+		shm_free(data->carrier_map);
+	}
+	if (data->domain_map) {
+		for (i = 0; i < data->domain_num; ++i) {
+			if (data->domain_map[i].name.s) shm_free(data->domain_map[i].name.s);
+		}
+		shm_free(data->domain_map);
+	}
+	shm_free(data);
+	return;
+}
+
+
+/**
+ * adds a carrier_data struct for given carrier.
+ *
+ * @param rd route data to be searched
+ * @param carrier_data the carrier data struct to be inserted
+ *
+ * @return 0 on success, -1 on failure
+ */
+int add_carrier_data(struct route_data_t * rd, struct carrier_data_t * carrier_data) {
+	if (rd->first_empty_carrier >= rd->carrier_num) {
+		LM_ERR("carrier array already full");
+		return -1;
+	}
+
+	if (rd->carriers[rd->first_empty_carrier] != 0) {
+		LM_ERR("invalid pointer in first empty carrier entry");
+		return -1;
+	}
+
+	rd->carriers[rd->first_empty_carrier] = carrier_data;
+	rd->first_empty_carrier++;
+	return 0;
+}
+
+
+/**
+ * Loads the routing data into the routing trees and sets the
+ * global_data pointer to the new data. The old_data is removed
+ * when it is not locked anymore.
+ *
+ * @return 0 on success, -1 on failure
+ */
+int reload_route_data(void) {
+	struct route_data_t * old_data;
+	struct route_data_t * new_data = NULL;
+	int i;
+
+	if ((new_data = shm_malloc(sizeof(struct route_data_t))) == NULL) {
+		SHM_MEM_ERROR;
+		return -1;
+	}
+	memset(new_data, 0, sizeof(struct route_data_t));
+
+	switch (mode) {
+	case CARRIERROUTE_MODE_DB:
+		if (load_route_data_db(new_data) < 0) {
+			LM_ERR("could not load routing data\n");
+			goto errout;
+		}
+		break;
+	case CARRIERROUTE_MODE_FILE:
+		if (load_config(new_data) < 0) {
+			LM_ERR("could not load routing data\n");
+			goto errout;
+		}
+		break;
+	default:
+		LM_ERR("invalid mode");
+		goto errout;
+	}
+	if (new_data == NULL) {
+		LM_ERR("loading routing data failed (NULL pointer)");
+		goto errout;
+	}
+
+	/* sort carriers by id for faster access */
+	qsort(new_data->carriers, new_data->carrier_num, sizeof(new_data->carriers[0]), compare_carrier_data);
+
+	/* sort domains by id for faster access */
+	for (i=0; i<new_data->carrier_num; i++) {
+		qsort(new_data->carriers[i]->domains, new_data->carriers[i]->domain_num, sizeof(new_data->carriers[i]->domains[0]), compare_domain_data);
+	}
+
+	if (rule_fixup(new_data) < 0) {
+		LM_ERR("could not fixup rules\n");
+		goto errout;
+	}
+
+	if (carrier_data_fixup(new_data) < 0){
+		LM_ERR("could not fixup trees\n");
+		goto errout;
+	}
+
+	new_data->proc_cnt = 0;
+
+	if (*global_data == NULL) {
+		*global_data = new_data;
+	} else {
+		old_data = *global_data;
+		*global_data = new_data;
+		i = 0;
+		while (old_data->proc_cnt > 0) {
+			LM_ERR("data is still locked after %i seconds\n", i);
+			sleep_us(i*1000000);
+			i++;
+		}
+		clear_route_data(old_data);
+	}
+	return 0;
+
+ errout:
+	clear_route_data(new_data);
+	return -1;
+}
+
+
+/**
+ * Increases lock counter and returns a pointer to the
+ * current routing data
+ *
+ * @return pointer to the global routing data on success,
+ * NULL on failure
+*/
+struct route_data_t * get_data(void) {
+	struct route_data_t *ret;
+	if (!global_data || !*global_data) {
+		return NULL;
+	}
+	ret = *global_data;
+	lock_get(&ret->lock);
+	++ret->proc_cnt;
+	lock_release(&ret->lock);
+	if (ret == *global_data) {
+		return ret;
+	} else {
+		lock_get(&ret->lock);
+		--ret->proc_cnt;
+		lock_release(&ret->lock);
+		return NULL;
+	}
+}
+
+
+/**
+ * decrements the lock counter of the routing data
+ *
+ * @param data data to be released
+ */
+void release_data(struct route_data_t *data) {
+	lock_get(&data->lock);
+	--data->proc_cnt;
+	lock_release(&data->lock);
+}
+
+
+/**
+ * Returns the carrier data for the given id by doing a binary search.
+ * @note The carrier array must be sorted!
+ *
+ * @param rd route data to be searched
+ * @param carrier_id the id of the desired carrier
+ *
+ * @return a pointer to the desired carrier data, NULL if not found.
+ */
+struct carrier_data_t *get_carrier_data(struct route_data_t * rd, int carrier_id) {
+	struct carrier_data_t **ret;
+	struct carrier_data_t key;
+	struct carrier_data_t *pkey = &key;
+
+	if (!rd) {
+		LM_ERR("NULL pointer in parameter\n");
+		return NULL;
+	}
+	key.id = carrier_id;
+	ret = bsearch(&pkey, rd->carriers, rd->carrier_num, sizeof(rd->carriers[0]), compare_carrier_data);
+	if (ret) return *ret;
+	return NULL;
+}
+
+
+typedef int (*cmpfunc_t)(const void *v1, const void *v2);
+
+
+/**
+ * Implements a binary search algorithm using the function cmpfunc
+ * for comparison.
+ *
+ * @param base pointer to the beginning of the array
+ * @param len length of array
+ * @param elemsize size of array elements
+ * @param key pointer to the key we are looking for
+ * @param cmpfunc function to be used for comparison
+ * @param index  If index is not NULL it is set to:
+ *     -1 if an error occured,
+ *     the index of the first entry equal to v
+ *     or the index of the first entry greater than v in the case v was not found.
+ *   Be careful: The index returned can be greater than the length of the array!
+ *
+ * @return -1 on error, 0 if the value was not found, 1 if it was found.
+ */
+static int binary_search(void *base, unsigned int len, int elemsize, void *key, cmpfunc_t cmpfunc, int *index) {
+	int left, right, mid;
+
+	if (index) *index=-1;
+	if (!base) {
+		LM_ERR("NULL pointer in parameter\n");
+		return -1;
+	}
+	if (len == 0) {
+		if (index) *index=0;
+		return 0;
+	}
+
+	left=0;
+	right=len-1;
+	if (cmpfunc(base+elemsize*left, key) > 0) {
+		LM_DBG("not found (out of left bound)\n");
+		if (index) *index=0; /* not found, must be inserted at the beginning of array */
+		return 0;
+	}
+	if (cmpfunc(base+elemsize*right, key) < 0) {
+		LM_DBG("not found (out of right bound)\n");
+		if (index) *index=len; /* not found, must be inserted at the end of array */
+		return 0;
+	}
+
+	while (left < right) {
+		mid = left + ((right - left) / 2);
+		if (cmpfunc(base+elemsize*mid, key) < 0) left = mid + 1;
+		else right = mid;
+	}
+
+	/* left == right here! */
+	if (index) *index=left;
+	if (cmpfunc(base+elemsize*left, key) == 0) return 1;
+	else return 0;
+}
+
+
+/**
+ * Returns the domain data for the given id by doing a binary search.
+ * If not found, a new domain data structure is added.
+ *
+ * @param rd route data to used for name - id mapping
+ * @param carrier_data carrier data to be searched
+ * @param domain_id the id of desired domain
+ *
+ * @return a pointer to the desired domain data, NULL on error.
+ */
+static struct domain_data_t * get_domain_data_or_add(struct route_data_t * rd, struct carrier_data_t * carrier_data, int domain_id) {
+	struct domain_data_t key;
+	struct domain_data_t *pkey = &key;
+	struct domain_data_t *domain_data = NULL;
+	str *domain_name;
+	int i;
+	int res;
+
+	if ((!rd) || (!carrier_data)) {
+		LM_ERR("NULL pointer in parameter\n");
+		return NULL;
+	}
+
+	key.id = domain_id;
+	res = binary_search(carrier_data->domains, carrier_data->first_empty_domain, sizeof(struct domain_data_t *), &pkey, compare_domain_data, &i);
+	if (res<0) {
+		LM_ERR("error while searching for domain_id %d\n", domain_id);
+		return NULL;
+	}
+	else if (res>0) {
+		/* found domain id */
+		domain_data = carrier_data->domains[i];
+	}
+	else {
+		/* did not find domain id - insert new entry! */
+		if ((domain_name = map_id2name(rd->domain_map, rd->domain_num, domain_id)) == NULL) {
+			LM_ERR("could not find domain name for id %d\n", domain_id);
+			return NULL;
+		}
+		if ((domain_data = create_domain_data(domain_id, domain_name)) == NULL) {
+			LM_ERR("could not create new domain data\n");
+			return NULL;
+		}
+
+		/* keep the array sorted! */
+		if (add_domain_data(carrier_data, domain_data, i) < 0) {
+			LM_ERR("could not add domain data\n");
+			destroy_domain_data(domain_data);
+			return NULL;
+		}
+		LM_INFO("added domain %d '%.*s' to carrier %d '%.*s'", domain_id, domain_name->len, domain_name->s, carrier_data->id, carrier_data->name->len, carrier_data->name->s);
+	}
+
+	return domain_data;
+}
+
+
+/**
+ * Adds the given route information to the routing domain identified by
+ * domain. scan_prefix identifies the number for which the information
+ * is and the rewrite_* parameters define what to do in case of a match.
+ * prob gives the probability with which this rule applies if there are
+ * more than one for a given prefix.
+ *
+ * @param rd the route data to which the route shall be added
+ * @param carrier_id the carrier id of the route to be added
+ * @param domain_id the routing domain id of the new route
+ * @param scan_prefix the number prefix
+ * @param flags user defined flags
+ * @param mask mask for user defined flags
+ * @param max_targets the number of targets
+ * @param prob the weight of the rule
+ * @param rewrite_hostpart the rewrite_host of the rule
+ * @param strip the number of digits to be stripped off userpart before prepending prefix
+ * @param rewrite_local_prefix the rewrite prefix
+ * @param rewrite_local_suffix the rewrite suffix
+ * @param status the status of the rule
+ * @param hash_index the hash index of the rule
+ * @param backup indicates if the route is backed up by another. only 
+                 useful if status==0, if set, it is the hash value
+                 of another rule
+ * @param backed_up an -1-termintated array of hash indices of the route 
+                    for which this route is backup
+ * @param comment a comment for the route rule
+ *
+ * @return 0 on success, -1 on error in which case it LOGs a message.
+ */
+int add_route(struct route_data_t * rd, int carrier_id,
+		int domain_id, const str * scan_prefix, flag_t flags, flag_t mask, int max_targets,
+		double prob, const str * rewrite_hostpart, int strip,
+		const str * rewrite_local_prefix, const str * rewrite_local_suffix,
+		int status, int hash_index, int backup, int * backed_up, const str * comment) {
+	struct carrier_data_t * carrier_data = NULL;
+	struct domain_data_t * domain_data = NULL;
+	LM_INFO("adding prefix %.*s, prob %f\n", scan_prefix->len, scan_prefix->s, prob);
+
+	if ((carrier_data = get_carrier_data(rd, carrier_id)) == NULL) {
+		LM_ERR("could not retrieve carrier data for carrier id %d\n", carrier_id);
+		return -1;
+	}
+
+	if ((domain_data = get_domain_data_or_add(rd, carrier_data, domain_id)) == NULL) {
+		LM_ERR("could not retrieve domain data\n");
+		return -1;
+	}
+
+	LM_INFO("found carrier and domain, now adding route\n");
+	return add_route_to_tree(domain_data->tree, scan_prefix, flags, mask, scan_prefix, max_targets, prob, rewrite_hostpart,
+	                         strip, rewrite_local_prefix, rewrite_local_suffix, status,
+	                         hash_index, backup, backed_up, comment);
+}
+
+
+/**
+ * Adds the given failure route information to the failure routing domain identified by
+ * domain. scan_prefix, host, reply_code and flags identifies the number for which
+ * the information is and the next_domain parameter defines where to continue routing
+ * in case of a match.
+ *
+ * @param rd the route data to which the route shall be added
+ * @param carrier_id the carrier id of the route to be added
+ * @param domain_id the routing domain id of the new route
+ * @param scan_prefix the number prefix
+ * @param host the hostname last tried
+ * @param reply_code the reply code 
+ * @param flags user defined flags
+ * @param mask for user defined flags
+ * @param next_domain_id continue routing with this domain id
+ * @param comment a comment for the failure route rule
+ *
+ * @return 0 on success, -1 on error in which case it LOGs a message.
+ */
+int add_failure_route(struct route_data_t * rd, int carrier_id, int domain_id,
+		const str * scan_prefix, const str * host, const str * reply_code,
+		flag_t flags, flag_t mask, int next_domain_id, const str * comment) {
+	struct carrier_data_t * carrier_data = NULL;
+	struct domain_data_t * domain_data = NULL;
+	LM_INFO("adding prefix %.*s, reply code %.*s\n", scan_prefix->len, scan_prefix->s, reply_code->len, reply_code->s);
+		
+	if (reply_code->len!=3) {
+		LM_ERR("invalid reply_code '%.*s'!\n", reply_code->len, reply_code->s);
+		return -1;
+	}
+	
+	if ((carrier_data = get_carrier_data(rd, carrier_id)) == NULL) {
+		LM_ERR("could not retrieve carrier data\n");
+		return -1;
+	}
+	
+	if ((domain_data = get_domain_data_or_add(rd, carrier_data, domain_id)) == NULL) {
+		LM_ERR("could not retrieve domain data\n");
+		return -1;
+	}
+
+	LM_INFO("found carrier and domain, now adding failure route\n");
+	return add_failure_route_to_tree(domain_data->failure_tree, scan_prefix, scan_prefix, host, reply_code,
+			flags, mask, next_domain_id, comment);
+}
+
+
+static int fixup_rule_backup(struct route_flags * rf, struct route_rule * rr){
+	struct route_rule_p_list * rl;
+	if(!rr->status && rr->backup){
+		if((rr->backup->rr = find_rule_by_hash(rf, rr->backup->hash_index)) == NULL){
+			LM_ERR("didn't find backup route\n");
+			return -1;
+		}
+	}
+	rl = rr->backed_up;
+	while(rl){
+		if((rl->rr = find_rule_by_hash(rf, rl->hash_index)) == NULL){
+			LM_ERR("didn't find backed up route\n");
+			return -1;
+		}
+		rl = rl->next;
+	}
+	return 0;
+}
+
+
+/**
+ * Does the work for rule_fixup recursively.
+ * First, it tries to set a pointer the rules with an existing hash index
+ * at the marching array index. Afterward, remaining rules are populated
+ * with incrementing hash indices.
+ *
+ * @param node the prefix tree node to be fixed up
+ *
+ * @return 0 on success, -1 on failure
+ */
+static int rule_fixup_recursor(struct dtrie_node_t *node) {
+	struct route_rule * rr;
+	struct route_flags * rf;
+	int i, p_dice, ret = 0;
+
+	for (rf=(struct route_flags *)(node->data); rf!=NULL; rf=rf->next) {
+		p_dice = 0;
+		if (rf->rule_list) {
+			rr = rf->rule_list;
+			rf->rule_num = 0;
+			while (rr) {
+				rf->rule_num++;
+				rf->dice_max += rr->prob * DICE_MAX;
+				rr = rr->next;
+			}
+			rr = rf->rule_list;
+			while (rr) {
+				rr->dice_to = (rr->prob * DICE_MAX) + p_dice;
+				p_dice = rr->dice_to;
+				rr = rr->next;
+			}
+			
+			if (rf->rule_num != rf->max_targets) {
+				LM_ERR("number of rules(%i) differs from max_targets(%i), maybe your config is wrong?\n", rf->rule_num, rf->max_targets);
+				return -1;
+			}
+			if(rf->rules) {
+				shm_free(rf->rules);
+				rf->rules = NULL;
+			}
+			if ((rf->rules = shm_malloc(sizeof(struct route_rule *) * rf->rule_num)) == NULL) {
+				SHM_MEM_ERROR;
+				return -1;
+			}
+			memset(rf->rules, 0, sizeof(struct route_rule *) * rf->rule_num);
+			for (rr = rf->rule_list; rr; rr = rr->next) {
+				if (rr->hash_index) {
+					if (rr->hash_index > rf->rule_num) {
+						LM_ERR("too large hash index %i, max is %i\n", rr->hash_index, rf->rule_num);
+						shm_free(rf->rules);
+						return -1;
+					}
+					if (rf->rules[rr->hash_index - 1]) {
+						LM_ERR("duplicate hash index %i\n", rr->hash_index);
+						shm_free(rf->rules);
+						return -1;
+					}
+					rf->rules[rr->hash_index - 1] = rr;
+					LM_INFO("rule with host %.*s hash has hashindex %i.\n", rr->host.len, rr->host.s, rr->hash_index);
+				}
+			}
+			
+			rr = rf->rule_list;
+			i=0;
+			while (rr && i < rf->rule_num) {
+				if (!rr->hash_index) {
+					if (rf->rules[i]) {
+						i++;
+					} else {
+						rf->rules[i] = rr;
+						rr->hash_index = i + 1;
+						LM_INFO("hashless rule with host %.*s hash, hash_index %i\n", rr->host.len, rr->host.s, i+1);
+						rr = rr->next;
+					}
+				} else {
+					rr = rr->next;
+				}
+			}
+			if (rr) {
+				LM_ERR("Could not populate rules: rr: %p\n", rr);
+				return -1;
+			}
+			for(i=0; i<rf->rule_num; i++){
+				ret += fixup_rule_backup(rf, rf->rules[i]);
+			}
+		}
+	}
+
+	for (i=0; i<cr_match_mode; i++) {
+		if (node->child[i]) {
+			ret += rule_fixup_recursor(node->child[i]);
+		}
+	}
+
+	return ret;
+}
+
+
+/**
+ * Fixes the route rules by creating an array for accessing
+ * route rules by hash index directly
+ *
+ * @param rd route data to be fixed
+ *
+ * @return 0 on success, -1 on failure
+ */
+int rule_fixup(struct route_data_t * rd) {
+	int i,j;
+	for (i=0; i<rd->carrier_num; i++) {
+		for (j=0; j<rd->carriers[i]->domain_num; j++) {
+			if (rd->carriers[i]->domains[j] && rd->carriers[i]->domains[j]->tree) {
+				LM_INFO("fixing tree %.*s\n", rd->carriers[i]->domains[j]->name->len, rd->carriers[i]->domains[j]->name->s);
+				if (rule_fixup_recursor(rd->carriers[i]->domains[j]->tree) < 0) {
+					return -1;
+				}
+			} else {
+				LM_NOTICE("empty tree at [%i][%i]\n", i, j);
+			}
+		}
+	}
+	return 0;
+}

+ 199 - 0
modules/carrierroute/cr_data.h

@@ -0,0 +1,199 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_data.h
+ * \brief Contains the functions to manage routing data.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#ifndef CR_DATA_H
+#define CR_DATA_H
+
+#include <sys/types.h>
+#include "../../locking.h"
+#include "../../flags.h"
+#include "cr_map.h"
+
+
+/**
+ * contains all routing data.
+ */
+struct route_data_t {
+	struct name_map_t * carrier_map; /*!< holds the map between carrier names and numbers */
+	struct name_map_t * domain_map; /*!< holds the map between domain names and numbers */
+	struct carrier_data_t ** carriers; /*!< array of carriers */
+	size_t carrier_num; /*!< number of carriers */
+	size_t first_empty_carrier; /*!< the index of the first empty entry in carriers */
+	size_t domain_num; /*!< total number of different domains */
+	int default_carrier_id;
+	int proc_cnt; /*!< a ref counter for the shm data */
+	gen_lock_t lock; /*!< lock for ref counter updates */
+};
+
+/**
+ * initialises the routing data, initialises the global data pointer
+ *
+ * @return 0 on success, -1 on failure
+ */
+int init_route_data(void);
+
+
+/**
+ * Frees the routing data
+ */
+void destroy_route_data(void);
+
+
+/**
+ * Clears the complete routing data.
+ *
+ * @param data route data to be cleared
+ */
+void clear_route_data(struct route_data_t *data);
+
+
+/**
+ * adds a carrier_data struct for given carrier
+ *
+ * @param rd route data to be searched
+ * @param carrier_data the carrier data struct to be inserted
+ *
+ * @return 0 on success, -1 on failure
+ */
+int add_carrier_data(struct route_data_t * rd, struct carrier_data_t * carrier_data);
+
+
+/**
+ * Loads the routing data into the routing trees and sets the
+ * global_data pointer to the new data. The old_data is removed
+ * when it is not locked anymore.
+ *
+ * @return 0 on success, -1 on failure
+ */
+int reload_route_data(void);
+
+
+/**
+ * Increases lock counter and returns a pointer to the
+ * current routing data
+ *
+ * @return pointer to the global routing data on success,
+ * NULL on failure
+*/
+struct route_data_t * get_data(void);
+
+
+/**
+ * decrements the lock counter of the routing data
+ *
+ * @param data data to be released
+ */
+void release_data(struct route_data_t *data);
+
+
+/**
+ * Returns the carrier data for the given id by doing a binary search.
+ * @note The carrier array must be sorted!
+ *
+ * @param rd route data to be searched
+ * @param carrier_id the id of the desired carrier
+ *
+ * @return a pointer to the desired carrier data, NULL if not found.
+ */
+struct carrier_data_t *get_carrier_data(struct route_data_t * rd, int carrier_id);
+
+
+/**
+ * Adds the given route information to the routing domain identified by
+ * domain. scan_prefix identifies the number for which the information
+ * is and the rewrite_* parameters define what to do in case of a match.
+ * prob gives the probability with which this rule applies if there are
+ * more than one for a given prefix.
+ *
+ * @param rd the route data to which the route shall be added
+ * @param carrier_id the carrier id of the route to be added
+ * @param domain_id the routing domain id of the new route
+ * @param scan_prefix the number prefix
+ * @param flags user defined flags
+ * @param mask mask for user defined flags
+ * @param max_targets the number of targets
+ * @param prob the weight of the rule
+ * @param strip the number of digits to be stripped off userpart before prepending prefix
+ * @param rewrite_hostpart the rewrite_host of the rule
+ * @param rewrite_local_prefix the rewrite prefix
+ * @param rewrite_local_suffix the rewrite suffix
+ * @param status the status of the rule
+ * @param hash_index the hash index of the rule
+ * @param backup indicates if the route is backed up by another. only
+                 useful if status==0, if set, it is the hash value
+                 of another rule
+  * @param backed_up an -1-termintated array of hash indices of the route
+                    for which this route is backup
+ * @param comment a comment for the route rule
+ *
+ * @return 0 on success, -1 on error in which case it LOGs a message.
+ */
+int add_route(struct route_data_t * rd, int carrier_id,
+		int domain_id, const str * scan_prefix, flag_t flags, flag_t mask, int max_targets,
+		double prob, const str * rewrite_hostpart, int strip, const str * rewrite_local_prefix,
+		const str * rewrite_local_suffix, int status, int hash_index, int backup, int * backed_up,
+		const str * comment);
+
+
+/**
+ * Adds the given failure route information to the failure routing domain identified by
+ * domain. scan_prefix, host, reply_code and flags identifies the number for which
+ * the information is and the next_domain parameter defines where to continue routing
+ * in case of a match.
+ *
+ * @param rd the route data to which the route shall be added
+ * @param carrier_id the carrier id of the route to be added
+ * @param domain_id the routing domain id of the new route
+ * @param scan_prefix the number prefix
+ * @param host the hostname last tried
+ * @param reply_code the reply code 
+ * @param flags user defined flags
+ * @param mask for user defined flags
+ * @param next_domain_id continue routing with this domain id
+ * @param comment a comment for the failure route rule
+ *
+ * @return 0 on success, -1 on error in which case it LOGs a message.
+ */
+int add_failure_route(struct route_data_t * rd, int carrier_id, int domain_id,
+		const str * scan_prefix, const str * host, const str * reply_code,
+		flag_t flags, flag_t mask, int next_domain_id, const str * comment);
+
+
+/**
+ * Fixes the route rules by creating an array for accessing
+ * route rules by hash index directly
+ *
+ * @param rd route data to be fixed
+ *
+ * @return 0 on success, -1 on failure
+ */
+int rule_fixup(struct route_data_t * rd);
+
+
+#endif

+ 441 - 0
modules/carrierroute/cr_db.c

@@ -0,0 +1,441 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_db.c
+ * \brief Functions for loading routing data from a database.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include "carrierroute.h"
+#include "cr_db.h"
+#include "cr_carrier.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#define QUERY_LEN 2048
+
+static char query[QUERY_LEN];
+
+str * columns[COLUMN_NUM] = { &carrierroute_id_col, &carrierroute_carrier_col,
+	&carrierroute_domain_col,
+	&carrierroute_scan_prefix_col,
+	&carrierroute_flags_col,
+	&carrierroute_mask_col,
+	&carrierroute_prob_col,
+	&carrierroute_rewrite_host_col,
+	&carrierroute_strip_col,
+	&carrierroute_rewrite_prefix_col,
+	&carrierroute_rewrite_suffix_col,
+	&carrierroute_description_col
+};
+
+str * carrier_name_columns[CARRIER_NAME_COLUMN_NUM] = {
+	&carrier_name_id_col,
+	&carrier_name_carrier_col
+};
+
+str * domain_name_columns[DOMAIN_NAME_COLUMN_NUM] = {
+	&domain_name_id_col,
+	&domain_name_domain_col
+};
+
+str * failure_columns[FAILURE_COLUMN_NUM] = {
+	&carrierfailureroute_id_col,
+	&carrierfailureroute_carrier_col,
+	&carrierfailureroute_domain_col,
+	&carrierfailureroute_scan_prefix_col,
+	&carrierfailureroute_host_name_col,
+	&carrierfailureroute_reply_code_col,
+	&carrierfailureroute_flags_col,
+	&carrierfailureroute_mask_col,
+	&carrierfailureroute_next_domain_col,
+	&carrierfailureroute_description_col
+};
+
+
+static int load_carrier_map(struct route_data_t *rd) {
+	db_res_t * res = NULL;
+	int i, count;
+	if(!rd){
+		LM_ERR("invalid parameter\n");
+		return -1;
+	}
+	if (carrierroute_dbf.use_table(carrierroute_dbh, &carrier_name_table) < 0) {
+		LM_ERR("couldn't use table\n");
+		return -1;
+	}
+
+	if (carrierroute_dbf.query(carrierroute_dbh, 0, 0, 0, (db_key_t *)carrier_name_columns, 0, CARRIER_NAME_COLUMN_NUM, 0, &res) < 0) {
+		LM_ERR("couldn't query table\n");
+		return -1;
+	}
+
+	count = RES_ROW_N(res);
+	if (count == 0) {
+		LM_ERR("empty %.*s table", carrier_name_table.len, carrier_name_table.s);
+		carrierroute_dbf.free_result(carrierroute_dbh, res);
+		return 0;
+	}
+
+	rd->carrier_map = shm_malloc(sizeof(struct name_map_t) * count);
+	if (rd->carrier_map == NULL) {
+		SHM_MEM_ERROR;
+		carrierroute_dbf.free_result(carrierroute_dbh, res);
+		return -1;
+	}
+	memset(rd->carrier_map, 0, sizeof(struct name_map_t) * count);
+
+	for (i=0; i<count; i++) {
+		rd->carrier_map[i].id = res->rows[i].values[CARRIER_NAME_ID_COL].val.int_val;
+		rd->carrier_map[i].name.len = strlen(res->rows[i].values[CARRIER_NAME_NAME_COL].val.string_val);
+		rd->carrier_map[i].name.s = shm_malloc(rd->carrier_map[i].name.len);
+		if (rd->carrier_map[i].name.s == NULL) {
+			SHM_MEM_ERROR;
+			carrierroute_dbf.free_result(carrierroute_dbh, res);
+			shm_free(rd->carrier_map);
+			rd->carrier_map = NULL;
+			return -1;
+		}
+		memcpy(rd->carrier_map[i].name.s, res->rows[i].values[CARRIER_NAME_NAME_COL].val.string_val, rd->carrier_map[i].name.len);
+	}
+
+	/* sort carrier map by id for faster access */
+	qsort(rd->carrier_map, count, sizeof(rd->carrier_map[0]), compare_name_map);
+
+	carrierroute_dbf.free_result(carrierroute_dbh, res);
+	return count;
+}
+
+
+
+
+static int load_domain_map(struct route_data_t *rd) {
+	db_res_t * res = NULL;
+	int i, count;
+	if(!rd){
+		LM_ERR("invalid parameter\n");
+		return -1;
+	}
+	if (carrierroute_dbf.use_table(carrierroute_dbh, &domain_name_table) < 0) {
+		LM_ERR("couldn't use table\n");
+		return -1;
+	}
+
+	if (carrierroute_dbf.query(carrierroute_dbh, 0, 0, 0, (db_key_t *)domain_name_columns, 0, DOMAIN_NAME_COLUMN_NUM, 0, &res) < 0) {
+		LM_ERR("couldn't query table\n");
+		return -1;
+	}
+
+	count = RES_ROW_N(res);
+	if (count == 0) {
+		LM_ERR("empty %.*s table", domain_name_table.len, domain_name_table.s);
+		carrierroute_dbf.free_result(carrierroute_dbh, res);
+		return 0;
+	}
+
+	rd->domain_map = shm_malloc(sizeof(struct name_map_t) * count);
+	if (rd->domain_map == NULL) {
+		SHM_MEM_ERROR;
+		carrierroute_dbf.free_result(carrierroute_dbh, res);
+		return -1;
+	}
+	memset(rd->domain_map, 0, sizeof(struct name_map_t) * count);
+
+	for (i=0; i<count; i++) {
+		rd->domain_map[i].id = res->rows[i].values[DOMAIN_NAME_ID_COL].val.int_val;
+		rd->domain_map[i].name.len = strlen(res->rows[i].values[DOMAIN_NAME_NAME_COL].val.string_val);
+		rd->domain_map[i].name.s = shm_malloc(rd->domain_map[i].name.len);
+		if (rd->domain_map[i].name.s == NULL) {
+			SHM_MEM_ERROR;
+			carrierroute_dbf.free_result(carrierroute_dbh, res);
+			shm_free(rd->domain_map);
+			rd->domain_map = NULL;
+			return -1;
+		}
+		memcpy(rd->domain_map[i].name.s, res->rows[i].values[DOMAIN_NAME_NAME_COL].val.string_val, rd->domain_map[i].name.len);
+	}
+
+	/* sort domain map by id for faster access */
+	qsort(rd->domain_map, count, sizeof(rd->domain_map[0]), compare_name_map);
+
+	carrierroute_dbf.free_result(carrierroute_dbh, res);
+	return count;
+}
+
+
+
+
+int load_user_carrier(str * user, str * domain) {
+	db_res_t * res;
+	db_key_t cols[1];
+	db_key_t keys[2];
+	db_val_t vals[2];
+	db_op_t op[2];
+	int id;
+	if (!user || (use_domain && !domain)) {
+		LM_ERR("NULL pointer in parameter\n");
+		return -1;
+	}
+
+	cols[0] = subscriber_columns[SUBSCRIBER_CARRIER_COL];
+
+	keys[0] = subscriber_columns[SUBSCRIBER_USERNAME_COL];
+	op[0] = OP_EQ;
+	VAL_TYPE(vals) = DB_STR;
+	VAL_NULL(vals) = 0;
+	VAL_STR(vals) = *user;
+
+	keys[1] = subscriber_columns[SUBSCRIBER_DOMAIN_COL];
+	op[1] = OP_EQ;
+	VAL_TYPE(vals+1) = DB_STR;
+	VAL_NULL(vals+1) = 0;
+	VAL_STR(vals+1) = *domain;
+
+	if (carrierroute_dbf.use_table(carrierroute_dbh, &subscriber_table) < 0) {
+		LM_ERR("can't use table\n");
+		return -1;
+	}
+
+	if (carrierroute_dbf.query(carrierroute_dbh, keys, op, vals, cols, use_domain ? 2 : 1, 1, NULL, &res) < 0) {
+		LM_ERR("can't query database\n");
+		return -1;
+	}
+
+	if (RES_ROW_N(res) == 0) {
+		carrierroute_dbf.free_result(carrierroute_dbh, res);
+		return 0;
+	}
+
+	if (VAL_NULL(ROW_VALUES(RES_ROWS(res)))) {
+		carrierroute_dbf.free_result(carrierroute_dbh, res);
+		return 0;
+	}
+
+	id = VAL_INT(ROW_VALUES(RES_ROWS(res)));
+	carrierroute_dbf.free_result(carrierroute_dbh, res);
+	return id;
+}
+
+
+
+
+/**
+ * Loads the routing data from the database given in global
+ * variable db_url and stores it in routing tree rd.
+ *
+ * @param rd Pointer to the route data tree where the routing data
+ * shall be loaded into
+ *
+ * @return 0 means ok, -1 means an error occured
+ *
+ */
+int load_route_data_db(struct route_data_t * rd) {
+	db_res_t * res = NULL;
+	db_row_t * row = NULL;
+	int i, ret;
+	struct carrier_data_t * tmp_carrier_data;
+	static str query_str;
+	str tmp_scan_prefix, tmp_rewrite_host, tmp_rewrite_prefix,
+		tmp_rewrite_suffix, tmp_host_name, tmp_reply_code, tmp_comment;
+
+	if( (strlen("SELECT DISTINCT  FROM  WHERE = ")
+			+ carrierroute_table.len + columns[COL_DOMAIN]->len
+			+ columns[COL_CARRIER]->len + 20) >  QUERY_LEN) {
+		LM_ERR("query too long\n");
+		return -1;
+	}
+
+	if((rd->carrier_num = load_carrier_map(rd)) <= 0){
+		LM_ERR("error while retrieving carriers\n");
+		goto errout;
+	}
+
+	if((rd->domain_num = load_domain_map(rd)) <= 0){
+		LM_ERR("error while retrieving domains\n");
+		goto errout;
+	}
+
+	if ((rd->carriers = shm_malloc(sizeof(struct carrier_data_t *) * rd->carrier_num)) == NULL) {
+		SHM_MEM_ERROR;
+		goto errout;
+	}
+	memset(rd->carriers, 0, sizeof(struct carrier_data_t *) * rd->carrier_num);
+
+	for (i=0; i<rd->carrier_num; i++) {
+		memset(query, 0, QUERY_LEN);
+		ret = snprintf(query, QUERY_LEN, "SELECT DISTINCT %.*s FROM %.*s WHERE %.*s=%i",
+		columns[COL_DOMAIN]->len, columns[COL_DOMAIN]->s, carrierroute_table.len,
+		carrierroute_table.s, columns[COL_CARRIER]->len, columns[COL_CARRIER]->s, rd->carrier_map[i].id);
+		if (ret < 0) {
+			LM_ERR("error in snprintf");
+			goto errout;
+		}
+		query_str.s = query;
+		query_str.len = ret;
+
+		if (carrierroute_dbf.raw_query(carrierroute_dbh, &query_str, &res) < 0) {
+			LM_ERR("Failed to query database.\n");
+			goto errout;
+		}
+		LM_INFO("carrier '%.*s' (id %i) has %i domains\n", rd->carrier_map[i].name.len, rd->carrier_map[i].name.s, rd->carrier_map[i].id, RES_ROW_N(res));
+		tmp_carrier_data = create_carrier_data(rd->carrier_map[i].id, &rd->carrier_map[i].name, RES_ROW_N(res));
+		if (tmp_carrier_data == NULL) {
+			LM_ERR("can't create new carrier '%.*s'\n", rd->carrier_map[i].name.len, rd->carrier_map[i].name.s);
+			goto errout;
+		}
+		if (add_carrier_data(rd, tmp_carrier_data) < 0) {
+			LM_ERR("can't add carrier '%.*s'\n", rd->carrier_map[i].name.len, rd->carrier_map[i].name.s);
+			destroy_carrier_data(tmp_carrier_data);
+			goto errout;
+		}
+		carrierroute_dbf.free_result(carrierroute_dbh, res);
+		res = NULL;
+	}
+
+	if (carrierroute_dbf.use_table(carrierroute_dbh, &carrierroute_table) < 0) {
+		LM_ERR("Cannot set database table '%.*s'.\n", carrierroute_table.len, carrierroute_table.s);
+		return -1;
+	}
+
+	if (DB_CAPABILITY(carrierroute_dbf, DB_CAP_FETCH)) {
+		if (carrierroute_dbf.query(carrierroute_dbh, NULL, NULL, NULL, (db_key_t *) columns, 0,
+					COLUMN_NUM, NULL, NULL) < 0) {
+			LM_ERR("Failed to query database to prepare fetch row.\n");
+			return -1;
+		}
+		if(carrierroute_dbf.fetch_result(carrierroute_dbh, &res, cr_fetch_rows) < 0) {
+			LM_ERR("Fetching rows failed\n");
+			return -1;
+		}
+	} else {
+		if (carrierroute_dbf.query(carrierroute_dbh, NULL, NULL, NULL, (db_key_t *) columns, 0,
+				 COLUMN_NUM, NULL, &res) < 0) {
+			LM_ERR("Failed to query database.\n");
+			return -1;
+		}
+	}
+	int n = 0;
+	do {
+		LM_DBG("loading, cycle %d", n++);
+		for (i = 0; i < RES_ROW_N(res); ++i) {
+			row = &RES_ROWS(res)[i];
+			tmp_scan_prefix.s=(char *)row->values[COL_SCAN_PREFIX].val.string_val;
+			tmp_rewrite_host.s=(char *)row->values[COL_REWRITE_HOST].val.string_val;
+			tmp_rewrite_prefix.s=(char *)row->values[COL_REWRITE_PREFIX].val.string_val;
+			tmp_rewrite_suffix.s=(char *)row->values[COL_REWRITE_SUFFIX].val.string_val;
+			tmp_comment.s=(char *)row->values[COL_COMMENT].val.string_val;
+			if (tmp_scan_prefix.s==NULL) tmp_scan_prefix.s="";
+			if (tmp_rewrite_host.s==NULL) tmp_rewrite_host.s="";
+			if (tmp_rewrite_prefix.s==NULL) tmp_rewrite_prefix.s="";
+			if (tmp_rewrite_suffix.s==NULL) tmp_rewrite_suffix.s="";
+			if (tmp_comment.s==NULL) tmp_comment.s="";
+			tmp_scan_prefix.len=strlen(tmp_scan_prefix.s);
+			tmp_rewrite_host.len=strlen(tmp_rewrite_host.s);
+			tmp_rewrite_prefix.len=strlen(tmp_rewrite_prefix.s);
+			tmp_rewrite_suffix.len=strlen(tmp_rewrite_suffix.s);
+			tmp_comment.len=strlen(tmp_comment.s);
+			if (add_route(rd,
+					row->values[COL_CARRIER].val.int_val,
+					row->values[COL_DOMAIN].val.int_val,
+					&tmp_scan_prefix,
+					row->values[COL_FLAGS].val.int_val,
+					row->values[COL_MASK].val.int_val,
+					0,
+					row->values[COL_PROB].val.double_val,
+					&tmp_rewrite_host,
+					row->values[COL_STRIP].val.int_val,
+					&tmp_rewrite_prefix,
+					&tmp_rewrite_suffix,
+					1,
+					0,
+					-1,
+					NULL,
+					&tmp_comment) == -1) {
+				goto errout;
+			}
+		}
+		if (DB_CAPABILITY(carrierroute_dbf, DB_CAP_FETCH)) {
+			if(carrierroute_dbf.fetch_result(carrierroute_dbh, &res, cr_fetch_rows) < 0) {
+				LM_ERR("fetching rows failed\n");
+				carrierroute_dbf.free_result(carrierroute_dbh, res);
+				return -1;
+			}
+		} else {
+			break;
+		}
+	} while(RES_ROW_N(res) > 0);
+
+	carrierroute_dbf.free_result(carrierroute_dbh, res);
+	res = NULL;
+	
+	if (carrierroute_dbf.use_table(carrierroute_dbh, &carrierfailureroute_table) < 0) {
+		LM_ERR("cannot set database table '%.*s'.\n",
+				carrierfailureroute_table.len, carrierfailureroute_table.s);
+		return -1;
+	}
+	if (carrierroute_dbf.query(carrierroute_dbh, NULL, NULL, NULL, (db_key_t *)failure_columns, 0,
+								FAILURE_COLUMN_NUM, NULL, &res) < 0) {
+		LM_ERR("failed to query database.\n");
+		return -1;
+	}
+	for (i = 0; i < RES_ROW_N(res); ++i) {
+		row = &RES_ROWS(res)[i];
+		tmp_scan_prefix.s=(char *)row->values[FCOL_SCAN_PREFIX].val.string_val;
+		tmp_host_name.s=(char *)row->values[FCOL_HOST_NAME].val.string_val;
+		tmp_reply_code.s=(char *)row->values[FCOL_REPLY_CODE].val.string_val;
+		tmp_comment.s=(char *)row->values[FCOL_COMMENT].val.string_val;
+		if (tmp_scan_prefix.s==NULL) tmp_scan_prefix.s="";
+		if (tmp_host_name.s==NULL) tmp_host_name.s="";
+		if (tmp_reply_code.s==NULL) tmp_reply_code.s="";
+		if (tmp_comment.s==NULL) tmp_comment.s="";
+		tmp_scan_prefix.len=strlen(tmp_scan_prefix.s);
+		tmp_host_name.len=strlen(tmp_host_name.s);
+		tmp_reply_code.len=strlen(tmp_reply_code.s);
+		tmp_comment.len=strlen(tmp_comment.s);
+		if (add_failure_route(rd,
+				row->values[FCOL_CARRIER].val.int_val,
+				row->values[COL_DOMAIN].val.int_val,
+				&tmp_scan_prefix,
+				&tmp_host_name,
+				&tmp_reply_code,
+				row->values[FCOL_FLAGS].val.int_val,
+				row->values[FCOL_MASK].val.int_val,
+				row->values[FCOL_NEXT_DOMAIN].val.int_val,
+				&tmp_comment) == -1) {
+			goto errout;
+		}
+	}
+
+	carrierroute_dbf.free_result(carrierroute_dbh, res);
+	return 0;
+
+errout:
+	if (res) {
+		carrierroute_dbf.free_result(carrierroute_dbh, res);
+	}
+	return -1;
+}

+ 91 - 0
modules/carrierroute/cr_db.h

@@ -0,0 +1,91 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * @file cr_db.h
+ * @brief Functions for loading routing data from a database.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#ifndef CR_DB_H
+#define CR_DB_H
+
+#include "../../db/db.h"
+#include "db_carrierroute.h"
+#include "cr_data.h"
+
+
+#define COLUMN_NUM 12
+#define COL_ID             0
+#define COL_CARRIER        1
+#define COL_DOMAIN         2
+#define COL_SCAN_PREFIX    3
+#define COL_FLAGS          4
+#define COL_MASK           5
+#define COL_PROB           6
+#define COL_REWRITE_HOST   7
+#define COL_STRIP          8
+#define COL_REWRITE_PREFIX 9
+#define COL_REWRITE_SUFFIX 10
+#define COL_COMMENT        11
+
+#define FAILURE_COLUMN_NUM 10
+#define FCOL_ID             0
+#define FCOL_CARRIER        1
+#define FCOL_DOMAIN         2
+#define FCOL_SCAN_PREFIX    3
+#define FCOL_HOST_NAME      4
+#define FCOL_REPLY_CODE     5
+#define FCOL_FLAGS          6
+#define FCOL_MASK           7
+#define FCOL_NEXT_DOMAIN    8
+#define FCOL_COMMENT        9
+
+#define CARRIER_NAME_COLUMN_NUM 2
+#define CARRIER_NAME_ID_COL 0
+#define CARRIER_NAME_NAME_COL 1
+
+#define DOMAIN_NAME_COLUMN_NUM 2
+#define DOMAIN_NAME_ID_COL 0
+#define DOMAIN_NAME_NAME_COL 1
+
+extern str * columns[];
+extern str * carrier_columns[];
+extern str * failure_columns[];
+
+
+/**
+ * Loads the routing data from the database given in global
+ * variable db_url and stores it in routing tree rd.
+ *
+ * @param rd Pointer to the route data tree where the routing data
+ * shall be loaded into
+ *
+ * @return 0 means ok, -1 means an error occured
+ *
+ */
+int load_route_data_db (struct route_data_t * rd);
+
+int load_user_carrier(str * user, str * domain);
+
+#endif

+ 247 - 0
modules/carrierroute/cr_domain.c

@@ -0,0 +1,247 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_domain.c
+ * \brief Contains the functions to manage routing domains.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#include <stdlib.h>
+#include "../../mem/shm_mem.h"
+#include "../../ut.h"
+#include "cr_domain.h"
+#include "cr_rule.h"
+#include "carrierroute.h"
+
+
+/**
+ * Destroys route_flags list in shared memory by freing all its memory.
+ *
+ * @param data the start of the route_flags list to be destroyed
+ */
+static void destroy_route_flags_list(void *data) {
+	struct route_flags *rf, *rf_tmp;
+
+	rf=(struct route_flags *)(data);
+	while (rf!=NULL) {
+		rf_tmp = rf->next;
+		destroy_route_flags(rf);
+		rf = rf_tmp;
+	}
+}
+
+
+/**
+ * Destroys failure_route_rule list in shared memory by freing all its memory.
+ *
+ * @param data the start of the failure_route_rule list to be destroyed
+ */
+static void destroy_failure_route_rule_list(void *data) {
+	struct failure_route_rule *rs, *rs_tmp;
+	
+	rs = (struct failure_route_rule *)(data);
+	while (rs != NULL) {
+		rs_tmp = rs->next;
+		destroy_failure_route_rule(rs);
+		rs = rs_tmp;
+	}
+}
+
+
+/**
+ * Create a new domain in shared memory and set it up.
+ *
+ * @param domain_id the id of the domain
+ * @param domain_name the name of the domain
+ *
+ * @return a pointer to the newly allocated domain data or NULL on
+ * error, in which case it LOGs an error message.
+ */
+struct domain_data_t * create_domain_data(int domain_id, str * domain_name) {
+	struct domain_data_t * tmp;
+	if ((tmp = shm_malloc(sizeof(struct domain_data_t))) == NULL) {
+		SHM_MEM_ERROR;
+		return NULL;
+	}
+	memset(tmp, 0, sizeof(struct domain_data_t));
+	tmp->id = domain_id;
+	tmp->name = domain_name;
+	if ((tmp->tree = dtrie_init(cr_match_mode)) == NULL) {
+		shm_free(tmp);
+		return NULL;
+	}
+	if ((tmp->failure_tree = dtrie_init(cr_match_mode)) == NULL) {
+		dtrie_destroy(&tmp->tree, NULL, cr_match_mode);
+		shm_free(tmp);
+		return NULL;
+	}
+	return tmp;
+}
+
+
+/**
+ * Destroys the given domain and frees the used memory.
+ *
+ * @param domain_data the structure to be destroyed.
+ */
+void destroy_domain_data(struct domain_data_t *domain_data) {
+	if (domain_data) {
+		dtrie_destroy(&domain_data->tree, destroy_route_flags_list, cr_match_mode);
+		dtrie_destroy(&domain_data->failure_tree, destroy_failure_route_rule_list,
+				cr_match_mode);
+		shm_free(domain_data);
+	}
+}
+
+
+/**
+ * Adds the given route information to the prefix tree identified by
+ * node. scan_prefix identifies the number for which the information
+ * is. The rewrite_* parameters define what to do in case of a match.
+ * prob gives the probability with which this rule applies if there are
+ * more than one for a given prefix.
+ *
+ * @param node the root of the routing tree
+ * @param scan_prefix the prefix for which to add the rule (must not contain non-digits)
+ * @param flags user defined flags
+ * @param mask mask for user defined flags
+ * @param full_prefix the whole scan prefix
+ * @param max_targets the number of targets
+ * @param prob the weight of the rule
+ * @param rewrite_hostpart the rewrite_host of the rule
+ * @param strip the number of digits to be stripped off userpart before prepending prefix
+ * @param rewrite_local_prefix the rewrite prefix
+ * @param rewrite_local_suffix the rewrite suffix
+ * @param status the status of the rule
+ * @param hash_index the hash index of the rule
+ * @param backup indicates if the route is backed up by another. only 
+                 useful if status==0, if set, it is the hash value
+                 of another rule
+  * @param backed_up an -1-termintated array of hash indices of the route 
+                    for which this route is backup
+ * @param comment a comment for the route rule
+ *
+ * @return 0 on success, -1 on failure
+ *
+ * @see add_route()
+ */
+int add_route_to_tree(struct dtrie_node_t *node, const str * scan_prefix,
+		flag_t flags, flag_t mask, const str * full_prefix, int max_targets, double prob,
+		const str * rewrite_hostpart, int strip, const str * rewrite_local_prefix,
+		const str * rewrite_local_suffix, int status, int hash_index, 
+		int backup, int * backed_up, const str * comment) {
+	void **ret;
+	struct route_flags *rf;
+
+	ret = dtrie_contains(node, scan_prefix->s, scan_prefix->len, cr_match_mode);
+
+	rf = add_route_flags((struct route_flags **)ret, flags, mask);
+	if (rf == NULL) {
+		LM_ERR("cannot insert route flags into list\n");
+		return -1;
+	}
+
+	if (ret == NULL) {
+		/* node does not exist */
+		if (dtrie_insert(node, scan_prefix->s, scan_prefix->len, rf, cr_match_mode) != 0) {
+			LM_ERR("cannot insert route flags into d-trie\n");
+			return -1;
+		}
+	}
+
+	/* Now add rule to flags */
+	return add_route_rule(rf, full_prefix, max_targets, prob, rewrite_hostpart, strip,
+												rewrite_local_prefix, rewrite_local_suffix, status, hash_index,
+												backup, backed_up, comment);
+}
+
+
+/**
+ * Adds the given failure route information to the failure prefix tree identified by
+ * failure_node. scan_prefix, host, reply_code, flags identifies the number for which
+ * the information is and the next_domain parameters defines where to continue
+ * routing in case of a match.
+ *
+ * @param failure_node the root of the failure routing tree
+ * @param scan_prefix the prefix for which to add the rule (must not contain non-digits)
+ * @param full_prefix the whole scan prefix
+ * @param host the hostname last tried
+ * @param reply_code the reply code 
+ * @param flags user defined flags
+ * @param mask mask for user defined flags
+ * @param next_domain continue routing with this domain id
+ * @param comment a comment for the route rule
+ *
+ * @return 0 on success, -1 on failure
+ *
+ * @see add_route()
+ */
+int add_failure_route_to_tree(struct dtrie_node_t * failure_node, const str * scan_prefix,
+		const str * full_prefix, const str * host, const str * reply_code,
+		const flag_t flags, const flag_t mask, const int next_domain, const str * comment) {
+	void **ret;
+	struct failure_route_rule *frr;
+
+	ret = dtrie_contains(failure_node, scan_prefix->s, scan_prefix->len, cr_match_mode);
+
+	frr = add_failure_route_rule((struct failure_route_rule **)ret, full_prefix, host, reply_code, flags, mask, next_domain, comment);
+	if (frr == NULL) {
+		LM_ERR("cannot insert failure route rule into list\n");
+		return -1;
+		}
+
+	if (ret == NULL) {
+		/* node does not exist */
+		if (dtrie_insert(failure_node, scan_prefix->s, scan_prefix->len, frr, cr_match_mode) != 0) {
+			LM_ERR("cannot insert failure route rule into d-trie\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * Compares the IDs of two domain data structures.
+ * A NULL pointer is always greater than any ID.
+ *
+ * @return -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
+ */
+int compare_domain_data(const void *v1, const void *v2) {
+  struct domain_data_t *d1 = *(struct domain_data_t * const *)v1;
+	struct domain_data_t *d2 = *(struct domain_data_t * const *)v2;
+	if (d1 == NULL) {
+		if (d2 == NULL) return 0;
+		else return 1;
+	}
+	else {
+		if (d2 == NULL) return -1;
+		else {
+			if (d1->id < d2->id) return -1;
+			else if (d1->id > d2->id) return 1;
+			else return 0;
+		}
+	}
+}

+ 142 - 0
modules/carrierroute/cr_domain.h

@@ -0,0 +1,142 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_domain.h
+ * \brief Contains the functions to manage routing domains.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#ifndef CR_DOMAIN_H
+#define CR_DOMAIN_H
+
+#include "../../str.h"
+#include "../../flags.h"
+#include "../../trie/dtrie.h"
+
+
+/**
+ * The struct for the domain.
+ * Contains the head of each prefix tree.
+ */
+struct domain_data_t {
+	int id; /*!< the numerical id of the routing tree */
+	str * name; /*!< the name of the routing tree. This points to the name in domain_map to avoid duplication. */
+	struct dtrie_node_t * tree; /*!< the root node of the routing tree. Payload is of type (struct route_flags *) */
+	struct dtrie_node_t * failure_tree; /*!< the root node of the failure routing tree. Payload is of type (struct failure_route_rule *) */
+};
+
+
+/**
+ * Create a new domain in shared memory and set it up.
+ *
+ * @param domain_id the id of the domain
+ * @param domain_name the name of the domain
+ *
+ * @return a pointer to the newly allocated domain data or NULL on
+ * error, in which case it LOGs an error message.
+ */
+struct domain_data_t * create_domain_data(int id, str * domain);
+
+
+/**
+ * Destroys the given domain and frees the used memory.
+ *
+ * @param domain_data the to the structure to be destroyed.
+ */
+void destroy_domain_data(struct domain_data_t *domain_data);
+
+
+/**
+ * Adds the given route information to the prefix tree identified by
+ * node. scan_prefix identifies the number for which the information
+ * is. The rewrite_* parameters define what to do in case of a match.
+ * prob gives the probability with which this rule applies if there are
+ * more than one for a given prefix.
+ *
+ * @param node the root of the routing tree
+ * @param scan_prefix the prefix for which to add the rule (must not contain non-digits)
+ * @param flags user defined flags
+ * @param mask mask for user defined flags
+ * @param full_prefix the whole scan prefix
+ * @param max_targets the number of targets
+ * @param prob the weight of the rule
+ * @param rewrite_hostpart the rewrite_host of the rule
+ * @param strip the number of digits to be stripped off userpart before prepending prefix
+ * @param rewrite_local_prefix the rewrite prefix
+ * @param rewrite_local_suffix the rewrite suffix
+ * @param status the status of the rule
+ * @param hash_index the hash index of the rule
+ * @param backup indicates if the route is backed up by another. only 
+                 useful if status==0, if set, it is the hash value
+                 of another rule
+ * @param backed_up an -1-termintated array of hash indices of the route 
+                    for which this route is backup
+ * @param comment a comment for the route rule
+ *
+ * @return 0 on success, -1 on failure
+ *
+ * @see add_route()
+ */
+int add_route_to_tree(struct dtrie_node_t *node, const str * scan_prefix,
+		flag_t flags, flag_t mask, const str * full_prefix, int max_targets, double prob,
+		const str * rewrite_hostpart, int strip, const str * rewrite_local_prefix,
+		const str * rewrite_local_suffix, int status, int hash_index, 
+		int backup, int * backed_up, const str * comment);
+
+
+/**
+ * Adds the given failure route information to the failure prefix tree identified by
+ * failure_node. scan_prefix, host, reply_code, flags identifies the number for which
+ * the information is and the next_domain parameters defines where to continue
+ * routing in case of a match.
+ *
+ * @param failure_node the root of the failure routing tree
+ * @param scan_prefix the prefix for which to add the rule (must not contain non-digits)
+ * @param full_prefix the whole scan prefix
+ * @param host the hostname last tried
+ * @param reply_code the reply code 
+ * @param flags user defined flags
+ * @param mask mask for user defined flags
+ * @param next_domain continue routing with this domain id
+ * @param comment a comment for the route rule
+ *
+ * @return 0 on success, -1 on failure
+ *
+ * @see add_route()
+ */
+int add_failure_route_to_tree(struct dtrie_node_t * failure_node, const str * scan_prefix,
+		const str * full_prefix, const str * host, const str * reply_code,
+		const flag_t flags, const flag_t mask, const int next_domain, const str * comment);
+
+
+/**
+ * Compares the IDs of two domain data structures.
+ * A NULL pointer is always greater than any ID.
+ *
+ * @return -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
+ */
+int compare_domain_data(const void *v1, const void *v2);
+
+
+#endif

+ 1028 - 0
modules/carrierroute/cr_fifo.c

@@ -0,0 +1,1028 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_fifo.c
+ * \brief Functions for modifying routing data via fifo commands.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include "../../str.h"
+#include "../../ut.h"
+
+#include "cr_fifo.h"
+#include "carrierroute.h"
+#include "cr_config.h"
+#include "cr_carrier.h"
+#include "cr_domain.h"
+#include "cr_rule.h"
+
+
+/**
+ * Defines the option set for the different fifo commands
+ * Every line is for a command,
+ * The first field defines the required options, the second field defines the
+ * optional options and the third field defines the invalid options.
+ */
+static unsigned int opt_settings[5][3] = {{O_PREFIX|O_DOMAIN|O_HOST|O_PROB, O_R_PREFIX|O_R_SUFFIX|O_H_INDEX, O_NEW_TARGET},
+        {O_HOST|O_DOMAIN|O_PREFIX, O_PROB, O_R_PREFIX|O_R_SUFFIX|O_NEW_TARGET|O_H_INDEX},
+        {O_HOST|O_NEW_TARGET, O_PREFIX|O_DOMAIN|O_PROB, O_R_PREFIX|O_R_SUFFIX|O_H_INDEX},
+        {O_HOST|O_DOMAIN|O_PREFIX, O_PROB|O_NEW_TARGET, O_R_PREFIX|O_R_SUFFIX|O_H_INDEX},
+        {O_HOST|O_DOMAIN|O_PREFIX, O_PROB, O_R_PREFIX|O_R_SUFFIX|O_NEW_TARGET|O_H_INDEX}};
+
+int fifo_err;
+
+static int updated;
+
+static int dump_tree_recursor (struct mi_node* msg, struct dtrie_node_t *node, char *prefix);
+
+static struct mi_root* print_replace_help(void);
+
+static int get_fifo_opts(str * buf, fifo_opt_t * opts, unsigned int opt_set[]);
+
+static int update_route_data(fifo_opt_t * opts);
+
+static int update_route_data_recursor(struct dtrie_node_t *node, str * act_domain, fifo_opt_t * opts);
+
+static struct mi_root* print_fifo_err(void);
+
+
+static int str_toklen(str * str, const char * delims)
+{
+	int len;
+	
+	if ((str==NULL) || (str->s==NULL)) {
+		/* No more tokens */
+		return -1;
+	}
+	
+	len=0;
+	while (len<str->len) {
+		if (strchr(delims,str->s[len])!=NULL) {
+			return len;
+		}
+		len++;
+	}
+	
+	return len;
+}
+
+
+/**
+ * reloads the routing data
+ *
+ * @param cmd_tree the MI command tree
+ * @param param the parameter
+ *
+ * @return code 200 on success, code 500 on failure
+ */
+struct mi_root* reload_fifo (struct mi_root* cmd_tree, void *param) {
+	struct mi_root * tmp = NULL;
+
+	if (reload_route_data () == -1) {
+		tmp = init_mi_tree(500, "failed to re-built tree, see log", 33);
+	}
+	else {
+		tmp = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+	}
+	return tmp;
+}
+
+
+/**
+ * prints the routing data
+ *
+ * @param cmd_tree the MI command tree
+ * @param param the parameter
+ *
+ * @return code 200 on success, code 400 or 500 on failure
+ */
+struct mi_root* dump_fifo (struct mi_root* cmd_tree, void *param) {
+	struct route_data_t * rd;
+	str *tmp_str;
+	str empty_str = str_init("<empty>");
+
+	if((rd = get_data ()) == NULL) {
+		LM_ERR("error during retrieve data\n");
+		return init_mi_tree(500, "error during command processing", 31);
+	}
+	
+	struct mi_root* rpl_tree;
+	struct mi_node* node = NULL;
+	rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+	if(rpl_tree == NULL)
+		return 0;
+	node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "Printing routing information:");
+	if(node == NULL)
+		goto error;
+
+	LM_DBG("start processing of data\n");
+	int i, j;
+ 	for (i = 0; i < rd->carrier_num; i++) {
+ 		if (rd->carriers[i]) {
+			tmp_str = (rd->carriers[i] ? rd->carriers[i]->name : &empty_str);
+			node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "Printing tree for carrier '%.*s' (%i)\n", tmp_str->len, tmp_str->s, rd->carriers[i] ? rd->carriers[i]->id : 0);
+			if(node == NULL)
+				goto error;
+ 			for (j=0; j<rd->carriers[i]->domain_num; j++) {
+ 				if (rd->carriers[i]->domains[j] && rd->carriers[i]->domains[j]->tree) {
+					tmp_str = (rd->carriers[i]->domains[j] ? rd->carriers[i]->domains[j]->name : &empty_str);
+					node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "Printing tree for domain '%.*s' (%i)\n", tmp_str->len, tmp_str->s, rd->carriers[i]->domains[j]->id);
+					if(node == NULL)
+						goto error;
+ 					dump_tree_recursor (&rpl_tree->node, rd->carriers[i]->domains[j]->tree, "");
+				}
+ 			}
+		}
+	}
+	release_data (rd);
+	return rpl_tree;
+	return 0;
+
+error:
+	release_data (rd);
+	free_mi_tree(rpl_tree);
+	return 0;
+}
+
+
+/**
+ * replaces the host specified by parameters in the
+ * fifo command, can be used only in file mode
+ * expect one mi node that contains the command
+ *
+ * @param cmd_tree the MI command tree
+ * @param param the parameter
+ *
+ * @return code 200 on success, code 400 or 500 on failure
+ */
+struct mi_root* replace_host (struct mi_root* cmd_tree, void *param) {
+	struct mi_node *node = NULL;
+
+	int ret;
+	fifo_opt_t options;
+
+	if(mode != CARRIERROUTE_MODE_FILE) {
+		return init_mi_tree(400, "Not running in config file mode, cannot modify route from command line", 70);
+	}
+	
+	node = cmd_tree->node.kids;
+	if (node==NULL || node->next!=NULL)
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	
+	/* look for command */
+	if (node->value.s==NULL)
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	if((ret = get_fifo_opts(&node->value, &options, opt_settings[OPT_REPLACE])) <  0) {
+		return print_fifo_err();
+	}
+
+	options.status = 1;
+	options.cmd = OPT_REPLACE;
+
+	if(update_route_data(&options) < 0) {
+		return init_mi_tree(500, "failed to update route data, see log", 37);
+	}
+
+	return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+}
+
+
+/**
+ * deactivates the host given in the command line options,
+ * can be used only in file mode
+ * expect one mi node that contains the command
+ *
+ * @param cmd_tree the MI command tree
+ * @param param the parameter
+ *
+ * @return code 200 on success, code 400 or 500 on failure
+ */
+struct mi_root* deactivate_host (struct mi_root* cmd_tree, void *param) {
+	struct mi_node *node = NULL;
+
+	int ret;
+	fifo_opt_t options;
+
+	if(mode != CARRIERROUTE_MODE_FILE) {
+		return init_mi_tree(400, "Not running in config file mode, cannot modify route from command line", 70);
+	}
+
+	node = cmd_tree->node.kids;
+	if (node==NULL || node->next!=NULL)
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	
+	/* look for command */
+	if (node->value.s==NULL)
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	if((ret = get_fifo_opts(&node->value, &options, opt_settings[OPT_DEACTIVATE])) <  0) {
+		return print_fifo_err();
+	}
+
+	options.status = 0;
+	options.cmd = OPT_DEACTIVATE;
+
+	if(update_route_data(&options) < 0) {
+		return init_mi_tree(500, "failed to update route data, see log", 37);
+	}
+
+	return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+}
+
+
+/**
+ * activates the host given in the command line options,
+ * can be used only in file mode
+ * expect one mi node that contains the command
+ *
+ * @param cmd_tree the MI command tree
+ * @param param the parameter
+ *
+ * @return code 200 on success, code 400 or 500 on failure
+ */
+struct mi_root* activate_host (struct mi_root* cmd_tree, void *param) {
+	struct mi_node *node = NULL;
+
+	int ret;
+	fifo_opt_t options;
+
+	if(mode != CARRIERROUTE_MODE_FILE) {
+		return init_mi_tree(400, "Not running in config file mode, cannot modify route from command line", 70);
+	}
+
+	node = cmd_tree->node.kids;
+	if (node==NULL || node->next!=NULL)
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	
+	/* look for command */
+	if (node->value.s==NULL)
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	if((ret = get_fifo_opts(&node->value, &options, opt_settings[OPT_ACTIVATE])) <  0) {
+		return print_fifo_err();
+	}
+
+	options.status = 1;
+	options.cmd = OPT_ACTIVATE;
+
+	if(update_route_data(&options) < 0) {
+		return init_mi_tree(500, "failed to update route data, see log", 37);
+	}
+
+	return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+}
+
+
+/**
+ * adds the host specified by the command line args,
+ * can be used only in file mode
+ * expect one mi node that contains the command
+ *
+ * @param cmd_tree the MI command tree
+ * @param param the parameter
+ *
+ * @return code 200 on success, code 400 or 500 on failure
+ */
+struct mi_root* add_host (struct mi_root* cmd_tree, void *param) {
+	struct mi_node *node = NULL;
+
+	int ret;
+	fifo_opt_t options;
+
+	if(mode != CARRIERROUTE_MODE_FILE) {
+		return init_mi_tree(400, "Not running in config file mode, cannot modify route from command line", 70);
+	}
+
+	node = cmd_tree->node.kids;
+	if (node==NULL || node->next!=NULL || node->value.s==NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	if((ret = get_fifo_opts(&node->value, &options, opt_settings[OPT_ADD])) <  0) {
+		return print_fifo_err();
+	}
+
+	options.status = 1;
+	options.cmd = OPT_ADD;
+
+	if(update_route_data(&options) < 0) {
+		return init_mi_tree(500, "failed to update route data, see log", 37);
+	}
+
+	return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+}
+
+
+/**
+ * deletes the host specified by the command line args,
+ * can be used only in file mode
+ * expect one mi node that contains the command
+ *
+ * @param cmd_tree the MI command tree
+ * @param param the parameter
+ *
+ * @return code 200 on success, code 400 or 500 on failure
+ */
+struct mi_root* delete_host (struct mi_root* cmd_tree, void * param) {
+	struct mi_node *node = NULL;
+
+	int ret;
+	fifo_opt_t options;
+
+	if(mode != CARRIERROUTE_MODE_FILE) {
+		return init_mi_tree(400, "Not running in config file mode, cannot modify route from command line", 70);
+	}
+
+	node = cmd_tree->node.kids;
+	if (node==NULL || node->next!=NULL)
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	
+	/* look for command */
+	if (node->value.s==NULL)
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	if((ret = get_fifo_opts(&node->value, &options, opt_settings[OPT_REMOVE])) <  0) {
+		return print_fifo_err();
+	}
+
+	options.cmd = OPT_REMOVE;
+
+	if(update_route_data(&options) < 0) {
+		return init_mi_tree(500, "failed to update route data, see log", 37);
+	}
+
+	return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+}
+
+
+/**
+ * does the work for dump_fifo, traverses the routing tree
+ * and prints route rules if present.
+ *
+ * @param msg MI node that is used to append the informations
+ * @param node pointer to the routing tree node
+ * @param prefix carries the current scan prefix
+ *
+ * @return mi node containing the route rules
+ */
+static int dump_tree_recursor (struct mi_node* msg, struct dtrie_node_t *node, char *prefix) {
+	char s[256];
+	char *p;
+	int i;
+	struct route_flags *rf;
+	struct route_rule *rr;
+	struct route_rule_p_list * rl;
+	double prob;
+
+	strcpy (s, prefix);
+	p = s + strlen (s);
+	p[1] = '\0';
+	for (i = 0; i < cr_match_mode; ++i) {
+		if (node->child[i] != NULL) {
+			*p = i + '0';
+			dump_tree_recursor (msg->next, node->child[i], s);
+		}
+	}
+	*p = '\0';
+	for (rf = (struct route_flags *)(node->data); rf != NULL; rf = rf->next) {
+		for (rr = rf->rule_list; rr != NULL; rr = rr->next) {
+			if(rf->dice_max){
+				prob = (double)(rr->prob * DICE_MAX)/(double)rf->dice_max;
+			} else {
+				prob = rr->prob;
+			}
+			addf_mi_node_child(msg->next, 0, 0, 0, "%10s: %0.3f %%, '%.*s': %s, '%i', '%.*s', '%.*s', '%.*s'\n",
+												 strlen(prefix) > 0 ? prefix : "NULL", prob * 100, rr->host.len, rr->host.s,
+												 (rr->status ? "ON" : "OFF"), rr->strip,
+												 rr->local_prefix.len, rr->local_prefix.s,
+												 rr->local_suffix.len, rr->local_suffix.s,
+												 rr->comment.len, rr->comment.s);
+			if(!rr->status && rr->backup && rr->backup->rr){
+				addf_mi_node_child(msg->next, 0, 0, 0, "            Rule is backed up by: %.*s\n", rr->backup->rr->host.len, rr->backup->rr->host.s);
+			}
+			if(rr->backed_up){
+				rl = rr->backed_up;
+				i=0;
+				while(rl){
+					if(rl->rr){
+						addf_mi_node_child(msg->next, 0, 0, 0, "            Rule is backup for: %.*s", rl->rr->host.len, rl->rr->host.s);
+					}
+					rl = rl->next;
+					i++;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+
+/**
+ * parses the command line argument for options
+ *
+ * @param buf the command line argument
+ * @param opts fifo options
+ * @param opt_set set of the options
+ *
+ * @return 0 on success, -1 on failure
+ *
+ * @see dump_fifo()
+ */
+static int get_fifo_opts(str * buf, fifo_opt_t * opts, unsigned int opt_set[]) {
+	int opt_argc = 0;
+	str opt_argv[20];
+	int i, op = -1;
+	unsigned int used_opts = 0;
+	int toklen;
+
+	memset(opt_argv, 0, sizeof(opt_argv));
+	memset(opts, 0, sizeof(fifo_opt_t));
+	opts->prob = -1;
+
+	while((toklen = str_toklen(buf, " \t\r\n")) >=0 && opt_argc < 20) {
+		buf->s[toklen] = '\0'; /* insert zero termination, since strtod might be used later on it */
+		opt_argv[opt_argc].len = toklen;
+		opt_argv[opt_argc].s = buf->s;
+		buf->s += toklen + 1;
+		buf->len -= toklen + 1;
+		LM_DBG("found arg[%i]: %.*s\n", opt_argc, opt_argv[opt_argc].len, opt_argv[opt_argc].s);
+		opt_argc++;
+	}
+	for (i=0; i<opt_argc; i++) {
+		LM_DBG("token %.*s", opt_argv[i].len, opt_argv[i].s);
+		if (opt_argv[i].len >= 1) {
+			switch(*opt_argv[i].s) {
+					case '-': switch(opt_argv[i].s[1]) {
+							case OPT_DOMAIN_CHR:
+							op = OPT_DOMAIN;
+							used_opts |= O_DOMAIN;
+							break;
+							case OPT_PREFIX_CHR:
+							op = OPT_PREFIX;
+							used_opts |= O_PREFIX;
+							break;
+							case OPT_HOST_CHR:
+							op = OPT_HOST;
+							used_opts |= O_HOST;
+							break;
+							case OPT_NEW_TARGET_CHR:
+							op = OPT_NEW_TARGET;
+							used_opts |= O_NEW_TARGET;
+							break;
+							case OPT_PROB_CHR:
+							op = OPT_PROB;
+							used_opts |= O_PROB;
+							break;
+							case OPT_R_PREFIX_CHR:
+							op = OPT_R_PREFIX;
+							used_opts |= O_R_PREFIX;
+							break;
+							case OPT_R_SUFFIX_CHR:
+							op = OPT_R_SUFFIX;
+							used_opts |= O_R_SUFFIX;
+							break;
+							case OPT_HASH_INDEX_CHR:
+							op = OPT_HASH_INDEX;
+							used_opts |= O_H_INDEX;
+							break;
+							case OPT_HELP_CHR:
+							FIFO_ERR(E_HELP);
+							return -1;
+							default: {
+								FIFO_ERR(E_WRONGOPT);
+								LM_DBG("Unknown option: %.*s\n", opt_argv[i].len, opt_argv[i].s);
+								return -1;
+							}
+					}
+					break;
+					default: switch(op) {
+							case OPT_DOMAIN:
+							opts->domain = opt_argv[i];
+							op = -1;
+							break;
+							case OPT_PREFIX:
+							if (str_strcasecmp(&opt_argv[i], &CR_EMPTY_PREFIX) == 0) {
+								opts->prefix.s = NULL;
+								opts->prefix.len = 0;
+							} else {
+								opts->prefix = opt_argv[i];
+							}
+							op = -1;
+							break;
+							case OPT_HOST:
+							opts->host = opt_argv[i];
+							op = -1;
+							break;
+							case OPT_NEW_TARGET:
+							opts->new_host = opt_argv[i];
+							op = -1;
+							break;
+							case OPT_PROB:
+							opts->prob = strtod(opt_argv[i].s, NULL); /* we can use str.s since we zero terminated it earlier */
+							op = -1;
+							break;
+							case OPT_R_PREFIX:
+							opts->rewrite_prefix = opt_argv[i];
+							op = -1;
+							break;
+							case OPT_STRIP:
+							str2sint(&opt_argv[i], &opts->strip);
+							op = -1;
+							break;
+							case OPT_R_SUFFIX:
+							opts->rewrite_suffix = opt_argv[i];
+							op = -1;
+							break;
+							case OPT_HASH_INDEX:
+							str2sint(&opt_argv[i], &opts->hash_index);
+							op = -1;
+							break;
+							default: {
+								LM_DBG("No option given\n");
+								FIFO_ERR(E_NOOPT);
+								return -1;
+							}
+					}
+					break;
+			}
+		}
+	}
+	if((used_opts & opt_set[OPT_INVALID]) != 0) {
+		LM_DBG("invalid option\n");
+		FIFO_ERR(E_INVALIDOPT);
+		return -1;
+	}
+	if((used_opts & opt_set[OPT_MANDATORY]) != opt_set[OPT_MANDATORY]) {
+		LM_DBG("option missing\n");
+		FIFO_ERR(E_MISSOPT);
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * loads the config data into shared memory (but doesn't really
+ * share it), updates the routing data and writes it to the config
+ * file. Afterwards, the global routing data is reloaded.
+ *
+ * @param opts pointer to the option structure which contains
+ * data to be modified or to be added
+ *
+ * @return 0 on success, -1 on failure
+ */
+static int update_route_data(fifo_opt_t * opts) {
+	struct route_data_t * rd;
+	int i,j;
+	int domain_id;
+	str tmp_domain;
+	str tmp_prefix;
+	str tmp_host;
+	str tmp_rewrite_prefix;
+	str tmp_rewrite_suffix;
+	str tmp_comment = str_init("");
+
+	if ((rd = shm_malloc(sizeof(struct route_data_t))) == NULL) {
+		SHM_MEM_ERROR;
+		return -1;
+	}
+	memset(rd, 0, sizeof(struct route_data_t));
+	if (load_config(rd) < 0) {
+		LM_ERR("could not load config");
+		FIFO_ERR(E_LOADCONF);
+		return -1;
+	}
+
+	if (rule_fixup(rd) < 0) {
+		LM_ERR("could not fixup rules");
+		FIFO_ERR(E_RULEFIXUP);
+		return -1;
+	}
+	updated = 0;
+
+	if (opts->cmd == OPT_ADD) {
+		tmp_domain=opts->domain;
+		tmp_prefix=opts->prefix;
+		tmp_host=opts->host;
+		tmp_rewrite_prefix=opts->rewrite_prefix;
+		tmp_rewrite_suffix=opts->rewrite_suffix;
+		if (tmp_domain.s==NULL) {
+			tmp_domain.s="";
+			tmp_domain.len=0;
+		}
+		if (tmp_prefix.s==NULL) {
+			tmp_prefix.s="";
+			tmp_prefix.len=0;
+		}
+		if (tmp_host.s==NULL) {
+			tmp_host.s="";
+			tmp_host.len=0;
+		}
+		if (tmp_rewrite_prefix.s==NULL) {
+			tmp_rewrite_prefix.s="";
+			tmp_rewrite_prefix.len=0;
+		}
+		if (tmp_rewrite_suffix.s==NULL) {
+			tmp_rewrite_suffix.s="";
+			tmp_rewrite_suffix.len=0;
+		}
+
+		domain_id = map_name2id(rd->domain_map, rd->domain_num, &tmp_domain);
+		if (domain_id < 0) {
+			LM_ERR("cannot find id for domain '%.*s'", tmp_domain.len, tmp_domain.s);
+			goto errout;
+		}
+
+		if (add_route(rd, 1, domain_id, &tmp_prefix, 0, 0, 0, opts->prob,
+		              &tmp_host, opts->strip, &tmp_rewrite_prefix, &tmp_rewrite_suffix,
+		              opts->status, opts->hash_index, -1, NULL, &tmp_comment) < 0) {
+			goto errout;
+		}
+		updated = 1;
+		if (rule_fixup(rd) < 0) {
+			LM_ERR("could not fixup rules after route appending");
+			FIFO_ERR(E_RULEFIXUP);
+			goto errout;
+		}
+	} else {
+		for (i=0; i<rd->carrier_num; i++) {
+			if(rd->carriers[i]){
+				for (j=0; j<rd->carriers[i]->domain_num; j++) {
+					if (rd->carriers[i]->domains[j] && rd->carriers[i]->domains[j]->tree) {
+						if (update_route_data_recursor(rd->carriers[i]->domains[j]->tree, rd->carriers[i]->domains[j]->name, opts) < 0) {
+							goto errout;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	if(!updated){
+		LM_ERR("no match for update found");
+		FIFO_ERR(E_NOUPDATE);
+		goto errout;
+	}
+
+	if (save_config(rd) < 0) {
+		LM_ERR("could not save config");
+		FIFO_ERR(E_SAVECONF);
+		goto errout;
+	}
+
+	if (reload_route_data() == -1) {
+		LM_ERR("could not reload route data");
+		FIFO_ERR(E_LOADCONF);
+		goto errout;
+	}
+
+	clear_route_data(rd);
+	return 0;
+errout:
+	clear_route_data(rd);
+	return -1;
+}
+
+
+/**
+ * Does the work for update_route_data by recursively
+ * traversing the routing tree
+ *
+ * @param node points to the current routing tree node
+ * @param act_domain routing domain which is currently
+ * searched
+ * @param opts points to the fifo command option structure
+ *
+ * @see update_route_data()
+ *
+ * @return 0 on success, -1 on failure
+ */
+static int update_route_data_recursor(struct dtrie_node_t *node, str * act_domain, fifo_opt_t * opts) {
+	int i, hash = 0;
+	struct route_rule * rr, * prev = NULL, * tmp, * backup;
+	struct route_flags *rf;
+
+	rf = (struct route_flags *)(node->data);
+	if (rf && rf->rule_list) {
+		rr = rf->rule_list;
+		while (rr) {
+			if ((!opts->domain.len || (strncmp(opts->domain.s, OPT_STAR, strlen(OPT_STAR)) == 0)
+			        || ((opts->domain.len == act_domain->len) && (strncmp(opts->domain.s, act_domain->s, opts->domain.len) == 0)))
+			        && ((!opts->prefix.len && !rr->prefix.len) || (strncmp(opts->prefix.s, OPT_STAR, strlen(OPT_STAR)) == 0)
+			            || (rr->prefix.len == opts->prefix.len && (strncmp(opts->prefix.s, rr->prefix.s, opts->prefix.len) == 0)))
+			        && ((!opts->host.len && !rr->host.s) || (strncmp(opts->host.s, OPT_STAR, strlen(OPT_STAR)) == 0)
+			            || ((strncmp(rr->host.s, opts->host.s, opts->host.len) == 0) && (rr->host.len == opts->host.len)))
+			        && ((opts->prob < 0) || (opts->prob == rr->prob))) {
+				switch (opts->cmd) {
+					case OPT_REPLACE:
+						LM_INFO("replace host %.*s with %.*s\n", rr->host.len, rr->host.s, opts->new_host.len, opts->new_host.s);
+						if (rr->host.s) {
+							shm_free(rr->host.s);
+						}
+						if (opts->new_host.len) {
+							if ((rr->host.s = shm_malloc(opts->new_host.len + 1)) == NULL) {
+								SHM_MEM_ERROR;
+								FIFO_ERR(E_NOMEM);
+								return -1;
+							}
+							memmove(rr->host.s, opts->new_host.s, opts->new_host.len + 1);
+							rr->host.len = opts->new_host.len;
+							rr->host.s[rr->host.len] = '\0';
+						} else {
+							rr->host.len = 0;
+						}
+						rr->status = opts->status;
+						prev = rr;
+						rr = rr->next;
+						updated = 1;
+						break;
+					case OPT_DEACTIVATE:
+						if (remove_backed_up(rr) < 0) {
+							LM_ERR("could not reset backup hosts\n");
+							FIFO_ERR(E_RESET);
+							return -1;
+						}
+						if (opts->new_host.len > 0) {
+							LM_INFO("deactivating host %.*s\n", rr->host.len, rr->host.s);
+							if (opts->new_host.len == 1 && opts->new_host.s[0] == 'a') {
+								if ((backup = find_auto_backup(rf, rr)) == NULL) {
+									LM_ERR("didn't find auto backup route\n");
+									FIFO_ERR(E_NOAUTOBACKUP);
+									return -1;
+								}
+							} else {
+								errno = 0;
+								hash = strtol(opts->new_host.s, NULL, 10);
+								if (errno == EINVAL || errno == ERANGE) {
+									if ((backup = find_rule_by_hash(rf, hash)) == NULL) {
+										LM_ERR("didn't find given backup route (hash %i)\n", hash);
+										FIFO_ERR(E_NOHASHBACKUP);
+										return -1;
+									}
+								} else {
+									if ((backup = find_rule_by_host(rf, &opts->new_host)) == NULL) {
+										LM_ERR("didn't find given backup route (host %.*s)\n", opts->new_host.len, opts->new_host.s);
+										FIFO_ERR(E_NOHOSTBACKUP);
+										return -1;
+									}
+								}
+							}
+							if (add_backup_rule(rr, backup) < 0) {
+								LM_ERR("couldn't set backup route\n");
+								FIFO_ERR(E_ADDBACKUP);
+								return -1;
+							}
+						} else {
+							if(rr->backed_up){
+								LM_ERR("can't deactivate route without backup route because it is backup route for others\n");
+								FIFO_ERR(E_DELBACKUP);
+								return -1;
+							}
+						}
+						rr->status = opts->status;
+						prev = rr;
+						rr = rr->next;
+						updated = 1;
+						break;
+					case OPT_ACTIVATE:
+						LM_INFO("activating host %.*s\n", rr->host.len, rr->host.s);
+						if (remove_backed_up(rr) < 0) {
+							LM_ERR("could not reset backup hosts\n");
+							FIFO_ERR(E_RESET);
+							return -1;
+						}
+						rr->status = opts->status;
+						prev = rr;
+						rr = rr->next;
+						updated = 1;
+						break;
+					case OPT_REMOVE:
+						LM_INFO("removing host %.*s\n", rr->host.len, rr->host.s);
+						if (rr->backed_up){
+							LM_ERR("cannot remove host %.*s which is backup for other hosts\n", rr->host.len, rr->host.s);
+							FIFO_ERR(E_DELBACKUP);
+							return -1;
+						}
+						if (remove_backed_up(rr) < 0) {
+							LM_ERR("could not reset backup hosts\n");
+							FIFO_ERR(E_RESET);
+							return -1;
+						}
+						if (prev) {
+							prev->next = rr->next;
+							tmp = rr;
+							rr = prev;
+							destroy_route_rule(tmp);
+							prev = rr;
+							rr = rr->next;
+						} else {
+							rf->rule_list = rr->next;
+							tmp = rr;
+							rr = rf->rule_list;
+							destroy_route_rule(tmp);
+						}
+						rf->rule_num--;
+						rf->max_targets--;
+						updated = 1;
+						break;
+					default:
+						rr = rr->next;
+						break;
+				}
+			} else {
+				prev = rr;
+				rr = rr->next;
+			}
+		}
+	}
+	for (i=0; i<cr_match_mode; i++) {
+		if (node->child[i]) {
+			if (update_route_data_recursor(node->child[i], act_domain, opts) < 0) {
+				return -1;
+			}
+		}
+	}
+	return 0;
+}
+
+
+/**
+ * prints a short help text for fifo command usage
+ */
+static struct mi_root* print_replace_help(void) {
+       struct mi_root* rpl_tree;
+       struct mi_node* node;
+
+       rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN );
+       if(rpl_tree == NULL)
+               return 0;
+
+       node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "carrierroute options usage:");
+       if(node == NULL)
+               goto error;
+       node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c searched/new remote host\n", OPT_HOST_CHR);
+       if(node == NULL)
+               goto error;
+       node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c replacement/backup host", OPT_NEW_TARGET_CHR);
+       if(node == NULL)
+               goto error;
+       node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c: searched/new domain", OPT_DOMAIN_CHR);
+       if(node == NULL)
+               goto error;
+       node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c: searched/new prefix", OPT_PREFIX_CHR);
+       if(node == NULL)
+               goto error;
+       node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c: searched/new weight (0..1)", OPT_PROB_CHR);
+       if(node == NULL)
+               goto error;
+       node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c: new rewrite prefix", OPT_R_PREFIX_CHR);
+       if(node == NULL)
+               goto error;
+       node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c: new rewrite suffix", OPT_R_SUFFIX_CHR);
+       if(node == NULL)
+               goto error;
+       node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c: new hash index", OPT_HASH_INDEX_CHR);
+       if(node == NULL)
+               goto error;
+       node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "\t-%c: prints this help", OPT_HELP_CHR);
+       if(node == NULL)
+               goto error;
+
+       return rpl_tree;
+
+error:
+       free_mi_tree(rpl_tree);
+       return 0;
+}
+
+
+/**
+ * interpret the fifo errors, creates a mi tree
+ * @todo this is currently not evaluated for errors during update_route_data
+ */
+struct mi_root* print_fifo_err(void) {
+	struct mi_root* rpl_tree;
+	
+	switch (fifo_err) {
+		case E_MISC: 
+			rpl_tree = init_mi_tree( 400, "An error occured", 17);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_NOOPT:
+			rpl_tree = init_mi_tree( 400, "No option given", 16);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_WRONGOPT:
+			rpl_tree = init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_NOMEM:
+			rpl_tree = init_mi_tree( 500, "Out of memory", 14);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_RESET:
+			rpl_tree = init_mi_tree( 500, "Could not reset backup routes", 30);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_NOAUTOBACKUP:
+			rpl_tree = init_mi_tree( 400, "No auto backup route found", 27);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_NOHASHBACKUP:
+			rpl_tree = init_mi_tree( 400, "No backup route for given hash found", 37);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_NOHOSTBACKUP:
+			rpl_tree = init_mi_tree( 400, "No backup route for given host found", 37);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_ADDBACKUP:
+			rpl_tree = init_mi_tree( 500, "Could not set backup route", 27);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_DELBACKUP:
+			rpl_tree = init_mi_tree( 400, "Could not delete or deactivate route, it is backup for other routes", 68);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_LOADCONF:
+			rpl_tree = init_mi_tree( 500, "Could not load config from file", 32);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_SAVECONF:
+			rpl_tree = init_mi_tree( 500, "Could not save config", 22);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_INVALIDOPT:
+			rpl_tree = init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_MISSOPT:
+			rpl_tree = init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_RULEFIXUP:
+			rpl_tree = init_mi_tree( 500, "Could not fixup rules", 22);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_NOUPDATE:
+			rpl_tree = init_mi_tree( 500, "No match for update found", 26);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+		case E_HELP:
+			return print_replace_help();
+			break;
+		default:
+			rpl_tree = init_mi_tree( 500, "An error occured", 17);
+			if(rpl_tree == NULL)
+				return 0;
+			break;
+	}
+	return rpl_tree;
+}

+ 142 - 0
modules/carrierroute/cr_fifo.h

@@ -0,0 +1,142 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_fifo.h
+ * \brief Functions for modifying routing data via fifo commands.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#ifndef CR_FIFO_H
+#define CR_FIFO_H
+
+#include "../../mi/mi.h"
+
+extern int fifo_err;
+
+#define E_MISC -1
+#define E_NOOPT -2
+#define E_WRONGOPT -3
+#define E_NOMEM -4
+#define E_RESET -5
+#define E_NOAUTOBACKUP -6
+#define E_NOHASHBACKUP -7
+#define E_NOHOSTBACKUP -8
+#define E_ADDBACKUP -9
+#define E_DELBACKUP -10
+#define E_LOADCONF -11
+#define E_SAVECONF -12
+#define E_INVALIDOPT -13
+#define E_MISSOPT -14
+#define E_RULEFIXUP -15
+#define E_NOUPDATE -16
+#define E_HELP -17
+
+#define FIFO_ERR(e) (fifo_err = e)
+
+/**
+ * @struct fifo_opt
+ * Holds values and command type when a command was passed to fifo
+ */
+typedef struct fifo_opt {
+	unsigned int cmd; /*!< Command type passed to FIFO */
+	unsigned int opts; /*!< Options used */
+	str domain; /*!< the routing domain */
+	str prefix; /*!< the scan prefix */
+	double prob; /*!< the weight of the route rule */
+	str host; /*!< the hostname or address */
+	int strip; /*!< the number of digits to be stripped off */
+	str new_host; /*!< the new host when a host is going to be replaced */
+	str rewrite_prefix; /*!< the rewrite prefix */
+	str rewrite_suffix; /*!< the rewrite suffix */
+	int hash_index; /*!< the hash index */
+	int status; /*!< the status */
+}
+fifo_opt_t;
+
+#define FBUF_SIZE 2048
+
+#define OPT_ADD 0
+#define OPT_REMOVE 1
+#define OPT_REPLACE 2
+#define OPT_DEACTIVATE 3
+#define OPT_ACTIVATE 4
+
+#define OPT_MANDATORY 0
+#define OPT_OPTIONAL 1
+#define OPT_INVALID 2
+
+#define OPT_PREFIX 0
+#define OPT_DOMAIN 1
+#define OPT_HOST 2
+#define OPT_NEW_TARGET 3
+#define OPT_PROB 4
+#define OPT_R_PREFIX 5
+#define OPT_R_SUFFIX 6
+#define OPT_HASH_INDEX 7
+#define OPT_STRIP 8
+
+/**
+ * Flags for options to determine which options are used
+ */
+#define O_PREFIX           1
+#define O_DOMAIN     (1 << 1)
+#define O_HOST       (1 << 2)
+#define O_NEW_TARGET (1 << 3)
+#define O_PROB       (1 << 4)
+#define O_R_PREFIX   (1 << 5)
+#define O_R_SUFFIX   (1 << 6)
+#define O_H_INDEX    (1 << 7)
+#define O_STRIP      (1 << 8)
+
+/**
+ * Constants define option characters
+ */
+#define OPT_PREFIX_CHR 'p'
+#define OPT_DOMAIN_CHR 'd'
+#define OPT_HOST_CHR 'h'
+#define OPT_NEW_TARGET_CHR 't'
+#define OPT_PROB_CHR 'w'
+#define OPT_R_PREFIX_CHR 'P'
+#define OPT_R_SUFFIX_CHR 'S'
+#define OPT_HASH_INDEX_CHR 'i'
+#define OPT_STRIP_CHR 's'
+#define OPT_HELP_CHR '?'
+
+#define OPT_STAR "*"
+
+struct mi_root* reload_fifo (struct mi_root* cmd_tree, void *param);
+
+struct mi_root* dump_fifo (struct mi_root* cmd_tree, void *param);
+
+struct mi_root* replace_host (struct mi_root* cmd_tree, void *param);
+
+struct mi_root* deactivate_host (struct mi_root* cmd_tree, void *param);
+
+struct mi_root* activate_host (struct mi_root* cmd_tree, void *param);
+
+struct mi_root* add_host (struct mi_root* cmd_tree, void *param);
+
+struct mi_root* delete_host (struct mi_root* cmd_tree, void * param);
+
+#endif

+ 326 - 0
modules/carrierroute/cr_fixup.c

@@ -0,0 +1,326 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_fixup.c
+ * \brief Fixup functions.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#include "../../mod_fix.h"
+#include "../../mem/mem.h"
+#include "cr_fixup.h"
+#include "carrierroute.h"
+#include "cr_map.h"
+#include "cr_domain.h"
+#include "prime_hash.h"
+#include "cr_data.h"
+
+
+/**
+ * The fixup funcions will use the initial mapping.
+ * If the mapping changes afterwards (eg. due to cr_reload_routes),
+ * the names used in the routing script will not be mapped
+ * to the correct IDs!
+ * @param name carrier name
+ * @return carrier id
+ */
+static int carrier_name_2_id(const str *name) {
+	int id;
+	struct route_data_t * rd;
+	
+	do {
+		rd = get_data();
+	} while (rd == NULL);
+
+	id = map_name2id(rd->carrier_map, rd->carrier_num, name);
+	
+	release_data(rd);
+
+	return id;
+}
+
+
+/**
+ * The fixup funcions will use the initial mapping.
+ * If the mapping changes afterwards (eg. due to cr_reload_routes),
+ * the names used in the routing script will not be mapped
+ * to the correct IDs!
+ * @param name domain name
+ * @return domain id 
+ */
+static int domain_name_2_id(const str *name) {
+	int id;
+	struct route_data_t * rd;
+
+	do {
+		rd = get_data();
+	} while (rd == NULL);
+	
+	id = map_name2id(rd->domain_map, rd->domain_num, name);
+	
+	release_data(rd);
+
+	return id;
+}
+
+
+/**
+ * Fixes the hash source to enum values
+ *
+ * @param my_hash_source the hash source as string
+ *
+ * @return the enum value on success, -1 on failure
+ */
+static enum hash_source hash_fixup(const char * my_hash_source) {
+	if (strcasecmp("call_id", my_hash_source) == 0) {
+		return shs_call_id;
+	} else if (strcasecmp("from_uri", my_hash_source) == 0) {
+		return shs_from_uri;
+	} else if (strcasecmp("from_user", my_hash_source) == 0) {
+		return shs_from_user;
+	} else if (strcasecmp("to_uri", my_hash_source) == 0) {
+		return shs_to_uri;
+	} else if (strcasecmp("to_user", my_hash_source) == 0) {
+		return shs_to_user;
+	} else {
+		return shs_error;
+	}
+}
+
+
+/**
+ * Fixes the module functions' parameters if it is a carrier.
+ * supports name string and PVs.
+ *
+ * @param param the parameter
+ *
+ * @return 0 on success, -1 on failure
+ */
+static int carrier_fixup(void ** param) {
+	int id;
+
+	if (fixup_spve_null(param, 1) !=0) {
+		LM_ERR("could not fixup parameter");
+		return -1;
+	}
+
+	if (((gparam_p)(*param))->type == GPARAM_TYPE_STR) {
+		/* This is a name string, convert to a int */
+		((gparam_p)(*param))->type=GPARAM_TYPE_INT;
+		/* get carrier id */
+		if ((id = carrier_name_2_id(&((gparam_p)(*param))->v.sval)) < 0) {
+			LM_ERR("could not find carrier name '%.*s' in map\n", ((gparam_p)(*param))->v.sval.len, ((gparam_p)(*param))->v.sval.s);
+			pkg_free(*param);
+			return -1;
+		}
+		((gparam_p)(*param))->v.ival = id;
+	}
+	return 0;
+}
+
+
+/**
+ * Fixes the module functions' parameters if it is a domain.
+ * supports name string, and PVs.
+ *
+ * @param param the parameter
+ *
+ * @return 0 on success, -1 on failure
+ */
+static int domain_fixup(void ** param) {
+	int id;
+
+	if (fixup_spve_null(param, 1) !=0) {
+		LM_ERR("could not fixup parameter");
+		return -1;
+	}
+
+	if (((gparam_p)(*param))->type == GPARAM_TYPE_STR) {
+		/* This is a name string, convert to a int */
+		((gparam_p)(*param))->type=GPARAM_TYPE_INT;
+		/* get domain id */
+		if ((id = domain_name_2_id(&(((gparam_p)(*param))->v.sval))) < 0) {
+			LM_ERR("could not find domain name '%.*s' in map\n", ((gparam_p)(*param))->v.sval.len, ((gparam_p)(*param))->v.sval.s);
+			pkg_free(*param);
+			return -1;
+		}
+		((gparam_p)(*param))->v.ival = id;
+	}
+	return 0;
+}
+
+
+/**
+ * Fixes the module functions' parameters in case of AVP names.
+ *
+ * @param param the parameter
+ *
+ * @return 0 on success, -1 on failure
+ */
+static int avp_name_fixup(void ** param) {
+
+	if (fixup_spve_null(param, 1) !=0) {
+		LM_ERR("could not fixup parameter");
+		return -1;
+	}
+	if (((gparam_p)(*param))->v.pve->spec.type == PVT_AVP &&
+			((gparam_p)(*param))->v.pve->spec.pvp.pvn.u.isname.name.s.len == 0 &&
+			((gparam_p)(*param))->v.pve->spec.pvp.pvn.u.isname.name.s.s == 0) {
+		LM_ERR("malformed or non AVP type definition\n");
+		return -1;
+	}
+	return 0;
+}
+
+
+/**
+ * Fixes the module functions' parameters, i.e. it maps
+ * the routing domain names to numbers for faster access
+ * at runtime
+ *
+ * @param param the parameter
+ * @param param_no the number of the parameter
+ *
+ * @return 0 on success, -1 on failure
+ */
+int cr_route_fixup(void ** param, int param_no) {
+	enum hash_source my_hash_source;
+
+	if (param_no == 1) {
+		/* carrier */
+		if (carrier_fixup(param) < 0) {
+			LM_ERR("cannot fixup parameter %d\n", param_no);
+			return -1;
+		}
+	}
+	else if (param_no == 2) {
+		/* domain */
+		if (domain_fixup(param) < 0) {
+			LM_ERR("cannot fixup parameter %d\n", param_no);
+			return -1;
+		}
+	}
+	else if ((param_no == 3) || (param_no == 4)){
+		/* prefix matching, rewrite user */
+		if (fixup_spve_null(param, 1) != 0) {
+			LM_ERR("cannot fixup parameter %d\n", param_no);
+			return -1;
+		}
+	}
+	else if (param_no == 5) {
+		/* hash source */
+		if ((my_hash_source = hash_fixup((char *)*param)) == shs_error) {
+			LM_ERR("invalid hash source\n");
+			return -1;
+		}
+		pkg_free(*param);
+		*param = (void *)my_hash_source;
+	}
+	else if (param_no == 6) {
+		/* destination avp name */
+		if (avp_name_fixup(param) < 0) {
+			LM_ERR("cannot fixup parameter %d\n", param_no);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * fixes the module functions' parameters, i.e. it maps
+ * the routing domain names to numbers for faster access
+ * at runtime
+ *
+ * @param param the parameter
+ * @param param_no the number of the parameter
+ *
+ * @return 0 on success, -1 on failure
+ */
+int cr_load_next_domain_fixup(void ** param, int param_no) {
+	if (param_no == 1) {
+		/* carrier */
+		if (carrier_fixup(param) < 0) {
+			LM_ERR("cannot fixup parameter %d\n", param_no);
+			return -1;
+		}
+	}
+	else if (param_no == 2) {
+		/* domain */
+		if (domain_fixup(param) < 0) {
+			LM_ERR("cannot fixup parameter %d\n", param_no);
+			return -1;
+		}
+	}
+	else if ((param_no == 3) || (param_no == 4) || (param_no == 5)) {
+		/* prefix matching, host, reply code */
+		if (fixup_spve_null(param, 1) != 0) {
+			LM_ERR("cannot fixup parameter %d\n", param_no);
+			return -1;
+		}
+	}
+	else if (param_no == 6) {
+		/* destination avp name */
+		if (avp_name_fixup(param) < 0) {
+			LM_ERR("cannot fixup parameter %d\n", param_no);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * Fixes the module functions' parameters.
+ *
+ * @param param the parameter
+ * @param param_no the number of the parameter
+ *
+ * @return 0 on success, -1 on failure
+ */
+int cr_load_user_carrier_fixup(void ** param, int param_no) {
+	if (mode == CARRIERROUTE_MODE_FILE) {
+		LM_ERR("command cr_user_rewrite_uri can't be used in file mode\n");
+		return -1;
+	}
+
+	if ((param_no == 1) || (param_no == 2)) {
+		/* user, domain */
+		if (fixup_spve_null(param, 1) != 0) {
+			LM_ERR("cannot fixup parameter %d\n", param_no);
+			return -1;
+		}
+	}
+	else if (param_no == 3) {
+		/* destination avp name */
+		if (avp_name_fixup(param) < 0) {
+			LM_ERR("cannot fixup parameter %d\n", param_no);
+			return -1;
+		}
+	}
+
+	return 0;
+}

+ 71 - 0
modules/carrierroute/cr_fixup.h

@@ -0,0 +1,71 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ *
+ */
+
+/**
+ * \file cr_fixup.h
+ * \brief Fixup functions.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#ifndef CR_FIXUP_H
+#define CR_FIXUP_H
+
+
+/**
+ * fixes the module functions' parameters, i.e. it maps
+ * the routing domain names to numbers for faster access
+ * at runtime
+ *
+ * @param param the parameter
+ * @param param_no the number of the parameter
+ *
+ * @return 0 on success, -1 on failure
+ */
+int cr_route_fixup(void ** param, int param_no);
+
+
+/**
+ * Fixes the module functions' parameters.
+ *
+ * @param param the parameter
+ * @param param_no the number of the parameter
+ *
+ * @return 0 on success, -1 on failure
+ */
+int cr_load_user_carrier_fixup(void ** param, int param_no);
+
+
+/**
+ * Fixes the module functions' parameters, i.e. it maps
+ * the routing domain names to numbers for faster access
+ * at runtime
+ *
+ * @param param the parameter
+ * @param param_no the number of the parameter
+ *
+ * @return 0 on success, -1 on failure
+ */
+int cr_load_next_domain_fixup(void ** param, int param_no);
+
+#endif

+ 770 - 0
modules/carrierroute/cr_func.c

@@ -0,0 +1,770 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_func.c
+ * \brief Routing and balancing functions.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#include <ctype.h>
+#include <assert.h>
+#include <stdlib.h>
+#include "cr_func.h"
+#include "cr_db.h"
+#include "../../sr_module.h"
+#include "../../action.h"
+#include "../../parser/parse_uri.h"
+#include "../../parser/parse_from.h"
+#include "../../ut.h"
+#include "../../parser/digest/digest.h"
+#include "../../parser/hf.h"
+#include "../../mem/mem.h"
+#include "../../qvalue.h"
+#include "../../dset.h"
+#include "cr_map.h"
+#include "cr_rule.h"
+#include "cr_domain.h"
+#include "cr_carrier.h"
+#include "carrierroute.h"
+
+
+enum hash_algorithm {
+	alg_crc32 = 1, /*!< hashing algorithm is CRC32 */
+	alg_prime, /*!< hashing algorithm is (right 18 digits of hash_source % prime_number) % max_targets + 1 */
+	alg_error
+};
+
+
+static const str SIP_URI  = { .s="sip:",  .len=4 };
+static const str SIPS_URI = { .s="sips:", .len=5 };
+static const str AT_SIGN  = { .s="@",     .len=1 };
+
+
+/**
+ * Get the id that belongs to a string name from gparam_t structure.
+ *
+ * Get the id that belongs to a string name from gparam_t structure, use the
+ * search_id function for the lookup.
+ * @param _msg SIP message
+ * @param gp id as integer, pseudo-variable or AVP name of carrier
+ * @param map lookup function
+ * @param size size of the list
+ * @return id on success, -1 otherwise
+ */
+static inline int cr_gp2id(struct sip_msg *_msg, gparam_t *gp, struct name_map_t *map, int size) {
+	int id;
+	struct usr_avp *avp;
+	int_str avp_val;
+	str tmp;
+
+	switch (gp->type) {
+	case GPARAM_TYPE_INT:
+		return gp->v.ival;
+		break;
+	case GPARAM_TYPE_PVE:
+		/* does this PV hold an AVP? */
+		if (gp->v.pve->spec.type==PVT_AVP) {
+			avp = search_first_avp(gp->v.pve->spec.pvp.pvn.u.isname.type,
+						gp->v.pve->spec.pvp.pvn.u.isname.name, &avp_val, 0);
+			if (!avp) {
+				LM_ERR("cannot find AVP '%.*s'\n", gp->v.pve->spec.pvp.pvn.u.isname.name.s.len,
+						gp->v.pve->spec.pvp.pvn.u.isname.name.s.s);
+				return -1;
+			}
+			if ((avp->flags&AVP_VAL_STR)==0) {
+				return avp_val.n;
+			} else {
+				id = map_name2id(map, size, &avp_val.s);
+				if (id < 0) {
+					LM_ERR("could not find id '%.*s' from AVP\n",
+							gp->v.pve->spec.pvp.pvn.u.isname.name.s.len,
+							gp->v.pve->spec.pvp.pvn.u.isname.name.s.s);
+					return -1;
+				}
+				return id;
+			}
+		} else {
+			/* retrieve name from parameter */
+			if (fixup_get_svalue(_msg, gp, &tmp)<0) {
+				LM_ERR("cannot print the name from PV\n");
+				return -1;
+			}
+			id = map_name2id(map, size, &tmp);
+			if (id < 0) {
+				LM_ERR("could not find id '%.*s' from PV\n", tmp.len, tmp.s);
+				return -1;
+			}
+			return id;
+		}
+	default:
+		LM_ERR("invalid parameter type\n");
+		return -1;
+	}
+}
+
+
+/**
+ * Try to match the reply code rc to the reply code with wildcards.
+ *
+ * @param rcw reply code specifier with wildcards
+ * @param rc the current reply code
+ *
+ * @return 0 on match, -1 otherwise
+ */
+static inline int reply_code_matcher(const str *rcw, const str *rc) {
+	int i;
+	
+	if (rcw->len==0) return 0;
+	
+	if (rcw->len != rc->len) return -1;
+	
+	for (i=0; i<rc->len; i++) {
+		if (rcw->s[i]!='.' && rcw->s[i]!=rc->s[i]) return -1;
+	}
+	
+	return 0;
+}
+
+
+/**
+ * writes the next_domain avp using the rule list of failure_tree
+ *
+ * @param frr_head the head of the failure route rule list
+ * @param host last tried host
+ * @param reply_code the last reply code
+ * @param flags flags for the failure route rule
+ * @param dstavp the name of the AVP where to store the next domain
+ *
+ * @return 0 on success, -1 on failure
+ */
+static int set_next_domain_on_rule(struct failure_route_rule *frr_head,
+		const str *host, const str *reply_code, const flag_t flags,
+		const gparam_t *dstavp) {
+	struct failure_route_rule * rr;
+	int_str avp_val;
+	
+	assert(frr_head != NULL);
+	
+	LM_DBG("searching for matching routing rules");
+	for (rr = frr_head; rr != NULL; rr = rr->next) {
+		/*
+		LM_DBG("rr.flags=%d rr.mask=%d flags=%d\n", rr->flags, rr->mask, flags);
+		LM_DBG("rr.host.len=%d host.len=%d\n", rr->host.len, host->len);
+		LM_DBG("rr.host.s='%.*s' host.s='%.*s'\n", rr->host.len, rr->host.s, host->len, host->s);
+		LM_DBG("rr.reply_code.len=%d reply_code.len=%d\n", rr->reply_code.len, reply_code->len);
+		LM_DBG("rr.reply_code.s='%.*s' reply_code.s='%.*s'\n", rr->reply_code.len, rr->reply_code.s, reply_code->len, reply_code->s);
+		*/
+		if (((rr->mask & flags) == rr->flags) &&
+				((rr->host.len == 0) || (str_strcmp(host, &rr->host)==0)) &&
+				(reply_code_matcher(&(rr->reply_code), reply_code)==0)) {
+			avp_val.n = rr->next_domain;
+			if (add_avp(dstavp->v.pve->spec.pvp.pvn.u.isname.type,
+					dstavp->v.pve->spec.pvp.pvn.u.isname.name, avp_val)<0) {
+				LM_ERR("set AVP failed\n");
+				return -1;
+			}
+			
+			LM_INFO("next_domain is %d\n", rr->next_domain);
+			return 0;
+		}
+	}
+	
+	LM_INFO("no matching rule for (flags=%d, host='%.*s', reply_code='%.*s') found\n", flags, host->len, host->s, reply_code->len, reply_code->s);
+	return -1;
+}
+
+
+/**
+ * traverses the failure routing tree until a matching rule is found.
+ * The longest match is taken, so it is possible to define
+ * failure route rules for a single number
+ *
+ * @param failure_node the current routing tree node
+ * @param uri the uri to be rewritten at the current position
+ * @param host last tried host
+ * @param reply_code the last reply code
+ * @param flags flags for the failure route rule
+ * @param dstavp the name of the AVP where to store the next domain
+ *
+ * @return 0 on success, -1 on failure, 1 on no more matching child node and no rule list
+ */
+static int set_next_domain_recursor(struct dtrie_node_t *failure_node,
+		const str *uri, const str *host, const str *reply_code, const flag_t flags,
+		const gparam_t *dstavp) {
+	str re_uri = *uri;
+	void **ret;
+	
+	/* Skip over non-digits.  */
+	while (re_uri.len > 0 && (!isdigit(*re_uri.s) && cr_match_mode == 10)) {
+		++re_uri.s;
+		--re_uri.len;
+	}
+	ret = dtrie_longest_match(failure_node, re_uri.s, re_uri.len, NULL, cr_match_mode);
+
+	if (ret == NULL) {
+		LM_INFO("URI or prefix tree nodes empty, empty rule list\n");
+		return 1;
+	}
+	else return set_next_domain_on_rule(*ret, host, reply_code, flags, dstavp);
+}
+
+
+/**
+ * searches for a rule int rt with hash_index prob - 1
+ * If the rule with the desired hash index is deactivated,
+ * the next working rule is used.
+ *
+ * @param rf the route_flags node to search for rule
+ * @param prob the hash index
+ *
+ * @return pointer to route rule on success, NULL on failure
+ */
+static struct route_rule * get_rule_by_hash(const struct route_flags * rf,
+		const int prob) {
+	struct route_rule * act_hash = NULL;
+
+	if (prob > rf->rule_num) {
+		LM_WARN("too large desired hash, taking highest\n");
+		act_hash = rf->rules[rf->rule_num - 1];
+	}
+	act_hash = rf->rules[prob - 1];
+
+	if (!act_hash->status) {
+		if (act_hash->backup && act_hash->backup->rr) {
+			act_hash = act_hash->backup->rr;
+		} else {
+			act_hash = NULL;
+		}
+	}
+	LM_INFO("desired hash was %i, return %i\n", prob, act_hash ? act_hash->hash_index : -1);
+	return act_hash;
+}
+
+
+/**
+ * does the work for rewrite_on_rule, writes the new URI into dest
+ *
+ * @param rs the route rule used for rewriting
+ * @param dest the returned new destination URI
+ * @param msg the sip message
+ * @param user the localpart of the uri to be rewritten
+ * @param descavp the name of the AVP where the description is stored
+ *
+ * @return 0 on success, -1 on failure
+ *
+ * @see rewrite_on_rule()
+ */
+static int actually_rewrite(const struct route_rule *rs, str *dest,
+		const struct sip_msg *msg, const str * user, gparam_t *descavp) {
+	size_t len;
+	char *p;
+	int_str avp_val;
+	int strip = 0;
+
+	strip = (rs->strip > user->len ? user->len : rs->strip);
+	strip = (strip < 0 ? 0 : strip);
+
+	len = rs->local_prefix.len + user->len + rs->local_suffix.len +
+	      AT_SIGN.len + rs->host.len - strip;
+	if (msg->parsed_uri.type == SIPS_URI_T) {
+		len += SIPS_URI.len;
+	} else {
+		len += SIP_URI.len;
+	}
+	dest->len = 0;
+	dest->s = (char *)pkg_malloc(len + 1);
+	if (dest->s == NULL) {
+		PKG_MEM_ERROR;
+		return -1;
+	}
+	dest->len = len;
+	p = dest->s;
+	if (msg->parsed_uri.type == SIPS_URI_T) {
+		memcpy(p, SIPS_URI.s, SIPS_URI.len);
+		p += SIPS_URI.len;
+	} else {
+		memcpy(p, SIP_URI.s, SIP_URI.len);
+		p += SIP_URI.len;
+	}
+	if (user->len) {
+		memcpy(p, rs->local_prefix.s, rs->local_prefix.len);
+		p += rs->local_prefix.len;
+		memcpy(p, user->s + strip, user->len - strip);
+		p += user->len - strip;
+		memcpy(p, rs->local_suffix.s, rs->local_suffix.len);
+		p += rs->local_suffix.len;
+		memcpy(p, AT_SIGN.s, AT_SIGN.len);
+		p += AT_SIGN.len;
+	}
+	/* this could be an error, or a blacklisted destination */
+	if (rs->host.len == 0) {
+		*p = '\0';
+		pkg_free(dest->s);
+		return -1;
+	}
+	memcpy(p, rs->host.s, rs->host.len);
+	p += rs->host.len;
+	*p = '\0';
+
+	if (descavp) {
+		avp_val.s = rs->comment;
+		if (add_avp(AVP_VAL_STR | descavp->v.pve->spec.pvp.pvn.u.isname.type,
+					descavp->v.pve->spec.pvp.pvn.u.isname.name, avp_val)<0) {
+			LM_ERR("set AVP failed\n");
+			pkg_free(dest->s);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * writes the uri dest using the flags and rule list of rf_head
+ *
+ * @param rf_head the head of the route flags list
+ * @param flags user defined flags
+ * @param dest the returned new destination URI
+ * @param msg the sip message
+ * @param user the localpart of the uri to be rewritten
+ * @param hash_source the SIP header used for hashing
+ * @param alg the algorithm used for hashing
+ * @param dstavp the name of the destination AVP where the used host name is stored
+ *
+ * @return 0 on success, -1 on failure, 1 on empty rule list
+ */
+static int rewrite_on_rule(struct route_flags *rf_head, flag_t flags, str * dest,
+		struct sip_msg * msg, const str * user, const enum hash_source hash_source,
+		const enum hash_algorithm alg, gparam_t *dstavp) {
+	struct route_flags * rf;
+	struct route_rule * rr;
+	int prob;
+
+	assert(rf_head != NULL);
+
+	LM_DBG("searching for matching routing rules");
+	for (rf = rf_head; rf != NULL; rf = rf->next) {
+		/* LM_DBG("actual flags %i, searched flags %i, mask %i and match %i", rf->flags, flags, rf->mask, flags&rf->mask); */
+		if ((flags&rf->mask) == rf->flags) break;
+	}
+
+	if (rf==NULL) {
+		LM_INFO("did not find a match for flags %d\n", flags);
+		return -1;
+	}
+
+	if (rf->rule_list == NULL) {
+		LM_INFO("empty rule list\n");
+		return 1;
+	}
+
+	switch (alg) {
+		case alg_prime:
+			if ((prob = prime_hash_func(msg, hash_source, rf->max_targets)) < 0) {
+				LM_ERR("could not hash message with prime algorithm");
+				return -1;
+			}
+			if ((rr = get_rule_by_hash(rf, prob)) == NULL) {
+				LM_CRIT("no route found\n");
+				return -1;
+			}
+			break;
+		case alg_crc32:
+			if(rf->dice_max == 0) {
+				LM_ERR("invalid dice_max value\n");
+				return -1;
+			}
+			if ((prob = hash_func(msg, hash_source, rf->dice_max)) < 0) {
+				LM_ERR("could not hash message with CRC32");
+				return -1;
+			}
+			/* This auto-magically takes the last rule if anything is broken.
+			 * Sometimes the hash result is zero. If the first rule is off
+			 * (has a probablility of zero) then it has also a dice_to of
+			 * zero and the message could not be routed at all if we use
+			 * '<' here. Thus the '<=' is necessary.
+			 */
+			for (rr = rf->rule_list;
+			        rr->next != NULL && rr->dice_to <= prob;
+		        rr = rr->next) {}
+			if (!rr->status) {
+				if (!rr->backup) {
+					LM_ERR("all routes are off\n");
+					return -1;
+				} else {
+					if (!rr->backup->rr) {
+						LM_ERR("all routes are off\n");
+						return -1;
+					}
+					rr = rr->backup->rr;
+				}
+			}
+			break;
+		default: 
+			LM_ERR("invalid hash algorithm\n");
+			return -1;
+	}
+	return actually_rewrite(rr, dest, msg, user, dstavp);
+}
+
+
+/**
+ * traverses the routing tree until a matching rule is found
+ * The longest match is taken, so it is possible to define
+ * route rules for a single number
+ *
+ * @param node the current routing tree node
+ * @param pm the user to be used for prefix matching
+ * @param flags user defined flags
+ * @param dest the returned new destination URI
+ * @param msg the sip message
+ * @param user the localpart of the uri to be rewritten
+ * @param hash_source the SIP header used for hashing
+ * @param alg the algorithm used for hashing
+ * @param dstavp the name of the destination AVP where the used host name is stored
+ *
+ * @return 0 on success, -1 on failure, 1 on no more matching child node and no rule list
+ */
+static int rewrite_uri_recursor(struct dtrie_node_t * node,
+		const str * pm, flag_t flags, str * dest, struct sip_msg * msg, const str * user,
+		const enum hash_source hash_source, const enum hash_algorithm alg,
+		gparam_t *dstavp) {
+	str re_pm = *pm;
+	void **ret;
+	
+	/* Skip over non-digits.  */
+	while (re_pm.len > 0 && (!isdigit(*re_pm.s) && cr_match_mode == 10)) {
+		++re_pm.s;
+		--re_pm.len;
+	}
+	ret = dtrie_longest_match(node, re_pm.s, re_pm.len, NULL, cr_match_mode);
+
+	if (ret == NULL) {
+		LM_INFO("URI or prefix tree nodes empty, empty rule list\n");
+		return 1;
+	}
+	else return rewrite_on_rule(*ret, flags, dest, msg, user, hash_source, alg, dstavp);
+}
+
+
+/**
+ * rewrites the request URI of msg after determining the
+ * new destination URI
+ *
+ * @param _msg the current SIP message
+ * @param _carrier the requested carrier
+ * @param _domain the requested routing domain
+ * @param _prefix_matching the user to be used for prefix matching
+ * @param _rewrite_user the localpart of the URI to be rewritten
+ * @param _hsrc the SIP header used for hashing
+ * @param _halg the hash algorithm used for hashing
+ * @param _dstavp the name of the destination AVP where the used host name is stored
+ *
+ * @return 1 on success, -1 on failure
+ */
+int cr_do_route(struct sip_msg * _msg, gparam_t *_carrier,
+		gparam_t *_domain, gparam_t *_prefix_matching,
+		gparam_t *_rewrite_user, enum hash_source _hsrc,
+		enum hash_algorithm _halg, gparam_t *_dstavp) {
+
+	int carrier_id, domain_id, ret = -1;
+	str rewrite_user, prefix_matching, dest;
+	flag_t flags;
+	struct route_data_t * rd;
+	struct carrier_data_t * carrier_data;
+	struct domain_data_t * domain_data;
+	struct action act;
+
+	if (fixup_get_svalue(_msg, _rewrite_user, &rewrite_user)<0) {
+		LM_ERR("cannot print the rewrite_user\n");
+		return -1;
+	}
+
+	if (fixup_get_svalue(_msg, _prefix_matching, &prefix_matching)<0) {
+		LM_ERR("cannot print the prefix_matching\n");
+		return -1;
+	}
+
+	flags = _msg->flags;
+
+	do {
+		rd = get_data();
+	} while (rd == NULL);
+
+	carrier_id = cr_gp2id(_msg, _carrier, rd->carrier_map, rd->carrier_num);
+	if (carrier_id < 0) {
+		LM_ERR("invalid carrier id %d\n", carrier_id);
+		release_data(rd);
+		return -1;
+	}
+
+	domain_id = cr_gp2id(_msg, _domain, rd->domain_map, rd->domain_num);
+	if (domain_id < 0) {
+		LM_ERR("invalid domain id %d\n", domain_id);
+		release_data(rd);
+		return -1;
+	}
+	
+	carrier_data=NULL;
+	if (carrier_id < 0) {
+		if (fallback_default) {
+			LM_NOTICE("invalid tree id %i specified, using default tree\n", carrier_id);
+			carrier_data = get_carrier_data(rd, rd->default_carrier_id);
+		}
+	} else if (carrier_id == 0) {
+		carrier_data = get_carrier_data(rd, rd->default_carrier_id);
+	} else {
+		carrier_data = get_carrier_data(rd, carrier_id);
+		if (carrier_data == NULL) {
+			if (fallback_default) {
+				LM_NOTICE("invalid tree id %i specified, using default tree\n", carrier_id);
+				carrier_data = get_carrier_data(rd, rd->default_carrier_id);
+			}
+		}
+	}
+	if (carrier_data == NULL) {
+		LM_ERR("cannot get carrier data\n");
+		goto unlock_and_out;
+	}
+
+	domain_data = get_domain_data(carrier_data, domain_id);
+	if (domain_data == NULL) {
+		LM_ERR("desired routing domain doesn't exist, prefix %.*s, carrier %d, domain %d\n",
+			prefix_matching.len, prefix_matching.s, carrier_id, domain_id);
+		goto unlock_and_out;
+	}
+
+	if (rewrite_uri_recursor(domain_data->tree, &prefix_matching, flags, &dest, _msg, &rewrite_user, _hsrc, _halg, _dstavp) != 0) {
+		/* this is not necessarily an error, rewrite_recursor does already some error logging */
+		LM_INFO("rewrite_uri_recursor doesn't complete, uri %.*s, carrier %d, domain %d\n", prefix_matching.len,
+			prefix_matching.s, carrier_id, domain_id);
+		goto unlock_and_out;
+	}
+
+	LM_INFO("uri %.*s was rewritten to %.*s, carrier %d, domain %d\n", rewrite_user.len, rewrite_user.s, dest.len, dest.s, carrier_id, domain_id);
+
+	act.type = SET_URI_T;
+	act.elem[0].type= STRING_ST;
+	act.elem[0].u.string = dest.s;
+	act.next = NULL;
+
+	ret = do_action(&act, _msg);
+	if (ret < 0) {
+		LM_ERR("Error in do_action()\n");
+	}
+	pkg_free(dest.s);
+
+unlock_and_out:
+	release_data(rd);
+	return ret;
+}
+
+
+/**
+ * Loads user carrier from subscriber table and stores it in an AVP.
+ *
+ * @param _msg the current SIP message
+ * @param _user the user to determine the carrier data
+ * @param _domain the domain to determine the domain data
+ * @param _dstavp the name of the AVP where to store the carrier id
+ *
+ * @return 1 on success, -1 on failure
+ */
+int cr_load_user_carrier(struct sip_msg * _msg, gparam_t *_user, gparam_t *_domain, gparam_t *_dstavp) {
+	str user, domain;
+	int_str avp_val;
+	
+	if (fixup_get_svalue(_msg, _user, &user)<0) {
+		LM_ERR("cannot print the user\n");
+		return -1;
+	}
+
+	if (fixup_get_svalue(_msg, _domain, &domain)<0) {
+		LM_ERR("cannot print the domain\n");
+		return -1;
+	}
+	
+	/* get carrier id */
+	if ((avp_val.n = load_user_carrier(&user, &domain)) < 0) {
+		LM_ERR("error in load user carrier");
+		return -1;
+	} else {
+		/* set avp */
+		if (add_avp(_dstavp->v.pve->spec.pvp.pvn.u.isname.type,
+					_dstavp->v.pve->spec.pvp.pvn.u.isname.name, avp_val)<0) {
+			LM_ERR("add AVP failed\n");
+			return -1;
+		}
+	}
+	return 1;
+}
+
+
+/**
+ * rewrites the request URI of msg after determining the
+ * new destination URI with the crc32 hash algorithm.
+ *
+ * @param _msg the current SIP message
+ * @param _carrier the requested carrier
+ * @param _domain the requested routing domain
+ * @param _prefix_matching the user to be used for prefix matching
+ * @param _rewrite_user the localpart of the URI to be rewritten
+ * @param _hsrc the SIP header used for hashing
+ * @param _dstavp the name of the destination AVP where the used host name is stored
+ *
+ * @return 1 on success, -1 on failure
+ */
+int cr_route(struct sip_msg * _msg, gparam_t *_carrier,
+		gparam_t *_domain, gparam_t *_prefix_matching,
+		gparam_t *_rewrite_user, enum hash_source _hsrc,
+		gparam_t *_dstavp)
+{
+	return cr_do_route(_msg, _carrier, _domain, _prefix_matching,
+		_rewrite_user, _hsrc, alg_crc32, _dstavp);
+}
+
+
+/**
+ * rewrites the request URI of msg after determining the
+ * new destination URI with the prime hash algorithm.
+ *
+ * @param _msg the current SIP message
+ * @param _carrier the requested carrier
+ * @param _domain the requested routing domain
+ * @param _prefix_matching the user to be used for prefix matching
+ * @param _rewrite_user the localpart of the URI to be rewritten
+ * @param _hsrc the SIP header used for hashing
+ * @param _dstavp the name of the destination AVP where the used host name is stored
+ *
+ * @return 1 on success, -1 on failure
+ */
+int cr_prime_route(struct sip_msg * _msg, gparam_t *_carrier,
+		gparam_t *_domain, gparam_t *_prefix_matching,
+		gparam_t *_rewrite_user, enum hash_source _hsrc,
+		gparam_t *_dstavp)
+{
+	return cr_do_route(_msg, _carrier, _domain, _prefix_matching,
+		_rewrite_user, _hsrc, alg_prime, _dstavp);
+}
+
+
+/**
+ * Loads next domain from failure routing table and stores it in an AVP.
+ *
+ * @param _msg the current SIP message
+ * @param _carrier the requested carrier
+ * @param _domain the requested routing domain
+ * @param _prefix_matching the user to be used for prefix matching
+ * @param _host the host name to be used for rule matching
+ * @param _reply_code the reply code to be used for rule matching
+ * @param _dstavp the name of the destination AVP
+ *
+ * @return 1 on success, -1 on failure
+ */
+int cr_load_next_domain(struct sip_msg * _msg, gparam_t *_carrier,
+		gparam_t *_domain, gparam_t *_prefix_matching,
+		gparam_t *_host, gparam_t *_reply_code, gparam_t *_dstavp) {
+
+	int carrier_id, domain_id, ret = -1;
+	str prefix_matching, host, reply_code;
+	flag_t flags;
+	struct route_data_t * rd;
+	struct carrier_data_t * carrier_data;
+	struct domain_data_t * domain_data;
+
+	if (fixup_get_svalue(_msg, _prefix_matching, &prefix_matching)<0) {
+		LM_ERR("cannot print the prefix_matching\n");
+		return -1;
+	}
+	if (fixup_get_svalue(_msg, _host, &host)<0) {
+		LM_ERR("cannot print the host\n");
+		return -1;
+	}
+	if (fixup_get_svalue(_msg, _reply_code, &reply_code)<0) {
+		LM_ERR("cannot print the reply_code\n");
+		return -1;
+	}
+
+	flags = _msg->flags;
+
+	do {
+		rd = get_data();
+	} while (rd == NULL);
+	
+	carrier_id = cr_gp2id(_msg, _carrier, rd->carrier_map, rd->carrier_num);
+	if (carrier_id < 0) {
+		LM_ERR("invalid carrier id %d\n", carrier_id);
+		release_data(rd);
+		return -1;
+	}
+
+	domain_id = cr_gp2id(_msg, _domain, rd->domain_map, rd->domain_num);
+	if (domain_id < 0) {
+		LM_ERR("invalid domain id %d\n", domain_id);
+		release_data(rd);
+		return -1;
+	}
+
+	carrier_data=NULL;
+	if (carrier_id < 0) {
+		if (fallback_default) {
+			LM_NOTICE("invalid tree id %i specified, using default tree\n", carrier_id);
+			carrier_data = get_carrier_data(rd, rd->default_carrier_id);
+		}
+	} else if (carrier_id == 0) {
+		carrier_data = get_carrier_data(rd, rd->default_carrier_id);
+	} else {
+		carrier_data = get_carrier_data(rd, carrier_id);
+		if (carrier_data == NULL) {
+			if (fallback_default) {
+				LM_NOTICE("invalid tree id %i specified, using default tree\n", carrier_id);
+				carrier_data = get_carrier_data(rd, rd->default_carrier_id);
+			}
+		}
+	}
+	if (carrier_data == NULL) {
+		LM_ERR("cannot get carrier data\n");
+		goto unlock_and_out;
+	}
+
+	domain_data = get_domain_data(carrier_data, domain_id);
+	if (domain_data == NULL) {
+		LM_ERR("desired routing domain doesn't exist, prefix %.*s, carrier %d, domain %d\n",
+			prefix_matching.len, prefix_matching.s, carrier_id, domain_id);
+		goto unlock_and_out;
+	}
+
+	if (set_next_domain_recursor(domain_data->failure_tree, &prefix_matching, &host, &reply_code, flags, _dstavp) != 0) {
+		LM_INFO("set_next_domain_recursor doesn't complete, prefix '%.*s', carrier %d, domain %d\n", prefix_matching.len,
+			prefix_matching.s, carrier_id, domain_id);
+		goto unlock_and_out;
+	}
+	
+	ret = 1;
+	
+unlock_and_out:
+	release_data(rd);
+	return ret;
+}

+ 111 - 0
modules/carrierroute/cr_func.h

@@ -0,0 +1,111 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ *
+ */
+
+/**
+ * \file cr_func.h
+ * \brief Routing and balancing functions.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#ifndef CR_FUNC_H
+#define CR_FUNC_H
+
+#include "../../parser/msg_parser.h"
+#include "../../pvar.h"
+#include "../../mod_fix.h"
+#include "prime_hash.h"
+
+
+/**
+ * Loads user carrier from subscriber table and stores it in an AVP.
+ *
+ * @param _msg the current SIP message
+ * @param _user the user to determine the carrier data
+ * @param _domain the domain to determine the domain data
+ * @param _dstavp the name of the AVP where to store the carrier id
+ *
+ * @return 1 on success, -1 on failure
+ */
+int cr_load_user_carrier(struct sip_msg * _msg, gparam_t *_user,
+		gparam_t *_domain, gparam_t *_dstavp);
+
+
+/**
+ * rewrites the request URI of msg after determining the
+ * new destination URI
+ *
+ * @param _msg the current SIP message
+ * @param _carrier the requested carrier
+ * @param _domain the requested routing domain
+ * @param _prefix_matching the user to be used for prefix matching
+ * @param _rewrite_user the localpart of the URI to be rewritten
+ * @param _hsrc the SIP header used for hashing
+ * @param _dstavp the name of the destination AVP where the used host name is stored
+ *
+ * @return 1 on success, -1 on failure
+ */
+int cr_route(struct sip_msg * _msg, gparam_t *_carrier,
+		gparam_t *_domain, gparam_t *_prefix_matching,
+		gparam_t *_rewrite_user, enum hash_source _hsrc,
+		gparam_t *_dstavp);
+
+
+/**
+ * rewrites the request URI of msg after determining the
+ * new destination URI
+ *
+ * @param _msg the current SIP message
+ * @param _carrier the requested carrier
+ * @param _domain the requested routing domain
+ * @param _prefix_matching the user to be used for prefix matching
+ * @param _rewrite_user the localpart of the URI to be rewritten
+ * @param _hsrc the SIP header used for hashing
+ * @param _dstavp the name of the destination AVP where the used host name is stored
+ *
+ * @return 1 on success, -1 on failure
+ */
+int cr_prime_route(struct sip_msg * _msg, gparam_t *_carrier,
+		gparam_t *_domain, gparam_t *_prefix_matching,
+		gparam_t *_rewrite_user, enum hash_source _hsrc,
+		gparam_t *_dstavp);
+
+
+/**
+ * Loads next domain from failure routing table and stores it in an AVP.
+ *
+ * @param _msg the current SIP message
+ * @param _carrier the requested carrier
+ * @param _domain the requested routing domain
+ * @param _prefix_matching the user to be used for prefix matching
+ * @param _host the host name to be used for rule matching
+ * @param _reply_code the reply code to be used for rule matching
+ * @param _dstavp the name of the destination AVP
+ *
+ * @return 1 on success, -1 on failure
+ */
+int cr_load_next_domain(struct sip_msg * _msg, gparam_t *_carrier,
+		gparam_t *_domain, gparam_t *_prefix_matching, gparam_t *_host,
+		gparam_t *_reply_code, gparam_t *_dstavp);
+
+#endif

+ 89 - 0
modules/carrierroute/cr_map.c

@@ -0,0 +1,89 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_map.c
+ * \brief Contains the functions to map domain and carrier names to ids.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#include <stdlib.h>
+#include "cr_map.h"
+#include "../../mem/shm_mem.h"
+#include "../../ut.h"
+
+
+
+/**
+ * Searches for the ID of a name
+ *
+ * @param map the mapping list to search in
+ * @param size the size of the list
+ * @param name the name, we are looking for
+ *
+ * @return values: on succcess the id for this name, -1 on failure
+ */
+int map_name2id(struct name_map_t * map, int size, const str * name) {
+	int i;
+
+	if ((!name) || (name->len <= 0)) {
+		return -1;
+	}
+
+	for (i=0; i<size; i++) {
+		if (str_strcmp(&map[i].name, name) == 0) return map[i].id;
+	}
+	return -1;
+}
+
+
+/**
+ * Searches for the name of an ID
+ *
+ * @param map the mapping list to search in
+ * @param size the size of the list
+ * @param id the id, we are looking for
+ *
+ * @return values: on succcess the name for this id, NULL on failure
+ */
+str * map_id2name(struct name_map_t * map, int size, int id) {
+	struct name_map_t key;
+	struct name_map_t * tmp;
+
+	key.id = id;
+	tmp = bsearch(&key, map, size, sizeof(struct name_map_t), compare_name_map);
+	if (tmp == NULL) return NULL;
+	return &tmp->name;
+}
+
+
+/**
+ * Compares the IDs of two name_map_t structures.
+ *
+ * @return -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
+ */
+int compare_name_map(const void *v1, const void *v2) {
+	if (((struct name_map_t *)v1)->id < ((struct name_map_t *)v2)->id) return -1;
+	else if (((struct name_map_t *)v1)->id > ((struct name_map_t *)v2)->id) return 1;
+	else return 0;
+}

+ 79 - 0
modules/carrierroute/cr_map.h

@@ -0,0 +1,79 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_map.h
+ * \brief Contains the functions to map domain and carrier names to ids.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#ifndef CR_MAP_H
+#define CR_MAP_H
+
+#include "../../str.h"
+
+
+/**
+ * used to map names to numbers for faster access.
+ */
+struct name_map_t {
+	str name; /*!< name of the routing domain or carrier */
+	int id; /*!< the corresponding id */
+};
+
+
+
+
+/**
+ * Searches for the ID of a name
+ *
+ * @param map the mapping list to search in
+ * @param size the size of the list
+ * @param name the name, we are looking for
+ *
+ * @return values: on succcess the id for this name, -1 on failure
+ */
+int map_name2id(struct name_map_t * map, int size, const str * name);
+
+
+/**
+ * Searches for the name of an ID
+ *
+ * @param map the mapping list to search in
+ * @param size the size of the list
+ * @param id the id, we are looking for
+ *
+ * @return values: on succcess the name for this id, NULL on failure
+ */
+str * map_id2name(struct name_map_t * map, int size, int id);
+
+
+/**
+ * Compares the IDs of two name_map_t structures.
+ *
+ * @return -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
+ */
+int compare_name_map(const void *v1, const void *v2);
+
+
+#endif

+ 526 - 0
modules/carrierroute/cr_rule.c

@@ -0,0 +1,526 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_rule.c
+ * \brief Contains the functions to manage routing rule data.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#include "../../ut.h"
+#include "cr_rule.h"
+
+
+/**
+ * Adds a route rule to rf. prefix, rewrite_hostpart, rewrite_local_prefix,
+ * rewrite_local_suffix, and comment must not contain NULL pointers.
+ *
+ * @param rf the current route_flags struct
+ * @param prefix the whole scan prefix
+ * @param max_targets the number of targets
+ * @param prob the weight of the rule
+ * @param rewrite_hostpart the rewrite_host of the rule
+ * @param strip the strip value of the rule
+ * @param rewrite_local_prefix the rewrite prefix
+ * @param rewrite_local_suffix the rewrite suffix
+ * @param status the status of the rule
+ * @param hash_index the hash index of the rule
+ * @param backup indicates if the route is backed up by another. only
+                 useful if status==0, if set, it is the hash value
+                 of another rule
+ * @param backed_up an NULL-termintated array of hash indices of the route
+                    for which this route is backup
+ * @param comment a comment for the route rule
+ *
+ * @return 0 on success, -1 on failure
+ *
+ * @see add_route_to_tree()
+ */
+int add_route_rule(struct route_flags *rf, const str * prefix,
+		int max_targets, double prob, const str * rewrite_hostpart, int strip,
+		const str * rewrite_local_prefix, const str * rewrite_local_suffix,
+		int status, int hash_index, int backup, int * backed_up, 
+		const str * comment) {
+	struct route_rule * shm_rr, * prev = NULL, * tmp = NULL;
+	struct route_rule_p_list * t_rl;
+	int * t_bu;
+
+	if (max_targets) {
+		rf->max_targets = max_targets;
+	} else {
+		rf->max_targets++;
+	}
+
+	if ((shm_rr = shm_malloc(sizeof(struct route_rule))) == NULL) {
+		SHM_MEM_ERROR;
+		return -1;
+	}
+	memset(shm_rr, 0, sizeof(struct route_rule));
+
+	if (shm_str_dup(&shm_rr->host, rewrite_hostpart) != 0) {
+		goto mem_error;
+	}
+
+	if (shm_str_dup(&shm_rr->prefix, prefix) != 0) {
+		goto mem_error;
+ 	}
+
+	shm_rr->strip = strip;
+
+	if (shm_str_dup(&shm_rr->local_prefix, rewrite_local_prefix) != 0) {
+		goto mem_error;
+	}
+
+	if (shm_str_dup(&shm_rr->local_suffix, rewrite_local_suffix) != 0) {
+		goto mem_error;
+	}
+
+	if (shm_str_dup(&shm_rr->comment, comment) != 0) {
+		goto mem_error;
+	}
+
+	shm_rr->status = status;
+	shm_rr->hash_index = hash_index;
+	shm_rr->orig_prob = prob;
+	if (shm_rr->status || backup != -1) {
+		shm_rr->prob = prob;
+	}	else {
+	    shm_rr->prob = 0;
+	}
+	if (backup >= 0) {
+		if ((shm_rr->backup = shm_malloc(sizeof(struct route_rule_p_list))) == NULL) {
+			goto mem_error;
+		}
+		memset(shm_rr->backup, 0, sizeof(struct route_rule_p_list));
+		shm_rr->backup->hash_index = backup;
+	}
+	shm_rr->backed_up = NULL;
+	t_bu = backed_up;
+	if(!backed_up){
+		LM_INFO("no backed up rules\n");
+	}
+	while (t_bu && *t_bu != -1) {
+		if ((t_rl = shm_malloc(sizeof(struct route_rule_p_list))) == NULL) {
+			goto mem_error;
+		}
+		memset(t_rl, 0, sizeof(struct route_rule_p_list));
+		t_rl->hash_index = *t_bu;
+		t_rl->next = shm_rr->backed_up;
+		shm_rr->backed_up = t_rl;
+		t_bu++;
+	}
+
+	/* rules with a probability of zero are always at the beginning of the list */
+	tmp = rf->rule_list;
+	while(tmp && tmp->prob == 0){
+		prev = tmp;
+		tmp = tmp->next;
+	}
+	/* rules with prob > 0 are sorted by hash_index */
+	while(tmp && (tmp->hash_index < shm_rr->hash_index)){
+		prev = tmp;
+		tmp = tmp->next;
+	}
+	if(prev){
+		shm_rr->next = prev->next;
+		prev->next = shm_rr;
+	} else {
+		shm_rr->next = rf->rule_list;
+		rf->rule_list = shm_rr;
+	}
+
+	return 0;
+
+mem_error:
+	SHM_MEM_ERROR;
+	destroy_route_rule(shm_rr);
+	return -1;
+}
+
+
+/**
+ * Destroys route rule rr by freeing all its memory.
+ *
+ * @param rr route rule to be destroyed
+ */
+void destroy_route_rule(struct route_rule * rr) {
+	struct route_rule_p_list * t_rl;
+	if (rr->host.s) {
+		shm_free(rr->host.s);
+	}
+	if (rr->local_prefix.s) {
+		shm_free(rr->local_prefix.s);
+	}
+	if (rr->local_suffix.s) {
+		shm_free(rr->local_suffix.s);
+	}
+	if (rr->comment.s) {
+		shm_free(rr->comment.s);
+	}
+	if (rr->prefix.s) {
+		shm_free(rr->prefix.s);
+	}
+	if(rr->backup){
+		shm_free(rr->backup);
+	}
+	while(rr->backed_up){
+		t_rl = rr->backed_up->next;
+		shm_free(rr->backed_up);
+		rr->backed_up = t_rl;
+	}
+	shm_free(rr);
+	return;
+}
+
+
+/**
+ * Try to find a matching route_flags struct in rt and return it, add it if not found.
+ *
+ * @param rf_head pointer to the head of the route flags list, might be changed during insert.
+ * @param flags user defined flags
+ * @param mask mask for user defined flags
+ *
+ * @return pointer to the route_flags struct on success, NULL on failure.
+ *
+ */
+struct route_flags * add_route_flags(struct route_flags **rf_head, const flag_t flags, const flag_t mask)
+{
+	struct route_flags *shm_rf;
+	struct route_flags *prev_rf, *tmp_rf;
+	prev_rf = tmp_rf = NULL;
+
+	if (rf_head) {
+		/* search for matching route_flags struct */
+		for (tmp_rf=*rf_head; tmp_rf!=NULL; tmp_rf=tmp_rf->next) {
+			if ((tmp_rf->flags == flags) && (tmp_rf->mask == mask)) return tmp_rf;
+		}
+		
+		/* not found, insert one */
+		for (tmp_rf=*rf_head; tmp_rf!=NULL; tmp_rf=tmp_rf->next) {
+			if (tmp_rf->mask < mask) break;
+			prev_rf=tmp_rf;
+		}
+	}
+
+	if ((shm_rf = shm_malloc(sizeof(struct route_flags))) == NULL) {
+		SHM_MEM_ERROR;
+		return NULL;
+	}
+	memset(shm_rf, 0, sizeof(struct route_flags));
+
+	shm_rf->flags=flags;
+	shm_rf->mask=mask;
+	shm_rf->next=tmp_rf;
+	
+	if (prev_rf) {
+		prev_rf->next = shm_rf;
+	}
+	else {
+		if (rf_head) *rf_head=shm_rf;
+	}
+
+	return shm_rf;
+}
+
+
+/**
+ * Destroys route_flags in shared memory by freing all its memory.
+ *
+ * @param rf route_flags struct to be destroyed
+ */
+void destroy_route_flags(struct route_flags *rf) {
+	struct route_rule *rs, *rs_tmp;
+
+	if (rf->rules) {
+		shm_free(rf->rules);
+	}
+	rs = rf->rule_list;
+	while (rs != NULL) {
+		rs_tmp = rs->next;
+		destroy_route_rule(rs);
+		rs = rs_tmp;
+	}
+	shm_free(rf);
+}
+
+
+/**
+ * Compares the priority of two failure route rules.
+ *
+ * @param frr1 first failure rule
+ * @param frr2 second failure rule
+ *
+ * @return 0 if frr1 and frr2 have the same priority, -1 if frr1 has higher priority than frr2, 1 if frr1 has lower priority than frr2.
+ *
+ * @see add_failure_route_to_tree()
+ */
+static int failure_rule_prio_cmp(struct failure_route_rule *frr1, struct failure_route_rule *frr2) {
+	int n1, n2, i;
+	
+	/* host has highest priority */
+	if ((frr1->host.len == 0) && (frr2->host.len > 0)) {
+		/* host1 is wildcard -> frr1 has lower priority */
+		return 1;
+	}
+	else if ((frr1->host.len > 0) && (frr2->host.len == 0)) {
+		/* host2 is wildcard -> frr1 has higher priority */
+		return -1;
+	}
+	else {
+		/* reply_code has second highest priority */
+		n1=0;
+		n2=0;
+		for (i=0; i < frr1->reply_code.len; i++) {
+			if (frr1->reply_code.s[i]=='.') n1++;
+		}
+		for (i=0; i < frr2->reply_code.len; i++) {
+			if (frr2->reply_code.s[i]=='.') n2++;
+		}
+		if (n1 < n2) {
+			/* reply_code1 has fewer wildcards -> frr1 has higher priority */
+			return -1;
+		}
+		else if (n1 > n2) {
+			/* reply_code1 has more wildcards -> frr1 has lower priority */
+			return 1;
+		}
+		else {
+			/* flags have lowest priority */
+			if (frr1->mask > frr2->mask) {
+				return -1;
+			}
+			else if (frr1->mask < frr2->mask) {
+				return 1;
+			}
+		}
+	}
+	
+	return 0;
+}
+
+
+/**
+ * Adds a failure route rule to rule list. prefix, host, reply_code, and comment
+ * must not contain NULL pointers.
+ *
+ * @param frr_head pointer to the head of the failure route rule list, might be changed during insert
+ * @param prefix the whole scan prefix
+ * @param host the hostname last tried
+ * @param reply_code the reply code 
+ * @param flags user defined flags
+ * @param mask mask for user defined flags
+ * @param next_domain continue routing with this domain
+ * @param comment a comment for the route rule
+ *
+ * @return pointer to the failure_route_rul struct on success, NULL on failure.
+ *
+ * @see add_failure_route_to_tree()
+ */
+struct failure_route_rule *add_failure_route_rule(struct failure_route_rule **frr_head,
+		const str * prefix, const str * host, const str * reply_code,
+		flag_t flags, flag_t mask, const int next_domain, const str * comment) {
+	struct failure_route_rule *shm_frr, *frr, *prev;
+	frr = prev = NULL;
+	
+	if ((shm_frr = shm_malloc(sizeof(struct failure_route_rule))) == NULL) {
+		SHM_MEM_ERROR;
+		return NULL;
+	}
+	memset(shm_frr, 0, sizeof(struct failure_route_rule));
+	
+	if (shm_str_dup(&shm_frr->host, host) != 0) {
+		goto mem_error;
+	}
+	
+	if (shm_str_dup(&shm_frr->reply_code, reply_code) != 0) {
+		goto mem_error;
+	}
+	
+	shm_frr->flags = flags;
+	shm_frr->mask = mask;
+	shm_frr->next_domain = next_domain;
+	
+	if (shm_str_dup(&shm_frr->comment, comment) != 0) {
+		goto mem_error;
+	}
+	
+	/* before inserting into list, check priorities! */
+	if (frr_head) {
+		frr=*frr_head;
+		prev=NULL;
+		while ((frr != NULL) && (failure_rule_prio_cmp(shm_frr, frr) > 0)) {
+			prev=frr;
+			frr=frr->next;
+		}
+	}
+
+	shm_frr->next = frr;
+
+	if(prev){
+		prev->next = shm_frr;
+	}
+	else {
+		if (frr_head) *frr_head=shm_frr;
+	}
+
+	return shm_frr;
+	
+mem_error:
+	SHM_MEM_ERROR;
+	destroy_failure_route_rule(shm_frr);
+	return NULL;
+}
+
+
+/**
+ * Destroys failure route rule frr by freeing all its memory.
+ *
+ * @param frr route rule to be destroyed
+ */
+void destroy_failure_route_rule(struct failure_route_rule * frr) {
+	if (frr->host.s) {
+		shm_free(frr->host.s);
+	}
+	if (frr->comment.s) {
+		shm_free(frr->comment.s);
+	}
+	if (frr->prefix.s) {
+		shm_free(frr->prefix.s);
+	}
+	if (frr->reply_code.s) {
+		shm_free(frr->reply_code.s);
+	}
+	shm_free(frr);
+	return;
+}
+
+
+struct route_rule * find_rule_by_hash(struct route_flags * rf, int hash){
+	struct route_rule * rr;
+	rr = rf->rule_list;
+	while(rr){
+		if(rr->hash_index == hash){
+			return rr;
+		}
+		rr = rr->next;
+	}
+	return NULL;
+}
+
+
+struct route_rule * find_rule_by_host(struct route_flags * rf, str * host){
+	struct route_rule * rr;
+	rr = rf->rule_list;
+	while(rr){
+		if(str_strcmp(&(rr->host), host) == 0){
+			return rr;
+		}
+		rr = rr->next;
+	}
+	return NULL;
+}
+
+
+int add_backup_rule(struct route_rule * rule, struct route_rule * backup){
+	struct route_rule_p_list * tmp = NULL;
+	if(!backup->status){
+		LM_ERR("desired backup route is inactive\n");
+		return -1;
+	}
+	if((tmp = shm_malloc(sizeof(struct route_rule_p_list))) == NULL) {
+		SHM_MEM_ERROR;
+		return -1;
+	}
+	memset(tmp, 0, sizeof(struct route_rule_p_list));
+	tmp->hash_index = rule->hash_index;
+	tmp->rr = rule;
+	tmp->next = backup->backed_up;
+	backup->backed_up =  tmp;
+
+	tmp = NULL;
+	if((tmp = shm_malloc(sizeof(struct route_rule_p_list))) == NULL) {
+		SHM_MEM_ERROR;
+		return -1;
+	}
+	memset(tmp, 0, sizeof(struct route_rule_p_list));
+	tmp->hash_index = backup->hash_index;
+	tmp->rr = backup;
+	rule->backup = tmp;
+
+	if(rule->backed_up){
+		tmp = rule->backed_up;
+		while(tmp->next) {
+			tmp = tmp->next;
+		}
+		tmp->next = backup->backed_up;
+		backup->backed_up = rule->backed_up;
+		rule->backed_up = NULL;
+	}
+	tmp = rule->backup->rr->backed_up;
+	while(tmp) {
+		tmp->rr->backup->hash_index = rule->backup->hash_index;
+		tmp->rr->backup->rr = rule->backup->rr;
+		tmp = tmp->next;
+	}
+	return 0;
+}
+
+
+int remove_backed_up(struct route_rule * rule){
+	struct route_rule_p_list * rl, * prev = NULL;
+	if(rule->backup) {
+		if(rule->backup->rr) {
+			rl = rule->backup->rr->backed_up;
+			while(rl) {
+				if(rl->hash_index == rule->hash_index) {
+					if(prev) {
+						prev->next = rl->next;
+					} else {
+						rule->backup->rr->backed_up = rl->next;
+					}
+					shm_free(rl);
+					shm_free(rule->backup);
+					rule->backup = NULL;
+					return 0;
+				}
+				prev = rl;
+				rl = rl->next;
+			}
+		}
+		return -1;
+	}
+	return 0;
+}
+
+
+struct route_rule * find_auto_backup(struct route_flags * rf, struct route_rule * rule){
+	struct route_rule * rr;
+	rr = rf->rule_list;
+	while(rr){
+		if(!rr->backed_up && (rr->hash_index != rule->hash_index) && rr->status){
+			return rr;
+		}
+		rr = rr->next;
+	}
+	return NULL;
+}

+ 199 - 0
modules/carrierroute/cr_rule.h

@@ -0,0 +1,199 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007-2008 1&1 Internet AG
+ *
+ * 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
+ */
+
+/**
+ * \file cr_rule.h
+ * \brief Contains the functions to manage routing rule data.
+ * \ingroup carrierroute
+ * - Module; \ref carrierroute
+ */
+
+#ifndef CR_RULE_H
+#define CR_RULE_H
+
+#include "../../str.h"
+#include "../../flags.h"
+
+
+/*! list of rules */
+struct route_rule_p_list;
+
+/**
+ * Second stage of processing: Try to map the end of the user part of the URI
+ * to a given suffix. Then rewrite with given parameters.
+ */
+struct route_rule {
+	int dice_to; /*!< prob * DICE_MAX */
+	double prob; /*!< The probability for that rule, only useful when using crc32 hashing */
+	double orig_prob; /*!< The original probability for that rule, only useful when using crc32 hashing */
+	str host; /*!< The new target host for the request */
+	int strip; /*!< the number of digits to be stripped off from uri befor prepending prefix */
+	str local_prefix; /*!< the pefix to be attached to the new destination */
+	str local_suffix; /*!< the suffix to be appended to the localpart of the new destination */
+	str comment; /*!< A comment for the route rule */
+	str prefix; /*!< The prefix for which the route is valid */
+	int status; /*!< The status of the route rule, only useful when using prime number hashing */
+	struct route_rule_p_list * backed_up; /*!< indicates if the rule is already backup route for another */
+	struct route_rule_p_list * backup; /*!< if not NULL, it points to a route rule which shall be used instead (only used if status is 0) */
+	int hash_index; /*!< The hash index of the route rule, only useful when using prime number hashing */
+	struct route_rule * next; /*!< A pointer to the next route rule */
+};
+
+/**
+ * list of routing rules with hash index
+ */
+struct route_rule_p_list {
+	struct route_rule * rr;
+	int hash_index;
+	struct route_rule_p_list * next;
+};
+
+/**
+ * Use route rules only if message flags match stored mask/flags.
+ */
+struct route_flags {
+	flag_t flags;  /*!< The flags for which the route ist valid */
+	flag_t mask;  /*!< The mask for the flags field */
+	struct route_rule * rule_list; /*!< Each node MAY contain a rule list */
+	struct route_rule ** rules; /*!< The array points to the rules in order of hash indices */
+	int rule_num; /*!< The number of rules */
+	int dice_max; /*!< The DICE_MAX value for the rule set, calculated by rule_fixup */
+	int max_targets; /*!< upper edge of hashing via prime number algorithm, must be eqal to rule_num */
+	struct route_flags * next; /*!< A pointer to the next route flags struct */
+};
+
+/**
+ * Second stage of processing: Try to map the end of the user part of the URI
+ * to a given suffix. Then rewrite with given parameters.
+ */
+struct failure_route_rule {
+	str host; /*!< The new target host for the request */
+	str comment; /*!< A comment for the route rule */
+	str prefix; /*!< The prefix for which the route ist valid */
+	str reply_code;  /*!< The reply code for which the route ist valid */
+	int next_domain;  /*!< The domain id where to continue routing */
+	flag_t flags;  /*!< The flags for which the route ist valid */
+	flag_t mask;  /*!< The mask for the flags field */
+	struct failure_route_rule * next; /*!< A pointer to the next route rule */
+};
+
+
+/**
+ * Adds a route rule to rf
+ *
+ * @param rf the current route_flags struct
+ * @param prefix the whole scan prefix
+ * @param max_targets the number of targets
+ * @param prob the weight of the rule
+ * @param rewrite_hostpart the rewrite_host of the rule
+ * @param strip the number of digits to be stripped off userpart before prepending prefix
+ * @param rewrite_local_prefix the rewrite prefix
+ * @param rewrite_local_suffix the rewrite suffix
+ * @param status the status of the rule
+ * @param hash_index the hash index of the rule
+ * @param backup indicates if the route is backed up by another. only
+                 useful if status==0, if set, it is the hash value
+                 of another rule
+ * @param backed_up an NULL-termintated array of hash indices of the route
+                    for which this route is backup
+ * @param comment a comment for the route rule
+ *
+ * @return 0 on success, -1 on failure
+ *
+ * @see add_route_to_tree()
+ */
+int add_route_rule(struct route_flags *rf, const str * prefix,
+		int max_targets, double prob, const str * rewrite_hostpart, int strip,
+		const str * rewrite_local_prefix, const str * rewrite_local_suffix,
+		int status, int hash_index, int backup, int * backed_up,
+		const str * comment);
+
+
+/**
+ * Destroys route rule rr by freeing all its memory.
+ *
+ * @param rr route rule to be destroyed
+ */
+void destroy_route_rule(struct route_rule * rr);
+
+
+/**
+ * Try to find a matching route_flags struct in rt and return it, add it if not found.
+ *
+ * @param rf_head pointer to the head of the route flags list, might be changed during insert
+ * @param flags user defined flags
+ * @param mask mask for user defined flags
+ *
+ * @return pointer to the route_flags struct on success, NULL on failure.
+ *
+ */
+struct route_flags * add_route_flags(struct route_flags **rf_head, const flag_t flags, const flag_t mask);
+
+
+/**
+ * Destroys route_flags in shared memory by freing all its memory.
+ *
+ * @param rf route_flags struct to be destroyed
+ */
+void destroy_route_flags(struct route_flags *rf);
+
+
+/**
+ * Adds a failure route rule to rule list. prefix, host, reply_code, and comment
+ * must not contain NULL pointers.
+ *
+ * @param frr_head pointer to the head of the failure route rule list, might be changed during insert
+ * @param prefix the whole scan prefix
+ * @param host the hostname last tried
+ * @param reply_code the reply code 
+ * @param flags user defined flags
+ * @param mask mask for user defined flags
+ * @param next_domain continue routing with this domain
+ * @param comment a comment for the route rule
+ *
+ * @return pointer to the failure_route_rul struct on success, NULL on failure.
+ *
+ * @see add_failure_route_to_tree()
+ */
+struct failure_route_rule *add_failure_route_rule(struct failure_route_rule **frr_head,
+		const str * prefix, const str * host, const str * reply_code,
+		flag_t flags, flag_t mask, const int next_domain, const str * comment);
+
+
+/**
+ * Destroys failure route rule frr by freeing all its memory.
+ *
+ * @param frr route rule to be destroyed
+ */
+void destroy_failure_route_rule(struct failure_route_rule * frr);
+
+struct route_rule * find_rule_by_hash(struct route_flags * rf, int hash);
+
+struct route_rule * find_rule_by_host(struct route_flags * rf, str * host);
+
+int add_backup_rule(struct route_rule * rule, struct route_rule * backup);
+
+int remove_backed_up(struct route_rule * rule);
+
+struct route_rule * find_auto_backup(struct route_flags * rf, struct route_rule * rule);
+
+#endif

+ 185 - 0
modules/carrierroute/db_carrierroute.c

@@ -0,0 +1,185 @@
+
+/*!
+ * \file
+ * \ingroup db
+ * \brief Database support for modules.
+ *
+ * Database support functions for modules.
+ *
+ * @cond
+ * WARNING:
+ * This file was autogenerated from the XML source file
+ * ../../modules/carrierroute/kamailio-carrierroute.xml.
+ * It can be regenerated by running 'make modules' in the db/schema
+ * directory of the source code. You need to have xsltproc and
+ * docbook-xsl stylesheets installed.
+ * ALL CHANGES DONE HERE WILL BE LOST IF THE FILE IS REGENERATED
+ * @endcond
+ */
+
+#include "db_carrierroute.h"
+
+/* database variables */
+/* TODO assign read-write or read-only URI, introduce a parameter in XML */
+
+//extern str carrierroute_db_url;
+db_con_t * carrierroute_dbh = NULL;
+db_func_t carrierroute_dbf;
+
+str carrierroute_table = str_init("carrierroute");
+
+/* column names */
+str carrierroute_id_col = str_init("id");
+str carrierroute_carrier_col = str_init("carrier");
+str carrierroute_domain_col = str_init("domain");
+str carrierroute_scan_prefix_col = str_init("scan_prefix");
+str carrierroute_flags_col = str_init("flags");
+str carrierroute_mask_col = str_init("mask");
+str carrierroute_prob_col = str_init("prob");
+str carrierroute_strip_col = str_init("strip");
+str carrierroute_rewrite_host_col = str_init("rewrite_host");
+str carrierroute_rewrite_prefix_col = str_init("rewrite_prefix");
+str carrierroute_rewrite_suffix_col = str_init("rewrite_suffix");
+str carrierroute_description_col = str_init("description");
+
+/* table version */
+const unsigned int carrierroute_version = 3;
+
+str carrierfailureroute_table = str_init("carrierfailureroute");
+
+/* column names */
+str carrierfailureroute_id_col = str_init("id");
+str carrierfailureroute_carrier_col = str_init("carrier");
+str carrierfailureroute_domain_col = str_init("domain");
+str carrierfailureroute_scan_prefix_col = str_init("scan_prefix");
+str carrierfailureroute_host_name_col = str_init("host_name");
+str carrierfailureroute_reply_code_col = str_init("reply_code");
+str carrierfailureroute_flags_col = str_init("flags");
+str carrierfailureroute_mask_col = str_init("mask");
+str carrierfailureroute_next_domain_col = str_init("next_domain");
+str carrierfailureroute_description_col = str_init("description");
+
+/* table version */
+const unsigned int carrierfailureroute_version = 2;
+
+str carrier_name_table = str_init("carrier_name");
+
+/* column names */
+str carrier_name_id_col = str_init("id");
+str carrier_name_carrier_col = str_init("carrier");
+
+/* table version */
+const unsigned int carrier_name_version = 1;
+
+str domain_name_table = str_init("domain_name");
+
+/* column names */
+str domain_name_id_col = str_init("id");
+str domain_name_domain_col = str_init("domain");
+
+/* table version */
+const unsigned int domain_name_version = 1;
+
+
+/*
+ * Closes the DB connection.
+ */
+void carrierroute_db_close(void) {
+	if (carrierroute_dbh) {
+		carrierroute_dbf.close(carrierroute_dbh);
+		carrierroute_dbh = NULL;
+	}
+}
+
+
+/*!
+ * Initialises the DB API, check the table version and closes the connection.
+ * This should be called from the mod_init function.
+ *
+ * \return 0 means ok, -1 means an error occured.
+ */
+int carrierroute_db_init(void) {
+	if (!carrierroute_db_url.s || !carrierroute_db_url.len) {
+		LM_ERR("you have to set the db_url module parameter.\n");
+		return -1;
+	}
+	if (db_bind_mod(&carrierroute_db_url, &carrierroute_dbf) < 0) {
+		LM_ERR("can't bind database module.\n");
+		return -1;
+	}
+	if ((carrierroute_dbh = carrierroute_dbf.init(&carrierroute_db_url)) == NULL) {
+		LM_ERR("can't connect to database.\n");
+		return -1;
+	}
+	if (
+	(db_check_table_version(&carrierroute_dbf, carrierroute_dbh, &carrierroute_table, carrierroute_version) < 0) ||
+	(db_check_table_version(&carrierroute_dbf, carrierroute_dbh, &carrierfailureroute_table, carrierfailureroute_version) < 0) ||
+	(db_check_table_version(&carrierroute_dbf, carrierroute_dbh, &carrier_name_table, carrier_name_version) < 0) ||
+	(db_check_table_version(&carrierroute_dbf, carrierroute_dbh, &domain_name_table, domain_name_version) < 0)
+	) {
+		LM_ERR("during table version check.\n");
+		carrierroute_db_close();
+		return -1;
+	}
+	carrierroute_db_close();
+	return 0;
+}
+
+
+/*!
+ * Initialize the DB connection without checking the table version and DB URL.
+ * This should be called from child_init. An already existing database
+ * connection will be closed, and a new one created.
+ *
+ * \return 0 means ok, -1 means an error occured.
+ */
+int carrierroute_db_open(void) {
+	if (carrierroute_dbh) {
+		carrierroute_dbf.close(carrierroute_dbh);
+	}
+	if ((carrierroute_dbh = carrierroute_dbf.init(&carrierroute_db_url)) == NULL) {
+		LM_ERR("can't connect to database.\n");
+		return -1;
+	}
+	return 0;
+}
+
+
+/*!
+ * Update the variable length after eventual assignments from the config script.
+ * This is necessary because we're using the 'str' type.
+ */
+void carrierroute_db_vars(void) {
+	if (carrierroute_db_url.s) carrierroute_db_url.len = strlen(carrierroute_db_url.s);
+	carrierroute_table.len = strlen(carrierroute_table.s);
+	carrierroute_id_col.len = strlen(carrierroute_id_col.s);
+	carrierroute_carrier_col.len = strlen(carrierroute_carrier_col.s);
+	carrierroute_domain_col.len = strlen(carrierroute_domain_col.s);
+	carrierroute_scan_prefix_col.len = strlen(carrierroute_scan_prefix_col.s);
+	carrierroute_flags_col.len = strlen(carrierroute_flags_col.s);
+	carrierroute_mask_col.len = strlen(carrierroute_mask_col.s);
+	carrierroute_prob_col.len = strlen(carrierroute_prob_col.s);
+	carrierroute_strip_col.len = strlen(carrierroute_strip_col.s);
+	carrierroute_rewrite_host_col.len = strlen(carrierroute_rewrite_host_col.s);
+	carrierroute_rewrite_prefix_col.len = strlen(carrierroute_rewrite_prefix_col.s);
+	carrierroute_rewrite_suffix_col.len = strlen(carrierroute_rewrite_suffix_col.s);
+	carrierroute_description_col.len = strlen(carrierroute_description_col.s);
+	carrierfailureroute_table.len = strlen(carrierfailureroute_table.s);
+	carrierfailureroute_id_col.len = strlen(carrierfailureroute_id_col.s);
+	carrierfailureroute_carrier_col.len = strlen(carrierfailureroute_carrier_col.s);
+	carrierfailureroute_domain_col.len = strlen(carrierfailureroute_domain_col.s);
+	carrierfailureroute_scan_prefix_col.len = strlen(carrierfailureroute_scan_prefix_col.s);
+	carrierfailureroute_host_name_col.len = strlen(carrierfailureroute_host_name_col.s);
+	carrierfailureroute_reply_code_col.len = strlen(carrierfailureroute_reply_code_col.s);
+	carrierfailureroute_flags_col.len = strlen(carrierfailureroute_flags_col.s);
+	carrierfailureroute_mask_col.len = strlen(carrierfailureroute_mask_col.s);
+	carrierfailureroute_next_domain_col.len = strlen(carrierfailureroute_next_domain_col.s);
+	carrierfailureroute_description_col.len = strlen(carrierfailureroute_description_col.s);
+	carrier_name_table.len = strlen(carrier_name_table.s);
+	carrier_name_id_col.len = strlen(carrier_name_id_col.s);
+	carrier_name_carrier_col.len = strlen(carrier_name_carrier_col.s);
+	domain_name_table.len = strlen(domain_name_table.s);
+	domain_name_id_col.len = strlen(domain_name_id_col.s);
+	domain_name_domain_col.len = strlen(domain_name_domain_col.s);
+}
+

+ 161 - 0
modules/carrierroute/db_carrierroute.h

@@ -0,0 +1,161 @@
+
+/*!
+ * \file
+ * \ingroup db
+ * \brief Database support for modules.
+ *
+ * Database support functions for modules.
+ *
+ * @cond
+ * WARNING:
+ * This file was autogenerated from the XML source file
+ * ../../modules/carrierroute/kamailio-carrierroute.xml.
+ * It can be regenerated by running 'make modules' in the db/schema
+ * directory of the source code. You need to have xsltproc and
+ * docbook-xsl stylesheets installed.
+ * ALL CHANGES DONE HERE WILL BE LOST IF THE FILE IS REGENERATED
+ * @endcond
+ */
+
+#ifndef db_carrierroute_h
+#define db_carrierroute_h
+
+
+/* necessary includes */
+#include "../../db/db.h"
+#include "../../str.h"
+#include "../../ut.h"
+
+#include <string.h>
+
+
+/* database variables */
+
+extern str carrierroute_db_url;
+extern db_con_t * carrierroute_dbh;
+extern db_func_t carrierroute_dbf;
+
+#define carrierroute_DB_URL { "db_url", STR_PARAM, &carrierroute_db_url.s },
+
+#define carrierroute_DB_TABLE { "carrierroute_table", STR_PARAM, &carrierroute_table.s },
+
+extern str carrierroute_table;
+
+/* column names */
+extern str carrierroute_id_col;
+extern str carrierroute_carrier_col;
+extern str carrierroute_domain_col;
+extern str carrierroute_scan_prefix_col;
+extern str carrierroute_flags_col;
+extern str carrierroute_mask_col;
+extern str carrierroute_prob_col;
+extern str carrierroute_strip_col;
+extern str carrierroute_rewrite_host_col;
+extern str carrierroute_rewrite_prefix_col;
+extern str carrierroute_rewrite_suffix_col;
+extern str carrierroute_description_col;
+#define carrierroute_DB_COLS \
+{ "carrierroute_id_col", STR_PARAM, &carrierroute_id_col.s }, \
+{ "carrierroute_carrier_col", STR_PARAM, &carrierroute_carrier_col.s }, \
+{ "carrierroute_domain_col", STR_PARAM, &carrierroute_domain_col.s }, \
+{ "carrierroute_scan_prefix_col", STR_PARAM, &carrierroute_scan_prefix_col.s }, \
+{ "carrierroute_flags_col", STR_PARAM, &carrierroute_flags_col.s }, \
+{ "carrierroute_mask_col", STR_PARAM, &carrierroute_mask_col.s }, \
+{ "carrierroute_prob_col", STR_PARAM, &carrierroute_prob_col.s }, \
+{ "carrierroute_strip_col", STR_PARAM, &carrierroute_strip_col.s }, \
+{ "carrierroute_rewrite_host_col", STR_PARAM, &carrierroute_rewrite_host_col.s }, \
+{ "carrierroute_rewrite_prefix_col", STR_PARAM, &carrierroute_rewrite_prefix_col.s }, \
+{ "carrierroute_rewrite_suffix_col", STR_PARAM, &carrierroute_rewrite_suffix_col.s }, \
+{ "carrierroute_description_col", STR_PARAM, &carrierroute_description_col.s }, \
+
+/* table version */
+extern const unsigned int carrierroute_version;
+
+#define carrierfailureroute_DB_TABLE { "carrierfailureroute_table", STR_PARAM, &carrierroute_table.s },
+
+extern str carrierfailureroute_table;
+
+/* column names */
+extern str carrierfailureroute_id_col;
+extern str carrierfailureroute_carrier_col;
+extern str carrierfailureroute_domain_col;
+extern str carrierfailureroute_scan_prefix_col;
+extern str carrierfailureroute_host_name_col;
+extern str carrierfailureroute_reply_code_col;
+extern str carrierfailureroute_flags_col;
+extern str carrierfailureroute_mask_col;
+extern str carrierfailureroute_next_domain_col;
+extern str carrierfailureroute_description_col;
+#define carrierfailureroute_DB_COLS \
+{ "carrierfailureroute_id_col", STR_PARAM, &carrierfailureroute_id_col.s }, \
+{ "carrierfailureroute_carrier_col", STR_PARAM, &carrierfailureroute_carrier_col.s }, \
+{ "carrierfailureroute_domain_col", STR_PARAM, &carrierfailureroute_domain_col.s }, \
+{ "carrierfailureroute_scan_prefix_col", STR_PARAM, &carrierfailureroute_scan_prefix_col.s }, \
+{ "carrierfailureroute_host_name_col", STR_PARAM, &carrierfailureroute_host_name_col.s }, \
+{ "carrierfailureroute_reply_code_col", STR_PARAM, &carrierfailureroute_reply_code_col.s }, \
+{ "carrierfailureroute_flags_col", STR_PARAM, &carrierfailureroute_flags_col.s }, \
+{ "carrierfailureroute_mask_col", STR_PARAM, &carrierfailureroute_mask_col.s }, \
+{ "carrierfailureroute_next_domain_col", STR_PARAM, &carrierfailureroute_next_domain_col.s }, \
+{ "carrierfailureroute_description_col", STR_PARAM, &carrierfailureroute_description_col.s }, \
+
+/* table version */
+extern const unsigned int carrierfailureroute_version;
+
+#define carrier_name_DB_TABLE { "carrier_name_table", STR_PARAM, &carrierroute_table.s },
+
+extern str carrier_name_table;
+
+/* column names */
+extern str carrier_name_id_col;
+extern str carrier_name_carrier_col;
+#define carrier_name_DB_COLS \
+{ "carrier_name_id_col", STR_PARAM, &carrier_name_id_col.s }, \
+{ "carrier_name_carrier_col", STR_PARAM, &carrier_name_carrier_col.s }, \
+
+/* table version */
+extern const unsigned int carrier_name_version;
+
+#define domain_name_DB_TABLE { "domain_name_table", STR_PARAM, &carrierroute_table.s },
+
+extern str domain_name_table;
+
+/* column names */
+extern str domain_name_id_col;
+extern str domain_name_domain_col;
+#define domain_name_DB_COLS \
+{ "domain_name_id_col", STR_PARAM, &domain_name_id_col.s }, \
+{ "domain_name_domain_col", STR_PARAM, &domain_name_domain_col.s }, \
+
+/* table version */
+extern const unsigned int domain_name_version;
+
+
+/*
+ * Closes the DB connection.
+ */
+void carrierroute_db_close(void);
+
+/*!
+ * Initialises the DB API, check the table version and closes the connection.
+ * This should be called from the mod_init function.
+ *
+ * \return 0 means ok, -1 means an error occured.
+ */
+int carrierroute_db_init(void);
+
+/*!
+ * Initialize the DB connection without checking the table version and DB URL.
+ * This should be called from child_init. An already existing database
+ * connection will be closed, and a new one created.
+ *
+ * \return 0 means ok, -1 means an error occured.
+ */
+int carrierroute_db_open(void);
+
+/*!
+ * Update the variable length after eventual assignments from the config script.
+ * This is necessary because we're using the 'str' type.
+ */
+void carrierroute_db_vars(void);
+
+#endif

+ 66 - 0
modules/carrierroute/doc/carrierroute.xml

@@ -0,0 +1,66 @@
+<?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 "carrierroute_admin.xml">
+<!ENTITY db SYSTEM "carrierroute_db.xml">
+<!ENTITY faq SYSTEM "../../../doc/module_faq.xml">
+
+<!-- Include general Kamailio documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../doc/entities.xml">
+%docentities;
+
+]>
+
+<book>
+	<bookinfo>
+	<title>carrierroute</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+		<author>
+		<firstname>Jonas</firstname>
+		<surname>Appel</surname>
+		<affiliation><orgname>1&amp;1 Internet AG</orgname></affiliation>
+		</author>
+		<author>
+		<firstname>Hardy</firstname>
+		<surname>Kahl</surname>
+		<affiliation><orgname>1&amp;1 Internet AG</orgname></affiliation>
+		</author>
+		<author>
+		<firstname>Henning</firstname>
+		<surname>Westerholt</surname>
+		<affiliation><orgname>1&amp;1 Internet AG</orgname></affiliation>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</author>
+		<!--
+		<editor>
+		<firstname>EditorFirstname</firstname>
+		<surname>EditorLastname</surname>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</editor>
+		-->
+	</authorgroup>
+	<copyright>
+		<year>2007</year>
+		<holder>1&amp;1 Internet AG</holder>
+	</copyright>
+	<revhistory>
+		<revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+		</revision>
+	</revhistory>
+	</bookinfo>
+	<toc></toc>
+	
+	&admin;
+	&db;
+	&faq;
+	
+</book>

+ 1141 - 0
modules/carrierroute/doc/carrierroute_admin.xml

@@ -0,0 +1,1141 @@
+<!-- Module User's Guide -->
+
+<chapter>
+    <title>&adminguide;</title>
+
+    <section>
+	<title>Overview</title>
+	<para>A module which provides routing, balancing and blacklisting capabilities.</para>
+	<para>
+		The module provides routing, balancing and blacklisting capabilities.
+		It reads routing entries from a database source or from a config file at Kamailio
+		startup. It can uses one routing tree (for one carrier), or if needed for every user
+		a different routing tree (unique for each carrier) for number prefix based routing.
+		It supports several route tree domains,	e.g. for failback routes or different routing
+		rules for VoIP and PSTN targets.
+	</para>
+	<para>
+		Based on the tree, the module decides which number prefixes are forwarded to which
+		gateway. It can also distribute the traffic by ratio parameters. Furthermore, the
+		requests can be distributed by a hash funcion to predictable destinations. The hash
+		source is configurable, two different hash functions are available.
+	</para>
+	<para>
+		This modules scales up to more than a few million users, and is able to handle
+		more than several hundred thousand routing table entries. We recieved reports of
+		some setups that used more than a million routing table entries. It also supports
+		a large number of carriers and domains which can be efficiently looked up in most of
+		the cases (see below for more informations). In load balancing scenarios the usage
+		of the config file mode is recommended, to avoid the additional complexity that the
+		database driven routing creates.
+	</para>
+	<para>
+		Routing tables can be reloaded and edited (in config file mode) with the MI
+		interface, the config file is updated according the changes. This is not
+		implemented for the db interface, because its easier to do the changes
+		directly on the db. But the reload and dump functions works of course here
+		too.
+	</para>
+	<para>
+		Some module functionality is not fully available in the config file mode, as
+		it is not possible to specify all information that can be stored in the database
+		tables in the config file. Further information about these limitations is given
+		in later sections. For user based routing or LCR you should use the database mode.
+	</para>
+	<para>
+		In database mode, this module supports names and IDs for the carriers and domains.
+		When using IDs for the routing functions, efficient binary search is used to find the
+		needed data structures. If you are using constant strings as parameter, these will
+		be converted to IDs during the fixup procedure. However, if you are using AVPs as
+		parameter and they contain strings, this cannot be converted to IDs during the
+		fixup procedure. In that case linear search is performed to find the needed data
+		structures. So from a performance point of view it is better to pass only IDs in
+		AVPs to the routing functions.
+	</para>
+	<para>
+		Basically this module could be used as an replacement for the lcr and the 
+		dispatcher module, if you have certain performance, flexibility and/or 
+		integration requirements that these modules don't handle properly. But for 
+		small installations it probably make more sense to use the lcr and dispatcher
+		module.
+	</para>
+	<para>
+		If you want to use this module in failure routes, then you need to call
+		<quote>append_branch()</quote> after rewriting the request URI in order to
+		relay the message to the new target. Its also supportes the usage of database
+		derived failure routing descisions with the carrierfailureroute table.
+	</para>
+    </section>
+    <section>
+	<title>Dependencies</title>
+	<section>
+	    <title>&kamailio; Modules</title>
+	    <para>
+		The following module must be loaded before this module:
+	    	<itemizedlist>
+		    <listitem>
+			<para>
+				<emphasis>a database module</emphasis>, when a database is used as configuration data source.
+				Only SQL based databases are supported, as this module needs the capability to
+				issue raw queries. Its not possible to use the dbtext or db_berkeley module at the moment.
+			</para>
+		    </listitem>
+			<listitem>
+			<para>
+				The <emphasis>tm module</emphasis>, when you want to use the $T_reply_code pseudo-variable in
+				the <quote>cr_next_domain</quote> function.
+			</para>
+			</listitem>
+	    	</itemizedlist>
+	    </para>
+	</section>
+	<section>
+	    <title>External Libraries or Applications</title>
+	    <para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+		<itemizedlist>
+		    <listitem>
+			<para>
+				<emphasis>libconfuse</emphasis>, a configuration file parser library.
+				( http://www.nongnu.org/confuse/ )
+			</para>
+		    </listitem>
+		</itemizedlist>
+	    </para>
+	</section>
+    </section>
+    <section>
+	<title>Exported Parameters</title>
+    <section>
+	    <title><varname>subscriber_table</varname> (string)</title>
+	    <para>
+		    The name of the table containing the subscribers
+	    </para>
+	    <para>
+		    <emphasis>
+			    Default value is <quote>subscriber</quote>.
+		    </emphasis>
+	    </para>
+	    <example>
+		    <title>Set <varname>subscriber_table</varname> parameter</title>
+		    <programlisting format="linespecific">
+...
+modparam("carrierroute", "subscriber_table", "subscriber")
+...
+		    </programlisting>
+	    </example>
+    </section>
+
+    <section>
+	    <title><varname>subscriber_user_col</varname> (string)</title>
+	    <para>
+		    The name of the column in the subscriber table containing the usernames.
+	    </para>
+	    <para>
+		    <emphasis>
+			    Default value is <quote>username</quote>.
+		    </emphasis>
+	    </para>
+	    <example>
+		    <title>Set <varname>subscriber_user_col</varname> parameter</title>
+		    <programlisting format="linespecific">
+...
+modparam("carrierroute", "subscriber_user_col", "username")
+...
+		    </programlisting>
+	    </example>
+    </section>
+
+    <section>
+	    <title><varname>subscriber_domain_col</varname> (string)</title>
+	    <para>
+		    The name of the column in the subscriber table containing the domain of 
+		    the subscriber.
+	    </para>
+	    <para>
+		    <emphasis>
+			    Default value is <quote>domain</quote>.
+		    </emphasis>
+	    </para>
+	    <example>
+		    <title>Set <varname>subscriber_domain_col</varname> parameter</title>
+		    <programlisting format="linespecific">
+...
+modparam("carrierroute", "subscriber_domain_col", "domain")
+...
+		    </programlisting>
+	    </example>
+    </section>
+
+    <section>
+	    <title><varname>subscriber_carrier_col</varname> (string)</title>
+	    <para>
+		    The name of the column in the subscriber table containing the carrier id
+		    of the subscriber.
+
+	    </para>
+	    <para>
+		    <emphasis>
+			    Default value is <quote>cr_preferred_carrier</quote>.
+		    </emphasis>
+	    </para>
+	    <example>
+		    <title>Set <varname>subscriber_carrier_col</varname> parameter</title>
+		    <programlisting format="linespecific">
+...
+modparam("carrierroute", "subscriber_carrier_col", "cr_preferred_carrier")
+...
+		    </programlisting>
+	    </example>
+    </section>
+
+    <section>
+	    <title><varname>config_source</varname> (string)</title>
+	    <para>
+		    Specifies whether the module loads its config data from a file or from a
+		    database. Possible values are file or db.
+	    </para>
+	    <para>
+		    <emphasis>
+			    Default value is <quote>file</quote>.
+		    </emphasis>
+	    </para>
+	    <example>
+		    <title>Set <varname>config_source</varname> parameter</title>
+		    <programlisting format="linespecific">
+...
+modparam("carrierroute", "config_source", "file")
+...
+		    </programlisting>
+	    </example>
+    </section>
+
+    <section>
+	    <title><varname>config_file</varname> (string)</title>
+	    <para>
+		    Specifies the path to the config file.
+	    </para>
+	    <para>
+		    <emphasis>
+			    Default value is <quote>/etc/kamailio/carrierroute.conf</quote>.
+		    </emphasis>
+	    </para>
+	    <example>
+		    <title>Set <varname>config_file</varname> parameter</title>
+		    <programlisting format="linespecific">
+...
+modparam("carrierroute", "config_file", "/etc/kamailio/carrierroute.conf")
+...
+		    </programlisting>
+	    </example>
+    </section>
+
+    <section>
+	    <title><varname>default_tree</varname> (string)</title>
+	    <para>
+		    The name of the carrier tree used per default (if the current
+		    subscriber has no preferred tree)
+	    </para>
+	    <para>
+		    <emphasis>
+			    Default value is <quote>default</quote>.
+		    </emphasis>
+	    </para>
+	    <example>
+		    <title>Set <varname>default_tree</varname> parameter</title>
+		    <programlisting format="linespecific">
+...
+modparam("carrierroute", "default_tree", "default")
+...
+		    </programlisting>
+	    </example>
+    </section>
+
+    <section>
+	    <title><varname>use_domain</varname> (int)</title>
+	    <para>
+		    When using tree lookup per user, this parameter specifies whether
+		    to use the domain part for user matching or not.
+	    </para>
+	    <para>
+		    <emphasis>
+			    Default value is <quote>0</quote>.
+		    </emphasis>
+	    </para>
+	    <example>
+		    <title>Set <varname>use_domain</varname> parameter</title>
+		    <programlisting format="linespecific">
+...
+modparam("carrierroute", "use_domain", 0)
+...
+		    </programlisting>
+	    </example>
+    </section>
+
+    <section>
+	    <title><varname>fallback_default</varname> (int)</title>
+	    <para>
+		    This parameter defines the behaviour when using user-based tree
+		    lookup. If the user has a non-existing tree set and fallback_default
+		    is set to 1, the default tree is used. Otherwise, cr_user_rewrite_uri
+		    returns an error.
+	    </para>
+	    <para>
+		    <emphasis>
+			    Default value is <quote>1</quote>.
+		    </emphasis>
+	    </para>
+	    <example>
+		    <title>Set <varname>fallback_default</varname> parameter</title>
+		    <programlisting format="linespecific">
+...
+modparam("carrierroute", "fallback_default", 1)
+...
+		    </programlisting>
+	    </example>
+    </section>
+		<section>
+		<title><varname>fetch_rows</varname> (integer)</title>
+		<para>
+		The number of the rows to be fetched at once from database
+		when loading the routing data. This value can be used to tune
+		the load time at startup. For 1MB of private memory (default)
+		it should be below 3750. The database driver must support the
+		fetch_result() capability.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>2000</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>fetch_rows</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("carrierroute", "fetch_rows", 3000)
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>match_mode</varname> (integer)</title>
+		<para>
+		The number of individual characters that are used for matching.
+		Valid values are 10 or 128. When you specifiy 10, only digits
+		will be used for matching, this operation mode is equivalent to
+		the old behaviour. When configured with 128, all standard ascii
+		chars are available for matching. Please be aware that memory
+		requirements for storing the routing tree in shared memory
+		will also increase by a factor of 12.8.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>10</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>match_mode</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("carrierroute", "match_mode", 10)
+...
+</programlisting>
+		</example>
+	</section>
+</section>
+
+    <section>
+	<title>Exported Functions</title>
+	<para>
+    Previous versions of carrierroute had some more function. All the
+    old semantics can be achieved by using the few new functions
+    like this:
+	</para>
+
+  <programlisting format="linespecific">
+cr_rewrite_uri(domain, hash_source)
+-> cr_route("default", domain, "$rU", "$rU", hash_source)
+
+cr_prime_balance_uri(domain, hash_source)
+-> cr_prime_route("default", domain, "$rU", "$rU", hash_source)
+
+cr_rewrite_by_to(domain, hash_source)
+-> cr_route("default", domain, "$tU", "$rU", hash_source)
+
+cr_prime_balance_by_to(domain, hash_source)
+-> cr_prime_route("default", domain, "$tU", "$rU", hash_source)
+
+cr_rewrite_by_from(domain, hash_source)
+-> cr_route("default", domain, "$fU", "$rU", hash_source)
+
+cr_prime_balance_by_from(domain, hash_source)
+-> cr_prime_route("default", domain, "$fU", "$rU", hash_source)
+
+cr_user_rewrite_uri(uri, domain)
+-> cr_user_carrier(user, domain, "$avp(tree_avp)")
+-> cr_route("$avp(tree_avp)", domain, "$rU", "$rU", "call_id")
+
+cr_tree_rewrite_uri(tree, domain)
+-> cr_route(tree, domain, "$rU", "$rU", "call_id")
+  </programlisting>
+		
+	<section>
+	    <title>
+		<function moreinfo="none">cr_user_carrier(user, domain, dstavp)</function>
+	    </title>
+	    <para>
+      This function loads the carrier and stores it in an AVP.
+      It cannot be used in the config file mode, as it needs a mapping of the
+	  given user to a certain carrier. The is derived from a database entry
+	  belonging to the user parameter. This mapping must be available in the
+	  table that is specified in the <quote>subscriber_table</quote> variable.
+	  This data is not cached in memory, that means for every execution of this
+	  function a database query will be done.
+	    </para>
+	    <para>Meaning of the parameters is as follows:</para>
+	    <itemizedlist>
+        <listitem>
+          <para><emphasis>user</emphasis> - Name of the user for the carrier tree lookup.
+            Additional to a string any pseudo-variable could
+            be used as input.
+		      </para>
+        </listitem>
+        <listitem>
+          <para><emphasis>domain</emphasis> - Name of the routing domain to be used.
+            Additional to a string any pseudo-variable could
+            be used as input.
+		      </para>
+        </listitem>
+	      <listitem>
+          <para><emphasis>dstavp</emphasis> - Name of the AVP where to store the carrier id.
+		      </para>
+        </listitem>
+	    </itemizedlist>
+	</section>
+	<section>
+	    <title>
+		<function moreinfo="none">cr_route(carrier, domain, prefix_matching, rewrite_user, hash_source, descavp)</function>
+	    </title>
+	    <para>
+        This function searches for the longest match for the user given
+        in prefix_matching at the given domain in the given carrier tree.
+        The Request URI is rewritten using rewrite_user and the given
+        hash source and algorithm. Returns -1 if there is no data found
+        or an empty rewrite host on the longest match is found. On sucess
+        also the description is stored in the given AVP (if obmitted, nothing
+        is stored in an AVP). This is useful if you need some additional
+        informations that belongs to each gw, like the destination or the
+        number of channels.
+        </para>
+        <para>
+        This function is only usable with rewrite_user and prefix_matching
+        containing a valid string. This string needs to be numerical if the match_mode
+        parameter is set to 10. It uses the standard CRC32 algorithm to calculate
+        the hash values.
+        </para>
+        <para>
+        If flags and masks values are specified in the routing rule, they will be
+        compared by this function to the message flags. Specify a flag and mask value of
+        <quote>0</quote> to match to all possible message flags (this is the default value).
+        If flags and mask are not zero, and no match to the message flags is possible, no
+        routing will be done. The calculation of the hash and the load-balancing is done
+        after the flags matching.
+	    </para>
+	    <para>Meaning of the parameters is as follows:</para>
+	    <itemizedlist>
+        <listitem>
+		      <para><emphasis>carrier</emphasis> - The routing tree to be used. Additional to a string
+            any pseudo-variable could be used as input.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>domain</emphasis> - Name of the routing domain to be used.
+            Additional to a string any pseudo-variable could
+            be used as input.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>prefix_matching</emphasis> - User name to be used for prefix matching
+            in the routing tree.
+            Additional to a string any pseudo-variable could
+            be used as input.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>rewrite_user</emphasis> - The user name to be used for applying the
+            rewriting rule. Usually this is the user part of the request
+            URI. Additional to a string any pseudo-variable could
+            be used as input.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>hash_source</emphasis> - The hash values of the destination set must
+            be a contiguous range starting at 1, limited by the
+            configuration parameter max_targets. Possible values for
+            hash_source are: call_id, from_uri, from_user, to_uri
+            and to_user.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>decsavp</emphasis> - Name of the AVP where to store the description.
+			This parameter is optional.
+  		    </para>
+        </listitem>
+	    </itemizedlist>
+	</section>
+		<section>
+	    <title>
+		<function moreinfo="none">cr_prime_route(carrier, domain, prefix_matching, rewrite_user, hash_source, descavp)</function>
+	    </title>
+	    <para>
+        This function searches for the longest match for the user given
+        in prefix_matching at the given domain in the given carrier tree.
+        The Request URI is rewritten using rewrite_user and the given
+        hash source and algorithm. Returns -1 if there is no data found
+        or an empty rewrite host on the longest match is found. On success
+        also the description is stored in the given AVP (if obmitted, nothing
+        is stored in an AVP). This is useful if you need some additional
+        informations that belongs to each gw, like the destination or the
+        number of channels.
+        This function is only usable with rewrite_user and prefix_matching
+        containing a valid string. This string needs to be numerical if the match_mode
+		parameter is set to 10. It uses the prime hash algorithm to calculate the hash values.
+	    </para>
+	    <para>Meaning of the parameters is as follows:</para>
+	    <itemizedlist>
+        <listitem>
+		      <para><emphasis>carrier</emphasis> - The routing tree to be used. Additional to a string
+            any pseudo-variable could be used as input.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>domain</emphasis> - Name of the routing domain to be used.
+            Additional to a string any pseudo-variable could
+            be used as input.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>prefix_matching</emphasis> - User name to be used for prefix matching
+            in the routing tree.
+            Additional to a string any pseudo-variable could
+            be used as input.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>rewrite_user</emphasis> - The user name to be used for applying the
+            rewriting rule. Usually this is the user part of the request
+            URI. Additional to a string any pseudo-variable could
+            be used as input.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>hash_source</emphasis> - The hash values of the destination set must
+            be a contiguous range starting at 1, limited by the
+            configuration parameter max_targets. Possible values for
+            hash_source are: call_id, from_uri, from_user, to_uri
+            and to_user.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>descavp</emphasis> - Name of the AVP where to store the description.
+			This parameter is optional.
+  		    </para>
+        </listitem>
+	    </itemizedlist>
+	</section>
+
+	<section>
+	    <title>
+		<function moreinfo="none">cr_next_domain(carrier, domain, prefix_matching, host, reply_code, dstavp)</function>
+	    </title>
+	    <para>
+        This function searches for the longest match for the user given
+        in prefix_matching at the given domain in the given carrier
+        failure tree. It tries to find a next domain matching the given
+        host, reply_code and the message flags. The matching is done in this order:
+        host, reply_code and then flags. The more wildcards in reply_code
+        and the more bits used in flags, the lower the priority.
+        Returns -1 if there is no data found or an empty next_domain on
+        the longest match is found. Otherwise the next domain is stored
+        in the given AVP.
+        This function is only usable with rewrite_user and prefix_matching
+        containing a valid string. This string needs to be numerical if the match_mode
+		parameter is set to 10.
+	    </para>
+	    <para>Meaning of the parameters is as follows:</para>
+	    <itemizedlist>
+        <listitem>
+		      <para><emphasis>carrier</emphasis> - The routing tree to be used. Additional to a string
+            any pseudo-variable could be used as input.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>domain</emphasis> - Name of the routing domain to be used.
+            Additional to a string any pseudo-variable could
+            be used as input.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>prefix_matching</emphasis> - User name to be used for prefix matching
+            in the routing tree.
+            Additional to a string any pseudo-variable could
+            be used as input.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>host</emphasis> - The host name to be used for failure route rule
+            matching. Usually this is the last tried routing destination
+            stored in an avp by cr_route. Additional to a string any
+            pseudo-variable could be used as input.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>reply_code</emphasis> - The reply code to be used for failure route rule
+            matching. Additional to a string any pseudo-variable
+            could be used as input.
+  		    </para>
+        </listitem>
+        <listitem>
+		      <para><emphasis>dstavp</emphasis> - Name of the AVP where to store the next routing domain.
+  		    </para>
+        </listitem>
+	    </itemizedlist>
+	</section>
+    </section>
+
+    <section>
+	    <title><acronym>MI</acronym> Commands</title>
+		<para>All commands understand the "-?" parameter to print a short help message.
+		The options have to be quoted as one string to be passed to MI interface.
+		Each option except host and new host can be wildcarded by * (but only * and not things
+		like "-d prox*").</para>
+	<section>
+	    <title><function moreinfo="none">cr_reload_routes</function></title>
+	    <para>
+		This command reloads the routing data from the data source.
+		</para>
+		<para>
+		Important: When new domains have been added, a restart of the server must be
+		done, because the mapping of the ids used in the config script cannot be
+		updated at runtime at the moment. So a reload could result in a wrong routing
+		behaviour, because the ids used in the script could differ from the one used
+		internally from the server. Modifying of already existing domains is no problem.
+	    </para>
+	</section>
+	<section>
+	    <title><function moreinfo="none">cr_dump_routes</function></title>
+	    <para>
+		This command prints the route rules on the command line.
+	    </para>
+	</section>
+	<section>
+	    <title><function moreinfo="none">cr_replace_host</function></title>
+	    <para>
+		This command can replace the rewrite_host of a route rule, it is only
+		usable in file mode. Following options are possible:
+	    </para>
+	    <itemizedlist>
+		<listitem>
+		    <para><emphasis>-d</emphasis> - the domain containing the host</para>
+	        </listitem>
+		<listitem>
+		    <para><emphasis>-p</emphasis> - the prefix containing the host</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-h</emphasis> - the host to be replaced</para>
+		</listitem>
+		<listitem>
+			<para><emphasis>-t</emphasis> - the new host</para>
+		</listitem>
+	    </itemizedlist>
+	    <para>Use the "null" prefix to specify an empty prefix.</para>
+	    <example>
+		<title><function>cr_replace_host</function> usage</title>
+		<programlisting format="linespecific">
+...
+kamctl fifo cr_replace_host "-d proxy -p 49 -h proxy1 -t proxy2"
+...
+		</programlisting>
+	    </example>
+	</section>
+	<section>
+	    <title><function moreinfo="none">cr_deactivate_host</function></title>
+	    <para>
+		    This command deactivates the specified host, i.e. it sets its status to 0.
+		    It is only usable in file mode. Following options are possible:
+	    </para>
+	    <itemizedlist>
+		<listitem>
+		    <para><emphasis>-d</emphasis> - the domain containing the host</para>
+	        </listitem>
+		<listitem>
+		    <para><emphasis>-p</emphasis> - the prefix containing the host</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-h</emphasis> - the host to be deactivated</para>
+		</listitem>
+		<listitem>
+			<para><emphasis>-t</emphasis> - the new host used as backup</para>
+		</listitem>
+	    </itemizedlist>
+	    <para>When -t (new_host) is specified, the portion of traffic for the deactivated host
+		is routed to the host given by -t. This is indicated in the output of dump_routes.
+		The backup route is deactivated if the host is activated again.</para>
+		<para>Use the "null" prefix to specify an empty prefix.</para>
+	    <example>
+		<title><function>cr_deactivate_host</function> usage</title>
+		<programlisting format="linespecific">
+...
+kamctl fifo cr_deactivate_host "-d proxy -p 49 -h proxy1"
+...
+		</programlisting>
+	    </example>
+	</section>
+	<section>
+	    <title><function moreinfo="none">cr_activate_host</function></title>
+	    <para>
+		    This command activates the specified host, i.e. it sets its status to 1.
+		    It is only usable in file mode. Following options are possible:
+	    </para>
+	    <itemizedlist>
+		<listitem>
+		    <para><emphasis>-d</emphasis> - the domain containing the host</para>
+	        </listitem>
+		<listitem>
+		    <para><emphasis>-p</emphasis> - the prefix containing the host</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-h</emphasis> - the host to be activated</para>
+		</listitem>
+	    </itemizedlist>
+	    <para>Use the "null" prefix to specify an empty prefix.</para>
+	    <example>
+		<title><function>cr_activate_host</function> usage</title>
+		<programlisting format="linespecific">
+...
+kamctl fifo cr_activate_host "-d proxy -p 49 -h proxy1"
+...
+		</programlisting>
+	    </example>
+	</section>
+
+	<section>
+	    <title><function moreinfo="none">cr_add_host</function></title>
+	    <para>
+		    This command adds a route rule, it is only usable in file mode. Following options
+		    are possible:
+	    </para>
+	    <itemizedlist>
+		<listitem>
+		    <para><emphasis>-d</emphasis> - the domain containing the host</para>
+	        </listitem>
+		<listitem>
+		    <para><emphasis>-p</emphasis> - the prefix containing the host</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-h</emphasis> - the host to be added</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-w</emphasis> - the weight of the rule</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-P</emphasis> - an optional rewrite prefix</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-S</emphasis> - an optional rewrite suffix</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-i</emphasis> - an optional hash index</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-s</emphasis> - an optional strip value</para>
+		</listitem>
+	    </itemizedlist>
+		<para>Use the "null" prefix to specify an empty prefix.</para>
+	    <example>
+		<title><function>cr_add_host</function> usage</title>
+		<programlisting format="linespecific">
+...
+kamctl fifo cr_add_host "-d proxy -p 49 -h proxy1 -w 0.25"
+...
+		</programlisting>
+	    </example>
+	</section>
+
+	<section>
+	    <title><function moreinfo="none">cr_delete_host</function></title>
+	    <para>
+		    This command delete the specified hosts or rules, i.e. remove 
+		    them from the route tree. It is only usable in file mode.
+		    Following options are possible:
+	    </para>
+	    <itemizedlist>
+		<listitem>
+		    <para><emphasis>-d</emphasis> - the domain containing the host</para>
+	        </listitem>
+		<listitem>
+		    <para><emphasis>-p</emphasis> - the prefix containing the host</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-h</emphasis> - the host to be added</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-w</emphasis> - the weight of the rule</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-P</emphasis> - an optional rewrite prefix</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-S</emphasis> - an optional rewrite suffix</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-i</emphasis> - an optional hash index</para>
+		</listitem>
+		<listitem>
+		    <para><emphasis>-s</emphasis> - an optional strip value</para>
+		</listitem>
+	    </itemizedlist>
+	    <para>Use the "null" prefix to specify an empty prefix.</para>
+	    <example>
+		<title><function>cr_delete_host</function> usage</title>
+		<programlisting format="linespecific">
+...
+kamctl fifo cr_delete_host "-d proxy -p 49 -h proxy1 -w 0.25"
+...
+		</programlisting>
+	    </example>
+	</section>
+    </section>
+    <section>
+	<title>Configuration examples</title>
+	<example>
+		<title>Configuration example - Routing to default tree</title>
+		<programlisting format="linespecific">
+...
+route {
+	# route calls based on hash over callid
+	# choose route domain 0 of the default carrier
+	
+	if(!cr_route("default", "0", "$rU", "$rU", "call_id", "crc32")){
+		sl_send_reply("403", "Not allowed");
+	} else {
+		# In case of failure, re-route the request
+		t_on_failure("1");
+		# Relay the request to the gateway
+		t_relay();
+	}
+}
+
+failure_route[1] {
+	# In case of failure, send it to an alternative route:
+	if (t_check_status("408|5[0-9][0-9]")) {
+		#choose route domain 1 of the default carrier
+	if(!cr_route("default", "1", "$rU", "$rU", "call_id", "crc32")){
+			t_reply("403", "Not allowed");
+		} else {
+			t_on_failure("2");
+			t_relay();
+		}
+	}
+}
+
+failure_route[2] {
+	# further processing
+}
+
+		</programlisting>
+	</example>
+	
+	<example>
+		<title>Configuration example - Routing to user tree</title>
+		<programlisting format="linespecific">
+...
+route[1] {
+	cr_user_carrier("$fU", "$fd", "$avp(s:carrier)");
+
+	# just an example domain
+	$avp(s:domain)="start";
+	if (!cr_route("$avp(s:carrier)", "$avp(s:domain)", "$rU", "$rU",
+			"call_id")) {
+		xlog("L_ERR", "cr_route failed\n");
+		exit;
+	}
+	# if you store also the port as part of the rewrite host,
+	# otherwise you can just use $rd later
+	$avp(s:host)= $rd+":"+$rp;
+	t_on_failure("1");
+		if (!t_relay()) {
+			sl_reply_error();
+	};
+}
+
+failure_route[1] {
+	revert_uri();
+	if (!cr_next_domain("$avp(s:carrier)", "$avp(s:domain)", "$rU",
+			"$avp(s:host)", "$T_reply_code", "$avp(s:domain)")) {
+		xlog("L_ERR", "cr_next_domain failed\n");
+		exit;
+	}
+	if (!cr_route("$avp(s:carrier)", "$avp(s:domain)", "$rU", "$rU",
+			"call_id")) {
+		xlog("L_ERR", "cr_route failed\n");
+		exit;
+	}
+	$avp(s:host)= $rd+":"+$rp;
+	t_on_failure("1");
+	append_branch();
+	if (!t_relay()) {
+		xlog("L_ERR", "t_relay failed\n");
+		exit;
+	};
+}
+...
+		</programlisting>
+	</example>
+
+			
+	<example>
+		<title>Configuration example - module configuration</title>
+		<para>
+			The following config file specifies within the default carrier two
+			domains, each with an prefix that contains two hosts. It is not possible
+			to specify another carrier if you use the config file as data source.
+		</para>
+		<para>
+			All traffic will be equally distributed between the hosts, both are
+			active. The hash algorithm will working over the [1,2] set, messages
+			hashed to one will go to the first host, the other to the second one.
+			Don't use a hash index value of zero. If you ommit the hash completly,
+			the module gives them a autogenerated value, starting from one.
+		</para>
+		<para>
+			Use the <quote>NULL</quote> prefix to specify an empty prefix in the config file.
+			Please note that the prefix is matched against the request URI (or to URI),
+			if they did not contain a valid (numerical) URI, no match is possible. So
+			for loadbalancing purposes e.g. for your registrars, you should use an empty
+			prefix.
+		</para>
+		<programlisting format="linespecific">
+...
+domain proxy {
+   prefix 49 {
+     max_targets = 2
+      target proxy1.localdomain {
+         prob = 0.500000
+         hash_index = 1
+         status = 1
+         comment = "test target 1"
+      }
+      target proxy2.localdomain {
+         prob = 0.500000
+         hash_index = 2
+         status = 1
+         comment = "test target 2"
+      }
+   }
+}
+
+domain register {
+   prefix NULL {
+     max_targets = 2
+      target register1.localdomain {
+         prob = 0.500000
+         hash_index = 1
+         status = 1
+         comment = "test target 1"
+      }
+      target register2.localdomain {
+         prob = 0.500000
+         hash_index = 2
+         status = 1
+         comment = "test target 2"
+      }
+   }
+}
+...
+		</programlisting>
+	</example>
+    </section>
+
+    <section>
+	<title>Installation and Running</title>
+	<section>
+		<title>Database setup</title>
+		<para>
+			Before running &kamailio; with carrierroute, you have to setup the database 
+			table where the module will store the routing data. For that, if 
+			the table was not created by the installation script or you choose
+			to install everything by yourself you can use the carrierroute-create.sql
+			<acronym>SQL</acronym> script in the database directories in the 
+			kamailio/scripts folder as template. 
+			Database and table name can be set with module parameters so they 
+			can be changed, but the name of the columns must be as they are 
+			in the <acronym>SQL</acronym> script.
+			You can also find the complete database documentation on the
+			project webpage, &kamailiodbdocs;.
+			The flags and mask columns have the same function as in the
+			carrierfailureroute table. A zero value in the flags and mask
+			column means that any message flags will match this rule.
+		</para>
+		<para>
+			For a minimal configuration either use the config file given above, or
+			insert some data into the tables of the module.
+		</para>
+	</section>
+	
+	<section>
+		<title>Database examples</title>
+	<example>
+		<title>Example database content - carrierroute table</title>
+		<programlisting format="linespecific">
+...
++----+---------+--------+-------------+-------+------+---------------+
+| id | carrier | domain | scan_prefix | flags | prob | rewrite_host  |
++----+---------+--------+-------------+-------+------+---------------+
+| 1  |       1 |      0 | 49          |     0 |  0.5 | de-1.carrier1 |
+| 2  |       1 |      0 | 49          |     0 |  0.5 | de-2.carrier1 |
+| 3  |       1 |      0 | 49          |    16 |    1 | de-3.carrier1 |
+| 4  |       1 |      0 |             |     0 |    1 | gw.carrier1-1 |
+| 5  |       1 |      1 | 49          |     0 |    1 | gw.carrier1-1 |
+| 6  |       1 |      2 |             |     0 |    1 | gw.carrier1-2 |
+| 7  |       1 |      3 |             |     0 |    1 | gw.carrier1-3 |
+| 8  |       2 |      0 | 49          |     0 |  0.5 | de-1.carrier2 |
+| 9  |       2 |      0 | 49          |     0 |  0.5 | de-2.carrier2 |
+| 10 |       2 |      0 |             |     0 |    1 | gw.carrier2   |
+| 11 |       2 |      1 | 49          |     0 |    1 | gw.carrier2   |
+| 12 |       3 |      8 | 49          |     0 |    1 | de-gw.default |
+| 13 |       3 |      8 |             |     0 |    1 | gw.default    |
++----+---------+--------+-------------+-------+------+---------------+
+...
+		</programlisting>
+	</example>
+		<para>
+			This table contains three routes to two gateways for the <quote>49</quote> prefix,
+			and a default route for other prefixes over carrier 2 and carrier 1. The
+			gateways for the default carrier will be used for functions that don't
+			support the user specific carrier lookup. The routing rules for carrier 1
+			and carrier 2 for the <quote>49</quote> prefix contains a additional rule
+			with the domain 1, that can be used for example as fallback if the gateways
+			in domain 0 are not reachable. Two more fallback rules (domain 2 and 3) for 
+			carrier 1 are also supplied to support the functionality of the carrierfailureroute
+			table example that is provided in the next section.
+		</para>
+		<para>
+			This table provides also a <quote>carrier 1</quote> routing rule for the
+			<quote>49</quote> prefix, that is only choosen if some message flags are set.
+			If this flags are not set, the other two rules are used. The <quote>strip</quote>,
+			<quote>mask</quote> and <quote>comment</quote> colums are omitted for brevity.
+		</para>
+	<example>
+		<title>Example database content - simple carrierfailureroute table</title>
+		<programlisting format="linespecific">
+...
++----+---------+--------+---------------+------------+-------------+
+| id | carrier | domain | host_name     | reply_code | next_domain |
++----+---------+--------+---------------+------------+-------------+
+|  1 |       1 | 0      | gw.carrier1-2 | ...        | 3           |
+|  2 |       1 | 0      | gw.carrier1-3 | ...        | 2           |
++----+---------+--------+---------------+------------+-------------+
+...
+</programlisting>
+	</example>
+		<para>
+			This table contains two failure routes for the <quote>gw.carrier1-1</quote> and
+			<quote>-2</quote> gateways. For any (failure) reply code the respective next
+			domain is choosen. After that no more failure routes are available, an error will
+			be returned from the <quote>cr_next_domain</quote> function. Not all table
+			colums are show here for brevity.
+		</para>
+		<para>
+			For each failure route domain and carrier that is added to the carrierfailureroute
+			table there must be at least one corresponding entry in the carrierroute table,
+			otherwise the module will not load the routing data.
+		</para>
+
+	<example>
+		<title>Example database content - more complex carrierfailureroute table</title>
+		<programlisting format="linespecific">
+...
++----+---------+-----------+------------+--------+-----+-------------+
+| id | domain  | host_name | reply_code | flags | mask | next_domain |
++----+---------+-----------+------------+-------+------+-------------+
+|  1 |      99 |           | 408        |    16 |   16 |             |
+|  2 |      99 | gw1       | 404        |     0 |    0 | 100         |
+|  3 |      99 | gw2       | 50.        |     0 |    0 | 100         |
+|  4 |      99 |           | 404        |  2048 | 2112 | 101         |
++----+---------+-----------+------------+-------+------+-------------+
+...
+</programlisting>
+	</example>
+		<para>
+			This table contains four failure routes that shows the usage of more
+			advanced features. The first route matches to a 408, and to some flag
+			for example that indicates that ringing has happened. If this flag is set,
+			there will be no further forwarding, because next_domain is empty. In the
+			second and third routes are certain gateway errors matched, if this errors
+			have occured, then the next domain will be choosen. The last route does
+			forwarding according some flags, e.g. the customer came from a certain carrier,
+			and has call-forwarding deactivated. In order to use the routing that is
+			specified above, a matching carrierroute table must be provided, that holds
+			domain entries for this routing rules. Not all table colums are show here for
+			brevity.
+		</para>
+
+	<example>
+		<title>Example database content - carrier_name table</title>
+		<programlisting format="linespecific">
+...
++----+----------+
+| id | carrier  |
++----+----------+
+|  1 | carrier1 |
+|  2 | carrier2 |
+|  3 | default  |
++----+----------+
+...
+		</programlisting>
+		</example>
+		<para>
+			This table contains the mapping of the carrier id to actual names.
+		</para>
+		<example>
+		<title>Example database content - domain_name table</title>
+		<programlisting format="linespecific">
+...
++----+----------+
+| id | carrier  |
++----+----------+
+|  1 | domain1  |
+|  2 | domain2  |
+|  3 | domain3  |
++----+----------+
+...
+		</programlisting>
+		</example>
+		<para>
+			This table contains the mapping of the domain id to actual names.
+		</para>
+
+	</section>
+	<section>
+		<title>User specific routing</title>
+		<para>
+			For a functional routing the <quote>cr_preferred_carrier</quote> column must
+			be added to the subscriber table (or to the table and column that you specified
+			as modul parameter) to choose the actual carrier for the users.
+		</para>
+	<example>
+		<title>Necessary extensions for the user table</title>
+		<para>Suggested changes:</para>
+		<programlisting format="linespecific">
+...
+ALTER TABLE subscriber ADD cr_preferred_carrier int(10) default NULL; 
+...
+		</programlisting>
+	</example>
+		</section>
+    </section>
+</chapter>
+

+ 432 - 0
modules/carrierroute/doc/carrierroute_db.xml

@@ -0,0 +1,432 @@
+<!--
+WARNING:
+This file was autogenerated from the XML source file
+../../modules/carrierroute/doc/kamailio-carrierroute.xml.
+It can be regenerated by running 'make dbdoc' in the db/schema
+directory of the source code. You need to have xsltproc and
+docbook-xsl stylesheets installed.
+ALL CHANGES DONE HERE WILL BE LOST IF THE FILE IS REGENERATED
+-->
+
+<chapter>
+  <title>Module parameter for database access.</title>
+  <section>
+    <title><varname>db_url</varname> (String)</title>
+    <para>URL to the database containing the data.</para>
+    <para>
+      <emphasis>Default value is <quote>mysql://openserro:openserro@localhost/openser</quote>.</emphasis>
+    </para>
+    <example>
+      <title>Set <varname>db_url</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "db_url", "dbdriver://username:password@dbhost/dbname")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierroute_table</varname> (String)</title>
+    <para>Name of the carrierroute table for the carrierroute module.</para>
+    <para>
+      <emphasis>Default value is <quote>carrierroute</quote>.</emphasis>
+    </para>
+    <example>
+      <title>Set <varname>carrierroute_table</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierroute_table", "carrierroute")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierroute_id_col</varname> (string)</title>
+    <para>Name of the column contains the unique identifier of a route.</para>
+    <example>
+      <title>Set <varname>carrierroute_id_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierroute_id_col", "id")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierroute_carrier_col</varname> (string)</title>
+    <para>This column contains the carrier id.</para>
+    <example>
+      <title>Set <varname>carrierroute_carrier_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierroute_carrier_col", "carrier")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierroute_domain_col</varname> (string)</title>
+    <para>This column contains the routing domain id. You can define several routing
+			  domains to have different routing rules. Maybe you use domain 0 for normal routing and
+			  domain 1 if domain 0 failed.</para>
+    <example>
+      <title>Set <varname>carrierroute_domain_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierroute_domain_col", "domain")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierroute_scan_prefix_col</varname> (string)</title>
+    <para>Name of column contains the scan prefixes. Scan prefixes define
+			  the matching portion of a phone number, e.g. when we have the scan prefixes 49721
+			  and 49, the called number is 49721913740, it matches 49721, because the longest
+			  match is taken. If no prefix matches, the number is not routed. To prevent this,
+			  an empty prefix value of  could be added.</para>
+    <example>
+      <title>Set <varname>carrierroute_scan_prefix_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierroute_scan_prefix_col", "scan_prefix")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierroute_flags_col</varname> (string)</title>
+    <para>This column contains the flags used for rule matching.</para>
+    <example>
+      <title>Set <varname>carrierroute_flags_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierroute_flags_col", "flags")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierroute_mask_col</varname> (string)</title>
+    <para>This column contains the mask that is applied to the message flags before rule 
+			  matching.</para>
+    <example>
+      <title>Set <varname>carrierroute_mask_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierroute_mask_col", "mask")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierroute_prob_col</varname> (string)</title>
+    <para>
+			  Name of column contains the probability. The probability value is used to
+			  distribute the traffic between several gateways. Let's say 70 % of the
+			  traffic shall be routed to gateway A, the other 30 % shall be routed to
+			  gateway B, we define a rule for gateway A with a prob value of 0.7 and a
+			  rule for gateway B with a prob value of 0.3.
+			  
+			  
+			  If all probabilities for a given prefix, tree and domain don't add to 100%,
+			  the prefix values will be adjusted according the given prob values. E.g. if
+			  three hosts with prob values of 0.5, 0.5 and 0.4 are defined, the resulting
+			  probabilities are 35.714, 35.714 and 28.571%. But its better to choose meaningful
+			  values in the first place because of clarity.
+			  </para>
+    <example>
+      <title>Set <varname>carrierroute_prob_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierroute_prob_col", "prob")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierroute_strip_col</varname> (string)</title>
+    <para>Name of the column contains the number of digits to be stripped of the userpart of an URI before prepending rewrite_prefix.</para>
+    <example>
+      <title>Set <varname>carrierroute_strip_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierroute_strip_col", "strip")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierroute_rewrite_host_col</varname> (string)</title>
+    <para>Name of column contains the rewrite prefixes. Here you can define a rewrite prefix
+			  for the localpart of the SIP URI. An empty field represents a blacklist entry, anything else
+			  is put as domain part into the Request URI of the SIP message.</para>
+    <example>
+      <title>Set <varname>carrierroute_rewrite_host_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierroute_rewrite_host_col", "rewrite_host")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierroute_rewrite_prefix_col</varname> (string)</title>
+    <para>Name of column contains the rewrite prefixes. Here you can define a rewrite
+			  prefix for the localpart of the SIP URI.</para>
+    <example>
+      <title>Set <varname>carrierroute_rewrite_prefix_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierroute_rewrite_prefix_col", "rewrite_prefix")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierroute_rewrite_suffix_col</varname> (string)</title>
+    <para>Name of column contains the rewrite suffixes. Here you can define a rewrite
+			  suffix for the localpart of the SIP URI.</para>
+    <example>
+      <title>Set <varname>carrierroute_rewrite_suffix_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierroute_rewrite_suffix_col", "rewrite_suffix")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierroute_description_col</varname> (string)</title>
+    <para>A comment for the route entry, useful for larger routing tables.
+			  The comment is also displayed by the fifo cmd "cr_dump_routes".</para>
+    <example>
+      <title>Set <varname>carrierroute_description_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierroute_description_col", "description")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierfailureroute_table</varname> (String)</title>
+    <para>Name of the carrierfailureroute table for the carrierroute module.</para>
+    <para>
+      <emphasis>Default value is <quote>carrierfailureroute</quote>.</emphasis>
+    </para>
+    <example>
+      <title>Set <varname>carrierfailureroute_table</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierfailureroute_table", "carrierfailureroute")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierfailureroute_id_col</varname> (string)</title>
+    <para>This column contains the unique identifier of a failure route.</para>
+    <example>
+      <title>Set <varname>carrierfailureroute_id_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierfailureroute_id_col", "id")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierfailureroute_carrier_col</varname> (string)</title>
+    <para>This column contains the carrier id.</para>
+    <example>
+      <title>Set <varname>carrierfailureroute_carrier_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierfailureroute_carrier_col", "carrier")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierfailureroute_domain_col</varname> (string)</title>
+    <para>This column contains the routing domain id. You can define several routing domains
+			  to have different routing rules. Maybe you use domain 0 for normal routing and domain 1 if
+			  domain 0 failed.</para>
+    <example>
+      <title>Set <varname>carrierfailureroute_domain_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierfailureroute_domain_col", "domain")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierfailureroute_scan_prefix_col</varname> (string)</title>
+    <para>Name of column contains the the scan prefixes. Scan prexies define the matching
+			  portion of a phone number, e.g. we have the scan prefixes 49721 and 49, the called number is
+			  49721913740, it matches 49721, because the longest match is taken. If no prefix matches,
+			  the number is not failure routed. To prevent this, an empty prefix value of 
+			  could be added.</para>
+    <example>
+      <title>Set <varname>carrierfailureroute_scan_prefix_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierfailureroute_scan_prefix_col", "scan_prefix")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierfailureroute_host_name_col</varname> (string)</title>
+    <para>Name of the column containing the host name of the last routing destination,
+			  using for rules matching.</para>
+    <example>
+      <title>Set <varname>carrierfailureroute_host_name_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierfailureroute_host_name_col", "host_name")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierfailureroute_reply_code_col</varname> (string)</title>
+    <para>This column contains the reply code used for rule matching.</para>
+    <example>
+      <title>Set <varname>carrierfailureroute_reply_code_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierfailureroute_reply_code_col", "reply_code")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierfailureroute_flags_col</varname> (string)</title>
+    <para>This column contains the flags used for rule matching.</para>
+    <example>
+      <title>Set <varname>carrierfailureroute_flags_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierfailureroute_flags_col", "flags")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierfailureroute_mask_col</varname> (string)</title>
+    <para>This column contains the mask that is applied to the message flags before rule matching.</para>
+    <example>
+      <title>Set <varname>carrierfailureroute_mask_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierfailureroute_mask_col", "mask")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierfailureroute_next_domain_col</varname> (string)</title>
+    <para>This column contains the route domain id that should be used for the next routing attempt.</para>
+    <example>
+      <title>Set <varname>carrierfailureroute_next_domain_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierfailureroute_next_domain_col", "next_domain")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrierfailureroute_description_col</varname> (string)</title>
+    <para>A comment for the route entry, useful for larger routing tables.</para>
+    <example>
+      <title>Set <varname>carrierfailureroute_description_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrierfailureroute_description_col", "description")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrier_name_table</varname> (String)</title>
+    <para>Name of the carrier_name table for the carrierroute module.</para>
+    <para>
+      <emphasis>Default value is <quote>carrier_name</quote>.</emphasis>
+    </para>
+    <example>
+      <title>Set <varname>carrier_name_table</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrier_name_table", "carrier_name")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrier_name_id_col</varname> (string)</title>
+    <para>Name of the column containing the unique identifier of a carrier.</para>
+    <example>
+      <title>Set <varname>carrier_name_id_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrier_name_id_col", "id")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>carrier_name_carrier_col</varname> (string)</title>
+    <para>This column contains the carrier name.</para>
+    <example>
+      <title>Set <varname>carrier_name_carrier_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrier_name_carrier_col", "carrier")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>domain_name_table</varname> (String)</title>
+    <para>Name of the domain_name table for the carrierroute module.</para>
+    <para>
+      <emphasis>Default value is <quote>domain_name</quote>.</emphasis>
+    </para>
+    <example>
+      <title>Set <varname>domain_name_table</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "domain_name_table", "domain_name")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>domain_name_id_col</varname> (string)</title>
+    <para>Name of the column containing the unique identifier of a domain.</para>
+    <example>
+      <title>Set <varname>domain_name_id_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "domain_name_id_col", "id")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>domain_name_domain_col</varname> (string)</title>
+    <para>This column contains the domain name.</para>
+    <example>
+      <title>Set <varname>domain_name_domain_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "domain_name_domain_col", "domain")
+...
+</programlisting>
+    </example>
+  </section>
+</chapter>

+ 211 - 0
modules/carrierroute/prime_hash.c

@@ -0,0 +1,211 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * 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
+ */
+
+/*!
+ * \file
+ * \brief
+ * Functions for determinung a pseudo random number over a message's
+ * header field, based on CRC32 or a prime number algorithm.
+ */
+
+#include "../../sr_module.h"
+#include "../../parser/parse_uri.h"
+#include "../../parser/parse_to.h"
+#include "../../parser/parse_from.h"
+#include "../../crc.h"
+
+#include <ctype.h>
+
+#include "prime_hash.h"
+
+
+static int determine_source(struct sip_msg *msg, enum hash_source source,
+                            str *source_string);
+static int validate_msg(struct sip_msg * msg);
+static int determine_call_id (struct sip_msg *msg, str *source_string);
+static int determine_fromto_uri (struct to_body *fromto, str *source_string);
+static int determine_fromto_user (struct to_body *fromto, str *source_string);
+static int first_token (str *source_string);
+
+
+int hash_func (struct sip_msg * msg,
+                         enum hash_source source, int denominator) {
+	int ret;
+	unsigned int hash;
+	str source_string;
+
+	if(determine_source (msg, source, &source_string) == -1) {
+		return -1;
+	}
+	crc32_uint(&source_string, &hash);
+
+	ret = hash % denominator;
+	LM_DBG("hash: %u %% %i = %i\n", hash, denominator, ret);
+	return ret;
+}
+
+int prime_hash_func(struct sip_msg * msg,
+                              enum hash_source source, int denominator) {
+	str source_string;
+	if(source != shs_from_user && source != shs_to_user) {
+		LM_ERR("chosen hash source not usable (may contain letters)\n");
+		return -1;
+	}
+	if (determine_source (msg, source, &source_string) == -1) {
+		return -1;
+	}
+
+	static const int INT_DIGIT_LIMIT = 18;
+	static const int PRIME_NUMBER = 51797;
+	uint64_t number = 0;
+	uint64_t p10;
+	int i, j, limit = 0;
+	int ret;
+	char source_number_s[INT_DIGIT_LIMIT + 1];
+
+	i = INT_DIGIT_LIMIT - 1;
+	j = source_string.len - 1;
+	source_number_s[INT_DIGIT_LIMIT] ='\0';
+
+	while(i >= 0 && j >= 0) {
+		if(isdigit(source_string.s[j])) {
+			source_number_s[i] = source_string.s[j];
+			i--;
+		}
+		j--;
+	}
+	limit = i;
+
+	for(i=INT_DIGIT_LIMIT - 1, p10=1; i>limit; i--, p10=p10*10) {
+		number += (source_number_s[i] - '0') * p10;
+	}
+
+	LM_DBG("source_string is %.*s, source_number_s "
+	    "is: %s, number is %llu\n", source_string.len, source_string.s,
+	    source_number_s + (limit + 1), (long long unsigned int)number);
+	ret = number % PRIME_NUMBER;
+	ret = ret % denominator + 1;
+	LM_DBG("calculated hash is: %i\n", ret);
+	return ret;
+}
+
+static int determine_source (struct sip_msg *msg, enum hash_source source,
+                             str *source_string) {
+	source_string->s = NULL;
+	source_string->len = 0;
+
+	if(validate_msg(msg) < 0) {
+		return -1;
+	}
+
+	switch (source) {
+			case shs_call_id:
+			return determine_call_id (msg, source_string);
+			case shs_from_uri:
+			return determine_fromto_uri (get_from(msg), source_string);
+			case shs_from_user:
+			return determine_fromto_user (get_from(msg), source_string);
+			case shs_to_uri:
+			return determine_fromto_uri (get_to(msg), source_string);
+			case shs_to_user:
+			return determine_fromto_user (get_to(msg), source_string);
+			default:
+			LM_ERR("unknown hash source %i.\n",
+			     (int) source);
+			return -1;
+	}
+}
+
+static int validate_msg(struct sip_msg * msg) {
+	if(!msg->callid && ((parse_headers(msg, HDR_CALLID_F, 0) == -1) || !msg->callid)) {
+		LM_ERR("Message has no Call-ID header\n");
+		return -1;
+	}
+	if(!msg->to && ((parse_headers(msg, HDR_TO_F, 0) == -1) || !msg->to)) {
+		LM_ERR("Message has no To header\n");
+		return -1;
+	}
+	if(!msg->from && ((parse_headers(msg, HDR_FROM_F, 0) == -1) || !msg->from)) {
+		LM_ERR("Message has no From header\n");
+		return -1;
+	}
+	//TODO it would make more sense to do the parsing just if its needed
+	//     but parse_from_header is smart enough, so its probably not a huge problem
+	if (parse_from_header(msg) < 0) {
+		LM_ERR("Error while parsing From header field\n");
+		return -1;
+	}
+	return 0;
+}
+
+static int determine_call_id (struct sip_msg *msg, str *source_string) {
+	source_string->s = msg->callid->body.s;
+	source_string->len = msg->callid->body.len;
+	first_token (source_string);
+	return 0;
+}
+
+static int determine_fromto_uri (struct to_body *fromto, str *source_string) {
+	if (fromto == NULL) {
+		LM_ERR("fromto is NULL!\n");
+		return -1;
+	}
+	source_string->s = fromto->uri.s;
+	source_string->len = fromto->uri.len;
+	return 0;
+}
+
+static int determine_fromto_user (struct to_body *fromto, str *source_string) {
+	struct sip_uri uri;
+
+	if (fromto == NULL) {
+		LM_ERR("fromto is NULL!\n");
+		return -1;
+	}
+	if (parse_uri (fromto->uri.s, fromto->uri.len, &uri) < 0) {
+		LM_ERR("Failed to parse From or To URI.\n");
+		return -1;
+	}
+	source_string->s = uri.user.s;
+	source_string->len = uri.user.len;
+	return 0;
+}
+
+static int first_token (str *source_string) {
+	size_t len;
+
+	if (source_string->s == NULL || source_string->len == 0) {
+		return 0;
+	}
+
+	while (source_string->len > 0 && isspace (*source_string->s)) {
+		++source_string->s;
+		--source_string->len;
+	}
+	for (len = 0; len < source_string->len; ++len) {
+		if (isspace (source_string->s[len])) {
+			source_string->len = len;
+			break;
+		}
+	}
+	return 0;
+}

+ 80 - 0
modules/carrierroute/prime_hash.h

@@ -0,0 +1,80 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2007 1&1 Internet AG
+ *
+ * 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
+ *
+ */
+
+/*!
+ * \file
+ * \brief
+ * Functions for determinung a pseudo random number over a message's
+ * header field, based on CRC32 or a prime number algorithm.
+ */
+
+
+#ifndef PRIME_HASH_H
+#define PRIME_HASH_H 1
+
+#include "../../parser/msg_parser.h"
+
+
+/*!
+ * \brief
+ * Determines from which part of a message the hash shall be calculated.
+ * Possible values are:
+ * 
+ * - \b shs_call_id     the content of the Call-ID header field
+ * - \b shs_from_uri    the entire URI in the From header field
+ * - \b shs_from_user   the username part of the URI in the From header field
+ * - \b shs_to_uri      the entire URI in the To header field
+ * - \b shs_to_user     the username part of the URI in the To header field
+ * - \b shs_error       no hash specified
+*/
+enum hash_source {
+	shs_call_id = 1,
+	shs_from_uri,
+	shs_from_user,
+	shs_to_uri,
+	shs_to_user,
+	shs_error
+};
+
+/*! generic interface for hash functions */
+typedef int (*hash_func_t)(struct sip_msg * msg,
+	enum hash_source source, int denominator);
+
+
+/*!
+ * \brief CRC32 hash function
+ * Returns an integer number between 0 and denominator - 1 based on
+ * the hash source from the msg. The hash algorith is CRC32.
+*/
+int hash_func (struct sip_msg * msg,
+                         enum hash_source source, int denominator);
+
+/*!
+ * \brief prime hash function
+ * Returns an integer number between 0 and denominator - 1 based on
+ * the hash source from the msg. Use the prime number algorithm.
+*/
+int prime_hash_func (struct sip_msg * msg,
+                               enum hash_source source, int denominator);
+
+#endif