浏览代码

osp: module relocated to archive

Daniel-Constantin Mierla 1 年之前
父节点
当前提交
8a7ae16bf0

+ 39 - 0
src/modules/osp/Makefile

@@ -0,0 +1,39 @@
+
+# osp module makefile
+
+# WARNING: do not run this directly, it should be run by the main Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=osp.so
+
+ifeq ($(CROSS_COMPILE),)
+SSL_BUILDER=$(shell \
+	if pkg-config --exists libssl; then \
+		echo 'pkg-config libssl'; \
+	fi)
+endif
+
+ifneq ($(SSL_BUILDER),)
+	DEFS += $(shell $(SSL_BUILDER) --cflags)
+	LIBS += $(shell $(SSL_BUILDER) --libs)
+else
+	DEFS += -I$(LOCALBASE)/ssl/include
+	LIBS += -L$(LOCALBASE)/lib -L$(LOCALBASE)/ssl/lib \
+			-L$(LOCALBASE)/lib64 -L$(LOCALBASE)/ssl/lib64 \
+			-lssl -lcrypto
+	# NOTE: depending on the way in which libssl was compiled you might
+	#     #       have to add -lz -lkrb5   (zlib and kerberos5).
+	#       E.g.: make TLS_HOOKS=1 TLS_EXTRA_LIBS="-lz -lkrb5"
+endif
+LIBS+= $(TLS_EXTRA_LIBS)
+
+# Static linking, if you'd like to use TLS and OSP at the same time
+#
+#LIBS+= /usr/lib/libcurl.a /usr/lib/libssl.a /usr/lib/libcrypto.a -lkrb5 -lidn -lz -lgssapi_krb5 -lrt
+
+DEFS+=-D_POSIX_THREADS -I$(LOCALBASE)/include
+LIBS+=-L$(LOCALBASE)/lib -losptk -lpthread -lm
+
+include ../../Makefile.modules
+

+ 586 - 0
src/modules/osp/README

@@ -0,0 +1,586 @@
+OSP Module for Secure, Multi-Lateral Peering
+
+Ulrich Abend
+
+   FhG FOKUS
+
+   <[email protected]>
+
+Edited by
+
+Di-Shi Sun
+
+   TransNexus, Inc.
+
+   <[email protected]>
+
+   Copyright © 2003 FhG FOKUS
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+        3. Parameters
+
+              3.1. sp1_uri, sp2_uri, ..., sp16_uri
+              3.2. sp1_weight, sp2_weight, ..., sp16_weight
+              3.3. device_ip
+              3.4. device_port
+              3.5. token_format
+              3.6. private_key, local_certificate, ca_certificates
+              3.7. enable_crypto_hardware_support
+              3.8. ssl_lifetime
+              3.9. persistence
+              3.10. retry_delay
+              3.11. retry_limit
+              3.12. timeout
+              3.13. max_destinations
+              3.14. validate_call_id
+              3.15. use_rpid_for_calling_number
+              3.16. redirection_uri_format
+              3.17. source_networkid_avp
+
+        4. Functions
+
+              4.1. checkospheader()
+              4.2. validateospheader()
+              4.3. requestosprouting()
+              4.4. checkosproute()
+              4.5. prepareosproute()
+              4.6. checkcallingtranslation()
+              4.7. prepareallosproute()
+              4.8. reportospusage()
+
+   2. Developer Guide
+
+   List of Examples
+
+   1.1. Setting the OSP servers
+   1.2. Setting the OSP server weights
+   1.3. Setting the device IP address
+   1.4. Setting the device port
+   1.5. Setting the token format
+   1.6. Set authorization files
+   1.7. Setting the hardware support
+   1.8. Setting the ssl lifetime
+   1.9. Setting the persistence
+   1.10. Setting the retry delay
+   1.11. Setting the retry limit
+   1.12. Setting the timeout
+   1.13. Setting the number of destination
+   1.14. Instructing the module to validate call id
+   1.15. Instructing the module to use calling number in Remote-Party-ID
+   1.16. Setting the redirection URI format
+   1.17. Setting the source network ID AVP
+   1.18. checkospheader usage
+   1.19. validateospheader usage
+   1.20. requestosprouting usage
+   1.21. checkosproute usage
+   1.22. prepareosproute usage
+   1.23. checkcallingtranslation usage
+   1.24. prepareallosproute usage
+   1.25. reportospusage usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+   3. Parameters
+
+        3.1. sp1_uri, sp2_uri, ..., sp16_uri
+        3.2. sp1_weight, sp2_weight, ..., sp16_weight
+        3.3. device_ip
+        3.4. device_port
+        3.5. token_format
+        3.6. private_key, local_certificate, ca_certificates
+        3.7. enable_crypto_hardware_support
+        3.8. ssl_lifetime
+        3.9. persistence
+        3.10. retry_delay
+        3.11. retry_limit
+        3.12. timeout
+        3.13. max_destinations
+        3.14. validate_call_id
+        3.15. use_rpid_for_calling_number
+        3.16. redirection_uri_format
+        3.17. source_networkid_avp
+
+   4. Functions
+
+        4.1. checkospheader()
+        4.2. validateospheader()
+        4.3. requestosprouting()
+        4.4. checkosproute()
+        4.5. prepareosproute()
+        4.6. checkcallingtranslation()
+        4.7. prepareallosproute()
+        4.8. reportospusage()
+
+1. Overview
+
+   The OSP module enables Kamailio to support secure, multi-lateral
+   peering using the OSP standard defined by ETSI (TS 101 321 V4.1.1).
+   This module will enable your Kamailio to:
+     * Send a peering authorization request to a peering server.
+     * Validate a digitally signed peering authorization token received in
+       a SIP INVITE message.
+     * Report usage information to a peering server.
+
+2. Dependencies
+
+   The OSP module depends on the following modules which must be loaded
+   before the OSP module.
+     * sl -- stateless replier
+     * tm -- stateful processing
+     * rr -- Record-Route/Route operation
+     * textops -- text based operation
+     * siputils -- Remote-Party-ID operattion
+     * OSP Toolkit -- The OSP Toolkit, available from
+       http://sourceforge.net/projects/osp-toolkit, must be built before
+       building Kamailio with the OSP Module. For instructions on building
+       Kamailio with the OSP Toolkit, see
+       http://www.transnexus.com/White%20Papers/Multi-Lateral_Peering_with
+       _Kamailio_V1.4.pdf
+
+3. Parameters
+
+   3.1. sp1_uri, sp2_uri, ..., sp16_uri
+   3.2. sp1_weight, sp2_weight, ..., sp16_weight
+   3.3. device_ip
+   3.4. device_port
+   3.5. token_format
+   3.6. private_key, local_certificate, ca_certificates
+   3.7. enable_crypto_hardware_support
+   3.8. ssl_lifetime
+   3.9. persistence
+   3.10. retry_delay
+   3.11. retry_limit
+   3.12. timeout
+   3.13. max_destinations
+   3.14. validate_call_id
+   3.15. use_rpid_for_calling_number
+   3.16. redirection_uri_format
+   3.17. source_networkid_avp
+
+3.1. sp1_uri, sp2_uri, ..., sp16_uri
+
+   These sp_uri (string) parameters define peering servers to be used for
+   requesting peering authorization and routing information. At least one
+   peering server must be configured. Others are required only if there
+   are more than one peering servers. Each peering server address takes
+   the form of a standard URL, and consists of up to four components:
+     * An optional indication of the protocol to be used for communicating
+       with the peering server. Both HTTP and HTTP secured with SSL/TLS
+       are supported and are indicated by "http://" and "https://"
+       respectively. If the protocol is not explicitly indicated, the
+       Kamailio defaults to HTTP secured with SSL.
+     * The Internet domain name for the peering server. An IP address may
+       also be used, provided it is enclosed in square brackets such as
+       [172.16.1.1].
+     * An optional TCP port number for communicating with the peering
+       server. If the port number is omitted, the Kamailio defaults to
+       port 1080 (for HTTP) or port 1443 (for HTTP secured with SSL).
+       The uniform resource identifier for requests to the peering server.
+       This component is not optional and must be included.
+
+   Example 1.1. Setting the OSP servers
+modparam("osp","sp1_uri","http://osptestserver.transnexus.com:1080/osp")
+modparam("osp","sp2_uri","https://[1.2.3.4]:1443/osp")
+
+3.2. sp1_weight, sp2_weight, ..., sp16_weight
+
+   These sp_weight (integer) parameters are used for load balancing
+   peering requests to peering servers. These parameters are most
+   effective when configured as factors of 1000. For example, if sp1_uri
+   should manage twice the traffic load of sp2_uri, then set sp1_weight to
+   2000 and sp2_weight to 1000. Shared load balancing between peering
+   servers is recommended. However, peering servers can be configured as
+   primary and backup by assigning a sp_weight of 0 to the primary server
+   and a non-zero sp_weight to the back-up server. The default values for
+   sp1_weight and sp2_weight are 1000.
+
+   Example 1.2. Setting the OSP server weights
+modparam("osp","sp1_weight",1000)
+
+3.3. device_ip
+
+   The device_ip (string) is a recommended parameter that explicitly
+   defines the IP address of Kamailio in a peering request message (as
+   SourceAlternate type=transport). The IP address must be in brackets as
+   shown in the example below.
+
+   Example 1.3. Setting the device IP address
+modparam("osp","device_ip","[1.1.1.1]")
+
+3.4. device_port
+
+   The device_port (string) parameter is an optional field which includes
+   the SIP port being used by Kamailio in the peering request (as
+   SourceAlternate type=network) to the peering server. If it is not
+   configured, this information is not included in the peering request.
+   This field is useful if multiple Kamailio are running on the same Linux
+   computer since it enables the peering server to administer different
+   peering policies based on different SIP proxies. This parameter has not
+   been implemented yet.
+
+   Example 1.4. Setting the device port
+modparam("osp","device_port","5060")
+
+3.5. token_format
+
+   When Kamailio receives a SIP INVITE with a peering token, the OSP
+   Module will validate the token to determine whether or not the call has
+   been authorized by a peering server. Peering tokens may, or may not, be
+   digitally signed. The token_format (integer) parameter defines if
+   Kamailio will validate signed or unsigned tokens or both. The values
+   for token format are defined below. The default value is 2.
+
+   0 - Validate only signed tokens. Calls with valid signed tokens are
+   allowed.
+
+   1 - Validate only unsigned tokens. Calls with valid unsigned tokens are
+   allowed.
+
+   2 - Validate both signed and unsigned tokens are allowed. Calls with
+   valid tokens are allowed.
+
+   Example 1.5. Setting the token format
+modparam("osp","token_format",2)
+
+3.6. private_key, local_certificate, ca_certificates
+
+   These parameters identify files are used for validating peering
+   authorization tokens and establishing a secure channel between Kamailio
+   and a peering server using SSL. The files are generated using the
+   'Enroll' utility from the OSP Toolkit. By default, the proxy will look
+   for pkey.pem, localcert.pem, and cacart_0.pem in the default
+   configuration directory. The default config directory is set at compile
+   time using CFG_DIR and defaults to /usr/local/etc/kamailio/. The files
+   may be copied to the expected file location or the parameters below may
+   be changed.
+
+   Example 1.6. Set authorization files
+
+   If the default CFG_DIR value was used at compile time, the files will
+   be loaded from:
+modparam("osp","private_key","/usr/local/etc/kamailio/pkey.pem")
+modparam("osp","local_certificate","/usr/local/etc/kamailio/localcert.pem")
+modparam("osp","ca_certificates","/usr/local/etc/kamailio/cacert.pem")
+
+3.7. enable_crypto_hardware_support
+
+   The enable_crypto_hardware_support (integer) parameter is used to set
+   the cryptographic hardware acceleration engine in the openssl library.
+   The default value is 0 (no crypto hardware is present). If crypto
+   hardware is used, the value should be set to 1.
+
+   Example 1.7. Setting the hardware support
+modparam("osp","enable_crypto_hardware_support",0)
+
+3.8. ssl_lifetime
+
+   The ssl_lifetime (integer) parameter defines the lifetime, in seconds,
+   of a single SSL session key. Once this time limit is exceeded, the OSP
+   Module will negotiate a new session key. Communication exchanges in
+   progress will not be interrupted when this time limit expires. This is
+   an optional field with default value is 200 seconds.
+
+   Example 1.8. Setting the ssl lifetime
+modparam("osp","ssl_lifetime",200)
+
+3.9. persistence
+
+   The persistence (integer) parameter defines the time, in seconds, that
+   an HTTP connection should be maintained after the completion of a
+   communication exchange. The OSP Module will maintain the connection for
+   this time period in anticipation of future communication exchanges to
+   the same peering server.
+
+   Example 1.9. Setting the persistence
+modparam("osp","persistence",1000)
+
+3.10. retry_delay
+
+   The retry_delay (integer) parameter defines the time, in seconds,
+   between retrying connection attempts to an OSP peering server. After
+   exhausting all peering servers the OSP Module will delay for this
+   amount of time before resuming connection attempts. This is an optional
+   field with default value is 1 second.
+
+   Example 1.10. Setting the retry delay
+modparam("osp","retry_delay",1)
+
+3.11. retry_limit
+
+   The retry_limit (integer) parameter defines the maximum number of
+   retries for connection attempts to a peering server. If no connection
+   is established after this many retry attempts to all peering servers,
+   the OSP Module will cease connection attempts and return appropriate
+   error codes. This number does not count the initial connection attempt,
+   so that a retry_limit of 1 will result in a total of two connection
+   attempts to every peering server. The default value is 2.
+
+   Example 1.11. Setting the retry limit
+modparam("osp","retry_limit",2)
+
+3.12. timeout
+
+   The timeout (integer) parameter defines the maximum time in
+   milliseconds, to wait for a response from a peering server. If no
+   response is received within this time, the current connection is
+   aborted and the OSP Module attempts to contact the next peering server.
+   The default value is 10 seconds.
+
+   Example 1.12. Setting the timeout
+modparam("osp","timeout",10)
+
+3.13. max_destinations
+
+   The max_destinations (integer) parameter defines the maximum number of
+   destinations that Kamailio requests the peering server to return in a
+   peering response. The default value is 5.
+
+   Example 1.13. Setting the number of destination
+modparam("osp","max_destinations",5)
+
+3.14. validate_call_id
+
+   The validate_call_id (integer) parameter instructs the OSP module to
+   validate call id in the peering token. If this value is set to 1, the
+   OSP Module validates that the call id in the SIP INVITE message matches
+   the call id in the peering token. If they do not match the INVITE is
+   rejected. If this value is set to 0, the OSP Module will not validate
+   the call id in the peering token. The default value is 1.
+
+   Example 1.14. Instructing the module to validate call id
+modparam("osp","validate_call_id",1)
+
+3.15. use_rpid_for_calling_number
+
+   The use_rpid_for_calling_number (integer) parameter instructs the OSP
+   module to use the calling number in the Remote-Party-ID of the SIP
+   INVITE message. If this value is set to 1, the OSP Module uses the
+   calling number in the Remote-Party-ID header of the INVITE message when
+   a Remote-Party-ID exists. If this value is set to 0, the OSP Module
+   will use the calling number in the From header of the INVITE message.
+   The default value is 1.
+
+   Example 1.15. Instructing the module to use calling number in
+   Remote-Party-ID
+modparam("osp","use_rpid_calling_number",1)
+
+3.16. redirection_uri_format
+
+   The redirection_uri_format (integer) parameter instructs the OSP module
+   to use the different URI format in the SIP redirection message. If this
+   value is set to 0, the OSP Module uses "[email protected]" URI
+   in the SIP redirection messages. If this value is set to 1, the OSP
+   Module will use “<[email protected]>” URI in the SIP
+   redirection messages. The default value is 0
+
+   Example 1.16. Setting the redirection URI format
+modparam("osp","redirection_uri_format",1)
+
+3.17. source_networkid_avp
+
+   The source_networkid_avp (string) parameter instructs the OSP module to
+   use the defined AVP to pass the source network ID value. The default
+   value is "$avp(s:_osp_source_networkid_)". Then the source network ID
+   can be set by "$avp(s:_osp_source_networkid_) = pseudo-variables". All
+   pseudo variables are described in
+   https://www.kamailio.org/wikidocs/cookbooks/devel/pseudovariables/.
+
+   Example 1.17. Setting the source network ID AVP
+modparam("osp","source_networkid_avp","$avp(s:snid)")
+
+4. Functions
+
+   4.1. checkospheader()
+   4.2. validateospheader()
+   4.3. requestosprouting()
+   4.4. checkosproute()
+   4.5. prepareosproute()
+   4.6. checkcallingtranslation()
+   4.7. prepareallosproute()
+   4.8. reportospusage()
+
+4.1. checkospheader()
+
+   This function checks for the existence of the OSP-Auth-Token header
+   field.
+
+   This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+
+   Example 1.18. checkospheader usage
+...
+if (checkospheader()) {
+  log(1,"OSP header field found.\n");
+} else {
+  log(1,"no OSP header field present\n");
+};
+...
+
+4.2. validateospheader()
+
+   This function validates an OSP-Token specified in the
+   OSP-Auth-Tokenheader field of the SIP message. If a peering token is
+   present, it will be validated locally. If no OSP header is found or the
+   header token is invalid or expired, -1 is returned; on successful
+   validation 1 is returned.
+
+   This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+
+   Example 1.19. validateospheader usage
+...
+if (validateospheader()) {
+  log(1,"valid OSP header found\n");
+} else {
+  log(1,"OSP header not found, invalid or expired\n");
+};
+...
+
+4.3. requestosprouting()
+
+   This function launches a query to the peering server requesting the IP
+   address of one or more destination peers serving the called party. If
+   destination peers are available, the peering server will return the IP
+   address and a peering authorization token for each destination peer.
+   The OSP-Auth-Token Header field is inserted into the SIP message and
+   the SIP uri is rewritten to the IP address of destination peer provided
+   by the peering server.
+
+   The address of the called party must be a valid E164 number, otherwise
+   this function returns -1. If the transaction was accepted by the
+   peering server, the uri is being rewritten and 1 returned, on errors
+   (peering servers are not available, authentication failed or there is
+   no route to destination or the route is blocked) -1 is returned.
+
+   This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+
+   Example 1.20. requestosprouting usage
+...
+if (requestosprouting()) {
+  log(1,"successfully queried OSP server, now relaying call\n");
+} else {
+  log(1,"Authorization request was rejected from OSP server\n");
+};
+...
+
+4.4. checkosproute()
+
+   This function is used to check if there is any route for the call.
+
+   This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+
+   Example 1.21. checkosproute usage
+...
+if (checkosproute()) {
+  log(1,"There is at least one route for the call\n");
+} else {
+  log(1,"There is not any route for the call\n");
+};
+...
+
+4.5. prepareosproute()
+
+   This function tries to prepare the INVITE to be forwarded using the
+   destination in the list returned by the peering server. If the calling
+   number is translated, a RPID value for the RPID AVP will be set. If the
+   route could not be prepared, the function returns 'FALSE' back to the
+   script, which can then decide how to handle the failure. Note, if
+   checkosproute has been called and returns 'TRUE' before calling
+   prepareosproute, prepareosproute should not return 'FALSE' because
+   checkosproute has confirmed that there is at least one route.
+
+   This function can be used from BRANCH_ROUTE.
+
+   Example 1.22. prepareosproute usage
+...
+if (prepareosproute()) {
+  log(1,"successfully prepared the route, now relaying call\n");
+} else {
+  log(1,"could not prepare the route, there is not route\n");
+};
+...
+
+4.6. checkcallingtranslation()
+
+   This function is used to check if the calling number is translated.
+   Before calling checkcallingtranslation, prepareosproute should be
+   called. If the calling number does been translated, the original
+   Remote-Party-ID, if it exists, should be removed from the INVITE
+   message. And a new Remote-Party-ID header should be added (a RPID value
+   for the RPID AVP has been set by prepareosproute). If the calling
+   number is not translated, nothing should be done.
+
+   This function can be used from BRANCH_ROUTE.
+
+   Example 1.23. checkcallingtranslation usage
+...
+if (checkcallingtranslation()) {
+  # Remove the Remote_Party-ID from the received message
+  # Otherwise it will be forwarded on to the next hop
+  remove_hf("Remote-Party-ID");
+
+  # Append a new Remote_Party
+  append_rpid_hf();
+}
+...
+
+4.7. prepareallosproute()
+
+   This function tries to prepare all the routes in the list returned by
+   the peering server. The message is then either forked off or redirected
+   to the destination. If unsuccessful in preparing the routes a SIP 500
+   is sent back and a trace message is logged.
+
+   This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+
+   Example 1.24. prepareallosproute usage
+...
+if (prepareallosproute()) {
+  log(1,"Routes are prepared, now either forking or redirecting the call\n");
+} else {
+  log(1,"Could not prepare the routes. No destination available\n");
+};
+...
+
+4.8. reportospusage()
+
+   This function should be called after receiving a BYE message. If the
+   message contains an OSP cookie, the function will forward originating
+   and/or terminating duration usage information to a peering server. The
+   function returns TRUE if the BYE includes an OSP cookie. The actual
+   usage message will be sent on a different thread and will not delay BYE
+   processing. The function should be called before relaying the message.
+
+   Meaning of the parameter is as follows:
+     * "0" - Source device releases the call.
+     * "1" - Destination device releases the call.
+
+   This function can be used from REQUEST_ROUTE.
+
+   Example 1.25. reportospusage usage
+...
+if (is_direction("downstream")) {
+  log(1,"This BYE message is from SOURCE\n");
+  if (!reportospusage("0")) {
+    log(1,"This BYE message does not include OSP usage information\n");
+  }
+} else {
+  log(1,"This BYE message is from DESTINATION\n");
+  if (!reportospusage("1")) {
+    log(1,"This BYE message does not include OSP usage information\n");
+  }
+}
+...
+
+Chapter 2. Developer Guide
+
+   The functions of the OSP modules are not used by other Kamailio
+   modules.

+ 74 - 0
src/modules/osp/RELEASE-NOTES.txt

@@ -0,0 +1,74 @@
+
+2005 July 23
+  Implemented routing and authorization validation.  The module can:
+   o Request authorization and routing information from an OSP server
+   o Use provided routes to:
+     o Redirect the originating UI to all routes OR
+     o Try all routes simultaneously using parallel forking OR
+     o Try routes in the order they were received using sequential forking
+   o Add and retrieve P-OSP-Auth-Token header
+   o Perform local validation of the P-OSP-Auth-Token
+
+2005 September 12
+   o Bug 4863 - the module will remove OSP Auth token from the incoming Invite
+     after validating the token and before forwarding Invite to the next hop.
+   o Bug 4891 - added a new flag 'validate_call_id' for disabling call id
+     validation in OSP tokens.  The value is optional, call id validation
+     can be turned off by setting the value to 0 (No).  It may be used for
+     interoperability issues with some OSP clients sending invalid call ids
+     in Authorization Requests.
+   o Bug 4986 - changed return value for 'prepareallosproutes' from
+     0 - stop routing logic to 1 - true
+   o Bug 5039 - changed return value for 'preparenextosproute' when there is
+     no more routes from 0 - stop routing logic to -1 - false
+   o Bug 5039 - changed return value for 'preparenextosproute' when there is
+   o Bug 4893 - sequential routing (in failure_route[1]) now checks for code 
+     487 (Canceled) before trying the next OSP route.
+   o Bug 4892 - removed trailing binary chars from some log messages.
+   o Bug 4987 - fixed a compile time error on Solaris
+   o Bug 4946 - added README file
+
+2005 September 26
+   o Bug 5094 - don't route the call if OSP token validation fails.
+   o Bug 5109 - send "100 Trying" message before requesting OSP routes.
+   o Bug 5111 - fixed typos in error messages.
+   o Bug 5153 - removed trailing binary chars from P-OSP-Auth-Token header
+                value.
+
+2005 October 3
+   o Report OSP usage indications after the call set-up and tear down
+     transactions complete.
+
+2005 October 11
+   o Report call set-up usage indication after receiving either 200 or 202.
+   o Report termination cause code for duration usage indications as 10,016
+     instead of 1,016.
+   o Improved error checking and logging.
+   o Bug 5366 - removed a memory leak in usage reporting logic.
+
+2005 November 4
+   o Bug 5110 and 5531 - Updated the sample configuration files to not
+     fail-over on 486 - user busy and 408 - user not available.
+   o Bug 5535 - The module will report call set-up usage details after
+     sending any final response code to UAC.
+
+2005 November 7
+   o Copied files from cvs.berlios.de:/cvsroot/osp-module
+
+2006 August 3
+   o Using BRAND_ROUTE approach to add route specific header to avoid sending 
+     an OSP token to a non-OSP destination.
+     For bug 1524046 on OpenSER tracker.
+   o Using called number in Request-Line for token validation and auth request.
+     For bug 1524070 on OpenSER tracker.
+   o Using RPID approach for calling number translation.
+     For bug 1524079 on OpenSER tracker.
+   o Sending DeviceInfo in "[x.x.x.x]" format in OSP AuthReq messages.
+     For bug 1524085 on OpenSER tracker.
+   o Using fromtag in RR to report who releases the call first.
+     For bug 1527940 on OpenSER tracker.
+
+
+2007 April 20
+   o Report destination count in source-stop usage indications, requires
+     osptoolkit version 3.4.0

+ 432 - 0
src/modules/osp/destination.c

@@ -0,0 +1,432 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "../../core/str.h"
+#include "../../core/dprint.h"
+#include "../../core/usr_avp.h"
+#include "destination.h"
+#include "usage.h"
+
+/* Name of AVP of OSP destination list */
+const str OSP_ORIGDEST_NAME = {"_osp_orig_dests_", 16};
+const str OSP_TERMDEST_NAME = {"_osp_term_dests_", 16};
+
+static int ospSaveDestination(osp_dest *dest, const str *name);
+static void ospRecordCode(int code, osp_dest *dest);
+static int ospIsToReportUsage(int code);
+
+/*
+ * Initialize destination structure
+ * param dest Destination data structure
+ * return initialized destination sturcture
+ */
+osp_dest *ospInitDestination(osp_dest *dest)
+{
+	memset(dest, 0, sizeof(osp_dest));
+
+	dest->callidsize = sizeof(dest->callid);
+	dest->tokensize = sizeof(dest->token);
+
+	LM_DBG("callidsize '%d' tokensize '%d'\n", dest->callidsize,
+			dest->tokensize);
+
+	return dest;
+}
+
+/*
+ * Save destination as an AVP
+ *     name - OSP_ORIGDEST_NAME / OSP_TERMDEST_NAME
+ *     value - osp_dest wrapped in a string
+ * param dest Destination structure
+ * param name Name of AVP
+ * return 0 success, -1 failure
+ */
+static int ospSaveDestination(osp_dest *dest, const str *name)
+{
+	str wrapper;
+	int result = -1;
+
+	wrapper.s = (char *)dest;
+	wrapper.len = sizeof(osp_dest);
+
+	/*
+     * add_avp will make a private copy of both the name and value in shared memory
+     * which will be released by TM at the end of the transaction
+     */
+	if(add_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)*name, (int_str)wrapper)
+			== 0) {
+		LM_DBG("destination saved\n");
+		result = 0;
+	} else {
+		LM_ERR("failed to save destination\n");
+	}
+
+	return result;
+}
+
+/*
+ * Save originate destination
+ * param dest Originate destination structure
+ * return 0 success, -1 failure
+ */
+int ospSaveOrigDestination(osp_dest *dest)
+{
+	return ospSaveDestination(dest, &OSP_ORIGDEST_NAME);
+}
+
+/*
+ * Save terminate destination
+ * param dest Terminate destination structure
+ * return 0 success, -1 failure
+ */
+int ospSaveTermDestination(osp_dest *dest)
+{
+	return ospSaveDestination(dest, &OSP_TERMDEST_NAME);
+}
+
+/*
+ * Check if there is an unused and supported originate destination from an AVP
+ *     name - OSP_ORIGDEST_NAME
+ *     value - osp_dest wrapped in a string
+ *     search unused (used==0) & supported (support==1)
+ * return 0 success, -1 failure
+ */
+int ospCheckOrigDestination(void)
+{
+	struct usr_avp *destavp = NULL;
+	int_str destval;
+	osp_dest *dest = NULL;
+	int result = -1;
+	struct search_state st;
+
+	for(destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR,
+				(int_str)OSP_ORIGDEST_NAME, NULL, &st);
+			destavp != NULL; destavp = search_next_avp(&st, NULL)) {
+		get_avp_val(destavp, &destval);
+
+		/* OSP destintaion is wrapped in a string */
+		dest = (osp_dest *)destval.s.s;
+
+		if(dest->used == 0) {
+			if(dest->supported == 1) {
+				LM_DBG("orig dest exist\n");
+				result = 0;
+				break;
+			} else {
+				LM_DBG("destination does not been supported\n");
+			}
+		} else {
+			LM_DBG("destination has already been used\n");
+		}
+	}
+
+	if(result == -1) {
+		LM_DBG("there is not unused destination\n");
+	}
+
+	return result;
+}
+
+/*
+ * Retrieved an unused and supported originate destination from an AVP
+ *     name - OSP_ORIGDEST_NAME
+ *     value - osp_dest wrapped in a string
+ *     There can be 0, 1 or more originate destinations.
+ *     Find the 1st unused destination (used==0) & supported (support==1),
+ *     return it, and mark it as used (used==1).
+ * return NULL on failure
+ */
+osp_dest *ospGetNextOrigDestination(void)
+{
+	struct usr_avp *destavp = NULL;
+	int_str destval;
+	osp_dest *dest = NULL;
+	osp_dest *result = NULL;
+	struct search_state st;
+
+	for(destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR,
+				(int_str)OSP_ORIGDEST_NAME, NULL, &st);
+			destavp != NULL; destavp = search_next_avp(&st, NULL)) {
+		get_avp_val(destavp, &destval);
+
+		/* OSP destintaion is wrapped in a string */
+		dest = (osp_dest *)destval.s.s;
+
+		if(dest->used == 0) {
+			if(dest->supported == 1) {
+				LM_DBG("orig dest found\n");
+				dest->used = 1;
+				result = dest;
+				break;
+			} else {
+				/* Make it looks like used */
+				dest->used = 1;
+				/* 111 means wrong protocol */
+				dest->lastcode = 111;
+				LM_DBG("destination does not been supported\n");
+			}
+		} else {
+			LM_DBG("destination has already been used\n");
+		}
+	}
+
+	if(result == NULL) {
+		LM_DBG("there is not unused destination\n");
+	}
+
+	return result;
+}
+
+/*
+ * Retrieved the last used originate destination from an AVP
+ *    name - OSP_ORIGDEST_NAME
+ *    value - osp_dest wrapped in a string
+ *    There can be 0, 1 or more destinations.
+ *    Find the last used destination (used==1) & supported (support==1),
+ *    and return it.
+ *    In normal condition, this one is the current destination. But it may
+ *    be wrong for loop condition.
+ *  return NULL on failure
+ */
+osp_dest *ospGetLastOrigDestination(void)
+{
+	struct usr_avp *destavp = NULL;
+	int_str destval;
+	osp_dest *dest = NULL;
+	osp_dest *lastdest = NULL;
+	struct search_state st;
+
+	for(destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR,
+				(int_str)OSP_ORIGDEST_NAME, NULL, &st);
+			destavp != NULL; destavp = search_next_avp(&st, NULL)) {
+		get_avp_val(destavp, &destval);
+
+		/* OSP destination is wrapped in a string */
+		dest = (osp_dest *)destval.s.s;
+
+		if(dest->used == 1) {
+			if(dest->supported == 1) {
+				lastdest = dest;
+				LM_DBG("current destination '%s'\n", lastdest->host);
+			}
+		} else {
+			break;
+		}
+	}
+
+	return lastdest;
+}
+
+/*
+ * Retrieved the terminate destination from an AVP
+ *     name - OSP_TERMDEST_NAME
+ *     value - osp_dest wrapped in a string
+ *     There can be 0 or 1 term destinations. Find and return it.
+ *  return NULL on failure (no terminate destination)
+ */
+osp_dest *ospGetTermDestination(void)
+{
+	int_str destval;
+	osp_dest *dest = NULL;
+
+	if(search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_TERMDEST_NAME,
+			   &destval, 0)
+			!= NULL) {
+		/* OSP destination is wrapped in a string */
+		dest = (osp_dest *)destval.s.s;
+
+		LM_DBG("term dest found\n");
+	}
+
+	return dest;
+}
+
+/*
+ * Record destination status
+ * param code Destination status
+ * param dest Destination
+ */
+static void ospRecordCode(int code, osp_dest *dest)
+{
+	LM_DBG("code '%d'\n", code);
+	dest->lastcode = code;
+
+	switch(code) {
+		case 100:
+			if(!dest->time100) {
+				dest->time100 = time(NULL);
+			} else {
+				LM_DBG("100 already recorded\n");
+			}
+			break;
+		case 180:
+		case 181:
+		case 182:
+		case 183:
+			if(!dest->time180) {
+				dest->time180 = time(NULL);
+			} else {
+				LM_DBG("180, 181, 182 or 183 allready recorded\n");
+			}
+			break;
+		case 200:
+		case 202:
+			if(!dest->time200) {
+				dest->time200 = time(NULL);
+			} else {
+				LM_DBG("200 or 202 allready recorded\n");
+			}
+			break;
+		default:
+			LM_DBG("will not record time for '%d'\n", code);
+	}
+}
+
+/*
+ * Check destination status for reporting usage
+ * param code Destination status
+ * return 1 should report, 0 should not report
+ */
+static int ospIsToReportUsage(int code)
+{
+	int istime = 0;
+
+	LM_DBG("code '%d'\n", code);
+	if(code >= 200) {
+		istime = 1;
+	}
+
+	return istime;
+}
+
+/*
+ * Report call setup usage for both client and server side
+ * param clientcode Client status
+ * param servercode Server status
+ */
+void ospRecordEvent(int clientcode, int servercode)
+{
+	osp_dest *dest;
+
+	LM_DBG("client status '%d'\n", clientcode);
+	if((clientcode != 0) && (dest = ospGetLastOrigDestination())) {
+		ospRecordCode(clientcode, dest);
+
+		if(ospIsToReportUsage(servercode) == 1) {
+			ospReportOrigSetupUsage();
+		}
+	}
+
+	LM_DBG("server status '%d'\n", servercode);
+	if((servercode != 0) && (dest = ospGetTermDestination())) {
+		ospRecordCode(servercode, dest);
+
+		if(ospIsToReportUsage(servercode) == 1) {
+			ospReportTermSetupUsage();
+		}
+	}
+}
+
+/*
+ * Dump destination information
+ * param dest Destination
+ */
+void ospDumpDestination(osp_dest *dest)
+{
+	LM_DBG("dest->host..........'%s'\n", dest->host);
+	LM_DBG("dest->used..........'%d'\n", dest->used);
+	LM_DBG("dest->lastcode......'%d'\n", dest->lastcode);
+	LM_DBG("dest->time100.......'%d'\n", (unsigned int)dest->time100);
+	LM_DBG("dest->time180.......'%d'\n", (unsigned int)dest->time180);
+	LM_DBG("dest->time200.......'%d'\n", (unsigned int)dest->time200);
+}
+
+/*
+ * Dump all destination information
+ */
+void ospDumpAllDestination(void)
+{
+	struct usr_avp *destavp = NULL;
+	int_str destval;
+	osp_dest *dest = NULL;
+	int count = 0;
+	struct search_state st;
+
+	for(destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR,
+				(int_str)OSP_ORIGDEST_NAME, NULL, &st);
+			destavp != NULL; destavp = search_next_avp(&st, NULL)) {
+		get_avp_val(destavp, &destval);
+
+		/* OSP destination is wrapped in a string */
+		dest = (osp_dest *)destval.s.s;
+
+		LM_DBG("....originate '%d'....\n", count++);
+
+		ospDumpDestination(dest);
+	}
+	if(count == 0) {
+		LM_DBG("there is not originate destination AVP\n");
+	}
+
+	if(search_first_avp(AVP_NAME_STR | AVP_VAL_STR, (int_str)OSP_TERMDEST_NAME,
+			   &destval, 0)
+			!= NULL) {
+		/* OSP destination is wrapped in a string */
+		dest = (osp_dest *)destval.s.s;
+
+		LM_DBG("....terminate....\n");
+
+		ospDumpDestination(dest);
+	} else {
+		LM_DBG("there is not terminate destination AVP\n");
+	}
+}
+
+/*
+ * Convert address to "[x.x.x.x]" or "host.domain" format
+ * param src Source address
+ * param dst Destination address
+ * param buffersize Size of dst buffer
+ */
+void ospConvertAddress(char *src, char *dst, int buffersize)
+{
+	struct in_addr inp;
+
+	if(inet_aton(src, &inp) != 0) {
+		snprintf(dst, buffersize, "[%s]", src);
+	} else {
+		snprintf(dst, buffersize, "%s", src);
+	}
+}

+ 79 - 0
src/modules/osp/destination.h

@@ -0,0 +1,79 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _OSP_MOD_DESTINATION_H_
+#define _OSP_MOD_DESTINATION_H_
+
+#include <time.h>
+#include "osp_mod.h"
+
+typedef struct _osp_dest
+{
+	char validafter[OSP_STRBUF_SIZE];
+	char validuntil[OSP_STRBUF_SIZE];
+	char callid[OSP_STRBUF_SIZE];
+	char called[OSP_STRBUF_SIZE];
+	char calling[OSP_STRBUF_SIZE];
+	char source[OSP_STRBUF_SIZE];
+	char srcdev[OSP_STRBUF_SIZE];
+	char host[OSP_STRBUF_SIZE];
+	char destdev[OSP_STRBUF_SIZE];
+	char networkid[OSP_STRBUF_SIZE];
+	unsigned char token[OSP_TOKENBUF_SIZE];
+	unsigned int callidsize;
+	unsigned int tokensize;
+	unsigned int timelimit;
+	int lastcode;
+	time_t authtime;
+	time_t time100;
+	time_t time180;
+	time_t time200;
+	int type;
+	unsigned long long transid;
+	int supported;
+	int used;
+	int reported;
+	unsigned int destinationCount;
+	char origcalled[OSP_STRBUF_SIZE];
+} osp_dest;
+
+osp_dest *ospInitDestination(osp_dest *dest);
+int ospSaveOrigDestination(osp_dest *dest);
+int ospSaveTermDestination(osp_dest *dest);
+int ospCheckOrigDestination(void);
+osp_dest *ospGetNextOrigDestination(void);
+osp_dest *ospGetLastOrigDestination(void);
+osp_dest *ospGetTermDestination(void);
+void ospRecordEvent(int clientcode, int servercode);
+void ospDumpDestination(osp_dest *dest);
+void ospDumpAllDestination(void);
+void ospConvertAddress(char *src, char *dst, int buffersize);
+
+#endif /* _OSP_MOD_DESTINATION_H_ */

+ 4 - 0
src/modules/osp/doc/Makefile

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

+ 44 - 0
src/modules/osp/doc/osp.xml

@@ -0,0 +1,44 @@
+<?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" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+  <bookinfo>
+    <title>OSP Module for Secure, Multi-Lateral Peering</title>
+    <productname class="trade">&kamailioname;</productname>
+    <authorgroup>
+      <author>
+        <firstname>Ulrich</firstname>
+        <surname>Abend</surname>
+        <affiliation>
+          <orgname>&fhg;</orgname>
+          <address><email>[email protected]</email></address>
+        </affiliation>
+      </author>
+      <editor>
+        <firstname>Di-Shi</firstname>
+        <surname>Sun</surname>
+        <affiliation>
+          <orgname>TransNexus, Inc.</orgname>
+          <address><email>[email protected]</email></address>
+        </affiliation>
+      </editor>
+    </authorgroup>
+    <copyright>
+      <year>2003</year>
+      <holder>&fhg;</holder>
+    </copyright>
+  </bookinfo>
+  <toc></toc>
+
+  <xi:include href="osp_admin.xml"/>
+  <xi:include href="osp_devel.xml"/>
+
+
+</book>

+ 406 - 0
src/modules/osp/doc/osp_admin.xml

@@ -0,0 +1,406 @@
+<?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" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
+%docentities;
+
+]>
+<!-- OSP Module User's Guide -->
+
+<chapter>
+
+  <title>&adminguide;</title>
+  <section>
+    <title>Overview</title>
+    <para>The OSP module enables &kamailio; to support secure, multi-lateral peering using the OSP standard defined by ETSI (TS 101 321 V4.1.1). This module will enable your &kamailio; to:</para>
+    <itemizedlist>
+      <listitem>
+        <para>Send a peering authorization request to a peering server.</para>
+      </listitem>
+      <listitem>
+        <para>Validate a digitally signed peering authorization token received in a SIP INVITE message.</para>
+      </listitem>
+      <listitem>
+        <para>Report usage information to a peering server.</para>
+      </listitem>
+    </itemizedlist>
+  </section>
+  <section>
+    <title>Dependencies</title>
+    <para>The OSP module depends on the following modules which must be loaded before the OSP module.</para>
+    <itemizedlist>
+      <listitem>
+        <para><emphasis>sl</emphasis> -- stateless replier</para>
+      </listitem>
+      <listitem>
+        <para><emphasis>tm</emphasis> -- stateful processing</para>
+      </listitem>
+      <listitem>
+        <para><emphasis>rr</emphasis> -- Record-Route/Route operation</para>
+      </listitem>
+      <listitem>
+        <para><emphasis>textops</emphasis> -- text based operation</para>
+      </listitem>
+      <listitem>
+        <para><emphasis>siputils</emphasis> -- Remote-Party-ID operattion</para>
+      </listitem>
+      <listitem>
+        <para><emphasis>OSP Toolkit</emphasis> -- The OSP Toolkit, available from http://sourceforge.net/projects/osp-toolkit, must be built before building &kamailio; with the OSP Module.  For instructions on building &kamailio; with the OSP Toolkit, see http://www.transnexus.com/White%20Papers/Multi-Lateral_Peering_with_Kamailio_V1.4.pdf</para>
+      </listitem>
+    </itemizedlist>
+  </section>
+  <section>
+    <title>Parameters</title>
+    <section>
+      <title><varname>sp1_uri</varname>, <varname>sp2_uri</varname>, ..., <varname>sp16_uri</varname></title>
+      <para>These sp_uri (string) parameters define peering servers to be used for requesting peering authorization and routing information. At least one peering server must be configured. Others are required only if there are more than one peering servers. Each peering server address takes the form of a standard URL, and consists of up to four components:</para>
+      <itemizedlist>
+        <listitem>
+          <para>An optional indication of the protocol to be used for communicating with the peering server. Both HTTP and HTTP secured with SSL/TLS are supported and are indicated by "http://" and "https://" respectively. If the protocol is not explicitly indicated, the &kamailio; defaults to HTTP secured with SSL.</para>
+        </listitem>
+        <listitem>
+          <para>The Internet domain name for the peering server. An IP address may also be used, provided it is enclosed in square brackets such as [172.16.1.1].</para>
+        </listitem>
+        <listitem>
+          <para>An optional TCP port number for communicating with the peering server. If the port number is omitted, the &kamailio; defaults to port 1080 (for HTTP) or port 1443 (for HTTP secured with SSL).</para>
+          <para>The uniform resource identifier for requests to the peering server. This component is not optional and must be included.</para>
+        </listitem>
+      </itemizedlist>
+      <example>
+        <title>Setting the OSP servers</title>
+        <programlisting format="linespecific">
+modparam("osp","sp1_uri","http://osptestserver.transnexus.com:1080/osp")
+modparam("osp","sp2_uri","https://[1.2.3.4]:1443/osp")
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>sp1_weight</varname>, <varname>sp2_weight</varname>, ..., <varname>sp16_weight</varname></title>
+      <para>These sp_weight (integer) parameters are used for load balancing peering requests to peering servers. These parameters are most effective when configured as factors of 1000. For example, if sp1_uri should manage twice the traffic load of sp2_uri, then set sp1_weight to 2000 and sp2_weight to 1000. Shared load balancing between peering servers is recommended. However, peering servers can be configured as primary and backup by assigning a sp_weight of 0 to the primary server and a non-zero sp_weight to the back-up server. The default values for sp1_weight and sp2_weight are 1000.</para>
+      <example>
+        <title>Setting the OSP server weights</title>
+        <programlisting format="linespecific">
+modparam("osp","sp1_weight",1000)
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>device_ip</varname></title>
+      <para>The device_ip (string) is a recommended parameter that explicitly defines the IP address of &kamailio; in a peering request message (as SourceAlternate type=transport).  The IP address must be in brackets as shown in the example below.</para>
+      <example>
+        <title>Setting the device IP address</title>
+        <programlisting format="linespecific">
+modparam("osp","device_ip","[1.1.1.1]")
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>device_port</varname></title>
+      <para>The device_port (string) parameter is an optional field which includes the SIP port being used by &kamailio; in the peering request (as SourceAlternate type=network) to the peering server. If it is not configured, this information is not included in the peering request. This field is useful if multiple &kamailio; are running on the same Linux computer since it enables the peering server to administer different peering policies based on different SIP proxies. This parameter has not been implemented yet.</para>
+      <example>
+        <title>Setting the device port</title>
+        <programlisting format="linespecific">
+modparam("osp","device_port","5060")
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>token_format</varname></title>
+      <para>When &kamailio; receives a SIP INVITE with a peering token, the OSP Module will validate the token to determine whether or not the call has been authorized by a peering server. Peering tokens may, or may not, be digitally signed. The token_format (integer) parameter defines if &kamailio; will validate signed or unsigned tokens or both. The values for token format are defined below. The default value is 2.</para>
+      <para>0 - Validate only signed tokens. Calls with valid signed tokens are allowed.</para>
+      <para>1 - Validate only unsigned tokens. Calls with valid unsigned tokens are allowed.</para>
+      <para>2 - Validate both signed and unsigned tokens are allowed. Calls with valid tokens are allowed.</para>
+      <example>
+        <title>Setting the token format</title>
+        <programlisting format="linespecific">
+modparam("osp","token_format",2)
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>private_key</varname>, <varname>local_certificate</varname>, <varname>ca_certificates</varname></title>
+      <para>These parameters identify files are used for validating peering authorization tokens and establishing a secure channel between &kamailio; and a peering server using SSL.  The files are generated using the 'Enroll' utility from the OSP Toolkit. By default, the proxy will look for pkey.pem, localcert.pem, and cacart_0.pem in the default configuration directory. The default config directory is set at compile time using CFG_DIR and defaults to /usr/local/etc/kamailio/. The files may be copied to the expected file location or the parameters below may be changed.</para>
+      <example>
+        <title>Set authorization files</title>
+        <para>If the default CFG_DIR value was used at compile time, the files will be loaded from:</para>
+        <programlisting format="linespecific">
+modparam("osp","private_key","/usr/local/etc/kamailio/pkey.pem")
+modparam("osp","local_certificate","/usr/local/etc/kamailio/localcert.pem")
+modparam("osp","ca_certificates","/usr/local/etc/kamailio/cacert.pem")
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>enable_crypto_hardware_support</varname></title>
+      <para>The enable_crypto_hardware_support (integer) parameter is used to set the cryptographic hardware acceleration engine in the openssl library. The default value is 0 (no crypto hardware is present). If crypto hardware is used, the value should be set to 1.</para>
+      <example>
+        <title>Setting the hardware support</title>
+        <programlisting format="linespecific">
+modparam("osp","enable_crypto_hardware_support",0)
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>ssl_lifetime</varname></title>
+      <para>The ssl_lifetime (integer) parameter defines the lifetime, in seconds, of a single SSL session key. Once this time limit is exceeded, the OSP Module will negotiate a new session key. Communication exchanges in progress will not be interrupted when this time limit expires. This is an optional field with default value is 200 seconds.</para>
+      <example>
+        <title>Setting the ssl lifetime</title>
+        <programlisting format="linespecific">
+modparam("osp","ssl_lifetime",200)
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>persistence</varname></title>
+      <para>The persistence (integer) parameter defines the time, in seconds, that an HTTP connection should be maintained after the completion of a communication exchange. The OSP Module will maintain the connection for this time period in anticipation of future communication exchanges to the same peering server.</para>
+      <example>
+        <title>Setting the persistence</title>
+        <programlisting format="linespecific">
+modparam("osp","persistence",1000)
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>retry_delay</varname></title>
+      <para>The retry_delay (integer) parameter defines the time, in seconds, between retrying connection attempts to an OSP peering server. After exhausting all peering servers the OSP Module will delay for this amount of time before resuming connection attempts. This is an optional field with default value is 1 second.</para>
+      <example>
+        <title>Setting the retry delay</title>
+        <programlisting format="linespecific">
+modparam("osp","retry_delay",1)
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>retry_limit</varname></title>
+      <para>The retry_limit (integer) parameter defines the maximum number of retries for connection attempts to a peering server. If no connection is established after this many retry attempts to all peering servers, the OSP Module will cease connection attempts and return appropriate error codes. This number does not count the initial connection attempt, so that a retry_limit of 1 will result in a total of two connection attempts to every peering server. The default value is 2.</para>
+      <example>
+        <title>Setting the retry limit</title>
+        <programlisting format="linespecific">
+modparam("osp","retry_limit",2)
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>timeout</varname></title>
+      <para>The timeout (integer) parameter defines the maximum time in milliseconds, to wait for a response from a peering server. If no response is received within this time, the current connection is aborted and the OSP Module attempts to contact the next peering server. The default value is 10 seconds.</para>
+      <example>
+        <title>Setting the timeout</title>
+        <programlisting format="linespecific">
+modparam("osp","timeout",10)
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>max_destinations</varname></title>
+      <para>The max_destinations (integer) parameter defines the maximum number of destinations that &kamailio; requests the peering server to return in a peering response. The default value is 5.</para>
+      <example>
+        <title>Setting the number of destination</title>
+        <programlisting format="linespecific">
+modparam("osp","max_destinations",5)
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>validate_call_id</varname></title>
+      <para>The validate_call_id (integer) parameter instructs the OSP module to validate call id in the peering token. If this value is set to 1, the OSP Module validates that the call id in the SIP INVITE message matches the call id in the peering token. If they do not match the INVITE is rejected. If this value is set to 0, the OSP Module will not validate the call id in the peering token. The default value is 1.</para>
+      <example>
+        <title>Instructing the module to validate call id</title>
+        <programlisting format="linespecific">
+modparam("osp","validate_call_id",1)
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>use_rpid_for_calling_number</varname></title>
+      <para>The use_rpid_for_calling_number (integer) parameter instructs the OSP module to use the calling number in the Remote-Party-ID of the SIP INVITE message. If this value is set to 1, the OSP Module uses the calling number in the Remote-Party-ID header of the INVITE message when a Remote-Party-ID exists. If this value is set to 0, the OSP Module will use the calling number in the From header of the INVITE message. The default value is 1.</para>
+      <example>
+        <title>Instructing the module to use calling number in Remote-Party-ID</title>
+        <programlisting format="linespecific">
+modparam("osp","use_rpid_calling_number",1)
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>redirection_uri_format</varname></title>
+      <para>The redirection_uri_format (integer) parameter instructs the OSP module to use the different URI format in the SIP redirection message. If this value is set to 0, the OSP Module uses "[email protected]" URI in the SIP redirection messages. If this value is set to 1, the OSP Module will use <quote>&lt;[email protected]&gt;</quote> URI in the SIP redirection messages. The default value is 0</para>
+      <example>
+        <title>Setting the redirection URI format</title>
+        <programlisting format="linespecific">
+modparam("osp","redirection_uri_format",1)
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><varname>source_networkid_avp</varname></title>
+      <para>The source_networkid_avp (string) parameter instructs the OSP module to use the defined AVP to pass the source network ID value. The default value is "$avp(s:_osp_source_networkid_)".  Then the source network ID can be set by "$avp(s:_osp_source_networkid_) = pseudo-variables".  All pseudo variables are described in https://www.kamailio.org/wikidocs/cookbooks/devel/pseudovariables/.</para>
+      <example>
+        <title>Setting the source network ID AVP</title>
+        <programlisting format="linespecific">
+modparam("osp","source_networkid_avp","$avp(s:snid)")
+        </programlisting>
+      </example>
+    </section>
+  </section>
+  <section>
+    <title>Functions</title>
+    <section>
+      <title><function moreinfo="none">checkospheader()</function></title>
+      <para>This function checks for the existence of the OSP-Auth-Token header field.</para>
+      <para>This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.</para>
+      <example>
+        <title>checkospheader usage</title>
+        <programlisting format="linespecific">
+...
+if (checkospheader()) {
+  log(1,"OSP header field found.\n");
+} else {
+  log(1,"no OSP header field present\n");
+};
+...
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><function moreinfo="none">validateospheader()</function></title>
+      <para>This function validates an OSP-Token specified in the OSP-Auth-Tokenheader field of the SIP message. If a peering token is present, it will be validated locally. If no OSP header is found or the header token is invalid or expired, -1 is returned; on successful validation 1 is returned.</para>
+      <para>This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.</para>
+      <example>
+        <title>validateospheader usage</title>
+        <programlisting format="linespecific">
+...
+if (validateospheader()) {
+  log(1,"valid OSP header found\n");
+} else {
+  log(1,"OSP header not found, invalid or expired\n");
+};
+...
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><function moreinfo="none">requestosprouting()</function></title>
+      <para>This function launches a query to the peering server requesting the IP address of one or more destination peers serving the called party. If destination peers are available, the peering server will return the IP address and a peering authorization token for each destination peer. The OSP-Auth-Token Header field is inserted into the SIP message and the SIP uri is rewritten to the IP address of destination peer provided by the peering server.</para>
+      <para>The address of the called party must be a valid E164 number, otherwise this function returns -1. If the transaction was accepted by the peering server, the uri is being rewritten and 1 returned, on errors (peering servers are not available, authentication failed or there is no route to destination or the route is blocked) -1 is returned.</para>
+      <para>This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.</para>
+      <example>
+        <title>requestosprouting usage</title>
+        <programlisting format="linespecific">
+...
+if (requestosprouting()) {
+  log(1,"successfully queried OSP server, now relaying call\n");
+} else {
+  log(1,"Authorization request was rejected from OSP server\n");
+};
+...
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><function moreinfo="none">checkosproute()</function></title>
+      <para>This function is used to check if there is any route for the call.</para>
+      <para>This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.</para>
+      <example>
+        <title>checkosproute usage</title>
+        <programlisting format="linespecific">
+...
+if (checkosproute()) {
+  log(1,"There is at least one route for the call\n");
+} else {
+  log(1,"There is not any route for the call\n");
+};
+...
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><function moreinfo="none">prepareosproute()</function></title>
+      <para>This function tries to prepare the INVITE to be forwarded using the destination in the list returned by the peering server. If the calling number is translated, a RPID value for the RPID AVP will be set. If the route could not be prepared, the function returns 'FALSE' back to the script, which can then decide how to handle the failure. Note, if checkosproute has been called and returns 'TRUE' before calling prepareosproute, prepareosproute should not return 'FALSE' because checkosproute has confirmed that there is at least one route.</para>
+      <para>This function can be used from BRANCH_ROUTE.</para>
+      <example>
+        <title>prepareosproute usage</title>
+        <programlisting format="linespecific">
+...
+if (prepareosproute()) {
+  log(1,"successfully prepared the route, now relaying call\n");
+} else {
+  log(1,"could not prepare the route, there is not route\n");
+};
+...
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><function moreinfo="none">checkcallingtranslation()</function></title>
+      <para>This function is used to check if the calling number is translated. Before calling checkcallingtranslation, prepareosproute should be called. If the calling number does been translated, the original Remote-Party-ID, if it exists, should be removed from the INVITE message. And a new Remote-Party-ID header should be added (a RPID value for the RPID AVP has been set by prepareosproute). If the calling number is not translated, nothing should be done.</para>
+      <para>This function can be used from BRANCH_ROUTE.</para>
+      <example>
+        <title>checkcallingtranslation usage</title>
+        <programlisting format="linespecific">
+...
+if (checkcallingtranslation()) {
+  # Remove the Remote_Party-ID from the received message
+  # Otherwise it will be forwarded on to the next hop
+  remove_hf("Remote-Party-ID");
+
+  # Append a new Remote_Party
+  append_rpid_hf();
+}
+...
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><function moreinfo="none">prepareallosproute()</function></title>
+      <para>This function tries to prepare all the routes in the list returned by the peering server. The message is then either forked off or redirected to the destination. If unsuccessful in preparing the routes a SIP 500 is sent back and a trace message is logged.</para>
+      <para>This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.</para>
+      <example>
+        <title>prepareallosproute usage</title>
+        <programlisting format="linespecific">
+...
+if (prepareallosproute()) {
+  log(1,"Routes are prepared, now either forking or redirecting the call\n");
+} else {
+  log(1,"Could not prepare the routes. No destination available\n");
+};
+...
+        </programlisting>
+      </example>
+    </section>
+    <section>
+      <title><function moreinfo="none">reportospusage()</function></title>
+      <para>This function should be called after receiving a BYE message. If the message contains an OSP cookie, the function will forward originating and/or terminating duration usage information to a peering server. The function returns TRUE if the BYE includes an OSP cookie. The actual usage message will be sent on a different thread and will not delay BYE processing. The function should be called before relaying the message.</para>
+      <para>Meaning of the parameter is as follows:
+        <itemizedlist>
+          <listitem>
+            <para>"0" - Source device releases the call.</para>
+          </listitem>
+          <listitem>
+            <para>"1" - Destination device releases the call.</para>
+          </listitem>
+        </itemizedlist>
+      </para>
+      <para>This function can be used from REQUEST_ROUTE.</para>
+      <example>
+        <title>reportospusage usage</title>
+        <programlisting format="linespecific">
+...
+if (is_direction("downstream")) {
+  log(1,"This BYE message is from SOURCE\n");
+  if (!reportospusage("0")) {
+    log(1,"This BYE message does not include OSP usage information\n");
+  }
+} else {
+  log(1,"This BYE message is from DESTINATION\n");
+  if (!reportospusage("1")) {
+    log(1,"This BYE message does not include OSP usage information\n");
+  }
+}
+...
+        </programlisting>
+      </example>
+    </section>
+  </section>
+</chapter>
+

+ 17 - 0
src/modules/osp/doc/osp_devel.xml

@@ -0,0 +1,17 @@
+<?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" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
+%docentities;
+
+]>
+<!-- OSP Module Developer's Guide -->
+
+<chapter>
+
+  <title>&develguide;</title>
+  <para>The functions of the OSP modules are not used by other &kamailio; modules.</para>
+</chapter>
+

+ 10 - 0
src/modules/osp/etc/cacert_0.pem

@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBYDCCAQoCAQEwDQYJKoZIhvcNAQEEBQAwOzElMCMGA1UEAxMcb3NwdGVzdHNl
+cnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMB4XDTAyMDIw
+NDE4MjU1MloXDTEyMDIwMzE4MjU1MlowOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZl
+ci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMFwwDQYJKoZIhvcN
+AQEBBQADSwAwSAJBAPGeGwV41EIhX0jEDFLRXQhDEr50OUQPq+f55VwQd0TQNts0
+6BP29+UiNdRW3c3IRHdZcJdC1Cg68ME9cgeq0h8CAwEAATANBgkqhkiG9w0BAQQF
+AANBAGkzBSj1EnnmUxbaiG1N4xjIuLAWydun7o3bFk2tV8dBIhnuh445obYyk1En
+Q27kI7eACCILBZqi2MHDOIMnoN0=
+-----END CERTIFICATE-----

+ 10 - 0
src/modules/osp/etc/localcert.pem

@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBeTCCASMCEHqkOHVRRWr+1COq3CR/xsowDQYJKoZIhvcNAQEEBQAwOzElMCMG
+A1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQ
+U2VydmVyMB4XDTA1MDYyMzAwMjkxOFoXDTA2MDYyNDAwMjkxOFowRTELMAkGA1UE
+BhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdp
+ZGdpdHMgUHR5IEx0ZDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCvLeZfj1G0wuJb
+8JTcVeZaZftncEmRluOnaME3ustd918lRMDYokZmtmDZN8SrP0yd1pfVqZ7NkmBA
+CbBX0k7pAgMBAAEwDQYJKoZIhvcNAQEEBQADQQDnV8QNFVVJx/+7IselU0wsepqM
+urivXZzuxOmTEmTVDzCJx1xhA8jd3vGAj7XDIYiPub1PV23eY5a2ARJuw5w9
+-----END CERTIFICATE-----

+ 9 - 0
src/modules/osp/etc/pkey.pem

@@ -0,0 +1,9 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIBOgIBAAJBAK8t5l+PUbTC4lvwlNxV5lpl+2dwSZGW46dowTe6y133XyVEwNii
+Rma2YNk3xKs/TJ3Wl9Wpns2SYEAJsFfSTukCAwEAAQJAPz13vCm2GmZ8Zyp74usT
+xLCqSJZNyMRLHQWBM0g44Iuy4wE3vpi7Wq+xYuSOH2mu4OddnxswCP4QhaXVQavT
+AQIhAOBVCKXtppEw9UaOBL4vW0Ed/6EA/1D8hDW6St0h7EXJAiEAx+iRmZKhJD6V
+T84dtX5ZYNVk3j3dAcIOovpzUj9a0CECIEduTCapmZQ5xqAEsLXuVlxRtQgLTUD4
+ZxDElPn8x0MhAiBE2HlcND0+qDbvtwJQQOUzDgqg5xk3w8capboVdzAlQQIhAMC+
+lDL7+gDYkNAft5Mu+NObJmQs4Cr+DkDFsKqoxqrm
+-----END RSA PRIVATE KEY-----

+ 515 - 0
src/modules/osp/etc/sample-osp-kamailio.cfg

@@ -0,0 +1,515 @@
+
+debug=3         # debug level (cmd line: -dddddddddd)
+fork=yes
+log_stderror=no # (cmd line: -E)
+sip_warning=no
+
+/* Uncomment these lines to enter debugging mode 
+# fork=no
+# log_stderror=yes
+*/
+
+check_via=no    # (cmd. line: -v)
+dns=no          # (cmd. line: -r)
+rev_dns=no      # (cmd. line: -R)
+disable_dns_blocklist=yes
+disable_tcp=yes
+disable_tls=yes
+port=5060
+children=20
+
+# ------------------ module loading ----------------------------------
+mpath="/usr/local/lib/kamailio/modules"
+loadmodule "sl.so"
+loadmodule "tm.so"
+loadmodule "maxfwd.so"
+loadmodule "rr.so"
+loadmodule "textops.so"
+loadmodule "usrloc.so"
+loadmodule "registrar.so"
+loadmodule "auth.so"
+loadmodule "mi_fifo.so"
+loadmodule "options.so"
+loadmodule "xlog.so"
+loadmodule "avpops.so"
+# Load OSP module
+loadmodule "osp.so"
+
+# ----------------- setting module-specific parameters ---------------
+
+#
+# PEERING PARAMETERS:
+# ===================
+# This section contains OSP parameters that users may need to configure for multi-lateral
+# peering. (sp1_uri must be configured.) Additional detail on OSP Module parameters and
+# functions is provided in the "OSP Module for Secure, Multi-Lateral Peering" document
+# located at:http://developer.berlios.de/docman/?group_id=3799
+#
+# Configure Peering Servers:
+# ==========================
+# Kamailio can be configured to query two peering servers for routing information and peering 
+# authorization tokens using the sp1_uri and sp2_uri parameters. A configuration for sp1_uri
+# is required, configuring sp2_uri is optional. The peering server address should be 
+# configured as a standard URL beginning with either http:// or https:// followed by the 
+# domain name of the OSP server or the IP address enclosed in brackets. The domain name 
+# or IP address should be followed by the peering server TCP port number and uniform 
+# resource identifier. Below are example configurations.
+#
+modparam("osp", "sp1_uri", "http://osptestserver.transnexus.com:1080/osp")
+# modparam("osp", "sp2_uri", "https://[1.2.3.4]:1443/osp")
+
+#
+# Kamailio IP Address
+# ==============
+# device_ip is a recommended parameter that explicitly defines the IP address of Kamailio in 
+# a peering request message (as SourceAlternate type=transport).  The IP address must 
+# be in brackets as shown in the example below.
+#
+# modparam("osp", "device_ip", "[1.1.1.1]")
+
+#
+# Peering Token Validation
+# ========================
+# When Kamailio receives a SIP INVITE with a peering token, the OSP Module will validate the token to
+# determine whether or not the call has been authorized by a peering server.  Peering tokens may,
+# or may not, be digitally signed.  This parameter defines if Kamailio will validate signed or unsigned
+# tokens or both. The values for "token format" are defined below.  The default value is 2.
+#
+# 0 - Validate only signed tokens.  Calls with valid signed tokens are allowed.
+# 1 - Validate only unsigned tokens.  Calls with valid unsigned tokens are allowed.
+# 2 - Validate both signed and unsigned tokens are allowed.  Calls with valid tokens are allowed.
+#
+# modparam("osp", "token_format", 2)
+
+#
+# Crypto files from Peering Server Enrollment
+# ===========================================
+# These parameters identify crypto files used for validating peering authorization tokens
+# and establishing a secure channel between Kamailio and a peering server using SSL.  The files are 
+# generated using the 'Enroll' utility from the OSP toolkit.  By default, the proxy will look
+# for pkey.pem, localcert.pem, and cacart_0.pem in the default configuration directory. 
+# The default config directory is set at compile time using CFG_DIR and defaults to 
+# /usr/local/etc/kamailio/.  The files may be copied to the expected file location or the 
+# parameters below may be changed.
+#
+# If the default CFG_DIR value was used at compile time, the files will be loaded from:
+# modparam("osp", "private_key", "/usr/local/etc/kamailio/pkey.pem")
+# modparam("osp", "local_certificate", "/usr/local/etc/kamailio/localcert.pem")
+# modparam("osp", "ca_certificates", "/usr/local/etc/kamailio/cacert_0.pem")
+
+#
+# Use Remote-Party-ID for calling number
+# ===========================================
+# This parameter is used to tell OSP module if the calling number should be obtained from RPID header. 
+# The default value is 1.
+#
+# 0 - OSP module will use the calling number in From header.
+# 1 - OSP module will use the calling number in RPID header if a RPID header exists.
+#
+# modparam("osp", "use_rpid_for_calling_number", 1)
+
+#
+# URI Format for Redirection Messages
+# ===========================================
+# This parameter is used to tell OSP module which URI format should be used for redirection messages.
+# The default value is 0.
+#
+# 0 - "[email protected]"
+# 1 - "<[email protected]>". This is for Cisco 2600 IP-IP gateway.
+#
+# modparam("osp", "redirection_uri_format", 0)
+
+#
+# Source Network ID AVP
+# ===========================================
+# This parameter is used to tell OSP module which AVP is used to store source network ID
+# The default value is "$avp(s:_osp_source_networkid_)".
+#
+# modparam("osp", "source_networkid_avp", "$avp(s:_osp_source_networkid_)")
+# modparam("osp", "source_networkid_avp", "$avp(s:snid)")
+
+# -- mi_fifo params --
+modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")
+
+# -- usrloc params --
+modparam("usrloc", "db_mode", 0)
+
+# -- rr params --
+# add value to ;lr param to make some broken UAs happy
+modparam("rr", "enable_full_lr", 1)
+
+# enable append_fromtag, request's from-tag is appended to record-route; 
+# that's useful for understanding whether subsequent requests (such as BYE) come from
+# caller (route's from-tag==BYE's from-tag) or callee (route's from-tag==BYE's to-tag) 
+modparam("rr", "append_fromtag", 1)
+
+# Timer which hits if no final reply for a request or ACK for a
+# negative INVITE reply arrives (in seconds).  For example - UA server is off-line.
+# In other words, if the proxy does not receive a response to an Invite before this
+# timer expires, the proxy will retry the call and send an Invite to the next VoIP
+# destination in the routing list.
+modparam("tm", "fr_timer", 2)
+
+# Timer which hits if no final reply for an INVITE arrives after
+# a provisional message was received (in seconds).
+# For example - user is not picking up the phone
+modparam("tm", "fr_inv_timer", 300)
+
+
+# -------------------------  request routing logic -------------------
+
+# main routing logic
+route{
+    xlog("L_INFO", "----ROUTE: Route IN - M=$rm RURI=$ru F=$fu T=$tu IP=$si ID=$ci\n");
+
+    # initial sanity checks
+    if (!mf_process_maxfwd_header("10")) {
+        xlog("L_WARN", "----ROUTE: Too many hops, $rm from '$fu' to '$tu'\n");
+        sl_send_reply("483", "Too Many Hops");
+        return;
+    };
+
+    if (msg:len >= max_len) {
+        xlog("L_WARN", "----ROUTE: Message too big, $rm from '$fu' to '$tu'\n");
+        sl_send_reply("513", "Message Too Big");
+        return;
+    };
+
+    # we record-route all messages -- to make sure that
+    # subsequent messages will go through our proxy; that's
+    # particularly good if upstream and downstream entities
+    # use different transport protocol
+    record_route();
+
+    # loose-route processing
+    if (loose_route()) {
+        if (method=="INVITE") {
+            log(2, "----ROUTE: Relay re-INVITE\n");
+            # send it out now; use stateful forwarding as it works reliably even for UDP2TCP
+            if (!t_relay()) {
+                sl_reply_error();
+            }
+            return;
+        } else if (method=="ACK") {
+            log(2, "----ROUTE: Relay ACK\n");
+            # send it out now; use stateful forwarding as it works reliably even for UDP2TCP
+            if (!t_relay()) {
+                sl_reply_error();
+            }
+            return;
+        }
+    } else {
+        if (method=="BYE") {
+            xlog("L_WARN", "----ROUTE: Processing BYE without route header - F=$fu T=$tu IP=$si ID=$ci\n");
+            if (t_check_trans()) {
+                log(2, "----ROUTE: Duplicated BYE\n");
+            } else {
+                # NOTE - don't t_relay before reporting usage
+                if (is_direction("downstream")) {
+                    log(2, "----ROUTE: BYE from SOURCE\n");
+                    if (!reportospusage("0")) {
+                        xlog("L_WARN", "----ROUTE: failed to report usage, from '$fu' to '$tu'\n");
+                    }
+                } else {
+                    log(2, "----ROUTE: BYE from DESTINATION\n");
+                    if (!reportospusage("1")) {
+                        xlog("L_WARN", "----ROUTE: failed to report usage, from '$fu' to '$tu'\n");
+                    }
+                }
+                sl_send_reply("400", "Bad Request - no route header in BYE message");
+            }
+            return;
+        }
+    }
+
+    if (method=="INVITE") {
+        log(2, "----ROUTE: Processing INVITE\n");
+
+        # Stop retransmission
+        sl_send_reply("100", "Trying");
+
+        if (t_check_trans()) {
+            log(2, "----ROUTE: Duplicated INVITE\n");
+            return;
+        }
+
+        # Authentication
+        log(3, "OSP authorization validation logic\n");
+
+        # This function looks for OSP peering token in the message.  It will fail
+        # if the token is not present
+        if (checkospheader()) {
+            log(3, "With OSP token, validate it\n"); 
+
+            # The function validates OSP tokens.  It will fail
+            # if the token is not valid or has expired
+            validateospheader();
+            switch ($retcode) {
+                case 1:
+                    # Authorization is valid.  The proxy can now use its own database of
+                    # registered users for routing information.
+                    # The proxy could also issue another OSP peering authorization and
+                    # routing request by calling route(1) function.
+                    log(3, "OSP authorization valid\n");
+
+                    # Remove the OSP peering token from the received message
+                    # Otherwise it will be forwarded on to the next hop
+                    remove_hf("P-OSP-Auth-Token");
+                    break;
+                case -2:
+                    log(2, "----ROUTE: Internal server error\n");
+                    sl_send_reply("500", "Internal Server Error");
+                    return;
+                default:
+                    log(2, "----ROUTE: OSP authorization invalid\n");
+                    sl_send_reply("401", "Unauthorized");
+                    return;
+            };
+        } else {
+            log(3, "Without OSP token, apply different authentication strategy\n"); 
+            log(3, "Go ahead, everyone is welcomed\n");
+
+            # # Implement authentication strategy here or simply add the
+            # # statements below to block all invites without OSP peering tokens
+            # sl_send_reply("401", "Unauthorized");
+            # return;
+        }
+
+        log(2, "----ROUTE: Authentication passed\n");
+        
+        # Routing
+
+#        if (lookup("location")) {
+#            log(2, "----ROUTE: Registered user, forward the message\n");
+#            append_hf("P-hint: usrloc\r\n");
+#            t_relay();
+#        } else {
+#            log(2, "----ROUTE: Unregistered user, use OSP to get routing\n");
+#            route(2);
+#        }
+
+        log(2, "----ROUTE: Use OSP to get routing\n");
+        route(2);
+    } else if (method=="ACK") {
+        log(2, "----ROUTE: Processing ACK\n");
+
+        if (t_check_trans()) {
+            log(2, "----ROUTE: Relay E2E ACK\n");
+            t_relay();
+        } else {
+            log(2, "----ROUTE: Not to relay ACK");
+        }
+    } else if (method=="BYE") {
+        log(2, "----ROUTE: Processing BYE\n");
+
+        xlog("L_INFO", "----ROUTE: R=$hdr(Route)\n");
+
+        if (t_check_trans()) {
+            log(2, "----ROUTE: Duplicated BYE\n");
+            return;
+        }
+
+        # NOTE - don't t_relay before reporting usage
+        if (is_direction("downstream")) {
+            log(2, "----ROUTE: BYE from SOURCE\n");
+            if (!reportospusage("0")) {
+                xlog("L_WARN", "----ROUTE: failed to report usage, from '$fu' to '$tu'\n");
+            }
+        } else {
+            log(2, "----ROUTE: BYE from DESTINATION\n");
+            if (!reportospusage("1")) {
+                xlog("L_WARN", "----ROUTE: failed to report usage, from '$fu' to '$tu'\n");
+            }
+        }
+
+        t_relay();
+    } else if (method=="CANCEL") {
+        log(2, "----ROUTE: Processing CANCEL\n");
+
+        if (t_check_trans()) {
+            t_relay();
+        } else {
+            xlog("L_WARN", "----ROUTE: CANCEL without matching transaction, from '$fu' to '$tu'\n");
+        }
+    } else if ((method=="OPTIONS") && (uri==myself)) {
+        log(2, "----ROUTE: Processing OPTIONS\n");
+        options_reply();
+    } else if (method=="PRACK") {
+        log(2, "----ROUTE: Processing PRACK\n");
+        t_relay();
+    } else if (method=="INFO") {
+        log(2, "----ROUTE: Processing INFO\n");
+        t_relay();
+    } else if (method=="UPDATE") {
+        log(2, "----ROUTE: Processing UPDATE\n");
+        t_relay();
+#    } else if (method=="REGISTER") {
+#        log(2, "----ROUTE: Processing REGISTER\n");
+#
+#        # Stop retransmission
+#        sl_send_reply("100", "Trying");
+#        
+#        if (uri==myself) {
+#            log(2, "----ROUTE: Registered\n");
+#            save("location");
+#        } else {
+#            log(2, "----ROUTE: Register from outside domain rejected\n");
+#            sl_send_reply("488", "Unknown Domain");
+#        }
+    } else {
+        xlog("L_WARN", "----ROUTE: Unsupported message, $rm from '$fu' to '$tu'\n");
+        sl_send_reply("500", "Unsupported Message");
+    }
+
+    log(3, "----ROUTE: Route OUT\n");
+}
+
+
+# OSP Authorization and Routing
+route[2] {
+    log(3, "OSP authorization and routing logic\n");
+
+    # Is request to a phone number?
+    # A phone number consists of digits (0 through 9)
+    # and can begin with +
+#    if (uri=~"sip:[+,0-9][0-9]*@") {
+        # Requesting OSP peering routing and authorization
+        # The request may fail if:
+        #  o OSP peering servers are not available
+        #  o Authentication failed
+        #  o There is no route to destination or the route is blocked
+        log(3, "Requesting OSP authorization and routing\n");
+
+        # Get source network ID
+        # $avp(s:snid) = $hdr(Call-Owner);
+
+        requestosprouting();
+        switch ($retcode) {
+            case 1:
+                log(3, "Response received\n");
+
+                # Now we have 3 options.
+                #  o route(3) - sends a redirect to all available routes
+                #  o route(4) - fork off to all available routes
+                #  o route(5) in conjunction with failure_route(1) - sequentially tries all routes
+
+                # route(3);
+                # route(4);
+                route(5);
+
+                break;
+            case -403:
+                xlog("L_WARN", "----ROUTE: Call to '$tU' from source device '$si' is blocked on OSP Server.\n");
+                sl_send_reply("403", "Forbidden - Call is blocked");
+                break;
+            case -404:
+                xlog("L_WARN", "----ROUTE: No route on OSP server for call to '$tU' from source device '$si'.\n");
+                sl_send_reply("404", "Route Not Found");
+                break;
+            case -2:
+                log(2, "----ROUTE: Internal server error\n");
+                sl_send_reply("500", "Internal Server Error");
+                break;
+            default:
+                log(2, "----ROUTE: OSP Authorization failed\n");
+                sl_send_reply("503", "Service Not Available");
+        }
+#    } else {
+#        log(3, "Wrong phone number\n");
+#        sl_send_reply("401", "Not Phone Number");
+#    }
+}
+
+
+route[3] {
+    log(3, "Prepare all routes and redirect\n");
+
+    if (prepareallosproutes()) {
+        sl_send_reply("300", "Redirect");
+    } else {
+        log(3, "Failed to prepare all routes\n");
+        sl_send_reply("500", "Internal Server Error");
+    }
+}
+
+
+route[4] {
+    log(3, "Prepare all routes and fork-off\n");
+
+    if (prepareallosproutes()) {
+        t_relay();
+    } else {
+        log(3, "Failed to prepare all routes\n");
+        sl_send_reply("500", "Internal Server Error");
+    }
+}
+
+
+route[5] {
+    log(3, "Try the 1st route\n");
+
+    if (checkosproute()) {
+        t_on_branch("1");
+
+        t_on_failure("1");
+
+        t_relay();
+    } else {
+        log(3, "Could  not use the 1st route\n");
+        sl_send_reply("500", "Internal Server Error");
+    }
+}
+
+
+failure_route[1] {
+    if (t_check_status("487")) {
+        log(3, "Call canceled (status 487)\n");
+        return;
+    }
+
+    if (t_check_status("486")) {
+        log(3, "User busy (status 486)\n");
+        return;
+    }
+
+    if (t_check_status("408")) {
+        if (!t_local_replied("last")) {
+            log(3, "User unavailable (status 408)\n");
+            return;
+        }
+    }
+
+    log(3, "Try the next route\n");
+
+    if (checkosproute()) {
+        t_on_branch("1");
+
+        append_branch();
+
+        t_on_failure("1");
+
+        t_relay();
+    } else {
+        xlog("L_WARN", "----ROUTE: All destinations attempted for call ID '$ci'. Call cannot be completed.\n");
+        t_reply("503", "Service Not Available - Call cannot be completed");
+    }
+}
+
+
+branch_route[1] {
+    log(3, "Prepare route specific OSP information\n");
+
+    prepareosproute();
+
+    # Only add/change Remote-Party-ID if calling number is translated
+    if (checkcallingtranslation()) {
+        log(3, "Calling number translated, add a new RPID header\n");
+
+        # Remove the Remote_Party-ID from the received message
+        # Otherwise it will be forwarded on to the next hop
+        remove_hf("Remote-Party-ID");
+
+        # Append a new Remote_Party
+        append_rpid_hf();
+    }
+}

+ 66 - 0
src/modules/osp/globals.c

@@ -0,0 +1,66 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <stdio.h>
+#include <osp/osp.h>
+#include "../../core/usr_avp.h"
+#include "osp_mod.h"
+
+unsigned int _osp_sp_number;
+char *_osp_sp_uris[OSP_DEF_SPS];
+unsigned long _osp_sp_weights[OSP_DEF_SPS] = {OSP_DEF_WEIGHT, OSP_DEF_WEIGHT,
+		OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT,
+		OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT,
+		OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT, OSP_DEF_WEIGHT,
+		OSP_DEF_WEIGHT, OSP_DEF_WEIGHT};
+char *_osp_device_ip = NULL;
+char *_osp_device_port = NULL;
+unsigned char *_osp_private_key = NULL;
+unsigned char *_osp_local_certificate = NULL;
+unsigned char *_osp_ca_certificate = NULL;
+int _osp_crypto_hw = OSP_DEF_HW;
+int _osp_validate_callid = OSP_DEF_CALLID;
+int _osp_token_format = OSP_DEF_TOKEN;
+int _osp_ssl_lifetime = OSP_DEF_SSLLIFE;
+int _osp_persistence = OSP_DEF_PERSISTENCE;
+int _osp_retry_delay = OSP_DEF_DELAY;
+int _osp_retry_limit = OSP_DEF_RETRY;
+int _osp_timeout = OSP_DEF_TIMEOUT;
+int _osp_max_dests = OSP_DEF_DESTS;
+int _osp_use_rpid = OSP_DEF_USERPID;
+int _osp_redir_uri = OSP_DEF_REDIRURI;
+char _osp_PRIVATE_KEY[OSP_KEYBUF_SIZE];
+char _osp_LOCAL_CERTIFICATE[OSP_KEYBUF_SIZE];
+char _osp_CA_CERTIFICATE[OSP_KEYBUF_SIZE];
+char *_osp_snid_avp = OSP_DEF_SNIDAVP;
+avp_name_t _osp_snid_avpname;
+avp_flags_t _osp_snid_avptype;
+
+OSPTPROVHANDLE _osp_provider = -1;

+ 549 - 0
src/modules/osp/orig_transaction.c

@@ -0,0 +1,549 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <string.h>
+#include <osp/osp.h>
+#include "../../core/dset.h"
+#include "../../core/usr_avp.h"
+#include "../../core/mem/mem.h"
+#include "../../modules/siputils/siputils.h"
+#include "orig_transaction.h"
+#include "destination.h"
+#include "osptoolkit.h"
+#include "sipheader.h"
+#include "usage.h"
+
+extern char *_osp_device_ip;
+extern char *_osp_device_port;
+extern int _osp_max_dests;
+extern int _osp_redir_uri;
+extern avp_name_t _osp_snid_avpname;
+extern avp_flags_t _osp_snid_avptype;
+extern OSPTPROVHANDLE _osp_provider;
+extern siputils_api_t osp_siputils;
+
+const int OSP_FIRST_ROUTE = 1;
+const int OSP_NEXT_ROUTE = 0;
+const int OSP_MAIN_ROUTE = 1;
+const int OSP_BRANCH_ROUTE = 0;
+const str OSP_CALLING_NAME = {"_osp_calling_translated_", 24};
+
+static int ospLoadRoutes(OSPTTRANHANDLE transaction, int destcount,
+		char *source, char *sourcedev, char *origcalled, time_t authtime);
+static int ospPrepareDestination(
+		struct sip_msg *msg, int isfirst, int type, int format);
+static int ospSetRpid(struct sip_msg *msg, osp_dest *dest);
+
+/*
+ * Get routes from AuthRsp
+ * param transaction Transaction handle
+ * param destcount Expected destination count
+ * param source Source IP
+ * param sourcedev Source device IP
+ * param origcalled Original called number
+ * param authtime Request authorization time
+ * return 0 success, -1 failure
+ */
+static int ospLoadRoutes(OSPTTRANHANDLE transaction, int destcount,
+		char *source, char *sourcedev, char *origcalled, time_t authtime)
+{
+	int count;
+	int errorcode;
+	osp_dest *dest;
+	osp_dest dests[OSP_DEF_DESTS];
+	OSPE_DEST_PROT protocol;
+	OSPE_DEST_OSP_ENABLED enabled;
+	int result = 0;
+
+	for(count = 0; count < destcount; count++) {
+		/* This is necessary because we will save destinations in reverse order */
+		dest = ospInitDestination(&dests[count]);
+
+		if(dest == NULL) {
+			result = -1;
+			break;
+		}
+
+		dest->destinationCount = count + 1;
+		strncpy(dest->origcalled, origcalled, sizeof(dest->origcalled) - 1);
+
+		if(count == 0) {
+			errorcode = OSPPTransactionGetFirstDestination(transaction,
+					sizeof(dest->validafter), dest->validafter,
+					dest->validuntil, &dest->timelimit, &dest->callidsize,
+					(void *)dest->callid, sizeof(dest->called), dest->called,
+					sizeof(dest->calling), dest->calling, sizeof(dest->host),
+					dest->host, sizeof(dest->destdev), dest->destdev,
+					&dest->tokensize, dest->token);
+		} else {
+			errorcode = OSPPTransactionGetNextDestination(transaction, 0,
+					sizeof(dest->validafter), dest->validafter,
+					dest->validuntil, &dest->timelimit, &dest->callidsize,
+					(void *)dest->callid, sizeof(dest->called), dest->called,
+					sizeof(dest->calling), dest->calling, sizeof(dest->host),
+					dest->host, sizeof(dest->destdev), dest->destdev,
+					&dest->tokensize, dest->token);
+		}
+
+		if(errorcode != OSPC_ERR_NO_ERROR) {
+			LM_ERR("failed to load routes (%d) expected '%d' current '%d'\n",
+					errorcode, destcount, count);
+			result = -1;
+			break;
+		}
+
+		errorcode = OSPPTransactionGetDestProtocol(transaction, &protocol);
+		if(errorcode != OSPC_ERR_NO_ERROR) {
+			/* This does not mean an ERROR. The OSP server may not support OSP 2.1.1 */
+			LM_DBG("cannot get dest protocol (%d)\n", errorcode);
+			protocol = OSPE_DEST_PROT_SIP;
+		}
+		switch(protocol) {
+			case OSPE_DEST_PROT_H323_LRQ:
+			case OSPE_DEST_PROT_H323_SETUP:
+			case OSPE_DEST_PROT_IAX:
+				dest->supported = 0;
+				break;
+			case OSPE_DEST_PROT_SIP:
+			case OSPE_DEST_PROT_UNDEFINED:
+			case OSPE_DEST_PROT_UNKNOWN:
+			default:
+				dest->supported = 1;
+				break;
+		}
+
+		errorcode = OSPPTransactionIsDestOSPEnabled(transaction, &enabled);
+		if(errorcode != OSPC_ERR_NO_ERROR) {
+			/* This does not mean an ERROR. The OSP server may not support OSP 2.1.1 */
+			LM_DBG("cannot get dest OSP version (%d)\n", errorcode);
+		} else if(enabled == OSPE_OSP_FALSE) {
+			/* Destination device does not support OSP. Do not send token to it */
+			dest->token[0] = '\0';
+			dest->tokensize = 0;
+		}
+
+		errorcode =
+				OSPPTransactionGetDestNetworkId(transaction, dest->networkid);
+		if(errorcode != OSPC_ERR_NO_ERROR) {
+			/* This does not mean an ERROR. The OSP server may not support OSP 2.1.1 */
+			LM_DBG("cannot get dest network ID (%d)\n", errorcode);
+			dest->networkid[0] = '\0';
+		}
+
+		strncpy(dest->source, source, sizeof(dest->source) - 1);
+		strncpy(dest->srcdev, sourcedev, sizeof(dest->srcdev) - 1);
+		dest->type = OSPC_SOURCE;
+		dest->transid = ospGetTransactionId(transaction);
+		dest->authtime = authtime;
+
+		LM_INFO("get destination '%d': "
+				"valid after '%s' "
+				"valid until '%s' "
+				"time limit '%d' seconds "
+				"call id '%.*s' "
+				"calling '%s' "
+				"called '%s' "
+				"host '%s' "
+				"supported '%d' "
+				"network id '%s' "
+				"token size '%d'\n",
+				count, dest->validafter, dest->validuntil, dest->timelimit,
+				dest->callidsize, dest->callid, dest->calling, dest->called,
+				dest->host, dest->supported, dest->networkid, dest->tokensize);
+	}
+
+	/*
+     * Save destination in reverse order,
+     * when we start searching avps the destinations
+     * will be in order
+     */
+	if(result == 0) {
+		for(count = destcount - 1; count >= 0; count--) {
+			if(ospSaveOrigDestination(&dests[count]) == -1) {
+				LM_ERR("failed to save originate destination\n");
+				/* Report terminate CDR */
+				ospRecordEvent(0, 500);
+				result = -1;
+				break;
+			}
+		}
+	}
+
+	return result;
+}
+
+/*
+ * Request OSP authorization and routeing
+ * param msg SIP message
+ * param ignore1
+ * param ignore2
+ * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure, MODULE_RETURNCODE_ERROR error
+ */
+int ospRequestRouting(struct sip_msg *msg, char *ignore1, char *ignore2)
+{
+	int errorcode;
+	time_t authtime;
+	char called[OSP_E164BUF_SIZE];
+	char calling[OSP_E164BUF_SIZE];
+	char sourcedev[OSP_STRBUF_SIZE];
+	char deviceinfo[OSP_STRBUF_SIZE];
+	struct usr_avp *snidavp = NULL;
+	avp_value_t snidval;
+	char snid[OSP_STRBUF_SIZE];
+	unsigned int callidnumber = 1;
+	OSPTCALLID *callids[callidnumber];
+	unsigned int logsize = 0;
+	char *detaillog = NULL;
+	const char **preferred = NULL;
+	unsigned int destcount;
+	OSPTTRANHANDLE transaction = -1;
+	int result = MODULE_RETURNCODE_FALSE;
+
+	authtime = time(NULL);
+
+	destcount = _osp_max_dests;
+
+	if((errorcode = OSPPTransactionNew(_osp_provider, &transaction))
+			!= OSPC_ERR_NO_ERROR) {
+		LM_ERR("failed to create new OSP transaction (%d)\n", errorcode);
+	} else if((ospGetRpidUserpart(msg, calling, sizeof(calling)) != 0)
+			  && (ospGetFromUserpart(msg, calling, sizeof(calling)) != 0)) {
+		LM_ERR("failed to extract calling number\n");
+	} else if((ospGetUriUserpart(msg, called, sizeof(called)) != 0)
+			  && (ospGetToUserpart(msg, called, sizeof(called)) != 0)) {
+		LM_ERR("failed to extract called number\n");
+	} else if(ospGetCallId(msg, &(callids[0])) != 0) {
+		LM_ERR("failed to extract call id\n");
+	} else if(ospGetSourceAddress(msg, sourcedev, sizeof(sourcedev)) != 0) {
+		LM_ERR("failed to extract source deivce address\n");
+	} else {
+		ospConvertAddress(sourcedev, deviceinfo, sizeof(deviceinfo));
+
+		if((_osp_snid_avpname.n != 0)
+				&& ((snidavp = search_first_avp(
+							 _osp_snid_avptype, _osp_snid_avpname, &snidval, 0))
+						!= NULL)
+				&& (snidavp->flags & AVP_VAL_STR)
+				&& (snidval.s.s && snidval.s.len)) {
+			snprintf(snid, sizeof(snid), "%.*s", snidval.s.len, snidval.s.s);
+			OSPPTransactionSetNetworkIds(transaction, snid, "");
+		} else {
+			snid[0] = '\0';
+		}
+
+		LM_INFO("request auth and routing for: "
+				"source_ip '%s' "
+				"source_port '%s' "
+				"source_dev '%s' "
+				"source_networkid '%s' "
+				"calling '%s' "
+				"called '%s' "
+				"call_id '%.*s' "
+				"dest_count '%d'\n",
+				_osp_device_ip, _osp_device_port,
+				deviceinfo, /* in "[x.x.x.x]" or host.domain format */
+				snid, calling, called, callids[0]->ospmCallIdLen,
+				callids[0]->ospmCallIdVal, destcount);
+
+		/* try to request authorization */
+		errorcode = OSPPTransactionRequestAuthorisation(
+				transaction,	/* transaction handle */
+				_osp_device_ip, /* from the configuration file */
+				deviceinfo, /* source device of call, protocol specific, in OSP format */
+				calling,	  /* calling number in nodotted e164 notation */
+				OSPC_E164,	  /* calling number format */
+				called,		  /* called number */
+				OSPC_E164,	  /* called number format */
+				"",			  /* optional username string, used if no number */
+				callidnumber, /* number of call ids, here always 1 */
+				callids,	  /* sized-1 array of call ids */
+				preferred,	  /* preferred destinations, here always NULL */
+				&destcount,	  /* max destinations, after call dest_count */
+				&logsize, /* size allocated for detaillog (next param) 0=no log */
+				detaillog); /* memory location for detaillog to be stored */
+
+		if((errorcode == OSPC_ERR_NO_ERROR)
+				&& (ospLoadRoutes(transaction, destcount, _osp_device_ip,
+							sourcedev, called, authtime)
+						== 0)) {
+			LM_INFO("there are '%d' OSP routes, call_id '%.*s'\n", destcount,
+					callids[0]->ospmCallIdLen, callids[0]->ospmCallIdVal);
+			result = MODULE_RETURNCODE_TRUE;
+		} else {
+			LM_ERR("failed to request auth and routing (%d), call_id '%.*s'\n",
+					errorcode, callids[0]->ospmCallIdLen,
+					callids[0]->ospmCallIdVal);
+			switch(errorcode) {
+				case OSPC_ERR_TRAN_ROUTE_BLOCKED:
+					result = -403;
+					break;
+				case OSPC_ERR_TRAN_ROUTE_NOT_FOUND:
+					result = -404;
+					break;
+				case OSPC_ERR_NO_ERROR:
+					/* AuthRsp ok but ospLoadRoutes fails */
+					result = MODULE_RETURNCODE_ERROR;
+					break;
+				default:
+					result = MODULE_RETURNCODE_FALSE;
+					break;
+			}
+		}
+	}
+
+	if(callids[0] != NULL) {
+		OSPPCallIdDelete(&(callids[0]));
+	}
+
+	if(transaction != -1) {
+		OSPPTransactionDelete(transaction);
+	}
+
+	return result;
+}
+
+/*
+ * Check if there is a route
+ * param msg SIP message
+ * param ignore1
+ * param ignore2
+ * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure
+ */
+int ospCheckRoute(struct sip_msg *msg, char *ignore1, char *ignore2)
+{
+	if(ospCheckOrigDestination() == 0) {
+		return MODULE_RETURNCODE_TRUE;
+	} else {
+		return MODULE_RETURNCODE_FALSE;
+	}
+}
+
+/*
+ * Create RPID AVP
+ * param msg SIP message
+ * param dest Destination structure
+ * return 0 success, 1 calling number same, -1 failure
+ */
+static int ospSetRpid(struct sip_msg *msg, osp_dest *dest)
+{
+	str rpid;
+	char calling[OSP_STRBUF_SIZE];
+	char source[OSP_STRBUF_SIZE];
+	char buffer[OSP_STRBUF_SIZE];
+	int result = -1;
+
+	if((ospGetRpidUserpart(msg, calling, sizeof(calling)) != 0)
+			&& (ospGetFromUserpart(msg, calling, sizeof(calling)) != 0)) {
+		LM_ERR("failed to extract calling number\n");
+		return result;
+	}
+
+	if(strcmp(calling, dest->calling) == 0) {
+		/* Do nothing for this case */
+		result = 1;
+	} else if((osp_siputils.rpid_avp.s.s == NULL)
+			  || (osp_siputils.rpid_avp.s.len == 0)) {
+		LM_WARN("rpid_avp is not foune, cannot set rpid avp\n");
+		result = -1;
+	} else {
+		if(dest->source[0] == '[') {
+			/* Strip "[]" */
+			memset(source, 0, sizeof(source));
+			strncpy(source, &dest->source[1], sizeof(source) - 1);
+			source[strlen(source) - 1] = '\0';
+		}
+
+		snprintf(buffer, sizeof(buffer), "\"%s\" <sip:%s@%s>", dest->calling,
+				dest->calling, source);
+
+		rpid.s = buffer;
+		rpid.len = strlen(buffer);
+		add_avp(osp_siputils.rpid_avp_type | AVP_VAL_STR,
+				(avp_name_t)osp_siputils.rpid_avp, (avp_value_t)rpid);
+
+		result = 0;
+	}
+
+	return result;
+}
+
+/*
+ * Check if the calling number is translated.
+ *     This function checks the avp set by ospPrepareDestination.
+ * param msg SIP message
+ * param ignore1
+ * param ignore2
+ * return MODULE_RETURNCODE_TRUE calling number translated MODULE_RETURNCODE_FALSE without transaltion
+ */
+int ospCheckTranslation(struct sip_msg *msg, char *ignore1, char *ignore2)
+{
+	avp_value_t callingval;
+	int result = MODULE_RETURNCODE_FALSE;
+
+	if(search_first_avp(
+			   AVP_NAME_STR, (avp_name_t)OSP_CALLING_NAME, &callingval, 0)
+			!= NULL) {
+		if(callingval.n == 0) {
+			LM_DBG("the calling number has not been translated\n");
+		} else {
+			LM_DBG("the calling number is translated\n");
+			result = MODULE_RETURNCODE_TRUE;
+		}
+	} else {
+		LM_ERR("there is not calling translation avp\n");
+	}
+
+	return result;
+}
+
+/*
+ * Build SIP message for destination
+ * param msg SIP message
+ * param isfirst Is first destination
+ * param type Main or branch route block
+ * param format URI format
+ * return MODULE_RETURNCODE_TRUE success MODULE_RETURNCODE_FALSE failure
+ */
+static int ospPrepareDestination(
+		struct sip_msg *msg, int isfirst, int type, int format)
+{
+	avp_value_t val;
+	int res;
+	str newuri = {NULL, 0};
+	int result = MODULE_RETURNCODE_FALSE;
+
+	osp_dest *dest = ospGetNextOrigDestination();
+
+	if(dest != NULL) {
+		ospRebuildDestionationUri(
+				&newuri, dest->called, dest->host, "", format);
+
+		LM_INFO("prepare route to URI '%.*s' for call_id '%.*s' transaction_id "
+				"'%llu'\n",
+				newuri.len, newuri.s, dest->callidsize, dest->callid,
+				dest->transid);
+
+		if(type == OSP_MAIN_ROUTE) {
+			if(isfirst == OSP_FIRST_ROUTE) {
+				rewrite_uri(msg, &newuri);
+			} else {
+				km_append_branch(
+						msg, &newuri, NULL, NULL, Q_UNSPECIFIED, 0, NULL);
+			}
+			/* Do not add route specific OSP information */
+			result = MODULE_RETURNCODE_TRUE;
+		} else if(type == OSP_BRANCH_ROUTE) {
+			/* For branch route, add route specific OSP information */
+
+			/* Update the Request-Line */
+			rewrite_uri(msg, &newuri);
+
+			/* Add OSP token header */
+			ospAddOspHeader(msg, dest->token, dest->tokensize);
+
+			/* Add branch-specific OSP Cookie */
+			ospRecordOrigTransaction(msg, dest->transid, dest->srcdev,
+					dest->calling, dest->called, dest->authtime,
+					dest->destinationCount);
+
+			/* Add rpid avp for calling number translation */
+			res = ospSetRpid(msg, dest);
+			switch(res) {
+				case 0:
+					/* Calling number is translated */
+					val.n = 1;
+					add_avp(AVP_NAME_STR, (avp_name_t)OSP_CALLING_NAME, val);
+					break;
+				default:
+					LM_DBG("cannot set rpid avp\n");
+					/* Just like without calling translation */
+				case 1:
+					/* Calling number does not been translated */
+					val.n = 0;
+					add_avp(AVP_NAME_STR, (avp_name_t)OSP_CALLING_NAME, val);
+					break;
+			}
+
+			result = MODULE_RETURNCODE_TRUE;
+		} else {
+			LM_ERR("unsupported route block type\n");
+		}
+	} else {
+		LM_DBG("there is no more routes\n");
+		ospReportOrigSetupUsage();
+	}
+
+	if(newuri.len > 0) {
+		pkg_free(newuri.s);
+	}
+
+	return result;
+}
+
+/*
+ * Prepare OSP route
+ *     This function only works in branch route block.
+ *     This function is only for Kamailio.
+ * param msg SIP message
+ * param ignore1
+ * param ignore2
+ * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure
+ */
+int ospPrepareRoute(struct sip_msg *msg, char *ignore1, char *ignore2)
+{
+	int result = MODULE_RETURNCODE_TRUE;
+
+	/* The first parameter will be ignored */
+	result = ospPrepareDestination(msg, OSP_FIRST_ROUTE, OSP_BRANCH_ROUTE, 0);
+
+	return result;
+}
+
+/*
+ * Prepare all OSP routes
+ *     This function does not work in branch route block.
+ * param msg SIP message
+ * param ignore1
+ * param ignore2
+ * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure
+ */
+int ospPrepareAllRoutes(struct sip_msg *msg, char *ignore1, char *ignore2)
+{
+	int result = MODULE_RETURNCODE_TRUE;
+
+	for(result = ospPrepareDestination(
+				msg, OSP_FIRST_ROUTE, OSP_MAIN_ROUTE, _osp_redir_uri);
+			result == MODULE_RETURNCODE_TRUE;
+			result = ospPrepareDestination(
+					msg, OSP_NEXT_ROUTE, OSP_MAIN_ROUTE, _osp_redir_uri)) {
+	}
+
+	return MODULE_RETURNCODE_TRUE;
+}

+ 42 - 0
src/modules/osp/orig_transaction.h

@@ -0,0 +1,42 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _OSP_MOD_ORIG_TRANSACTION_H_
+#define _OSP_MOD_ORIG_TRANSACTION_H_
+
+#include "../../core/parser/msg_parser.h"
+
+int ospRequestRouting(struct sip_msg *, char *, char *);
+int ospCheckRoute(struct sip_msg *, char *, char *);
+int ospPrepareRoute(struct sip_msg *, char *, char *);
+int ospPrepareAllRoutes(struct sip_msg *, char *, char *);
+int ospCheckTranslation(struct sip_msg *, char *, char *);
+
+#endif /* _OSP_MOD_ORIG_TRANSACTION_H_ */

+ 351 - 0
src/modules/osp/osp_mod.c

@@ -0,0 +1,351 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * History:
+ * ---------
+ *  2006-03-13  RR functions are loaded via API function (bogdan)
+ */
+
+#include <osp/osp.h>
+#include "../rr/api.h"
+#include "../../modules/siputils/siputils.h"
+#include "osp_mod.h"
+#include "orig_transaction.h"
+#include "term_transaction.h"
+#include "usage.h"
+#include "tm.h"
+#include "provider.h"
+
+MODULE_VERSION
+
+extern unsigned int _osp_sp_number;
+extern char *_osp_sp_uris[];
+extern unsigned long _osp_sp_weights[];
+extern char *_osp_device_ip;
+extern char *_osp_device_port;
+extern unsigned char *_osp_private_key;
+extern unsigned char *_osp_local_certificate;
+extern unsigned char *_osp_ca_certificate;
+extern int _osp_crypto_hw;
+extern int _osp_validate_callid;
+extern int _osp_token_format;
+extern int _osp_ssl_lifetime;
+extern int _osp_persistence;
+extern int _osp_retry_delay;
+extern int _osp_retry_limit;
+extern int _osp_timeout;
+extern int _osp_max_dests;
+extern int _osp_use_rpid;
+extern int _osp_redir_uri;
+extern char _osp_PRIVATE_KEY[];
+extern char _osp_LOCAL_CERTIFICATE[];
+extern char _osp_CA_CERTIFICATE[];
+extern char *_osp_snid_avp;
+extern avp_name_t _osp_snid_avpname;
+extern avp_flags_t _osp_snid_avptype;
+extern OSPTPROVHANDLE _osp_provider;
+
+struct rr_binds osp_rr;
+siputils_api_t osp_siputils;
+int osp_index[OSP_DEF_SPS];
+
+static int ospInitMod(void);
+static void ospDestMod(void);
+static int ospInitChild(int);
+static int ospVerifyParameters(void);
+static void ospDumpParameters(void);
+
+static cmd_export_t cmds[] = {{"checkospheader", (cmd_function)ospCheckHeader,
+									  0, 0, 0, REQUEST_ROUTE | FAILURE_ROUTE},
+		{"validateospheader", (cmd_function)ospValidateHeader, 0, 0, 0,
+				REQUEST_ROUTE | FAILURE_ROUTE},
+		{"requestosprouting", (cmd_function)ospRequestRouting, 0, 0, 0,
+				REQUEST_ROUTE | FAILURE_ROUTE},
+		{"checkosproute", (cmd_function)ospCheckRoute, 0, 0, 0,
+				REQUEST_ROUTE | FAILURE_ROUTE},
+		{"prepareosproute", (cmd_function)ospPrepareRoute, 0, 0, 0,
+				BRANCH_ROUTE},
+		{"prepareallosproutes", (cmd_function)ospPrepareAllRoutes, 0, 0, 0,
+				REQUEST_ROUTE | FAILURE_ROUTE},
+		{"checkcallingtranslation", (cmd_function)ospCheckTranslation, 0, 0, 0,
+				BRANCH_ROUTE},
+		{"reportospusage", (cmd_function)ospReportUsage, 1, 0, 0,
+				REQUEST_ROUTE},
+		{0, 0, 0, 0, 0, 0}};
+
+static param_export_t params[] = {{"sp1_uri", PARAM_STRING, &_osp_sp_uris[0]},
+		{"sp2_uri", PARAM_STRING, &_osp_sp_uris[1]},
+		{"sp3_uri", PARAM_STRING, &_osp_sp_uris[2]},
+		{"sp4_uri", PARAM_STRING, &_osp_sp_uris[3]},
+		{"sp5_uri", PARAM_STRING, &_osp_sp_uris[4]},
+		{"sp6_uri", PARAM_STRING, &_osp_sp_uris[5]},
+		{"sp7_uri", PARAM_STRING, &_osp_sp_uris[6]},
+		{"sp8_uri", PARAM_STRING, &_osp_sp_uris[7]},
+		{"sp9_uri", PARAM_STRING, &_osp_sp_uris[8]},
+		{"sp10_uri", PARAM_STRING, &_osp_sp_uris[9]},
+		{"sp11_uri", PARAM_STRING, &_osp_sp_uris[10]},
+		{"sp12_uri", PARAM_STRING, &_osp_sp_uris[11]},
+		{"sp13_uri", PARAM_STRING, &_osp_sp_uris[12]},
+		{"sp14_uri", PARAM_STRING, &_osp_sp_uris[13]},
+		{"sp15_uri", PARAM_STRING, &_osp_sp_uris[14]},
+		{"sp16_uri", PARAM_STRING, &_osp_sp_uris[15]},
+		{"sp1_weight", INT_PARAM, &(_osp_sp_weights[0])},
+		{"sp2_weight", INT_PARAM, &(_osp_sp_weights[1])},
+		{"sp3_weight", INT_PARAM, &(_osp_sp_weights[2])},
+		{"sp4_weight", INT_PARAM, &(_osp_sp_weights[3])},
+		{"sp5_weight", INT_PARAM, &(_osp_sp_weights[4])},
+		{"sp6_weight", INT_PARAM, &(_osp_sp_weights[5])},
+		{"sp7_weight", INT_PARAM, &(_osp_sp_weights[6])},
+		{"sp8_weight", INT_PARAM, &(_osp_sp_weights[7])},
+		{"sp9_weight", INT_PARAM, &(_osp_sp_weights[8])},
+		{"sp10_weight", INT_PARAM, &(_osp_sp_weights[9])},
+		{"sp11_weight", INT_PARAM, &(_osp_sp_weights[10])},
+		{"sp12_weight", INT_PARAM, &(_osp_sp_weights[11])},
+		{"sp13_weight", INT_PARAM, &(_osp_sp_weights[12])},
+		{"sp14_weight", INT_PARAM, &(_osp_sp_weights[13])},
+		{"sp15_weight", INT_PARAM, &(_osp_sp_weights[14])},
+		{"sp16_weight", INT_PARAM, &(_osp_sp_weights[15])},
+		{"device_ip", PARAM_STRING, &_osp_device_ip},
+		{"device_port", PARAM_STRING, &_osp_device_port},
+		{"private_key", PARAM_STRING, &_osp_private_key},
+		{"local_certificate", PARAM_STRING, &_osp_local_certificate},
+		{"ca_certificates", PARAM_STRING, &_osp_ca_certificate},
+		{"enable_crypto_hardware_support", INT_PARAM, &_osp_crypto_hw},
+		{"validate_callid", INT_PARAM, &(_osp_validate_callid)},
+		{"token_format", INT_PARAM, &_osp_token_format},
+		{"ssl_lifetime", INT_PARAM, &_osp_ssl_lifetime},
+		{"persistence", INT_PARAM, &_osp_persistence},
+		{"retry_delay", INT_PARAM, &_osp_retry_delay},
+		{"retry_limit", INT_PARAM, &_osp_retry_limit},
+		{"timeout", INT_PARAM, &_osp_timeout},
+		{"max_destinations", INT_PARAM, &_osp_max_dests},
+		{"use_rpid_for_calling_number", INT_PARAM, &_osp_use_rpid},
+		{"redirection_uri_format", INT_PARAM, &_osp_redir_uri},
+		{"source_networkid_avp", PARAM_STRING, &_osp_snid_avp}, {0, 0, 0}};
+
+struct module_exports exports = {
+		"osp",			 /* module name */
+		DEFAULT_DLFLAGS, /* dlopen flags */
+		cmds,			 /* cmd (cfg function) exports */
+		params,			 /* param exports */
+		0,				 /* RPC method exports */
+		0,				 /* pseudo-variables exports */
+		0,				 /* response handling function */
+		ospInitMod,		 /* module init function */
+		ospInitChild,	 /* per-child init function */
+		ospDestMod		 /* module destroy function */
+};
+
+/*
+ * Initialize OSP module
+ * return 0 success, -1 failure
+ */
+static int ospInitMod(void)
+{
+	bind_siputils_t bind_su;
+
+	if(ospVerifyParameters() != 0) {
+		/* At least one parameter incorrect -> error */
+		return -1;
+	}
+
+	/* Load the RR API */
+	if(load_rr_api(&osp_rr) != 0) {
+		LM_WARN("failed to load the RR API. Check if you load the rr module\n");
+		LM_WARN("add_rr_param is required for reporting duration for OSP "
+				"transactions\n");
+		memset(&osp_rr, 0, sizeof(osp_rr));
+	}
+
+	/* Load the AUTH API */
+	bind_su = (bind_siputils_t)find_export("bind_siputils", 1, 0);
+	if((bind_su == NULL) || (bind_su(&osp_siputils) != 0)) {
+		LM_WARN("failed to load the SIPUTILS API. Check if you load the auth "
+				"module.\n");
+		LM_WARN("rpid_avp & rpid_avp_type is required for calling number "
+				"translation\n");
+		memset(&osp_siputils, 0, sizeof(osp_siputils));
+	}
+
+	if(ospInitTm() < 0) {
+		return -1;
+	}
+
+	/* everything is fine, initialization done */
+	return 0;
+}
+
+/*
+ * Destroy OSP module
+ */
+static void ospDestMod(void)
+{
+}
+
+/*
+ * Initializeild process of OSP module
+ * param rank
+ * return 0 success, -1 failure
+ */
+static int ospInitChild(int rank)
+{
+	int code = -1;
+
+	code = ospSetupProvider();
+
+	LM_DBG("provider '%d' (%d)\n", _osp_provider, code);
+
+	return 0;
+}
+
+/*
+ * Verify parameters for OSP module
+ * return 0 success, -1 failure
+ */
+static int ospVerifyParameters(void)
+{
+	int i;
+	pv_spec_t avp_spec;
+	str avp_str;
+	int result = 0;
+
+	/* Default location for the cert files is in the compile time variable CFG_DIR */
+	if(_osp_private_key == NULL) {
+		sprintf(_osp_PRIVATE_KEY, "%spkey.pem", CFG_DIR);
+		_osp_private_key = (unsigned char *)_osp_PRIVATE_KEY;
+	}
+
+	if(_osp_local_certificate == NULL) {
+		sprintf(_osp_LOCAL_CERTIFICATE, "%slocalcert.pem", CFG_DIR);
+		_osp_local_certificate = (unsigned char *)_osp_LOCAL_CERTIFICATE;
+	}
+
+	if(_osp_ca_certificate == NULL) {
+		sprintf(_osp_CA_CERTIFICATE, "%scacert_0.pem", CFG_DIR);
+		_osp_ca_certificate = (unsigned char *)_osp_CA_CERTIFICATE;
+	}
+
+	if(_osp_device_ip == NULL) {
+		_osp_device_ip = "";
+	}
+
+	if(_osp_device_port == NULL) {
+		_osp_device_port = "";
+	}
+
+	if(_osp_max_dests > OSP_DEF_DESTS || _osp_max_dests < 1) {
+		_osp_max_dests = OSP_DEF_DESTS;
+		LM_WARN("max_destinations is out of range, reset to %d\n",
+				OSP_DEF_DESTS);
+	}
+
+	if(_osp_token_format < 0 || _osp_token_format > 2) {
+		_osp_token_format = OSP_DEF_TOKEN;
+		LM_WARN("token_format is out of range, reset to %d\n", OSP_DEF_TOKEN);
+	}
+
+	_osp_sp_number = 0;
+	for(i = 0; i < OSP_DEF_SPS; i++) {
+		if(_osp_sp_uris[i] != NULL) {
+			if(_osp_sp_number != i) {
+				_osp_sp_uris[_osp_sp_number] = _osp_sp_uris[i];
+				_osp_sp_weights[_osp_sp_number] = _osp_sp_weights[i];
+				_osp_sp_uris[i] = NULL;
+				_osp_sp_weights[i] = OSP_DEF_WEIGHT;
+			}
+			osp_index[_osp_sp_number] = i + 1;
+			_osp_sp_number++;
+		}
+	}
+
+	if(_osp_sp_number == 0) {
+		LM_ERR("at least one service point uri must be configured\n");
+		result = -1;
+	}
+
+	if(_osp_snid_avp && *_osp_snid_avp) {
+		avp_str.s = _osp_snid_avp;
+		avp_str.len = strlen(_osp_snid_avp);
+		if(pv_parse_spec(&avp_str, &avp_spec) == NULL
+				|| avp_spec.type != PVT_AVP
+				|| pv_get_avp_name(0, &(avp_spec.pvp), &_osp_snid_avpname,
+						   &_osp_snid_avptype)
+						   != 0) {
+			LM_WARN("'%s' invalid AVP definition\n", _osp_snid_avp);
+			_osp_snid_avpname.n = 0;
+			_osp_snid_avptype = 0;
+		}
+	} else {
+		_osp_snid_avpname.n = 0;
+		_osp_snid_avptype = 0;
+	}
+
+	ospDumpParameters();
+
+	return result;
+}
+
+/*
+ * Dump OSP module configuration
+ */
+static void ospDumpParameters(void)
+{
+	int i;
+
+	LM_INFO("module configuration: ");
+	LM_INFO("    number of service points '%d'", _osp_sp_number);
+	for(i = 0; i < _osp_sp_number; i++) {
+		LM_INFO("    sp%d_uri '%s' sp%d_weight '%ld' ", osp_index[i],
+				_osp_sp_uris[i], osp_index[i], _osp_sp_weights[i]);
+	}
+	LM_INFO("    device_ip '%s' device_port '%s' ", _osp_device_ip,
+			_osp_device_port);
+	LM_INFO("    private_key '%s' ", _osp_private_key);
+	LM_INFO("    local_certificate '%s' ", _osp_local_certificate);
+	LM_INFO("    ca_certificates '%s' ", _osp_ca_certificate);
+	LM_INFO("    enable_crypto_hardware_support '%d' ", _osp_crypto_hw);
+	LM_INFO("    token_format '%d' ", _osp_token_format);
+	LM_INFO("    ssl_lifetime '%d' ", _osp_ssl_lifetime);
+	LM_INFO("    persistence '%d' ", _osp_persistence);
+	LM_INFO("    retry_delay '%d' ", _osp_retry_delay);
+	LM_INFO("    retry_limit '%d' ", _osp_retry_limit);
+	LM_INFO("    timeout '%d' ", _osp_timeout);
+	LM_INFO("    validate_call_id '%d' ", _osp_validate_callid);
+	LM_INFO("    use_rpid_for_calling_number '%d' ", _osp_use_rpid);
+	LM_INFO("    redirection_uri_format '%d' ", _osp_redir_uri);
+	LM_INFO("    max_destinations '%d'\n", _osp_max_dests);
+	if(_osp_snid_avpname.n == 0) {
+		LM_INFO("    source network ID disabled\n");
+	} else if(_osp_snid_avptype & AVP_NAME_STR) {
+		LM_INFO("    source network ID AVP name '%.*s'\n",
+				_osp_snid_avpname.s.len, _osp_snid_avpname.s.s);
+	} else {
+		LM_INFO("    source network ID AVP ID '%d'\n", _osp_snid_avpname.n);
+	}
+}

+ 61 - 0
src/modules/osp/osp_mod.h

@@ -0,0 +1,61 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _OSP_MOD_H_
+#define _OSP_MOD_H_
+
+#define MODULE_RETURNCODE_TRUE 1
+#define MODULE_RETURNCODE_STOPROUTE 0
+#define MODULE_RETURNCODE_FALSE -1
+#define MODULE_RETURNCODE_ERROR -2
+
+#define OSP_DEF_SPS 16
+#define OSP_DEF_WEIGHT 1000
+#define OSP_DEF_HW 0
+#define OSP_DEF_CALLID 1 /* Validate call ids, set to 0 to disable */
+#define OSP_DEF_TOKEN 2
+#define OSP_DEF_SSLLIFE 300
+#define OSP_DEF_PERSISTENCE (60 * 1000)
+#define OSP_DEF_DELAY 0
+#define OSP_DEF_RETRY 2
+#define OSP_DEF_TIMEOUT (60 * 1000)
+#define OSP_DEF_DESTS 5
+#define OSP_DEF_USERPID 1
+#define OSP_DEF_REDIRURI \
+	0 /* 0 for "[email protected]", 1 for "<[email protected]>" format */
+#define OSP_DEF_SNIDAVP "$avp(s:_osp_source_networkid_)"
+
+#define OSP_KEYBUF_SIZE 256
+#define OSP_STRBUF_SIZE 256
+#define OSP_E164BUF_SIZE 1024
+#define OSP_TOKENBUF_SIZE 2048
+#define OSP_HEADERBUF_SIZE 3072
+
+#endif /* _OSP_MOD_H_ */

+ 167 - 0
src/modules/osp/osptoolkit.c

@@ -0,0 +1,167 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <osp/osptrans.h>
+#include "../../core/dprint.h"
+#include "osptoolkit.h"
+
+static OSPTTHREADRETURN ospReportUsageWork(void *usagearg);
+
+typedef struct _osp_usage
+{
+	OSPTTRANHANDLE ospvTransaction; /* Transaction handle */
+	unsigned ospvReleaseCode;		/* Release code */
+	unsigned ospvDuration;			/* Length of call */
+	time_t ospvStartTime;			/* Call start time */
+	time_t ospvEndTime;				/* Call end time */
+	time_t ospvAlertTime;			/* Call alert time */
+	time_t ospvConnectTime;			/* Call connect time */
+	unsigned ospvIsPDDInfoPresent;	/* Is PDD Info present */
+	unsigned ospvPostDialDelay;		/* Post Dial Delay */
+	unsigned ospvReleaseSource;		/* EP that released the call */
+} osp_usage;
+
+/*
+ * Get OSP transaction ID from transaction handle
+ * param transaction OSP transaction headle
+ * return OSP transaction ID
+ */
+unsigned long long ospGetTransactionId(OSPTTRANHANDLE transaction)
+{
+	OSPTTRANS *context = NULL;
+	unsigned long long id = 0;
+	int errorcode = OSPC_ERR_NO_ERROR;
+
+	context = OSPPTransactionGetContext(transaction, &errorcode);
+
+	if(errorcode == OSPC_ERR_NO_ERROR) {
+		id = (unsigned long long)context->TransactionID;
+	} else {
+		LM_ERR("failed to extract transaction_id from transaction handle %d "
+			   "(%d)\n",
+				transaction, errorcode);
+	}
+
+	return id;
+}
+
+/*
+ * Create a thread to report OSP usage
+ * param ospvTransaction OSP transaction handle
+ * param ospvReleaseCode Call release reason
+ * param ospvDurating Call duration
+ * param ospvStartTime Call start time
+ * param ospvEndTime Call end time
+ * param ospvAlertTime Call alert time
+ * param ospvConnectTime Call connected  time
+ * param ospvIsPDDInfoPresent If post dial delay information avaliable
+ * param ospvPostDialDelay Post dial delay information
+ * param ospvReleaseSource Which side release the call
+ */
+void ospReportUsageWrapper(OSPTTRANHANDLE ospvTransaction,
+		unsigned ospvReleaseCode, unsigned ospvDuration, time_t ospvStartTime,
+		time_t ospvEndTime, time_t ospvAlertTime, time_t ospvConnectTime,
+		unsigned ospvIsPDDInfoPresent, unsigned ospvPostDialDelay,
+		unsigned ospvReleaseSource)
+{
+	osp_usage *usage;
+	OSPTTHREADID threadid;
+	OSPTTHRATTR threadattr;
+	int errorcode;
+
+	LM_DBG("schedule usage report for '%llu'\n",
+			ospGetTransactionId(ospvTransaction));
+
+	usage = (osp_usage *)malloc(sizeof(osp_usage));
+
+	usage->ospvTransaction = ospvTransaction;
+	usage->ospvReleaseCode = ospvReleaseCode;
+	usage->ospvDuration = ospvDuration;
+	usage->ospvStartTime = ospvStartTime;
+	usage->ospvEndTime = ospvEndTime;
+	usage->ospvAlertTime = ospvAlertTime;
+	usage->ospvConnectTime = ospvConnectTime;
+	usage->ospvIsPDDInfoPresent = ospvIsPDDInfoPresent;
+	usage->ospvPostDialDelay = ospvPostDialDelay;
+	usage->ospvReleaseSource = ospvReleaseSource;
+
+	OSPM_THRATTR_INIT(threadattr, errorcode);
+
+	OSPM_SETDETACHED_STATE(threadattr, errorcode);
+
+	OSPM_CREATE_THREAD(
+			threadid, &threadattr, ospReportUsageWork, usage, errorcode);
+
+	OSPM_THRATTR_DESTROY(threadattr);
+}
+
+/*
+ * Report OSP usage thread function
+ * param usagearg OSP usage information
+ * return
+ */
+static OSPTTHREADRETURN ospReportUsageWork(void *usagearg)
+{
+	int i;
+	const int MAX_RETRIES = 5;
+	osp_usage *usage;
+	int errorcode;
+
+	usage = (osp_usage *)usagearg;
+
+	OSPPTransactionRecordFailure(usage->ospvTransaction,
+			(enum OSPEFAILREASON)usage->ospvReleaseCode);
+
+	for(i = 1; i <= MAX_RETRIES; i++) {
+		errorcode = OSPPTransactionReportUsage(usage->ospvTransaction,
+				usage->ospvDuration, usage->ospvStartTime, usage->ospvEndTime,
+				usage->ospvAlertTime, usage->ospvConnectTime,
+				usage->ospvIsPDDInfoPresent, usage->ospvPostDialDelay,
+				usage->ospvReleaseSource, (unsigned char *)"", 0, 0, 0, 0, NULL,
+				NULL);
+
+		if(errorcode == OSPC_ERR_NO_ERROR) {
+			LM_DBG("reporte usage for '%llu'\n",
+					ospGetTransactionId(usage->ospvTransaction));
+			break;
+		} else {
+			LM_ERR("failed to report usage for '%llu' (%d) attempt '%d' of "
+				   "'%d'\n",
+					ospGetTransactionId(usage->ospvTransaction), errorcode, i,
+					MAX_RETRIES);
+		}
+	}
+
+	OSPPTransactionDelete(usage->ospvTransaction);
+
+	free(usage);
+
+	OSPTTHREADRETURN_NULL();
+}

+ 59 - 0
src/modules/osp/osptoolkit.h

@@ -0,0 +1,59 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _OSP_MOD_OSPTOOLKIT_H_
+#define _OSP_MOD_OSPTOOLKIT_H_
+
+#include <osp/osp.h>
+
+/*
+ * This module implements help functions for the OSP toolkit.
+ * Some of the functions may be implemented by the toolkit in the future.
+ */
+
+/*
+ * Returns the OSP transaction id generated by the server.  The id is
+ * returned in the Authorization Response and also packaged in the token.
+ */
+unsigned long long ospGetTransactionId(OSPTTRANHANDLE transaction);
+void ospReportUsageWrapper(
+		OSPTTRANHANDLE ospvTransaction, /* In - Transaction handle */
+		unsigned ospvReleaseCode,		/* In - Release code */
+		unsigned ospvDuration,			/* In - Length of call */
+		time_t ospvStartTime,			/* In - Call start time */
+		time_t ospvEndTime,				/* In - Call end time */
+		time_t ospvAlertTime,			/* In - Call alert time */
+		time_t ospvConnectTime,			/* In - Call connect time */
+		unsigned ospvIsPDDInfoPresent,	/* In - Is PDD Info present */
+		unsigned ospvPostDialDelay,		/* In - Post Dial Delay */
+		unsigned ospvReleaseSource		/* In - EP that released the call */
+);
+
+#endif /* _OSP_MOD_OSPTOOLKIT_H_ */

+ 120 - 0
src/modules/osp/provider.c

@@ -0,0 +1,120 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <osp/osp.h>
+#include <osp/osputils.h>
+#include "../../core/dprint.h"
+#include "provider.h"
+
+extern unsigned int _osp_sp_number;
+extern char *_osp_sp_uris[];
+extern unsigned long _osp_sp_weights[];
+extern unsigned char *_osp_private_key;
+extern unsigned char *_osp_local_certificate;
+extern unsigned char *_osp_ca_certificate;
+extern int _osp_ssl_lifetime;
+extern int _osp_persistence;
+extern int _osp_retry_delay;
+extern int _osp_retry_limit;
+extern int _osp_timeout;
+extern int _osp_crypto_hw;
+extern OSPTPROVHANDLE _osp_provider;
+
+/*
+ * Create a new OSP provider object per process
+ * return 0 success, others failure
+ */
+int ospSetupProvider(void)
+{
+	OSPTPRIVATEKEY privatekey;
+	OSPTCERT localcert;
+	OSPTCERT cacert;
+	OSPTCERT *cacerts[1];
+	int result;
+
+	cacerts[0] = &cacert;
+
+	if((result = OSPPInit(_osp_crypto_hw)) != 0) {
+		LM_ERR("failed to initialize OSP (%d)\n", result);
+	} else if(OSPPUtilLoadPEMPrivateKey(_osp_private_key, &privatekey) != 0) {
+		LM_ERR("failed to load private key from '%s'\n", _osp_private_key);
+	} else if(OSPPUtilLoadPEMCert(_osp_local_certificate, &localcert) != 0) {
+		LM_ERR("failed to load local certificate from '%s'\n",
+				_osp_local_certificate);
+	} else if(OSPPUtilLoadPEMCert(_osp_ca_certificate, &cacert) != 0) {
+		LM_ERR("failed to load CA certificate from '%s'\n",
+				_osp_ca_certificate);
+	} else {
+		result = OSPPProviderNew(_osp_sp_number, (const char **)_osp_sp_uris,
+				_osp_sp_weights, "http://localhost:1234", &privatekey,
+				&localcert, 1, (const OSPTCERT **)cacerts, 1, _osp_ssl_lifetime,
+				_osp_sp_number, _osp_persistence, _osp_retry_delay,
+				_osp_retry_limit, _osp_timeout, "", "", &_osp_provider);
+		if(result != 0) {
+			LM_ERR("failed to create provider (%d)\n", result);
+		} else {
+			LM_DBG("created new (per process) provider '%d'\n", _osp_provider);
+			result = 0;
+		}
+	}
+
+	/*
+     * Free space allocated while loading crypto information from PEM-encoded files.
+     * There are some problems to free the memory, do not free them
+     */
+	if(privatekey.PrivateKeyData != NULL) {
+		//free(privatekey.PrivateKeyData);
+	}
+
+	if(localcert.CertData != NULL) {
+		//free(localcert.CertData);
+	}
+
+	if(cacert.CertData != NULL) {
+		//free(localcert.CertData);
+	}
+
+	return result;
+}
+
+/*
+ * Erase OSP provider object
+ * return 0 success, others failure
+ */
+int ospDeleteProvider(void)
+{
+	int result;
+
+	if((result = OSPPProviderDelete(_osp_provider, 0)) != 0) {
+		LM_ERR("failed to erase provider '%d' (%d)\n", _osp_provider, result);
+	}
+
+	return result;
+}

+ 37 - 0
src/modules/osp/provider.h

@@ -0,0 +1,37 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _OSP_MOD_PROVIDER_H_
+#define _OSP_MOD_PROVIDER_H_
+
+int ospSetupProvider(void);
+int ospDeleteProvider(void);
+
+#endif /* _OSP_MOD_PROVIDER_H_ */

+ 595 - 0
src/modules/osp/sipheader.c

@@ -0,0 +1,595 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <osp/osp.h>
+#include <osp/ospb64.h>
+#include "../../core/forward.h"
+#include "../../core/parser/parse_from.h"
+#include "../../core/parser/parse_rpid.h"
+#include "../../core/parser/parse_rr.h"
+#include "../../core/parser/parse_uri.h"
+#include "../../core/data_lump.h"
+#include "../../core/mem/mem.h"
+#include "osp_mod.h"
+#include "sipheader.h"
+
+extern int _osp_use_rpid;
+
+static void ospSkipPlus(char *e164);
+static int ospAppendHeader(struct sip_msg *msg, str *header);
+
+/*
+ * Copy str to buffer and check overflow
+ * param source Str
+ * param buffer Buffer
+ * param buffersize Size of buffer
+ */
+void ospCopyStrToBuffer(str *source, char *buffer, int buffersize)
+{
+	int copybytes;
+
+	if(source->len > buffersize - 1) {
+		LM_ERR("buffer for copying '%.*s' is too small, will copy the first "
+			   "'%d' bytes\n",
+				source->len, source->s, buffersize);
+		copybytes = buffersize - 1;
+	} else {
+		copybytes = source->len;
+	}
+
+	strncpy(buffer, source->s, copybytes);
+	buffer[copybytes] = '\0';
+}
+
+/*
+ * Remove '+' in E164 string
+ * param e164 E164 string
+ */
+static void ospSkipPlus(char *e164)
+{
+	if(*e164 == '+') {
+		strncpy(e164, e164 + 1, strlen(e164) - 1);
+		e164[strlen(e164) - 1] = '\0';
+	}
+}
+
+/*
+ * Get calling number from From header
+ * param msg SIP message
+ * param fromuser User part of From header
+ * param buffersize Size of fromuser buffer
+ * return 0 success, -1 failure
+ */
+int ospGetFromUserpart(struct sip_msg *msg, char *fromuser, int buffersize)
+{
+	struct to_body *from;
+	struct sip_uri uri;
+	int result = -1;
+
+	fromuser[0] = '\0';
+
+	if(msg->from != NULL) {
+		if(parse_from_header(msg) == 0) {
+			from = get_from(msg);
+			if(parse_uri(from->uri.s, from->uri.len, &uri) == 0) {
+				ospCopyStrToBuffer(&uri.user, fromuser, buffersize);
+				ospSkipPlus(fromuser);
+				result = 0;
+			} else {
+				LM_ERR("failed to parse From uri\n");
+			}
+		} else {
+			LM_ERR("failed to parse From header\n");
+		}
+	} else {
+		LM_ERR("failed to find From header\n");
+	}
+
+	return result;
+}
+
+/*
+ * Get calling number from Remote-Party-ID header
+ * param msg SIP message
+ * param rpiduser User part of Remote-Party-ID header
+ * param buffersize Size of fromuser buffer
+ * return 0 success, -1 failure
+ */
+int ospGetRpidUserpart(struct sip_msg *msg, char *rpiduser, int buffersize)
+{
+	struct to_body *rpid;
+	struct sip_uri uri;
+	int result = -1;
+
+	rpiduser[0] = '\0';
+
+	if(_osp_use_rpid != 0) {
+		if(msg->rpid != NULL) {
+			if(parse_rpid_header(msg) == 0) {
+				rpid = get_rpid(msg);
+				if(parse_uri(rpid->uri.s, rpid->uri.len, &uri) == 0) {
+					ospCopyStrToBuffer(&uri.user, rpiduser, buffersize);
+					ospSkipPlus(rpiduser);
+					result = 0;
+				} else {
+					LM_ERR("failed to parse RPID uri\n");
+				}
+			} else {
+				LM_ERR("failed to parse RPID header\n");
+			}
+		} else {
+			LM_DBG("without RPID header\n");
+		}
+	} else {
+		LM_DBG("do not use RPID header\n");
+	}
+
+	return result;
+}
+
+/*
+ * Get called number from To header
+ * param msg SIP message
+ * param touser User part of To header
+ * param buffersize Size of touser buffer
+ * return 0 success, -1 failure
+ */
+int ospGetToUserpart(struct sip_msg *msg, char *touser, int buffersize)
+{
+	struct to_body *to;
+	struct sip_uri uri;
+	int result = -1;
+
+	touser[0] = '\0';
+
+	if(msg->to != NULL) {
+		if(parse_headers(msg, HDR_TO_F, 0) == 0) {
+			to = get_to(msg);
+			if(parse_uri(to->uri.s, to->uri.len, &uri) == 0) {
+				ospCopyStrToBuffer(&uri.user, touser, buffersize);
+				ospSkipPlus(touser);
+				result = 0;
+			} else {
+				LM_ERR("failed to parse To uri\n");
+			}
+		} else {
+			LM_ERR("failed to parse To header\n");
+		}
+	} else {
+		LM_ERR("failed to find To header\n");
+	}
+
+	return result;
+}
+
+/*
+ * Get called number from Request-Line header
+ * param msg SIP message
+ * param touser User part of To header
+ * param buffersize Size of touser buffer
+ * return 0 success, -1 failure
+ */
+int ospGetUriUserpart(struct sip_msg *msg, char *uriuser, int buffersize)
+{
+	int result = -1;
+
+	uriuser[0] = '\0';
+
+	if(parse_sip_msg_uri(msg) >= 0) {
+		ospCopyStrToBuffer(&msg->parsed_uri.user, uriuser, buffersize);
+		ospSkipPlus(uriuser);
+		result = 0;
+	} else {
+		LM_ERR("failed to parse Request-Line URI\n");
+	}
+
+	return result;
+}
+
+/*
+ * Append header to SIP message
+ * param msg SIP message
+ * param header Header to be appended
+ * return 0 success, -1 failure
+ */
+static int ospAppendHeader(struct sip_msg *msg, str *header)
+{
+	char *s;
+	struct lump *anchor;
+
+	if((msg == 0) || (header == 0) || (header->s == 0) || (header->len <= 0)) {
+		LM_ERR("bad parameters for appending header\n");
+		return -1;
+	}
+
+	if(parse_headers(msg, HDR_EOH_F, 0) == -1) {
+		LM_ERR("failed to parse message\n");
+		return -1;
+	}
+
+	anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0);
+	if(anchor == 0) {
+		LM_ERR("failed to get anchor\n");
+		return -1;
+	}
+
+	s = (char *)pkg_malloc(header->len);
+	if(s == 0) {
+		LM_ERR("no pkg memory\n");
+		return -1;
+	}
+
+	memcpy(s, header->s, header->len);
+
+	if(insert_new_lump_before(anchor, s, header->len, 0) == 0) {
+		LM_ERR("failed to insert lump\n");
+		pkg_free(s);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Add OSP token header to SIP message
+ * param msg SIP message
+ * param token OSP authorization token
+ * param tokensize Size of OSP authorization token
+ * return 0 success, -1 failure
+ */
+int ospAddOspHeader(
+		struct sip_msg *msg, unsigned char *token, unsigned int tokensize)
+{
+	str headerval;
+	char buffer[OSP_HEADERBUF_SIZE];
+	unsigned char encodedtoken[OSP_TOKENBUF_SIZE];
+	unsigned int encodedtokensize = sizeof(encodedtoken);
+	int result = -1;
+
+	if(tokensize == 0) {
+		LM_DBG("destination is not OSP device\n");
+		result = 0;
+	} else {
+		if(OSPPBase64Encode(token, tokensize, encodedtoken, &encodedtokensize)
+				== 0) {
+			snprintf(buffer, sizeof(buffer), "%s%.*s\r\n", OSP_TOKEN_HEADER,
+					encodedtokensize, encodedtoken);
+
+			headerval.s = buffer;
+			headerval.len = strlen(buffer);
+
+			LM_DBG("setting osp token header field '%s'\n", buffer);
+
+			if(ospAppendHeader(msg, &headerval) == 0) {
+				result = 0;
+			} else {
+				LM_ERR("failed to append osp header\n");
+			}
+		} else {
+			LM_ERR("failed to base64 encode token\n");
+		}
+	}
+
+	return result;
+}
+
+/*
+ * Get OSP token from SIP message
+ * param msg SIP message
+ * param token OSP authorization token
+ * param tokensize Size of OSP authorization token
+ * return 0 success, -1 failure
+ */
+int ospGetOspHeader(
+		struct sip_msg *msg, unsigned char *token, unsigned int *tokensize)
+{
+	struct hdr_field *hf;
+	int errorcode;
+	int result = -1;
+
+	parse_headers(msg, HDR_EOH_F, 0);
+
+	for(hf = msg->headers; hf; hf = hf->next) {
+		if((hf->type == HDR_OTHER_T) && (hf->name.len == OSP_HEADER_SIZE - 2)) {
+			// possible hit
+			if(strncasecmp(hf->name.s, OSP_TOKEN_HEADER, OSP_HEADER_SIZE)
+					== 0) {
+				if((errorcode = OSPPBase64Decode(
+							hf->body.s, hf->body.len, token, tokensize))
+						== OSPC_ERR_NO_ERROR) {
+					result = 0;
+				} else {
+					LM_ERR("failed to base64 decode token (%d)\n", errorcode);
+					LM_ERR("header '%.*s' length %d\n", hf->body.len,
+							hf->body.s, hf->body.len);
+				}
+				break;
+			}
+		}
+	}
+
+	return result;
+}
+
+/*
+ * Get first VIA header and use the IP or host name
+ * param msg SIP message
+ * param sourceaddress Source address
+ * param buffersize Size of sourceaddress
+ * return 0 success, -1 failure
+ */
+int ospGetSourceAddress(
+		struct sip_msg *msg, char *sourceaddress, int buffersize)
+{
+	struct hdr_field *hf;
+	struct via_body *via;
+	int result = -1;
+
+	/*
+     * No need to call parse_headers, called already and VIA is parsed
+     * anyway by default
+     */
+	for(hf = msg->headers; hf; hf = hf->next) {
+		if(hf->type == HDR_VIA_T) {
+			// found first VIA
+			via = (struct via_body *)hf->parsed;
+			ospCopyStrToBuffer(&via->host, sourceaddress, buffersize);
+
+			LM_DBG("source address '%s'\n", sourceaddress);
+
+			result = 0;
+			break;
+		}
+	}
+
+	return result;
+}
+
+/*
+ * Get Call-ID header from SIP message
+ * param msg SIP message
+ * param callid Call ID
+ * return 0 success, -1 failure
+ */
+int ospGetCallId(struct sip_msg *msg, OSPTCALLID **callid)
+{
+	struct hdr_field *hf;
+	int result = -1;
+
+	hf = (struct hdr_field *)msg->callid;
+	if(hf != NULL) {
+		*callid = OSPPCallIdNew(hf->body.len, (unsigned char *)hf->body.s);
+		if(*callid) {
+			result = 0;
+		} else {
+			LM_ERR("failed to allocate OSPCALLID object for '%.*s'\n",
+					hf->body.len, hf->body.s);
+		}
+	} else {
+		LM_ERR("failed to find Call-ID header\n");
+	}
+
+	return result;
+}
+
+/*
+ * Get route parameters from the 1st Route or Request-Line
+ * param msg SIP message
+ * param routeparameters Route parameters
+ * param buffersize Size of routeparameters
+ * return 0 success, -1 failure
+ */
+int ospGetRouteParameters(
+		struct sip_msg *msg, char *routeparameters, int buffersize)
+{
+	struct hdr_field *hf;
+	rr_t *rt;
+	struct sip_uri uri;
+	int result = -1;
+
+	LM_DBG("parsed uri host '%.*s' port '%d' vars '%.*s'\n",
+			msg->parsed_uri.host.len, msg->parsed_uri.host.s,
+			msg->parsed_uri.port_no, msg->parsed_uri.params.len,
+			msg->parsed_uri.params.s);
+
+	if(!(hf = msg->route)) {
+		LM_DBG("there is no Route headers\n");
+	} else if(!(rt = (rr_t *)hf->parsed)) {
+		LM_ERR("route headers are not parsed\n");
+	} else if(parse_uri(rt->nameaddr.uri.s, rt->nameaddr.uri.len, &uri) != 0) {
+		LM_ERR("failed to parse the Route uri '%.*s'\n", rt->nameaddr.uri.len,
+				rt->nameaddr.uri.s);
+	} else if(check_self(&uri.host, uri.port_no ? uri.port_no : SIP_PORT,
+					  PROTO_NONE)
+			  != 1) {
+		LM_DBG("the Route uri is NOT mine\n");
+		LM_DBG("host '%.*s' port '%d'\n", uri.host.len, uri.host.s,
+				uri.port_no);
+		LM_DBG("params '%.*s'\n", uri.params.len, uri.params.s);
+	} else {
+		LM_DBG("the Route uri IS mine - '%.*s'\n", uri.params.len,
+				uri.params.s);
+		LM_DBG("host '%.*s' port '%d'\n", uri.host.len, uri.host.s,
+				uri.port_no);
+		ospCopyStrToBuffer(&uri.params, routeparameters, buffersize);
+		result = 0;
+	}
+
+	if((result == -1) && (msg->parsed_uri.params.len > 0)) {
+		LM_DBG("using route parameters from Request-Line uri\n");
+		ospCopyStrToBuffer(
+				&msg->parsed_uri.params, routeparameters, buffersize);
+		routeparameters[msg->parsed_uri.params.len] = '\0';
+		result = 0;
+	}
+
+	return result;
+}
+
+/*
+ * Rebuild URI using called number, destination IP, and port
+ * param newuri URI to be built
+ * param called Called number
+ * param dest Destination IP
+ * param port Destination port
+ * param format URI format
+ * return 0 success, -1 failure
+ */
+int ospRebuildDestionationUri(
+		str *newuri, char *called, char *dest, char *port, int format)
+{
+	static const str TRANS = {";transport=tcp", 14};
+	char *buffer;
+	int calledsize;
+	int destsize;
+	int portsize;
+
+	calledsize = strlen(called);
+	destsize = strlen(dest);
+	portsize = strlen(port);
+
+	LM_DBG("'%s'(%d) '%s'(%d) '%s'(%d) '%d'\n", called, calledsize, dest,
+			destsize, port, portsize, format);
+
+	/* "sip:" + called + "@" + dest + : + port + " SIP/2.0" for URI format 0 */
+	/* "<sip:" + called + "@" + dest + : + port> + " SIP/2.0" for URI format 1 */
+	newuri->s = (char *)pkg_malloc(1 + 4 + calledsize + 1 + destsize + 1
+								   + portsize + 1 + 1 + 16 + TRANS.len);
+	if(newuri == NULL) {
+		LM_ERR("no pkg memory\n");
+		return -1;
+	}
+	buffer = newuri->s;
+
+	if(format == 1) {
+		*buffer++ = '<';
+	}
+	*buffer++ = 's';
+	*buffer++ = 'i';
+	*buffer++ = 'p';
+	*buffer++ = ':';
+
+	memcpy(buffer, called, calledsize);
+	buffer += calledsize;
+	*buffer++ = '@';
+
+	if(*dest == '[') {
+		/* leave out annoying [] */
+		memcpy(buffer, dest + 1, destsize - 2);
+		buffer += destsize - 2;
+	} else {
+		memcpy(buffer, dest, destsize);
+		buffer += destsize;
+	}
+
+	if(portsize > 0) {
+		*buffer++ = ':';
+		memcpy(buffer, port, portsize);
+		buffer += portsize;
+	}
+
+	if(format == 1) {
+		*buffer++ = '>';
+	}
+
+	/*
+    *buffer++ = ' ';
+    *buffer++ = 'S';
+    *buffer++ = 'I';
+    *buffer++ = 'P';
+    *buffer++ = '/';
+    *buffer++ = '2';
+    *buffer++ = '.';
+    *buffer++ = '0';
+
+    memcpy(buffer, TRANS.s, TRANS.len);
+    buffer += TRANS.len;
+    *buffer = '\0';
+*/
+
+	newuri->len = buffer - newuri->s;
+
+	LM_DBG("new uri '%.*s'\n", newuri->len, newuri->s);
+
+	return 0;
+}
+
+/*
+ * Get next hop using the first Route not generated by this proxy or URI from the Request-Line
+ * param msg SIP message
+ * param nexthop Next hop IP
+ * param buffersize Size of nexthop
+ */
+void ospGetNextHop(struct sip_msg *msg, char *nexthop, int buffersize)
+{
+	struct hdr_field *hf;
+	struct sip_uri uri;
+	rr_t *rt;
+	int found = 0;
+
+	for(hf = msg->headers; hf; hf = hf->next) {
+		if(hf->type == HDR_ROUTE_T) {
+			for(rt = (rr_t *)hf->parsed; rt; rt = rt->next) {
+				if(parse_uri(rt->nameaddr.uri.s, rt->nameaddr.uri.len, &uri)
+						== 0) {
+					LM_DBG("host '%.*s' port '%d'\n", uri.host.len, uri.host.s,
+							uri.port_no);
+
+					if(check_self(&uri.host,
+							   uri.port_no ? uri.port_no : SIP_PORT, PROTO_NONE)
+							!= 1) {
+						LM_DBG("it is NOT me, FOUND!\n");
+
+						ospCopyStrToBuffer(&uri.host, nexthop, buffersize);
+						found = 1;
+						break;
+					} else {
+						LM_DBG("it IS me, keep looking\n");
+					}
+				} else {
+					LM_ERR("failed to parse route uri '%.*s'\n",
+							rt->nameaddr.uri.len, rt->nameaddr.uri.s);
+				}
+			}
+			if(found == 1) {
+				break;
+			}
+		}
+	}
+
+	if(!found) {
+		LM_DBG("using the Request-Line instead host '%.*s' port '%d'\n",
+				msg->parsed_uri.host.len, msg->parsed_uri.host.s,
+				msg->parsed_uri.port_no);
+
+		ospCopyStrToBuffer(&msg->parsed_uri.host, nexthop, buffersize);
+		found = 1;
+	}
+}

+ 58 - 0
src/modules/osp/sipheader.h

@@ -0,0 +1,58 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _OSP_MOD_SIPHEADER_H_
+#define _OSP_MOD_SIPHEADER_H_
+
+#include <osp/osp.h>
+#include "../../core/parser/msg_parser.h"
+
+#define OSP_TOKEN_HEADER "P-OSP-Auth-Token: "
+#define OSP_HEADER_SIZE strlen(OSP_TOKEN_HEADER)
+
+void ospCopyStrToBuffer(str *source, char *buffer, int buffersize);
+int ospGetFromUserpart(struct sip_msg *msg, char *fromuser, int buffersize);
+int ospGetRpidUserpart(struct sip_msg *msg, char *fromuser, int buffersize);
+int ospGetToUserpart(struct sip_msg *msg, char *touser, int buffersize);
+int ospGetUriUserpart(struct sip_msg *msg, char *touser, int buffersize);
+int ospAddOspHeader(
+		struct sip_msg *msg, unsigned char *token, unsigned int tokensize);
+int ospGetOspHeader(
+		struct sip_msg *msg, unsigned char *token, unsigned int *tokensize);
+int ospGetSourceAddress(
+		struct sip_msg *msg, char *sourceaddress, int buffersize);
+int ospGetCallId(struct sip_msg *msg, OSPTCALLID **callid);
+int ospGetRouteParameters(
+		struct sip_msg *msg, char *routeparams, int buffersize);
+int ospRebuildDestionationUri(
+		str *newuri, char *called, char *dest, char *port, int format);
+void ospGetNextHop(struct sip_msg *msg, char *nexthop, int buffersize);
+
+#endif /* _OSP_MOD_SIPHEADER_H_ */

+ 174 - 0
src/modules/osp/term_transaction.c

@@ -0,0 +1,174 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "osp_mod.h"
+#include "term_transaction.h"
+#include "sipheader.h"
+#include "destination.h"
+#include "osptoolkit.h"
+#include "usage.h"
+
+extern char *_osp_device_ip;
+extern int _osp_token_format;
+extern int _osp_validate_callid;
+extern OSPTPROVHANDLE _osp_provider;
+
+/*
+ * Get OSP token
+ * param msg SIP message
+ * param ignore1
+ * param ignore2
+ * return  MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure
+ */
+int ospCheckHeader(struct sip_msg *msg, char *ignore1, char *ignore2)
+{
+	unsigned char buffer[OSP_TOKENBUF_SIZE];
+	unsigned int buffersize = sizeof(buffer);
+
+	if(ospGetOspHeader(msg, buffer, &buffersize) != 0) {
+		return MODULE_RETURNCODE_FALSE;
+	} else {
+		return MODULE_RETURNCODE_TRUE;
+	}
+}
+
+/*
+ * Validate OSP token
+ * param ignore1
+ * param ignore2
+ * return  MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure MODULE_RETURNCODE_ERROR error
+ */
+int ospValidateHeader(struct sip_msg *msg, char *ignore1, char *ignore2)
+{
+	int errorcode;
+	OSPTTRANHANDLE transaction = -1;
+	unsigned int authorized = 0;
+	unsigned int timelimit = 0;
+	void *detaillog = NULL;
+	unsigned int logsize = 0;
+	unsigned char *callidval = (unsigned char *)"";
+	OSPTCALLID *callid = NULL;
+	unsigned callidsize = 0;
+	unsigned char token[OSP_TOKENBUF_SIZE];
+	unsigned int tokensize = sizeof(token);
+	osp_dest dest;
+	int result = MODULE_RETURNCODE_FALSE;
+
+	ospInitDestination(&dest);
+
+	if((errorcode = OSPPTransactionNew(_osp_provider, &transaction)
+					!= OSPC_ERR_NO_ERROR)) {
+		LM_ERR("failed to create a new OSP transaction handle (%d)\n",
+				errorcode);
+	} else if((ospGetRpidUserpart(msg, dest.calling, sizeof(dest.calling)) != 0)
+			  && (ospGetFromUserpart(msg, dest.calling, sizeof(dest.calling))
+					  != 0)) {
+		LM_ERR("failed to extract calling number\n");
+	} else if((ospGetUriUserpart(msg, dest.called, sizeof(dest.called)) != 0)
+			  && (ospGetToUserpart(msg, dest.called, sizeof(dest.called))
+					  != 0)) {
+		LM_ERR("failed to extract called number\n");
+	} else if(ospGetCallId(msg, &callid) != 0) {
+		LM_ERR("failed to extract call id\n");
+	} else if(ospGetSourceAddress(msg, dest.source, sizeof(dest.source)) != 0) {
+		LM_ERR("failed to extract source address\n");
+	} else if(ospGetOspHeader(msg, token, &tokensize) != 0) {
+		LM_ERR("failed to extract OSP authorization token\n");
+	} else {
+		LM_INFO("validate token for: "
+				"transaction_handle '%d' "
+				"e164_source '%s' "
+				"e164_dest '%s' "
+				"validate_call_id '%s' "
+				"call_id '%.*s'\n",
+				transaction, dest.calling, dest.called,
+				_osp_validate_callid == 0 ? "No" : "Yes", callid->ospmCallIdLen,
+				callid->ospmCallIdVal);
+
+		if(_osp_validate_callid != 0) {
+			callidsize = callid->ospmCallIdLen;
+			callidval = callid->ospmCallIdVal;
+		}
+
+		errorcode = OSPPTransactionValidateAuthorisation(transaction, "", "",
+				"", "", dest.calling, OSPC_E164, dest.called, OSPC_E164,
+				callidsize, callidval, tokensize, token, &authorized,
+				&timelimit, &logsize, detaillog, _osp_token_format);
+
+		if((errorcode == OSPC_ERR_NO_ERROR) && (authorized == 1)) {
+			if(callid->ospmCallIdLen > sizeof(dest.callid) - 1) {
+				dest.callidsize = sizeof(dest.callid) - 1;
+			} else {
+				dest.callidsize = callid->ospmCallIdLen;
+			}
+			memcpy(dest.callid, callid->ospmCallIdVal, dest.callidsize);
+			dest.callid[dest.callidsize] = 0;
+			dest.transid = ospGetTransactionId(transaction);
+			dest.type = OSPC_DESTINATION;
+			dest.authtime = time(NULL);
+			strncpy(dest.host, _osp_device_ip, sizeof(dest.host) - 1);
+			strncpy(dest.origcalled, dest.called, sizeof(dest.origcalled) - 1);
+
+			if(ospSaveTermDestination(&dest) == -1) {
+				LM_ERR("failed to save terminate destination\n");
+				ospRecordEvent(0, 500);
+				result = MODULE_RETURNCODE_ERROR;
+			} else {
+				LM_DBG("call is authorized for %d seconds, call_id '%.*s' "
+					   "transaction_id '%llu'",
+						timelimit, dest.callidsize, dest.callid, dest.transid);
+				ospRecordTermTransaction(msg, dest.transid, dest.source,
+						dest.calling, dest.called, dest.authtime);
+				result = MODULE_RETURNCODE_TRUE;
+			}
+		} else {
+			LM_ERR("token is invalid (%d)\n", errorcode);
+
+			/*
+             * Update terminating status code to 401 and report terminating setup usage.
+             * We may need to make 401 configurable, just in case a user decides to reply with
+             * a different code.  Other options - trigger call setup usage reporting from the cpl
+             * (after replying with an error code), or maybe use a different tm callback.
+             */
+			ospRecordEvent(0, 401);
+			result = MODULE_RETURNCODE_FALSE;
+		}
+	}
+
+	if(transaction != -1) {
+		OSPPTransactionDelete(transaction);
+	}
+
+	if(callid != NULL) {
+		OSPPCallIdDelete(&callid);
+	}
+
+	return result;
+}

+ 39 - 0
src/modules/osp/term_transaction.h

@@ -0,0 +1,39 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _OSP_MOD_TERM_TRANSACTION_H_
+#define _OSP_MOD_TERM_TRANSACTION_H_
+
+#include "../../core/parser/msg_parser.h"
+
+int ospCheckHeader(struct sip_msg *, char *, char *);
+int ospValidateHeader(struct sip_msg *, char *, char *);
+
+#endif /* _OSP_MOD_TERM_TRANSACTION_H_ */

+ 133 - 0
src/modules/osp/tm.c

@@ -0,0 +1,133 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * History:
+ * ---------
+ *  2006-03-13  TM functions are loaded via API function (bogdan)
+ */
+
+#include "../../modules/tm/tm_load.h"
+#include "tm.h"
+#include "destination.h"
+
+struct tm_binds osp_tmb;
+
+static void ospOnReq(struct cell *t, int type, struct tmcb_params *ps);
+static void ospTmcbFunc(struct cell *t, int type, struct tmcb_params *ps);
+
+/*
+ * Load TM API
+ * return 0 success, -1 failure
+ */
+int ospInitTm(void)
+{
+	if(load_tm_api(&osp_tmb) != 0) {
+		LM_ERR("failed to load TM API\n");
+		LM_ERR("TM is required for reporting call setup usage\n");
+		return -1;
+	}
+
+	/* Register callbacks, listen for all incoming requests  */
+	if(osp_tmb.register_tmcb(0, 0, TMCB_REQUEST_IN, ospOnReq, 0, 0) <= 0) {
+		LM_ERR("failed to register TMCB_REQUEST_IN callback\n");
+		LM_ERR("TM callbacks are required for reporting call set up usage\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Register OSP callback function
+ * param t
+ * param type
+ * param ps
+ */
+static void ospOnReq(struct cell *t, int type, struct tmcb_params *ps)
+{
+	int tmcb_types;
+
+	/* install addaitional handlers */
+	tmcb_types =
+			//        TMCB_REQUEST_FWDED |
+			//        TMCB_RESPONSE_FWDED |
+			TMCB_ON_FAILURE |
+			//        TMCB_LOCAL_COMPLETED |
+			/* report on completed transactions */
+			TMCB_RESPONSE_OUT |
+			/* account e2e acks if configured to do so */
+			TMCB_E2EACK_IN |
+			/* report on missed calls */
+			TMCB_ON_FAILURE_RO |
+			/* get incoming replies ready for processing */
+			//        TMCB_RESPONSE_IN |
+			0;
+
+	if(osp_tmb.register_tmcb(0, t, tmcb_types, ospTmcbFunc, 0, 0) <= 0) {
+		LM_ERR("failed to register TM callbacks\n");
+		LM_ERR("TM callbacks are required for reporting call setup usage\n");
+		return;
+	}
+}
+
+/*
+ * OSP callback function
+ * param t
+ * param type
+ * param ps
+ */
+static void ospTmcbFunc(struct cell *t, int type, struct tmcb_params *ps)
+{
+	if(type & TMCB_RESPONSE_OUT) {
+		LM_DBG("RESPONSE_OUT\n");
+	} else if(type & TMCB_E2EACK_IN) {
+		LM_DBG("E2EACK_IN\n");
+	} else if(type & TMCB_ON_FAILURE_RO) {
+		LM_DBG("FAILURE_RO\n");
+	} else if(type & TMCB_RESPONSE_IN) {
+		LM_DBG("RESPONSE_IN\n");
+	} else if(type & TMCB_REQUEST_FWDED) {
+		LM_DBG("REQUEST_FWDED\n");
+	} else if(type & TMCB_RESPONSE_FWDED) {
+		LM_DBG("RESPONSE_FWDED\n");
+	} else if(type & TMCB_ON_FAILURE) {
+		LM_DBG("FAILURE\n");
+	} else if(type & TMCB_LOCAL_COMPLETED) {
+		LM_DBG("COMPLETED\n");
+	} else {
+		LM_DBG("something else '%d'\n", type);
+	}
+
+	if(t) {
+		ospRecordEvent(
+				t->uac[t->nr_of_outgoings - 1].last_received, t->uas.status);
+	} else {
+		LM_DBG("cell is empty\n");
+	}
+}

+ 41 - 0
src/modules/osp/tm.h

@@ -0,0 +1,41 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _OSP_MOD_TM_H_
+#define _OSP_MOD_TM_H_
+
+/*
+ * Register for tm events and use them to record and report information
+ * about the call set up transaction - return codes, call start, alert and
+ * connect times, etc.
+ */
+int ospInitTm(void);
+
+#endif /* _OSP_MOD_TM_H_ */

+ 564 - 0
src/modules/osp/usage.c

@@ -0,0 +1,564 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * History:
+ * ---------
+ *  2006-03-13  RR functions are loaded via API function (bogdan)
+ */
+
+#include <osp/osp.h>
+#include "../rr/api.h"
+#include "../../core/usr_avp.h"
+#include "usage.h"
+#include "destination.h"
+#include "osptoolkit.h"
+#include "sipheader.h"
+
+#define OSP_ORIG_COOKIE "osp-o"
+#define OSP_TERM_COOKIE "osp-t"
+
+#define OSP_RELEASE_ORIG 0
+#define OSP_RELEASE_TERM 1
+
+/* The up case tags for the destinations may corrupt OSP cookies */
+#define OSP_COOKIE_TRANSID 't'
+#define OSP_COOKIE_TRANSIDUP 'T'
+#define OSP_COOKIE_SRCIP 's'
+#define OSP_COOKIE_SRCIPUP 'S'
+#define OSP_COOKIE_AUTHTIME 'a'
+#define OSP_COOKIE_AUTHTIMEUP 'A'
+#define OSP_COOKIE_DSTCOUNT 'c'
+#define OSP_COOKIE_DSTCOUNTUP 'C'
+
+/* Flags for OSP cookies */
+#define OSP_COOKIEHAS_TRANSID (1 << 0)
+#define OSP_COOKIEHAS_SRCIP (1 << 1)
+#define OSP_COOKIEHAS_AUTHTIME (1 << 2)
+#define OSP_COOKIEHAS_DSTCOUNT (1 << 3)
+#define OSP_COOKIEHAS_ORIGALL                                             \
+	(OSP_COOKIEHAS_TRANSID | OSP_COOKIEHAS_SRCIP | OSP_COOKIEHAS_AUTHTIME \
+			| OSP_COOKIEHAS_DSTCOUNT)
+#define OSP_COOKIEHAS_TERMALL \
+	(OSP_COOKIEHAS_TRANSID | OSP_COOKIEHAS_SRCIP | OSP_COOKIEHAS_AUTHTIME)
+
+extern char *_osp_device_ip;
+extern OSPTPROVHANDLE _osp_provider;
+extern str OSP_ORIGDEST_NAME;
+extern struct rr_binds osp_rr;
+
+static void ospRecordTransaction(struct sip_msg *msg,
+		unsigned long long transid, char *uac, char *from, char *to,
+		time_t authtime, int isorig, unsigned destinationCount);
+static int ospBuildUsageFromDestination(
+		OSPTTRANHANDLE transaction, osp_dest *dest, int lastcode);
+static int ospReportUsageFromDestination(
+		OSPTTRANHANDLE transaction, osp_dest *dest);
+static int ospReportUsageFromCookie(struct sip_msg *msg, char *cooky,
+		OSPTCALLID *callid, int release, OSPE_MSG_ROLETYPES type);
+
+/*
+ * Create OSP cookie and insert it into Record-Route header
+ * param msg SIP message
+ * param tansid Transaction ID
+ * param uac Source IP
+ * param from
+ * param to
+ * param authtime Request authorization time
+ * param isorig Originate / Terminate
+ * param destinationCount Destination count
+ */
+static void ospRecordTransaction(struct sip_msg *msg,
+		unsigned long long transid, char *uac, char *from, char *to,
+		time_t authtime, int isorig, unsigned destinationCount)
+{
+	str cookie;
+	char buffer[OSP_STRBUF_SIZE];
+
+	if(osp_rr.add_rr_param == 0) {
+		LM_WARN("add_rr_param function is not found, cannot record information "
+				"about the OSP transaction\n");
+		return;
+	}
+
+	cookie.s = buffer;
+
+	if(isorig == 1) {
+		cookie.len = snprintf(buffer, sizeof(buffer),
+				";%s=%c%llu_%c%s_%c%d_%c%d", OSP_ORIG_COOKIE,
+				OSP_COOKIE_TRANSID, transid, OSP_COOKIE_SRCIP, uac,
+				OSP_COOKIE_AUTHTIME, (unsigned int)authtime,
+				OSP_COOKIE_DSTCOUNT, destinationCount);
+	} else {
+		cookie.len = snprintf(buffer, sizeof(buffer), ";%s=%c%llu_%c%s_%c%d",
+				OSP_TERM_COOKIE, OSP_COOKIE_TRANSID, transid, OSP_COOKIE_SRCIP,
+				uac, OSP_COOKIE_AUTHTIME, (unsigned int)authtime);
+	}
+
+	if(cookie.len < 0) {
+		LM_ERR("failed to create OSP cookie\n");
+		return;
+	}
+
+	LM_DBG("adding RR parameter '%s'\n", buffer);
+	osp_rr.add_rr_param(msg, &cookie);
+}
+
+/*
+ * Create OSP originate cookie and insert it into Record-Route header
+ * param msg SIP message
+ * param tansid Transaction ID
+ * param uac Source IP
+ * param from
+ * param to
+ * param authtime Request authorization time
+ * param destinationCount Destination count
+ */
+void ospRecordOrigTransaction(struct sip_msg *msg, unsigned long long transid,
+		char *uac, char *from, char *to, time_t authtime,
+		unsigned destinationCount)
+{
+	int isorig = 1;
+
+	ospRecordTransaction(
+			msg, transid, uac, from, to, authtime, isorig, destinationCount);
+}
+
+/*
+ * Create OSP terminate cookie and insert it into Record-Route header
+ * param msg SIP message
+ * param tansid Transaction ID
+ * param uac Source IP
+ * param from
+ * param to
+ * param authtime Request authorization time
+ */
+void ospRecordTermTransaction(struct sip_msg *msg, unsigned long long transid,
+		char *uac, char *from, char *to, time_t authtime)
+{
+	int isorig = 0;
+	unsigned destinationCount = 0; /* N/A */
+
+	ospRecordTransaction(
+			msg, transid, uac, from, to, authtime, isorig, destinationCount);
+}
+
+/*
+ * Report OSP usage from OSP cookie
+ * param msg SIP message
+ * param cookie OSP cookie (buffer owned by ospReportUsage, can be modified)
+ * param callid Call ID
+ * param release Who releases the call first. 0 orig, 1 term
+ * param type Usage type
+ * return
+ */
+static int ospReportUsageFromCookie(struct sip_msg *msg, char *cookie,
+		OSPTCALLID *callid, int release, OSPE_MSG_ROLETYPES type)
+{
+	char *tmp;
+	char *token;
+	char tag;
+	char *value;
+	unsigned long long transid = 0;
+	time_t authtime = 0;
+	unsigned destinationCount = 0;
+	time_t duration = 0;
+	time_t endtime = time(NULL);
+	int cookieflags = 0;
+	unsigned releasecode;
+	char firstvia[OSP_STRBUF_SIZE];
+	char from[OSP_STRBUF_SIZE];
+	char to[OSP_STRBUF_SIZE];
+	char nexthop[OSP_STRBUF_SIZE];
+	char *calling;
+	char *called;
+	char *originator = NULL;
+	char *terminator;
+	char *source;
+	char srcbuf[OSP_STRBUF_SIZE];
+	char *destination;
+	char dstbuf[OSP_STRBUF_SIZE];
+	char *srcdev;
+	char devbuf[OSP_STRBUF_SIZE];
+	OSPTTRANHANDLE transaction = -1;
+	int errorcode;
+
+	LM_DBG("'%s' type '%d'\n", cookie, type);
+	if(cookie != NULL) {
+		for(token = strtok_r(cookie, "_", &tmp); token;
+				token = strtok_r(NULL, "_", &tmp)) {
+			tag = *token;
+			value = token + 1;
+
+			switch(tag) {
+				case OSP_COOKIE_TRANSID:
+				case OSP_COOKIE_TRANSIDUP:
+					transid = atoll(value);
+					cookieflags |= OSP_COOKIEHAS_TRANSID;
+					break;
+				case OSP_COOKIE_AUTHTIME:
+				case OSP_COOKIE_AUTHTIMEUP:
+					authtime = atoi(value);
+					duration = endtime - authtime;
+					cookieflags |= OSP_COOKIEHAS_AUTHTIME;
+					break;
+				case OSP_COOKIE_SRCIP:
+				case OSP_COOKIE_SRCIPUP:
+					originator = value;
+					cookieflags |= OSP_COOKIEHAS_SRCIP;
+					break;
+				case OSP_COOKIE_DSTCOUNT:
+				case OSP_COOKIE_DSTCOUNTUP:
+					destinationCount = (unsigned)atoi(value);
+					cookieflags |= OSP_COOKIEHAS_DSTCOUNT;
+					break;
+				default:
+					LM_ERR("unexpected tag '%c' / value '%s'\n", tag, value);
+					break;
+			}
+		}
+	}
+
+	switch(type) {
+		case OSPC_DESTINATION:
+			if(cookieflags == OSP_COOKIEHAS_TERMALL) {
+				releasecode = 10016;
+			} else {
+				releasecode = 9016;
+			}
+			break;
+		case OSPC_SOURCE:
+		case OSPC_OTHER:
+		case OSPC_UNDEFINED_ROLE:
+		default:
+			if(cookieflags == OSP_COOKIEHAS_ORIGALL) {
+				releasecode = 10016;
+			} else {
+				releasecode = 9016;
+			}
+			break;
+	}
+
+	if(releasecode == 9016) {
+		transid = 0;
+		originator = NULL;
+		authtime = 0;
+		duration = 0;
+		destinationCount = 0;
+	}
+
+	ospGetSourceAddress(msg, firstvia, sizeof(firstvia));
+	ospGetFromUserpart(msg, from, sizeof(from));
+	ospGetToUserpart(msg, to, sizeof(to));
+	ospGetNextHop(msg, nexthop, sizeof(nexthop));
+
+	LM_DBG("first via '%s' from '%s' to '%s' next hop '%s'\n", firstvia, from,
+			to, nexthop);
+
+	if(release == OSP_RELEASE_ORIG) {
+		LM_DBG("orig '%s' released the call, call_id '%.*s' transaction_id "
+			   "'%llu'\n",
+				firstvia, callid->ospmCallIdLen, callid->ospmCallIdVal,
+				transid);
+		if(originator == NULL) {
+			originator = firstvia;
+		}
+		calling = from;
+		called = to;
+		terminator = nexthop;
+	} else {
+		release = OSP_RELEASE_TERM;
+		LM_DBG("term '%s' released the call, call_id '%.*s' transaction_id "
+			   "'%llu'\n",
+				firstvia, callid->ospmCallIdLen, callid->ospmCallIdVal,
+				transid);
+		if(originator == NULL) {
+			originator = nexthop;
+		}
+		calling = to;
+		called = from;
+		terminator = firstvia;
+	}
+
+	errorcode = OSPPTransactionNew(_osp_provider, &transaction);
+
+	LM_DBG("created transaction handle '%d' (%d)\n", transaction, errorcode);
+
+	switch(type) {
+		case OSPC_DESTINATION:
+			ospConvertAddress(originator, srcbuf, sizeof(srcbuf));
+			source = srcbuf;
+			destination = _osp_device_ip;
+			srcdev = "";
+			break;
+		case OSPC_SOURCE:
+		case OSPC_OTHER:
+		case OSPC_UNDEFINED_ROLE:
+		default:
+			source = _osp_device_ip;
+			ospConvertAddress(terminator, dstbuf, sizeof(dstbuf));
+			destination = dstbuf;
+			ospConvertAddress(originator, devbuf, sizeof(devbuf));
+			srcdev = devbuf;
+			break;
+	}
+
+	errorcode = OSPPTransactionBuildUsageFromScratch(transaction, transid, type,
+			source, destination, srcdev, "", calling, OSPC_E164, called,
+			OSPC_E164, callid->ospmCallIdLen, callid->ospmCallIdVal,
+			(enum OSPEFAILREASON)0, NULL, NULL);
+
+	LM_DBG("built usage handle '%d' (%d)\n", transaction, errorcode);
+
+	if((errorcode == OSPC_ERR_NO_ERROR) && (destinationCount > 0)) {
+		errorcode = OSPPTransactionSetDestinationCount(
+				transaction, destinationCount);
+	}
+
+	ospReportUsageWrapper(transaction, releasecode, duration, authtime, endtime,
+			0, 0, 0, 0, release);
+
+	return errorcode;
+}
+
+/*
+ * Report OSP usage
+ * param msg SIP message
+ * param whorelease Who releases the call first, 0 orig, 1 term
+ * param ignore2
+ * return MODULE_RETURNCODE_TRUE success, MODULE_RETURNCODE_FALSE failure
+ */
+int ospReportUsage(struct sip_msg *msg, char *whorelease, char *ignore2)
+{
+	int release;
+	char *tmp;
+	char *token;
+	char parameters[OSP_HEADERBUF_SIZE];
+	OSPTCALLID *callid = NULL;
+	int result = MODULE_RETURNCODE_FALSE;
+
+	ospGetCallId(msg, &callid);
+
+	if(callid != NULL) {
+		/* Who releases the call first, 0 orig, 1 term */
+		if(sscanf(whorelease, "%d", &release) != 1
+				|| (release != OSP_RELEASE_ORIG
+						&& release != OSP_RELEASE_TERM)) {
+			release = OSP_RELEASE_ORIG;
+		}
+		LM_DBG("who releases the call first '%d'\n", release);
+
+		if(ospGetRouteParameters(msg, parameters, sizeof(parameters)) == 0) {
+			for(token = strtok_r(parameters, ";", &tmp); token;
+					token = strtok_r(NULL, ";", &tmp)) {
+				if(strncmp(token, OSP_ORIG_COOKIE, strlen(OSP_ORIG_COOKIE))
+						== 0) {
+					LM_INFO("report orig duration for call_id '%.*s'\n",
+							callid->ospmCallIdLen, callid->ospmCallIdVal);
+					ospReportUsageFromCookie(msg,
+							token + strlen(OSP_ORIG_COOKIE) + 1, callid,
+							release, OSPC_SOURCE);
+					result = MODULE_RETURNCODE_TRUE;
+				} else if(strncmp(token, OSP_TERM_COOKIE,
+								  strlen(OSP_TERM_COOKIE))
+						  == 0) {
+					LM_INFO("report term duration for call_id '%.*s'\n",
+							callid->ospmCallIdLen, callid->ospmCallIdVal);
+					ospReportUsageFromCookie(msg,
+							token + strlen(OSP_TERM_COOKIE) + 1, callid,
+							release, OSPC_DESTINATION);
+					result = MODULE_RETURNCODE_TRUE;
+				} else {
+					LM_DBG("ignoring parameter '%s'\n", token);
+				}
+			}
+		}
+
+		if(result == MODULE_RETURNCODE_FALSE) {
+			LM_DBG("without orig or term OSP information\n");
+			LM_INFO("report other duration for call_id '%.*s'\n",
+					callid->ospmCallIdLen, callid->ospmCallIdVal);
+			ospReportUsageFromCookie(msg, NULL, callid, release, OSPC_SOURCE);
+			result = MODULE_RETURNCODE_TRUE;
+		}
+
+		OSPPCallIdDelete(&callid);
+	}
+
+	if(result == MODULE_RETURNCODE_FALSE) {
+		LM_ERR("failed to report usage\n");
+	}
+
+	return result;
+}
+
+/*
+ * Build OSP usage from destination
+ * param transaction OSP transaction handle
+ * param dest Destination
+ * param lastcode Destination status
+ * return 0 success, others failure
+ */
+static int ospBuildUsageFromDestination(
+		OSPTTRANHANDLE transaction, osp_dest *dest, int lastcode)
+{
+	int errorcode;
+	char addr[OSP_STRBUF_SIZE];
+	char *source;
+	char *srcdev;
+
+	if(dest->type == OSPC_SOURCE) {
+		ospConvertAddress(dest->srcdev, addr, sizeof(addr));
+		source = dest->source;
+		srcdev = addr;
+	} else {
+		ospConvertAddress(dest->source, addr, sizeof(addr));
+		source = addr;
+		srcdev = dest->srcdev;
+	}
+
+	errorcode = OSPPTransactionBuildUsageFromScratch(transaction, dest->transid,
+			dest->type, source, dest->host, srcdev, dest->destdev,
+			dest->calling, OSPC_E164,
+			dest->origcalled, /* Report original called number */
+			OSPC_E164, dest->callidsize, dest->callid,
+			(enum OSPEFAILREASON)lastcode, NULL, NULL);
+
+	return errorcode;
+}
+
+/*
+ * Report OSP usage from destination
+ * param transaction OSP transaction handle
+ * param dest Destination
+ * return 0 success
+ */
+static int ospReportUsageFromDestination(
+		OSPTTRANHANDLE transaction, osp_dest *dest)
+{
+	ospReportUsageWrapper(transaction, /* In - Transaction handle */
+			dest->lastcode,			   /* In - Release Code */
+			0,						   /* In - Length of call */
+			dest->authtime,			   /* In - Call start time */
+			0,						   /* In - Call end time */
+			dest->time180,			   /* In - Call alert time */
+			dest->time200,			   /* In - Call connect time */
+			dest->time180 ? 1 : 0,	   /* In - Is PDD Info present */
+			dest->time180 ? dest->time180 - dest->authtime
+						  : 0, /* In - Post Dial Delay */
+			0);
+
+	return 0;
+}
+
+/*
+ * Report originate call setup usage
+ */
+void ospReportOrigSetupUsage(void)
+{
+	osp_dest *dest = NULL;
+	osp_dest *lastused = NULL;
+	struct usr_avp *destavp = NULL;
+	avp_value_t destval;
+	OSPTTRANHANDLE transaction = -1;
+	int lastcode = 0;
+	int errorcode;
+	struct search_state st;
+
+	errorcode = OSPPTransactionNew(_osp_provider, &transaction);
+
+	for(destavp = search_first_avp(AVP_NAME_STR | AVP_VAL_STR,
+				(avp_name_t)OSP_ORIGDEST_NAME, NULL, &st);
+			destavp != NULL; destavp = search_next_avp(&st, 0)) {
+		get_avp_val(destavp, &destval);
+
+		/* OSP destination is wrapped in a string */
+		dest = (osp_dest *)destval.s.s;
+
+		if(dest->used == 1) {
+			if(dest->reported == 1) {
+				LM_DBG("orig setup already reported\n");
+				break;
+			} else {
+				dest->reported = 1;
+			}
+
+			LM_DBG("iterating through used destination\n");
+
+			ospDumpDestination(dest);
+
+			lastused = dest;
+
+			errorcode =
+					ospBuildUsageFromDestination(transaction, dest, lastcode);
+
+			lastcode = dest->lastcode;
+		} else {
+			LM_DBG("destination has not been used, breaking out\n");
+			break;
+		}
+	}
+
+	if(lastused) {
+		LM_INFO("report orig setup for call_id '%.*s' transaction_id '%llu'\n",
+				lastused->callidsize, lastused->callid, lastused->transid);
+		errorcode = ospReportUsageFromDestination(transaction, lastused);
+	} else {
+		/* If a Toolkit transaction handle was created, but we did not find
+         * any destinations to report, we need to release the handle. Otherwise,
+         * the ospReportUsageFromDestination will release it.
+         */
+		OSPPTransactionDelete(transaction);
+	}
+}
+
+/*
+ * Report terminate call setup usage
+ */
+void ospReportTermSetupUsage(void)
+{
+	osp_dest *dest = NULL;
+	OSPTTRANHANDLE transaction = -1;
+	int errorcode;
+
+	if((dest = ospGetTermDestination())) {
+		if(dest->reported == 0) {
+			dest->reported = 1;
+			LM_INFO("report term setup for call_id '%.*s' transaction_id "
+					"'%llu'\n",
+					dest->callidsize, dest->callid, dest->transid);
+			errorcode = OSPPTransactionNew(_osp_provider, &transaction);
+			errorcode = ospBuildUsageFromDestination(transaction, dest, 0);
+			errorcode = ospReportUsageFromDestination(transaction, dest);
+		} else {
+			LM_DBG("term setup already reported\n");
+		}
+	} else {
+		LM_ERR("without term setup to report\n");
+	}
+}

+ 61 - 0
src/modules/osp/usage.h

@@ -0,0 +1,61 @@
+/*
+ * Kamailio osp module.
+ *
+ * This module enables Kamailio to communicate with an Open Settlement
+ * Protocol (OSP) server.  The Open Settlement Protocol is an ETSI
+ * defined standard for Inter-Domain VoIP pricing, authorization
+ * and usage exchange.  The technical specifications for OSP
+ * (ETSI TS 101 321 V4.1.1) are available at www.etsi.org.
+ *
+ * Uli Abend was the original contributor to this module.
+ *
+ * Copyright (C) 2001-2005 Fhg Fokus
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _OSP_MOD_USAGE_H_
+#define _OSP_MOD_USAGE_H_
+
+#include <osp/osp.h>
+#include "../../core/parser/msg_parser.h"
+
+/* This module reports originating and terminating call set up and duration usage
+ * for OSP transactions.
+ *
+ * Call set-up usage is reported based on the osp_dest structures stored as AVPs.
+ * It includes OSP transaction id, response codes, start time, alert time,
+ * connect time, etc.
+ *
+ * Duration usage is reported based on the OSP cooky recorded into the route set
+ * (using add_rr_param) after requesting routing/authorization on the originating
+ * side, and validating authorization on the terminating side.  It include
+ * OSP transaction id, duration, stop time, etc.
+ *
+ * Actual conversation duration maybe calculated using connect time (from the call
+ * set up usage) and stop time (from the duration usage).
+ */
+void ospRecordOrigTransaction(struct sip_msg *msg, unsigned long long transid,
+		char *uac, char *from, char *to, time_t authtime,
+		unsigned destinationCount);
+void ospRecordTermTransaction(struct sip_msg *msg, unsigned long long transid,
+		char *uac, char *from, char *to, time_t authtime);
+void ospReportOrigSetupUsage(void);
+void ospReportTermSetupUsage(void);
+int ospReportUsage(struct sip_msg *msg, char *whorelease, char *ignore2);
+
+#endif /* _OSP_MOD_USAGE_H_ */