Merge pull request #881 from mslehto/doctypo
authorOlle E. Johansson <oej@edvina.net>
Sun, 4 Dec 2016 16:43:08 +0000 (17:43 +0100)
committerGitHub <noreply@github.com>
Sun, 4 Dec 2016 16:43:08 +0000 (17:43 +0100)
documentation fixes for various modules

202 files changed:
Makefile.defs
Makefile.groups
etc/kamailio.cfg
examples/pcscf/kamailio.cfg
fmsg.h
kemi.c
kemi.h
lib/kcore/faked_msg.h [deleted file]
lib/srdb1/schema/entities.xml
lib/srdb1/schema/subscriber.xml
lib/srdb1/schema/topos_d.xml
lib/srdb1/schema/topos_t.xml
modules/acc_radius/README
modules/acc_radius/doc/acc_radius_admin.xml
modules/app_lua/app_lua_exp.c
modules/app_lua/app_lua_mod.c
modules/app_python/apy_kemi.c
modules/app_python/apy_kemi.h
modules/auth_identity/README
modules/auth_identity/doc/auth_identity.xml
modules/cnxcc/cnxcc_mod.c
modules/dialog/dialog.c
modules/dialog/dlg_handlers.c
modules/dispatcher/README
modules/dispatcher/dispatch.c
modules/dispatcher/doc/dispatcher.cfg
modules/dispatcher/doc/dispatcher_admin.xml
modules/erlang/handle_emsg.c
modules/evapi/evapi_dispatch.c
modules/evapi/evapi_mod.c
modules/htable/README
modules/htable/doc/htable_admin.xml
modules/htable/ht_api.c
modules/htable/ht_api.h
modules/htable/htable.c
modules/http_async_client/README
modules/http_async_client/async_http.c
modules/http_async_client/async_http.h
modules/http_async_client/doc/http_async_client_admin.xml
modules/http_async_client/hm_hash.h
modules/http_async_client/http_async_client_mod.c
modules/http_async_client/http_multi.c
modules/ims_dialog/dlg_handlers.c
modules/ims_dialog/ims_dialog.c
modules/ims_ocs/Makefile [new file with mode: 0644]
modules/ims_ocs/README [new file with mode: 0644]
modules/ims_ocs/doc/Makefile [new file with mode: 0644]
modules/ims_ocs/doc/ims_ocs.xml [new file with mode: 0644]
modules/ims_ocs/examples/full/kamailio.cfg [new file with mode: 0644]
modules/ims_ocs/examples/full/ocs.cfg.sample [new file with mode: 0644]
modules/ims_ocs/examples/full/ocs.xml.sample [new file with mode: 0644]
modules/ims_ocs/examples/simple/kamailio.cfg [new file with mode: 0644]
modules/ims_ocs/examples/simple/ocs.cfg.sample [new file with mode: 0644]
modules/ims_ocs/examples/simple/ocs.xml.sample [new file with mode: 0644]
modules/ims_ocs/mod.c [new file with mode: 0644]
modules/ims_ocs/mod.h [new file with mode: 0644]
modules/ims_ocs/msg_faker.c [new file with mode: 0644]
modules/ims_ocs/msg_faker.h [new file with mode: 0644]
modules/ims_ocs/ocs_avp_helper.c [new file with mode: 0644]
modules/ims_ocs/ocs_avp_helper.h [new file with mode: 0644]
modules/ims_ocs/sem.h [new file with mode: 0644]
modules/ims_qos/README
modules/ims_qos/doc/ims_qos_admin.xml
modules/ims_qos/mod.c
modules/ims_qos/rx_aar.c
modules/ims_qos/rx_avp.c
modules/ims_registrar_scscf/cxdx_sar.c
modules/ims_registrar_scscf/save.c
modules/ims_usrloc_pcscf/README
modules/ims_usrloc_pcscf/doc/ims_usrloc_pcscf_admin.xml
modules/ims_usrloc_pcscf/udomain.c
modules/ims_usrloc_pcscf/ul_mod.c
modules/jsonrpc-s/README
modules/jsonrpc-s/doc/jsonrpc-s_admin.xml
modules/jsonrpc-s/jsonrpc-s_mod.c
modules/jsonrpc-s/jsonrpc-s_mod.h
modules/jsonrpc-s/jsonrpcs_fifo.c [new file with mode: 0644]
modules/jsonrpc-s/jsonrpcs_sock.c [new file with mode: 0644]
modules/kazoo/kz_amqp.h
modules/memcached/mcd_var.c
modules/mqueue/mqueue_api.c
modules/nsq/README
modules/nsq/doc/nsq_admin.xml
modules/nsq/nsq_mod.c
modules/nsq/nsq_mod.h
modules/nsq/nsq_pua.c
modules/pua_dialoginfo/pua_dialoginfo.c
modules/ratelimit/ratelimit.c
modules/registrar/regpv.c
modules/rtimer/rtimer_mod.c
modules/rtjson/rtjson_routing.c
modules/rtpengine/README
modules/rtpengine/config.c [new file with mode: 0644]
modules/rtpengine/config.h [new file with mode: 0644]
modules/rtpengine/doc/rtpengine_admin.xml
modules/rtpengine/rtpengine.c
modules/rtpproxy/README
modules/rtpproxy/doc/rtpproxy_admin.xml
modules/sdpops/README
modules/sdpops/api.h
modules/sdpops/doc/sdpops_admin.xml
modules/sdpops/sdpops_mod.c
modules/sipcapture/hep.c
modules/sipcapture/hep.h
modules/sipcapture/sipcapture.c
modules/sl/sl_funcs.c
modules/ss7ops/Makefile [new file with mode: 0644]
modules/ss7ops/README [new file with mode: 0644]
modules/ss7ops/doc/Makefile [new file with mode: 0644]
modules/ss7ops/doc/ss7ops.xml [new file with mode: 0644]
modules/ss7ops/doc/ss7ops_admin.xml [new file with mode: 0644]
modules/ss7ops/isup_generated.c [new file with mode: 0644]
modules/ss7ops/isup_generated.h [new file with mode: 0644]
modules/ss7ops/isup_parsed.c [new file with mode: 0644]
modules/ss7ops/isup_parsed.h [new file with mode: 0644]
modules/ss7ops/ss7ops_mod.c [new file with mode: 0644]
modules/tm/README
modules/tm/doc/params.xml
modules/tm/doc/tm.xml
modules/tm/rpc_uac.c
modules/tm/t_fwd.c
modules/tm/t_msgbuilder.c
modules/tm/t_reply.c
modules/tm/tm.c
modules/tm/uac.c
modules/tmx/README
modules/tmx/doc/tmx_admin.xml
modules/uac/uac_send.c
modules/utils/README
modules/utils/doc/utils_admin.xml
modules/websocket/ws_conn.c
modules/xcap_client/README
modules/xcap_client/doc/xcap_client_admin.xml
onsend.h
receive.c
test/unit/60-message-sdp9.sip [new file with mode: 0644]
test/unit/60.cfg
test/unit/60.sh
test/unit/61-message-sdp.sip [new file with mode: 0644]
test/unit/61.cfg [new file with mode: 0644]
test/unit/61.sh [new file with mode: 0755]
test/unit/include/common
test/unit/include/require.sh
udp_server.c
utils/kamctl/db_berkeley/kamailio/aliases
utils/kamctl/db_berkeley/kamailio/domain
utils/kamctl/db_berkeley/kamailio/domain_attrs
utils/kamctl/db_berkeley/kamailio/grp
utils/kamctl/db_berkeley/kamailio/location
utils/kamctl/db_berkeley/kamailio/location_attrs
utils/kamctl/db_berkeley/kamailio/sip_trace
utils/kamctl/db_berkeley/kamailio/subscriber
utils/kamctl/db_berkeley/kamailio/topos_d
utils/kamctl/db_berkeley/kamailio/topos_t
utils/kamctl/db_berkeley/kamailio/uri
utils/kamctl/db_berkeley/kamailio/usr_preferences
utils/kamctl/db_sqlite/auth_db-create.sql
utils/kamctl/db_sqlite/avpops-create.sql
utils/kamctl/db_sqlite/domain-create.sql
utils/kamctl/db_sqlite/group-create.sql
utils/kamctl/db_sqlite/registrar-create.sql
utils/kamctl/db_sqlite/siptrace-create.sql
utils/kamctl/db_sqlite/topos-create.sql
utils/kamctl/db_sqlite/uri_db-create.sql
utils/kamctl/db_sqlite/usrloc-create.sql
utils/kamctl/dbtext/kamailio/subscriber
utils/kamctl/dbtext/kamailio/topos_d
utils/kamctl/dbtext/kamailio/topos_t
utils/kamctl/kamctl
utils/kamctl/kamctl.fifo
utils/kamctl/kamctl.rpcfifo [new file with mode: 0644]
utils/kamctl/kamctl.unixsock
utils/kamctl/kamctlrc
utils/kamctl/mysql/auth_db-create.sql
utils/kamctl/mysql/avpops-create.sql
utils/kamctl/mysql/domain-create.sql
utils/kamctl/mysql/group-create.sql
utils/kamctl/mysql/registrar-create.sql
utils/kamctl/mysql/siptrace-create.sql
utils/kamctl/mysql/topos-create.sql
utils/kamctl/mysql/uri_db-create.sql
utils/kamctl/mysql/usrloc-create.sql
utils/kamctl/oracle/auth_db-create.sql
utils/kamctl/oracle/avpops-create.sql
utils/kamctl/oracle/domain-create.sql
utils/kamctl/oracle/group-create.sql
utils/kamctl/oracle/registrar-create.sql
utils/kamctl/oracle/siptrace-create.sql
utils/kamctl/oracle/topos-create.sql
utils/kamctl/oracle/uri_db-create.sql
utils/kamctl/oracle/usrloc-create.sql
utils/kamctl/postgres/auth_db-create.sql
utils/kamctl/postgres/avpops-create.sql
utils/kamctl/postgres/domain-create.sql
utils/kamctl/postgres/group-create.sql
utils/kamctl/postgres/registrar-create.sql
utils/kamctl/postgres/siptrace-create.sql
utils/kamctl/postgres/topos-create.sql
utils/kamctl/postgres/uri_db-create.sql
utils/kamctl/postgres/usrloc-create.sql
utils/kamunix/kamunix.8
utils/kamunix/kamunix.c

index 70f5d40..0193230 100644 (file)
@@ -1722,7 +1722,7 @@ ifeq ($(OS), linux)
        use_futex= yes
        C_DEFS+=-DHAVE_GETHOSTBYNAME2 -DHAVE_UNION_SEMUN -DHAVE_SCHED_YIELD \
                        -DHAVE_MSG_NOSIGNAL -DHAVE_MSGHDR_MSG_CONTROL -DHAVE_ALLOCA_H \
-                       -DHAVE_TIMEGM -DHAVE_SCHED_SETSCHEDULER
+                       -DHAVE_TIMEGM -DHAVE_SCHED_SETSCHEDULER -DHAVE_IP_MREQN
        ifneq ($(RAW_SOCKS), yes)
                C_DEFS+= -DUSE_RAW_SOCKS
        endif
@@ -1856,7 +1856,7 @@ ifeq ($(OS), freebsd)
        C_DEFS+=-DHAVE_SOCKADDR_SA_LEN -DHAVE_GETHOSTBYNAME2 -DHAVE_UNION_SEMUN \
                -DHAVE_SCHED_YIELD -DHAVE_MSGHDR_MSG_CONTROL \
                -DHAVE_CONNECT_ECONNRESET_BUG -DHAVE_TIMEGM \
-               -DHAVE_NETINET_IN_SYSTM
+               -DHAVE_NETINET_IN_SYSTM -DHAVE_IP_MREQN
        ifneq ($(RAW_SOCKS), yes)
                C_DEFS+= -DUSE_RAW_SOCKS
        endif
@@ -1955,7 +1955,7 @@ ifeq ($(OS), darwin)
                -DHAVE_SCHED_YIELD -DHAVE_MSGHDR_MSG_CONTROL \
                -DUSE_ANON_MMAP \
                -DNDEBUG -DHAVE_CONNECT_ECONNRESET_BUG -DHAVE_TIMEGM \
-               -DUSE_SIGWAIT
+               -DUSE_SIGWAIT -DHAVE_IP_MREQN
        # -DNDEBUG used to turn off assert (assert wants to call
        # eprintf which doesn't seem to be defined in any shared lib
        ifneq ($(found_lock_method), yes)
index e441f62..2ec00d5 100644 (file)
@@ -21,7 +21,7 @@ mod_list_basic=async auth benchmark blst cfg_rpc cfgutils corex counters \
 mod_list_extra=avp auth_diameter call_control dmq domainpolicy msrp pdb \
                             qos sca seas sms sst timer tmrec uac_redirect xhttp \
                                 xhttp_rpc xprint jsonrpc-s nosip dmq_usrloc statsd rtjson \
-                                log_custom
+                                log_custom ss7ops
 
 # - common modules depending on database
 mod_list_db=acc alias_db auth_db avpops cfg_db db_text db_flatstore \
@@ -155,7 +155,7 @@ mod_list_mono=app_mono
 # - modules related to IMS extensions
 mod_list_ims=cdp cdp_avp ims_dialog ims_auth ims_isc ims_icscf ims_qos \
                           ims_registrar_pcscf ims_registrar_scscf ims_usrloc_pcscf \
-                          ims_usrloc_scscf ims_charging
+                          ims_usrloc_scscf ims_charging ims_ocs
 
 # - modules depending on osp toolkit library
 mod_list_osp=osp
index 86ac524..0fc190d 100644 (file)
@@ -1,6 +1,6 @@
 #!KAMAILIO
 #
-# Kamailio (OpenSER) SIP Server v4.4 - default configuration script
+# Kamailio (OpenSER) SIP Server v5.0 - default configuration script
 #     - web: http://www.kamailio.org
 #     - git: http://sip-router.org
 #
@@ -9,6 +9,13 @@
 # Refer to the Core CookBook at http://www.kamailio.org/wiki/
 # for an explanation of possible statements, functions and parameters.
 #
+# Note: the comments can be:
+#     - lines starting with #, but not the pre-processor directives,
+#       which start with #!, like #!define, #!ifdef, #!endif, #!else, #!trydef,
+#       #!subst, #!substdef, ...
+#     - lines starting with //
+#     - blocks enclosed in between /* */
+#
 # Several features can be enabled using '#!define WITH_FEATURE' directives:
 #
 # *** To run in debug mode:
@@ -76,6 +83,9 @@
 # *** To block 3XX redirect replies execute:
 #     - define WITH_BLOCK3XX
 #
+# *** To block 401 and 407 authentication replies execute:
+#     - define WITH_BLOCK401407
+#
 # *** To enable VoiceMail routing execute:
 #     - define WITH_VOICEMAIL
 #     - set the value of voicemail.srv_ip
@@ -147,70 +157,65 @@ memlog=5
 
 log_facility=LOG_LOCAL0
 
-# number of SIP routing processes
+/* number of SIP routing processes */
 children=8
 
 /* uncomment the next line to disable TCP (default on) */
-#disable_tcp=yes
+# disable_tcp=yes
 
 /* uncomment the next line to disable the auto discovery of local aliases
  based on reverse DNS on IPs (default on) */
-#auto_aliases=no
* based on reverse DNS on IPs (default on) */
+# auto_aliases=no
 
 /* add local domain aliases */
 #alias="sip.mydomain.com"
 
 /* uncomment and configure the following line if you want Kamailio to
  bind on a specific interface/port/proto (default bind on all available) */
-#listen=udp:10.0.0.10:5060
* bind on a specific interface/port/proto (default bind on all available) */
+# listen=udp:10.0.0.10:5060
 
-/* port to listen to */
-#port=5060
+/* port to listen to (default 5060 for udp, tcp, scrtp, or 5061 for tls)*/
+# port=5060
 
 #!ifdef WITH_TLS
 enable_tls=yes
 #!endif
 
-# life time of TCP connection when there is no traffic
-# - a bit higher than registration expires to cope with UA behind NAT
+/* life time of TCP connection when there is no traffic
+ * - a bit higher than registration expires to cope with UA behind NAT */
 tcp_connection_lifetime=3605
 
 ####### Custom Parameters #########
 
-# These parameters can be modified runtime via RPC interface
-# - see the documentation of 'cfg_rpc' module.
-#
-# Format: group.id = value 'desc' description
-# Access: $sel(cfg_get.group.id) or @cfg_get.group.id
-#
+/* These parameters can be modified runtime via RPC interface
+ * - see the documentation of 'cfg_rpc' module.
+ *
+ * Format: group.id = value 'desc' description
+ * Access: $sel(cfg_get.group.id) or @cfg_get.group.id */
 
 #!ifdef WITH_PSTN
-# PSTN GW Routing
-#
-# - pstn.gw_ip: valid IP or hostname as string value, example:
-# pstn.gw_ip = "10.0.0.101" desc "My PSTN GW Address"
-#
-# - by default is empty to avoid misrouting
+/* PSTN GW Routing
+ *
+ * - pstn.gw_ip: valid IP or hostname as string value, example:
+ * pstn.gw_ip = "10.0.0.101" desc "My PSTN GW Address"
+ *
+ * - by default is empty to avoid misrouting */
 pstn.gw_ip = "" desc "PSTN GW Address"
 pstn.gw_port = "" desc "PSTN GW Port"
 #!endif
 
 #!ifdef WITH_VOICEMAIL
-# VoiceMail Routing on offline, busy or no answer
-#
-# - by default Voicemail server IP is empty to avoid misrouting
+/* VoiceMail Routing on offline, busy or no answer
+ *
+ * - by default Voicemail server IP is empty to avoid misrouting */
 voicemail.srv_ip = "" desc "VoiceMail IP Address"
 voicemail.srv_port = "5060" desc "VoiceMail Port"
 #!endif
 
 ####### Modules Section ########
 
-# set paths to location of modules (to sources or installation folders)
-#!ifdef WITH_SRCPATH
-mpath="modules/"
-#!else
-mpath="/usr/local/lib/kamailio/modules/"
-#!endif
+/* set paths to location of modules */
+# mpath="/usr/local/lib/kamailio/modules/"
 
 #!ifdef WITH_MYSQL
 loadmodule "db_mysql.so"
@@ -287,10 +292,12 @@ loadmodule "debugger.so"
 
 
 # ----- mi_fifo params -----
-#modparam("mi_fifo", "fifo_name", "/var/run/kamailio/kamailio_fifo")
+/* set the path to MI fifo control file */
+# modparam("mi_fifo", "fifo_name", "/var/run/kamailio/kamailio_fifo")
 
 # ----- ctl params -----
-#modparam("ctl", "binrpc", "unix:/var/run/kamailio/kamailio_ctl")
+/* set the path to RPC unix socket control file */
+# modparam("ctl", "binrpc", "unix:/var/run/kamailio/kamailio_ctl")
 
 # ----- tm params -----
 # auto-discard branches from previous serial forking leg
@@ -313,10 +320,10 @@ modparam("registrar", "method_filtering", 1)
 /* uncomment the next line to disable parallel forking via location */
 # modparam("registrar", "append_branches", 0)
 /* uncomment the next line not to allow more than 10 contacts per AOR */
-#modparam("registrar", "max_contacts", 10)
-# max value for expires of registrations
+# modparam("registrar", "max_contacts", 10)
+/* max value for expires of registrations */
 modparam("registrar", "max_expires", 3600)
-# set it to 1 to enable GRUU
+/* set it to 1 to enable GRUU */
 modparam("registrar", "gruu_enabled", 0)
 
 
@@ -326,8 +333,8 @@ modparam("acc", "early_media", 0)
 modparam("acc", "report_ack", 0)
 modparam("acc", "report_cancels", 0)
 /* by default ww do not adjust the direct of the sequential requests.
  if you enable this parameter, be sure the enable "append_fromtag"
  in "rr" module */
* if you enable this parameter, be sure the enable "append_fromtag"
* in "rr" module */
 modparam("acc", "detect_direction", 0)
 /* account triggers (flags) */
 modparam("acc", "log_flag", FLT_ACC)
@@ -390,7 +397,7 @@ modparam("speeddial", "use_domain", MULTIDOMAIN)
 # ----- domain params -----
 #!ifdef WITH_MULTIDOMAIN
 modparam("domain", "db_url", DBURL)
-# register callback to match myself condition with domains list
+/* register callback to match myself condition with domains list */
 modparam("domain", "register_myself", 1)
 #!endif
 
@@ -433,7 +440,7 @@ modparam("pike", "reqs_density_per_unit", 16)
 modparam("pike", "remove_latency", 4)
 
 # ----- htable params -----
-# ip ban htable with autoexpire after 5 minutes
+/* ip ban htable with autoexpire after 5 minutes */
 modparam("htable", "htable", "ipban=>size=8;autoexpire=300;")
 #!endif
 
@@ -452,9 +459,9 @@ modparam("debugger", "log_level_name", "exec")
 ####### Routing Logic ########
 
 
-# Main SIP request routing logic
-# - processing of any incoming SIP request starts with this route
-# - note: this is the same as route { ... }
+/* Main SIP request routing logic
+ * - processing of any incoming SIP request starts with this route
+ * - note: this is the same as route { ... } */
 request_route {
 
        # per request initial checks
@@ -936,6 +943,14 @@ failure_route[MANAGE_FAILURE] {
        }
 #!endif
 
+#!ifdef WITH_BLOCK401407
+       # block call redirect based on 401, 407 replies.
+       if (t_check_status("401|407")) {
+               t_reply("404","Not found");
+               exit;
+       }
+#!endif
+
 #!ifdef WITH_VOICEMAIL
        # serial forking
        # - route to voicemail on busy or no answer (timeout)
index 542b58e..aaa1fa8 100644 (file)
@@ -351,7 +351,6 @@ modparam("ims_usrloc_pcscf", "db_url", DB_URL)
 modparam("ims_usrloc_pcscf", "db_mode", 1)
 #!endif
 modparam("ims_usrloc_pcscf", "hashing_type", 2)
-modparam("ims_usrloc_pcscf", "lookup_check_received", 0)
 modparam("ims_usrloc_pcscf", "enable_debug_file", 0)
 modparam("ims_usrloc_pcscf", "match_contact_host_port", 1)
 modparam("ims_registrar_pcscf", "is_registered_fallback2ip", 1)
diff --git a/fmsg.h b/fmsg.h
index d442168..ada40c1 100644 (file)
--- a/fmsg.h
+++ b/fmsg.h
@@ -18,7 +18,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
-                      
+
 #ifndef _FMSG_H_
 #define _FMSG_H_
 
diff --git a/kemi.c b/kemi.c
index cbbcbb8..1b880a1 100644 (file)
--- a/kemi.c
+++ b/kemi.c
@@ -1040,6 +1040,7 @@ int sr_kemi_cbname_lookup_name(str *name)
                if(_sr_kemi_cbname_list[i].name.len==name->len
                                && strncmp(_sr_kemi_cbname_list[i].name.s,
                                                name->s, name->len)==0) {
+                       lock_release(_sr_kemi_cbname_lock);
                        return i+1;
                }
        }
diff --git a/kemi.h b/kemi.h
index f98448e..4145950 100644 (file)
--- a/kemi.h
+++ b/kemi.h
@@ -100,7 +100,7 @@ int sr_kemi_modules_add(sr_kemi_t *klist);
 int sr_kemi_modules_size_get(void);
 sr_kemi_module_t* sr_kemi_modules_get(void);
 
-typedef int (*sr_kemi_eng_route_f)(sip_msg_t*, int, str *);
+typedef int (*sr_kemi_eng_route_f)(sip_msg_t*, int, str*, str*);
 
 #define SR_KEMI_BNAME_SIZE     256
 typedef struct sr_kemi_eng {
diff --git a/lib/kcore/faked_msg.h b/lib/kcore/faked_msg.h
deleted file mode 100644 (file)
index 65ecb3f..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- *
- * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com)
- *
- * 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 _FAKED_SIP_MSG_H_
-#define _FAKED_SIP_MSG_H_
-
-#include "../../fmsg.h"
-
-#endif
index c14a801..b63d5ed 100644 (file)
@@ -28,6 +28,6 @@
 <!ENTITY DEFAULT_Q  "1.0">
 <!ENTITY DEFAULT_CALLID "Default-Call-ID">
 <!ENTITY DEFAULT_CSEQ "1">
-<!ENTITY DEFAULT_DATETIME "1900-01-01 00:00:01"> <!-- postgresql doesn't like 0000-00-00 00:00:00 -->
+<!ENTITY DEFAULT_DATETIME "2000-01-01 00:00:01"> <!-- postgresql doesn't like 0000-00-00 00:00:00 -->
 <!ENTITY DEFAULT_FLAGS "0">
 <!ENTITY KAMAILIO_MOD_DOC "http://www.kamailio.org/docs/modules/devel/">
index 7300177..21525d1 100644 (file)
@@ -72,7 +72,8 @@
         <name>email_address</name>
         <type>string</type>
         <size>&uri_len;</size>
-        <default/>
+        <null/>
+        <default><null/></default>
         <description>Email address</description>
     </column>
 
index cf62889..1f7fd35 100644 (file)
    <column id="a_rr">
         <name>a_rr</name>
         <type>largetext</type>
-        <default/>
+        <null/>
         <description>Record route - A side</description>
        </column>
 
     <column id="b_rr">
         <name>b_rr</name>
         <type>largetext</type>
-        <default/>
+        <null/>
         <description>Record route - B side</description>
     </column>
 
     <column id="s_rr">
         <name>s_rr</name>
         <type>largetext</type>
-        <default/>
+        <null/>
         <description>Record route - S side</description>
     </column>
 
index 86fcb2f..e3978f0 100644 (file)
@@ -88,7 +88,7 @@
     <column id="x_via">
         <name>x_via</name>
         <type>largetext</type>
-        <default/>
+        <null/>
         <description>Via stack - A or B side</description>
     </column>
 
     <column id="x_rr">
         <name>x_rr</name>
         <type>largetext</type>
-        <default/>
+        <null/>
         <description>RR stack - incoming A or B side</description>
        </column>
 
     <column id="y_rr">
         <name>y_rr</name>
         <type>largetext</type>
-        <default/>
+        <null/>
         <description>RR stack - the other A or B side</description>
        </column>
 
    <column id="s_rr">
         <name>s_rr</name>
         <type>largetext</type>
-        <default/>
+        <null/>
         <description>RR stack - the S side</description>
        </column>
 
index 6ff3318..b303682 100644 (file)
@@ -211,7 +211,10 @@ modparam("acc_radius", "rad_time_mode", 1)
    “radius_config”.
 
    Meaning of the parameters is as follows:
-     * comment - Comment to be appended.
+     * comment - Comment to be appended. The first token can be a SIP
+       response code that it is wanted to be interpreted as event type
+       (e.g., using 200 means the transaction is considered successfully
+       responded).
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
@@ -219,5 +222,6 @@ modparam("acc_radius", "rad_time_mode", 1)
 ...
 acc_rad_request("Some comment");
 ...
+# write record as when the transaction was responded with a 200 code
 acc_rad_request("200 From Config");
 ...
index 21860b3..969b7db 100644 (file)
@@ -226,7 +226,10 @@ modparam("acc_radius", "rad_time_mode", 1)
                Meaning of the parameters is as follows:</para>
                <itemizedlist>
                <listitem>
-                       <para><emphasis>comment</emphasis> - Comment to be appended.
+                       <para><emphasis>comment</emphasis> - Comment to be appended. The
+                               first token can be a SIP response code that it is wanted to
+                               be interpreted as event type (e.g., using 200 means the
+                               transaction is considered successfully responded).
                        </para>
                </listitem>
                </itemizedlist>
@@ -239,6 +242,7 @@ modparam("acc_radius", "rad_time_mode", 1)
 ...
 acc_rad_request("Some comment");
 ...
+# write record as when the transaction was responded with a 200 code
 acc_rad_request("200 From Config");
 ...
 </programlisting>
index df88648..f1b5703 100644 (file)
@@ -2157,7 +2157,8 @@ static int lua_sr_sdpops_remove_transport(lua_State *L)
 static int lua_sr_sdpops_remove_line_by_prefix(lua_State *L)
 {
        int ret;
-       str media;
+       str prefix = STR_NULL;
+       str media = STR_NULL;
        sr_lua_env_t *env_L;
 
        env_L = sr_lua_env_get();
@@ -2174,16 +2175,22 @@ static int lua_sr_sdpops_remove_line_by_prefix(lua_State *L)
                return app_lua_return_error(L);
        }
 
-       if(lua_gettop(L)!=1)
+       if(lua_gettop(L)==1)
+       {
+               prefix.s = (char *) lua_tostring(L, -1);
+               prefix.len = strlen(prefix.s);
+       } else if(lua_gettop(L)==2)
        {
+               prefix.s = (char *) lua_tostring(L, -2);
+               prefix.len = strlen(prefix.s);
+               media.s = (char *) lua_tostring(L, -1);
+               media.len = strlen(media.s);
+       } else {
                LM_ERR("incorrect number of arguments\n");
                return app_lua_return_error(L);
        }
 
-       media.s = (char*)lua_tostring(L, -1);
-       media.len = strlen(media.s);
-
-       ret = _lua_sdpopsb.sdp_remove_line_by_prefix(env_L->msg, &media);
+       ret = _lua_sdpopsb.sdp_remove_line_by_prefix(env_L->msg, &prefix, &media);
 
        return app_lua_return_int(L, ret);
 }
index fea80bb..84bb0cd 100644 (file)
@@ -108,13 +108,19 @@ struct module_exports exports = {
 /**
  *
  */
-int sr_kemi_config_engine_lua(sip_msg_t *msg, int rtype, str *rname)
+int sr_kemi_config_engine_lua(sip_msg_t *msg, int rtype, str *rname,
+               str *rparam)
 {
        int ret;
 
        ret = -1;
        if(rtype==REQUEST_ROUTE) {
-               ret = app_lua_run_ex(msg, "ksr_request_route", NULL, NULL, NULL, 1);
+               if(rname!=NULL && rname->s!=NULL) {
+                       ret = app_lua_run_ex(msg, rname->s,
+                                       (rparam && rparam->s)?rparam->s:NULL, NULL, NULL, 0);
+               } else {
+                       ret = app_lua_run_ex(msg, "ksr_request_route", NULL, NULL, NULL, 1);
+               }
        } else if(rtype==CORE_ONREPLY_ROUTE) {
                ret = app_lua_run_ex(msg, "ksr_reply_route", NULL, NULL, NULL, 0);
        } else if(rtype==BRANCH_ROUTE) {
@@ -137,7 +143,8 @@ int sr_kemi_config_engine_lua(sip_msg_t *msg, int rtype, str *rname)
                ret = app_lua_run_ex(msg, "ksr_onsend_route", NULL, NULL, NULL, 0);
        } else if(rtype==EVENT_ROUTE) {
                if(rname!=NULL && rname->s!=NULL) {
-                       ret = app_lua_run_ex(msg, rname->s, NULL, NULL, NULL, 0);
+                       ret = app_lua_run_ex(msg, rname->s,
+                                       (rparam && rparam->s)?rparam->s:NULL, NULL, NULL, 0);
                }
        } else {
                if(rname!=NULL) {
index fd4ed7f..859bebc 100644 (file)
 /**
  *
  */
-int sr_kemi_config_engine_python(sip_msg_t *msg, int rtype, str *rname)
+int sr_kemi_config_engine_python(sip_msg_t *msg, int rtype, str *rname,
+               str *rparam)
 {
        int ret;
 
        ret = -1;
        if(rtype==REQUEST_ROUTE) {
-               ret = apy_exec(msg, "ksr_request_route", NULL, 1);
+               if(rname!=NULL && rname->s!=NULL) {
+                       ret = apy_exec(msg, rname->s,
+                                       (rparam && rparam->s)?rparam->s:NULL, 0);
+               } else {
+                       ret = apy_exec(msg, "ksr_request_route", NULL, 1);
+               }
        } else if(rtype==CORE_ONREPLY_ROUTE) {
                ret = apy_exec(msg, "ksr_reply_route", NULL, 0);
        } else if(rtype==BRANCH_ROUTE) {
@@ -68,7 +74,8 @@ int sr_kemi_config_engine_python(sip_msg_t *msg, int rtype, str *rname)
                ret = apy_exec(msg, "ksr_onsend_route", NULL, 0);
        } else if(rtype==EVENT_ROUTE) {
                if(rname!=NULL && rname->s!=NULL) {
-                       ret = apy_exec(msg, rname->s, NULL, 0);
+                       ret = apy_exec(msg, rname->s,
+                                       (rparam && rparam->s)?rparam->s:NULL, 0);
                }
        } else {
                if(rname!=NULL) {
index 8840cd2..e34cdf5 100644 (file)
@@ -27,7 +27,8 @@
 
 int sr_apy_init_ksr(void);
 void sr_apy_destroy_ksr(void);
-int sr_kemi_config_engine_python(sip_msg_t *msg, int rtype, str *rname);
+int sr_kemi_config_engine_python(sip_msg_t *msg, int rtype, str *rname,
+               str *rparam);
 
 PyObject *sr_apy_kemi_exec_func(PyObject *self, PyObject *args, int idx);
 
index d9761bd..5ffdd5d 100644 (file)
@@ -59,6 +59,7 @@ Gergely Kovacs
 
         7. Authorizer service examples
         8. Verifier service examples
+        9. Remarks
 
    List of Examples
 
@@ -124,6 +125,7 @@ Chapter 1. Admin Guide
 
    7. Authorizer service examples
    8. Verifier service examples
+   9. Remarks
 
 1. Overview
 
@@ -535,3 +537,12 @@ route[VERIFY]
         }
 }
 ...
+
+9. Remarks
+
+   Note: libcurl leak in CentOS 6 - this module uses libcurl library and
+   in case if you are using CentOS 6, be aware that standard
+   libcurl-7.19.7-52 has a memory leak. To fix this memory, install
+   libcurl from city-fan repository. More details at:
+   https://www.digitalocean.com/community/questions/how-to-upgrade-curl-in
+   -centos6
index a3242ca..e049e5d 100644 (file)
@@ -225,5 +225,16 @@ route[VERIFY]
 ...
 ]]></programlisting>
        </section>
+       <section id="auth_identity.s.remarks">
+               <title>Remarks</title>
+               <para>
+                       Note: libcurl leak in CentOS 6 - this module uses libcurl library
+                       and in case if you are using CentOS 6, be aware that standard
+                       libcurl-7.19.7-52 has a memory leak. To fix this memory, install
+                       libcurl from city-fan repository. More details at:
+                       <ulink url="https://www.digitalocean.com/community/questions/how-to-upgrade-curl-in-centos6">
+                               https://www.digitalocean.com/community/questions/how-to-upgrade-curl-in-centos6</ulink>
+               </para>
+       </section>
        </chapter>
 </book>
index e238b44..b2728a8 100644 (file)
@@ -55,7 +55,7 @@
 #include "../dialog/dlg_load.h"
 #include "../dialog/dlg_hash.h"
 #include "../../mi/mi_types.h"
-#include "../../lib/kcore/faked_msg.h"
+#include "../../fmsg.h"
 #include "../../rpc.h"
 #include "../../rpc_lookup.h"
 
index bbe8e6f..504f7c0 100644 (file)
@@ -56,7 +56,7 @@
 #include "../../pvar.h"
 #include "../../mod_fix.h"
 #include "../../script_cb.h"
-#include "../../lib/kcore/faked_msg.h"
+#include "../../fmsg.h"
 #include "../../hashes.h"
 #include "../../lib/kcore/kstats_wrapper.h"
 #include "../../mem/mem.h"
index 2d838d5..121d33c 100644 (file)
@@ -37,7 +37,7 @@
 #include "../../action.h"
 #include "../../script_cb.h"
 #include "../../pt.h"
-#include "../../lib/kcore/faked_msg.h"
+#include "../../fmsg.h"
 #include "../../parser/parse_from.h"
 #include "../../parser/parse_cseq.h"
 #include "../../parser/contact/parse_contact.h"
index 0e30bfe..62a1dd0 100644 (file)
@@ -1355,7 +1355,7 @@ r,opt)
 
 7.2. Kamailio config file
 
-   Next picture shows a sample usage of the dispatcher module.
+   Next listing shows a sample config for using the dispatcher module.
 
    Example 1.43. Kamailio config script - sample dispatcher usage
 ...
@@ -1366,9 +1366,9 @@ r,opt)
 # - no TPC listening
 # - don't dispatch REGISTER and presence requests
 #
-# Kamailio (OpenSER) SIP Server v3.2
+# Kamailio SIP Server
 #     - web: http://www.kamailio.org
-#     - git: http://sip-router.org
+#     - git: http://github.com/kamailio/
 #
 # Direct your questions about this file to: sr-users@lists.sip-router.org
 #
@@ -1385,6 +1385,13 @@ r,opt)
 #!define DBURL "mysql://kamailio:kamailiorw@localhost/kamailio"
 #!endif
 
+# - flags
+#   FLT_ - per transaction (message) flags
+#       FLB_ - per branch flags
+#!define FLT_ACC 1
+#!define FLT_ACCMISSED 2
+#!define FLT_ACCFAILED 3
+
 ####### Global Parameters #########
 
 #!ifdef WITH_DEBUG
@@ -1423,8 +1430,8 @@ sip_warning=no
 
 ####### Modules Section ########
 
-#set module path
-mpath="/usr/local/lib/kamailio/modules_k/:/usr/local/lib/kamailio/modules/"
+# set module path
+mpath="/usr/local/lib/kamailio/modules/"
 
 loadmodule "db_mysql.so"
 loadmodule "mi_fifo.so"
@@ -1448,10 +1455,6 @@ loadmodule "dispatcher.so"
 # ----------------- setting module-specific parameters ---------------
 
 
-# ----- mi_fifo params -----
-modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")
-
-
 # ----- rr params -----
 # add value to ;lr param to cope with most of the UAs
 modparam("rr", "enable_full_lr", 1)
@@ -1460,8 +1463,8 @@ modparam("rr", "append_fromtag", 0)
 
 
 # ----- acc params -----
-modparam("acc", "log_flag", 1)
-modparam("acc", "failed_transaction_flag", 3)
+modparam("acc", "log_flag", FLT_ACC)
+modparam("acc", "failed_transaction_flag", FLT_ACCFAILED)
 modparam("acc", "log_extra",
         "src_user=$fU;src_domain=$fd;dst_ouser=$tU;dst_user=$rU;dst_domain=$rd;s
 rc_ip=$si")
@@ -1484,7 +1487,7 @@ modparam("dispatcher", "sock_avp", "$avp(AVP_SOCK)")
 
 # main request routing logic
 
-route {
+request_route {
 
         # per request initial checks
         route(REQINIT);
@@ -1502,6 +1505,11 @@ route {
                 exit;
         }
 
+        # handle retransmissions
+        if(t_precheck_trans()) {
+                t_check_trans();
+                exit;
+        }
         t_check_trans();
 
         # record routing for dialog forming requests (in case they are routed)
@@ -1511,9 +1519,8 @@ route {
                 record_route();
 
         # account only INVITEs
-        if (is_method("INVITE"))
-        {
-                setflag(1); # do accounting
+        if (is_method("INVITE")) {
+                setflag(FLT_ACC); # do accounting
         }
 
         # handle presence related requests
@@ -1522,8 +1529,7 @@ route {
         # handle registrations
         route(REGISTRAR);
 
-        if ($rU==$null)
-        {
+        if ($rU==$null) {
                 # request with no Username in RURI
                 sl_send_reply("484","Address Incomplete");
                 exit;
@@ -1548,8 +1554,7 @@ route[REQINIT] {
                 exit;
         }
 
-        if(!sanity_check("1511", "7"))
-        {
+        if(!sanity_check("1511", "7")) {
                 xlog("Malformed SIP message from $si:$sp\n");
                 exit;
         }
@@ -1562,8 +1567,9 @@ route[WITHINDLG] {
                 # take the path determined by record-routing
                 if (loose_route()) {
                         if (is_method("BYE")) {
-                                setflag(1); # do accounting ...
-                                setflag(3); # ... even if the transaction fails
+                                setflag(FLT_ACC); # do accounting ...
+                                setflag(FLT_ACCFAILED); # ... even if the transa
+ction fails
                         }
                         route(RELAY);
                 } else {
@@ -1611,8 +1617,7 @@ route[PRESENCE] {
 # Dispatch requests
 route[DISPATCH] {
         # round robin dispatching on gateways group '1'
-        if(!ds_select_dst("1", "4"))
-        {
+        if(!ds_select_dst("1", "4")) {
                 send_reply("404", "No destination");
                 exit;
         }
@@ -1622,17 +1627,15 @@ route[DISPATCH] {
         exit;
 }
 
-# Sample failure route
+# Try next destionations in failure route
 failure_route[RTF_DISPATCH] {
         if (t_is_canceled()) {
                 exit;
         }
         # next DST - only for 500 or local timeout
         if (t_check_status("500")
-                        or (t_branch_timeout() and !t_branch_replied()))
-        {
-                if(ds_next_dst())
-                {
+                        or (t_branch_timeout() and !t_branch_replied())) {
+                if(ds_next_dst()) {
                         t_on_failure("RTF_DISPATCH");
                         route(RELAY);
                         exit;
@@ -1640,8 +1643,6 @@ failure_route[RTF_DISPATCH] {
         }
 }
 
-
-
 ...
 
 8. Event routes
index fd58b12..4836f52 100644 (file)
@@ -52,7 +52,7 @@
 #include "../../lib/srdb1/db_res.h"
 #include "../../str.h"
 #include "../../script_cb.h"
-#include "../../lib/kcore/faked_msg.h"
+#include "../../fmsg.h"
 
 #include "ds_ht.h"
 #include "api.h"
index 08ea285..475941d 100644 (file)
@@ -5,9 +5,9 @@
 # - no TPC listening
 # - don't dispatch REGISTER and presence requests
 #
-# Kamailio (OpenSER) SIP Server v3.2
+# Kamailio SIP Server
 #     - web: http://www.kamailio.org
-#     - git: http://sip-router.org
+#     - git: http://github.com/kamailio/
 #
 # Direct your questions about this file to: sr-users@lists.sip-router.org
 #
@@ -16,7 +16,7 @@
 #
 # Several features can be enabled using '#!define WITH_FEATURE' directives:
 #
-# *** To run in debug mode: 
+# *** To run in debug mode:
 #     - define WITH_DEBUG
 #
 
 #!define DBURL "mysql://kamailio:kamailiorw@localhost/kamailio"
 #!endif
 
+# - flags
+#   FLT_ - per transaction (message) flags
+#      FLB_ - per branch flags
+#!define FLT_ACC 1
+#!define FLT_ACCMISSED 2
+#!define FLT_ACCFAILED 3
+
 ####### Global Parameters #########
 
 #!ifdef WITH_DEBUG
@@ -54,7 +61,7 @@ auto_aliases=no
 
 port=5060
 
-/* uncomment and configure the following line if you want Kamailio to 
+/* uncomment and configure the following line if you want Kamailio to
    bind on a specific interface/port/proto (default bind on all available) */
 # listen=udp:127.0.0.1:5060
 
@@ -62,8 +69,8 @@ sip_warning=no
 
 ####### Modules Section ########
 
-#set module path
-mpath="/usr/local/lib/kamailio/modules_k/:/usr/local/lib/kamailio/modules/"
+# set module path
+mpath="/usr/local/lib/kamailio/modules/"
 
 loadmodule "db_mysql.so"
 loadmodule "mi_fifo.so"
@@ -87,10 +94,6 @@ loadmodule "dispatcher.so"
 # ----------------- setting module-specific parameters ---------------
 
 
-# ----- mi_fifo params -----
-modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")
-
-
 # ----- rr params -----
 # add value to ;lr param to cope with most of the UAs
 modparam("rr", "enable_full_lr", 1)
@@ -99,9 +102,9 @@ modparam("rr", "append_fromtag", 0)
 
 
 # ----- acc params -----
-modparam("acc", "log_flag", 1)
-modparam("acc", "failed_transaction_flag", 3)
-modparam("acc", "log_extra", 
+modparam("acc", "log_flag", FLT_ACC)
+modparam("acc", "failed_transaction_flag", FLT_ACCFAILED)
+modparam("acc", "log_extra",
        "src_user=$fU;src_domain=$fd;dst_ouser=$tU;dst_user=$rU;dst_domain=$rd;src_ip=$si")
 
 # ----- tm params -----
@@ -122,7 +125,7 @@ modparam("dispatcher", "sock_avp", "$avp(AVP_SOCK)")
 
 # main request routing logic
 
-route {
+request_route {
 
        # per request initial checks
        route(REQINIT);
@@ -140,6 +143,11 @@ route {
                exit;
        }
 
+       # handle retransmissions
+       if(t_precheck_trans()) {
+               t_check_trans();
+               exit;
+       }
        t_check_trans();
 
        # record routing for dialog forming requests (in case they are routed)
@@ -149,9 +157,8 @@ route {
                record_route();
 
        # account only INVITEs
-       if (is_method("INVITE"))
-       {
-               setflag(1); # do accounting
+       if (is_method("INVITE")) {
+               setflag(FLT_ACC); # do accounting
        }
 
        # handle presence related requests
@@ -160,8 +167,7 @@ route {
        # handle registrations
        route(REGISTRAR);
 
-       if ($rU==$null)
-       {
+       if ($rU==$null) {
                # request with no Username in RURI
                sl_send_reply("484","Address Incomplete");
                exit;
@@ -186,8 +192,7 @@ route[REQINIT] {
                exit;
        }
 
-       if(!sanity_check("1511", "7"))
-       {
+       if(!sanity_check("1511", "7")) {
                xlog("Malformed SIP message from $si:$sp\n");
                exit;
        }
@@ -200,8 +205,8 @@ route[WITHINDLG] {
                # take the path determined by record-routing
                if (loose_route()) {
                        if (is_method("BYE")) {
-                               setflag(1); # do accounting ...
-                               setflag(3); # ... even if the transaction fails
+                               setflag(FLT_ACC); # do accounting ...
+                               setflag(FLT_ACCFAILED); # ... even if the transaction fails
                        }
                        route(RELAY);
                } else {
@@ -247,8 +252,7 @@ route[PRESENCE] {
 # Dispatch requests
 route[DISPATCH] {
        # round robin dispatching on gateways group '1'
-       if(!ds_select_dst("1", "4"))
-       {
+       if(!ds_select_dst("1", "4")) {
                send_reply("404", "No destination");
                exit;
        }
@@ -258,22 +262,18 @@ route[DISPATCH] {
        exit;
 }
 
-# Sample failure route
+# Try next destionations in failure route
 failure_route[RTF_DISPATCH] {
        if (t_is_canceled()) {
                exit;
        }
        # next DST - only for 500 or local timeout
        if (t_check_status("500")
-                       or (t_branch_timeout() and !t_branch_replied()))
-       {
-               if(ds_next_dst())
-               {
+                       or (t_branch_timeout() and !t_branch_replied())) {
+               if(ds_next_dst()) {
                        t_on_failure("RTF_DISPATCH");
                        route(RELAY);
                        exit;
                }
        }
 }
-
-
index 53fe4fb..4b462bd 100644 (file)
@@ -10,7 +10,7 @@
 <!-- Module User's Guide -->
 
 <chapter xmlns:xi="http://www.w3.org/2001/XInclude">
-       
+
        <title>&adminguide;</title>
 
        <section>
@@ -29,7 +29,7 @@
        </para>
        <para>
                It is very lightweight, therefore suitable for handling heavy SIP
-               traffic. As the module has a small footprint and the ability to load 
+               traffic. As the module has a small footprint and the ability to load
                balancing rules from a plain text file, it is suitable for embedded systems.
        </para>
        </section>
@@ -537,11 +537,11 @@ modparam("dispatcher", "force_dst", 1)
  ...
  </programlisting>
                </example>
-       </section>      
+       </section>
        <section id="dispatcher.p.ds_ping_from">
                <title><varname>ds_ping_from</varname> (string)</title>
                <para>
-               With this Method you can define the "From:"-Line for the request, sent to the failed gateways.          
+               With this Method you can define the "From:"-Line for the request, sent to the failed gateways.
                This method is only available, if compiled with the probing of failed gateways enabled.
                </para>
                <para>
@@ -557,7 +557,7 @@ modparam("dispatcher", "force_dst", 1)
  ...
  </programlisting>
                </example>
-       </section>      
+       </section>
 
        <section id="dispatcher.p.ds_ping_interval">
                <title><varname>ds_ping_interval</varname> (int)</title>
@@ -580,8 +580,8 @@ modparam("dispatcher", "force_dst", 1)
  ...
  </programlisting>
                </example>
-       </section>      
-       
+       </section>
+
        <section id="dispatcher.p.ds_probing_threshold">
                <title><varname>ds_probing_threshold</varname> (int)</title>
                <para>
@@ -640,7 +640,7 @@ modparam("dispatcher", "force_dst", 1)
                        PING-Method are not only the ones generated from the remote servers, but also those
                        that are generated locally. E.g.: setting code=408 or class=400 will never set
                        a backend down even if it is, because internally the Kamailio transaction layer
-                       generates a 408 in the case of no response from the remote server, and this 
+                       generates a 408 in the case of no response from the remote server, and this
                        internal code 408 is accepted as valid value.
                </para>
                <para>
@@ -656,11 +656,11 @@ modparam("dispatcher", "force_dst", 1)
  ...
  </programlisting>
                </example>
-       </section>      
+       </section>
        <section id="dispatcher.p.ds_probing_mode">
                <title><varname>ds_probing_mode</varname> (int)</title>
                <para>
-               Controls what gateways are tested to see if they are reachable. 
+               Controls what gateways are tested to see if they are reachable.
                </para>
 
                <itemizedlist>
@@ -827,7 +827,7 @@ modparam("dispatcher", "force_dst", 1)
                <title><varname>ds_timer_mode</varname> (int)</title>
                <para>
                        Specify the timer process to be used by the module for
-                       keepalives and active dialogs tracking. 
+                       keepalives and active dialogs tracking.
                </para>
                <para>
                        It can be set to:
@@ -984,17 +984,17 @@ modparam("dispatcher", "force_dst", 1)
                                <para>
                                <quote>11</quote> - use relative weight based load distribution.
                                You have to set the attribute 'rweight' per each address in
-                               destination set. Active host usage probability is 
-                               rweight/(SUM of all active host rweights in destination group). 
+                               destination set. Active host usage probability is
+                               rweight/(SUM of all active host rweights in destination group).
                                </para>
                                <para>
                                The major difference from the weight distribution is the
-                               probability recalculation according to rweight value in case of 
+                               probability recalculation according to rweight value in case of
                                host enabling/disabling
                                </para>
                                <para>
-                               For example, 100 calls in 3-hosts group with rweight params 1/2/1  
-                               will be distributed as 25/50/25. After third host failing 
+                               For example, 100 calls in 3-hosts group with rweight params 1/2/1
+                               will be distributed as 25/50/25. After third host failing
                                distribution will be changed to 33/67/0.
                                </para>
                        </listitem>
@@ -1374,7 +1374,7 @@ onreply_route {
                <function moreinfo="none">ds_set_state</function>
                </title>
                <para>
-        Sets the status for a destination address (can be use to mark the destination 
+        Sets the status for a destination address (can be use to mark the destination
                as active or inactive).
                </para>
                <para>
@@ -1385,17 +1385,17 @@ onreply_route {
                        <listitem><para>_state_ : state of the destination address</para>
                              <itemizedlist>
                         <listitem><para> <quote>a</quote>: active</para></listitem> 
-                                <listitem><para> <quote>i</quote>: inactive</para></listitem>  
-                                <listitem><para> <quote>t</quote>: trying</para></listitem>    
-                                <listitem><para> <quote>d</quote>: disabled</para></listitem>  
+                                <listitem><para> <quote>i</quote>: inactive</para></listitem>
+                                <listitem><para> <quote>t</quote>: trying</para></listitem>
+                                <listitem><para> <quote>d</quote>: disabled</para></listitem>
                                  </itemizedlist>
                                  <para>The states <quote>a</quote>, <quote>i</quote> or
                                          <quote>t</quote> can be followed by <quote>p</quote>
                                          to set probing mode (e.g. 'ap', 'ip' or 'tp').</para>
-                       </listitem>       
+                       </listitem>
 
                        <listitem><para>_group_: destination group id</para></listitem>
-                       
+
                        <listitem><para>_address_: address of the destination in the _group_</para></listitem>
                </itemizedlist>
                <para>
@@ -1628,10 +1628,10 @@ onreply_route {
                                        sent to that gateways.
                                </listitem>
                                <listitem>
-                                       'rweight' - used for relative weight based load 
-                                       distribution. It must be set to a positive integer value 
-                                       between 1 and 100 (otherwise host will be excluded from 
-                                       relative weight distribution type). 
+                                       'rweight' - used for relative weight based load
+                                       distribution. It must be set to a positive integer value
+                                       between 1 and 100 (otherwise host will be excluded from
+                                       relative weight distribution type).
                                </listitem>
                                <listitem>
                                        'socket' - used to set the sending socket for the gateway.
@@ -1675,11 +1675,11 @@ setid(int) destination(sip uri) flags(int,opt) priority(int,opt) attrs(str,opt)
                </example>
                </section>
                </section>
-       
+
                <section id="dispatcher.ex.config">
                <title>&kamailio; config file</title>
                <para>
-               Next picture shows a sample usage of the dispatcher module.
+               Next listing shows a sample config for using the dispatcher module.
                </para>
                <example>
                <title>&kamailio; config script - sample dispatcher usage</title>
index e1aa51f..c6a7f35 100644 (file)
@@ -37,7 +37,7 @@
 #include "../../dprint.h"
 #include "../../sr_module.h"
 #include "../../cfg/cfg_struct.h"
-#include "../../lib/kcore/faked_msg.h"
+#include "../../fmsg.h"
 
 int handle_msg_req_tuple(cnode_handler_t *phandler, erlang_msg * msg);
 int handle_req_ref_tuple(cnode_handler_t *phandler, erlang_msg * msg);
index 57603c4..0131e8f 100644 (file)
@@ -37,7 +37,7 @@
 #include "../../dprint.h"
 #include "../../ut.h"
 #include "../../cfg/cfg_struct.h"
-#include "../../lib/kcore/faked_msg.h"
+#include "../../fmsg.h"
 
 #include "evapi_dispatch.h"
 
index 6cb4e4f..3debd0e 100644 (file)
@@ -36,7 +36,7 @@
 #include "../../mod_fix.h"
 #include "../../pvar.h"
 #include "../../cfg/cfg_struct.h"
-#include "../../lib/kcore/faked_msg.h"
+#include "../../fmsg.h"
 
 #include "../../modules/tm/tm_load.h"
 
index b5d7fa6..16d812e 100644 (file)
@@ -52,6 +52,7 @@ Ovidiu Sas
               3.11. db_expires (integer)
               3.12. enable_dmq (integer)
               3.13. timer_procs (integer)
+              3.14. event_callback (str)
 
         4. Functions
 
@@ -106,15 +107,16 @@ Ovidiu Sas
    1.14. Set db_expires parameter
    1.15. Set enable_dmq parameter
    1.16. Set timer_procs parameter
-   1.17. sht_print usage
-   1.18. sht_rm_name_re usage
-   1.19. sht_rm_value_re usage
-   1.20. sht_reset usage
-   1.21. sht_lock usage
-   1.22. sht_unlock usage
-   1.23. sht_iterator_start usage
-   1.24. sht_iterator_end usage
-   1.25. sht_iterator_next usage
+   1.17. Set event_callback parameter
+   1.18. sht_print usage
+   1.19. sht_rm_name_re usage
+   1.20. sht_rm_value_re usage
+   1.21. sht_reset usage
+   1.22. sht_lock usage
+   1.23. sht_unlock usage
+   1.24. sht_iterator_start usage
+   1.25. sht_iterator_end usage
+   1.26. sht_iterator_next usage
 
 Chapter 1. Admin Guide
 
@@ -142,6 +144,7 @@ Chapter 1. Admin Guide
         3.11. db_expires (integer)
         3.12. enable_dmq (integer)
         3.13. timer_procs (integer)
+        3.14. event_callback (str)
 
    4. Functions
 
@@ -414,6 +417,7 @@ $ kamcmd htable.dump htable
    3.11. db_expires (integer)
    3.12. enable_dmq (integer)
    3.13. timer_procs (integer)
+   3.14. event_callback (str)
 
 3.1. htable (str)
 
@@ -623,6 +627,26 @@ modparam("htable", "enable_dmq", 1)
 modparam("htable", "timer_procs", 4)
 ...
 
+3.14. event_callback (str)
+
+   The name of the function in the kemi configuration file (embedded
+   scripting language such as Lua, Python, ...) to be executed instead of
+   event_route[...] blocks. The function receives a string parameter with
+   the name of the event.
+
+   Default value is 'empty' (no function is executed for events).
+
+   Example 1.17. Set event_callback parameter
+...
+modparam("htable", "event_callback", "ksr_htable_event")
+...
+-- event callback function implemented in Lua
+function ksr_htable_event(evname)
+        KSR.info("===== htable module triggered event: " .. evname .. "\n");
+        return 1;
+end
+...
+
 4. Functions
 
    4.1. sht_print()
@@ -643,7 +667,7 @@ modparam("htable", "timer_procs", 4)
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    ONREPLY_ROUTE, BRANCH_ROUTE.
 
-   Example 1.17. sht_print usage
+   Example 1.18. sht_print usage
 ...
 sht_print();
 ...
@@ -656,7 +680,7 @@ sht_print();
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    ONREPLY_ROUTE, BRANCH_ROUTE.
 
-   Example 1.18. sht_rm_name_re usage
+   Example 1.19. sht_rm_name_re usage
 ...
 sht_rm_name_re("ha=>.*");
 ...
@@ -669,7 +693,7 @@ sht_rm_name_re("ha=>.*");
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    ONREPLY_ROUTE, BRANCH_ROUTE.
 
-   Example 1.19. sht_rm_value_re usage
+   Example 1.20. sht_rm_value_re usage
 ...
 sht_rm_value_re("ha=>.*");
 ...
@@ -681,7 +705,7 @@ sht_rm_value_re("ha=>.*");
 
    This function can be used from ANY_ROUTE.
 
-   Example 1.20. sht_reset usage
+   Example 1.21. sht_reset usage
 ...
 sht_reset("ha$var(x)");
 ...
@@ -694,7 +718,7 @@ sht_reset("ha$var(x)");
 
    This function can be used from ANY_ROUTE.
 
-   Example 1.21. sht_lock usage
+   Example 1.22. sht_lock usage
 ...
 sht_lock("ha=>test");
 ...
@@ -707,7 +731,7 @@ sht_lock("ha=>test");
 
    This function can be used from ANY_ROUTE.
 
-   Example 1.22. sht_unlock usage
+   Example 1.23. sht_unlock usage
 ...
 sht_lock("ha=>test");
 $sht(ha=>test) = $sht(ha=>test) + 10;
@@ -729,7 +753,7 @@ sht_unlock("ha=>test");
 
    This function can be used from ANY_ROUTE.
 
-   Example 1.23. sht_iterator_start usage
+   Example 1.24. sht_iterator_start usage
 ...
 sht_iterator_start("i1", "h1");
 ...
@@ -744,7 +768,7 @@ sht_iterator_start("i1", "h1");
 
    This function can be used from ANY_ROUTE.
 
-   Example 1.24. sht_iterator_end usage
+   Example 1.25. sht_iterator_end usage
 ...
 sht_iterator_end("i1");
 ...
@@ -766,7 +790,7 @@ sht_iterator_end("i1");
 
    This function can be used from ANY_ROUTE.
 
-   Example 1.25. sht_iterator_next usage
+   Example 1.26. sht_iterator_next usage
 ...
     sht_iterator_start("i1", "h1");
     while(sht_iterator_next("i1")) {
index 034733c..53ceb8b 100644 (file)
@@ -10,9 +10,9 @@
 <!-- Module User's Guide -->
 
 <chapter>
-       
+
        <title>&adminguide;</title>
-       
+
        <section>
        <title>Overview</title>
        <para>
@@ -31,7 +31,7 @@
                be adjusted per item via assignment operation at runtime.
        </para>
        <para>
-               Replication between multiple servers is performed automatically (if 
+               Replication between multiple servers is performed automatically (if
                enabled) via the DMQ module.
        </para>
        <para>
@@ -621,9 +621,9 @@ modparam("htable", "db_expires", 1)
        <section id="htable.p.enable_dmq">
                <title><varname>enable_dmq</varname> (integer)</title>
                <para>
-                       If set to 1, will enable DMQ replication of actions performed upon 
-                       entries in all tables having "dmqreplicate" parameter set. Any update 
-                       action performed via pseudo-variables, MI and RPC commands will be 
+                       If set to 1, will enable DMQ replication of actions performed upon
+                       entries in all tables having "dmqreplicate" parameter set. Any update
+                       action performed via pseudo-variables, MI and RPC commands will be
                        repeated on all other nodes. Therefore, it is important to ensure the
                        table definition (size, autoexpire etc.) is identical across all instances.
                </para>
@@ -633,7 +633,7 @@ modparam("htable", "db_expires", 1)
                </emphasis>
                </para>
                <para>
-                       Currently, values are not replicated on load from DB as it is expected 
+                       Currently, values are not replicated on load from DB as it is expected
                        that in these cases, all servers will load their values from the same DB.
                </para>
                <para>
@@ -672,6 +672,34 @@ modparam("htable", "timer_procs", 4)
 </programlisting>
                </example>
        </section>
+       <section id="htable.p.event_callback">
+               <title><varname>event_callback</varname> (str)</title>
+               <para>
+                       The name of the function in the kemi configuration file (embedded
+                       scripting language such as Lua, Python, ...) to be executed instead
+                       of event_route[...] blocks. The function receives a string parameter
+                       with the name of the event.
+               </para>
+               <para>
+               <emphasis>
+                       Default value is 'empty' (no function is executed for events).
+               </emphasis>
+               </para>
+               <example>
+               <title>Set <varname>event_callback</varname> parameter</title>
+               <programlisting format="linespecific">
+...
+modparam("htable", "event_callback", "ksr_htable_event")
+...
+-- event callback function implemented in Lua
+function ksr_htable_event(evname)
+       KSR.info("===== htable module triggered event: " .. evname .. "\n");
+       return 1;
+end
+...
+</programlisting>
+               </example>
+       </section>
        </section>
        <section>
        <title>Functions</title>
index b96ff13..76dba54 100644 (file)
 #include "../../hashes.h"
 #include "../../ut.h"
 #include "../../re.h"
-#include "../../lib/kcore/faked_msg.h"
+#include "../../fmsg.h"
 #include "../../action.h"
 #include "../../route.h"
+#include "../../kemi.h"
 
 #include "ht_api.h"
 #include "ht_db.h"
 
 
+extern str ht_event_callback;
+
 ht_t *_ht_root = NULL;
 ht_cell_t *ht_expired_cell;
 
@@ -350,7 +353,6 @@ int ht_init_tables(void)
 {
        ht_t *ht;
        int i;
-       char route_name[64];
 
        ht = _ht_root;
 
@@ -358,16 +360,19 @@ int ht_init_tables(void)
        {
                LM_DBG("initializing htable [%.*s] with nr. of slots: %d\n",
                                ht->name.len, ht->name.s, ht->htsize);
-               if(ht->name.len + sizeof("htable:expired:") < 64)
+               if(ht->name.len + sizeof("htable:expired:") <  HT_EVEX_NAME_SIZE)
                {
-                       strcpy(route_name, "htable:expired:");
-                       strncat(route_name, ht->name.s, ht->name.len);
-                       ht->evrt_expired = route_get(&event_rt, route_name);
+                       strcpy(ht->evex_name_buf, "htable:expired:");
+                       strncat(ht->evex_name_buf, ht->name.s, ht->name.len);
+                       ht->evex_name.s = ht->evex_name_buf;
+                       ht->evex_name.len = strlen(ht->evex_name_buf);
+
+                       ht->evex_index = route_lookup(&event_rt, ht->evex_name_buf);
 
-                       if (ht->evrt_expired < 0
-                                       || event_rt.rlist[ht->evrt_expired] == NULL)
+                       if (ht->evex_index < 0
+                                       || event_rt.rlist[ht->evex_index] == NULL)
                        {
-                               ht->evrt_expired = -1;
+                               ht->evex_index = -1;
                                LM_DBG("event route for expired items in [%.*s] does not exist\n",
                                                ht->name.len, ht->name.s);
                        } else {
@@ -1073,42 +1078,54 @@ void ht_timer(unsigned int ticks, void *param)
 
 void ht_handle_expired_record(ht_t *ht, ht_cell_t *cell)
 {
-       if(ht->evrt_expired<0)
-               return;
-       ht_expired_cell = cell;
-
-       LM_DBG("running event_route[htable:expired:%.*s]\n",
-                       ht->name.len, ht->name.s);
-       ht_expired_run_event_route(ht->evrt_expired);
-
-       ht_expired_cell = NULL;
-}
-
-void ht_expired_run_event_route(int routeid)
-{
        int backup_rt;
        sip_msg_t *fmsg;
+       sr_kemi_eng_t *keng = NULL;
 
-       if (routeid < 0 || event_rt.rlist[routeid] == NULL)
-       {
-               LM_DBG("route does not exist\n");
-               return;
+       if(ht_event_callback.s==NULL || ht_event_callback.len<=0) {
+               if (ht->evex_index < 0 || event_rt.rlist[ht->evex_index] == NULL) {
+                       LM_DBG("route does not exist\n");
+                       return;
+               }
+       } else {
+               keng = sr_kemi_eng_get();
+               if(keng==NULL) {
+                       LM_DBG("event callback (%s) set, but no cfg engine\n",
+                                       ht_event_callback.s);
+                       return;
+               }
        }
 
-       if (faked_msg_init() < 0)
-       {
+       LM_DBG("running event_route[htable:expired:%.*s]\n",
+                       ht->name.len, ht->name.s);
+
+       if (faked_msg_init() < 0) {
                LM_ERR("faked_msg_init() failed\n");
                return;
        }
+
+       ht_expired_cell = cell;
+
        fmsg = faked_msg_next();
        fmsg->parsed_orig_ruri_ok = 0;
 
        backup_rt = get_route_type();
 
        set_route_type(EVENT_ROUTE);
-       run_top_route(event_rt.rlist[routeid], fmsg, 0);
+       if(ht->evex_index >= 0) {
+               run_top_route(event_rt.rlist[ht->evex_index], fmsg, 0);
+       } else {
+               if(keng!=NULL) {
+                       if(keng->froute(fmsg, EVENT_ROUTE,
+                                               &ht_event_callback, &ht->evex_name)<0) {
+                               LM_ERR("error running event route kemi callback\n");
+                       }
+               }
+       }
 
        set_route_type(backup_rt);
+
+       ht_expired_cell = NULL;
 }
 
 int ht_set_cell_expire(ht_t *ht, str *name, int type, int_str *val)
index a37e98f..a052733 100644 (file)
@@ -18,7 +18,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
-                      
+
 #ifndef _HT_API_H_
 #define _HT_API_H_
 
@@ -54,6 +54,8 @@ typedef struct _ht_entry
 } ht_entry_t;
 
 #define HT_MAX_COLS 8
+#define HT_EVEX_NAME_SIZE      64
+
 typedef struct _ht
 {
        str name;
@@ -69,7 +71,9 @@ typedef struct _ht
        int updateexpire;
        unsigned int htsize;
        int dmqreplicate;
-       int evrt_expired;
+       int evex_index;
+       char evex_name_buf[HT_EVEX_NAME_SIZE];
+       str evex_name;
        ht_entry_t *entries;
        struct _ht *next;
 } ht_t;
@@ -103,7 +107,6 @@ int ht_db_sync_tables(void);
 int ht_has_autoexpire(void);
 void ht_timer(unsigned int ticks, void *param);
 void ht_handle_expired_record(ht_t *ht, ht_cell_t *cell);
-void ht_expired_run_event_route(int routeid);
 int ht_set_cell_expire(ht_t *ht, str *name, int type, int_str *val);
 int ht_get_cell_expire(ht_t *ht, str *name, unsigned int *val);
 
index dd29cb7..d5bf8c5 100644 (file)
@@ -38,7 +38,7 @@
 #include "../../rpc_lookup.h"
 #include "../../kemi.h"
 #include "../../lib/kmi/mi.h"
-#include "../../lib/kcore/faked_msg.h"
+#include "../../fmsg.h"
 
 #include "../../pvar.h"
 #include "ht_api.h"
@@ -55,6 +55,8 @@ int  ht_db_expires_flag = 0;
 int  ht_enable_dmq = 0;
 int  ht_timer_procs = 0;
 
+str ht_event_callback = STR_NULL;
+
 static int htable_init_rpc(void);
 
 /** module functions */
@@ -148,6 +150,7 @@ static param_export_t params[]={
        {"db_expires",         INT_PARAM, &ht_db_expires_flag},
        {"enable_dmq",         INT_PARAM, &ht_enable_dmq},
        {"timer_procs",        PARAM_INT, &ht_timer_procs},
+       {"event_callback",     PARAM_STR, &ht_event_callback},
        {0,0,0}
 };
 
@@ -234,6 +237,8 @@ static int child_init(int rank)
        struct run_act_ctx ctx;
        int rtb, rt;
        int i;
+       sr_kemi_eng_t *keng = NULL;
+       str evname = str_init("htable:mod-init");
 
        LM_DBG("rank is (%d)\n", rank);
 
@@ -252,24 +257,48 @@ static int child_init(int rank)
        if (rank!=PROC_INIT)
                return 0;
 
-       rt = route_get(&event_rt, "htable:mod-init");
-       if(rt>=0 && event_rt.rlist[rt]!=NULL) {
-               LM_DBG("executing event_route[htable:mod-init] (%d)\n", rt);
+
+       rt = -1;
+       if(ht_event_callback.s==NULL || ht_event_callback.len<=0) {
+               rt = route_lookup(&event_rt, evname.s);
+               if(rt<0 || event_rt.rlist[rt]==NULL) {
+                       rt = -1;
+               }
+       } else {
+               keng = sr_kemi_eng_get();
+               if(keng==NULL) {
+                       LM_DBG("event callback (%s) set, but no cfg engine\n",
+                                       ht_event_callback.s);
+                       goto done;
+               }
+       }
+       if(rt>=0 || ht_event_callback.len>0) {
+               LM_DBG("executing event_route[%s] (%d)\n", evname.s, rt);
                if(faked_msg_init()<0)
                        return -1;
                fmsg = faked_msg_next();
                rtb = get_route_type();
                set_route_type(REQUEST_ROUTE);
                init_run_actions_ctx(&ctx);
-               run_top_route(event_rt.rlist[rt], fmsg, &ctx);
-               if(ctx.run_flags&DROP_R_F)
-               {
+               if(rt>0) {
+                       run_top_route(event_rt.rlist[rt], fmsg, &ctx);
+               } else {
+                       if(keng!=NULL) {
+                               if(keng->froute(fmsg, EVENT_ROUTE,
+                                                       &ht_event_callback, &evname)<0) {
+                                       LM_ERR("error running event route kemi callback\n");
+                                       return -1;
+                               }
+                       }
+               }
+               if(ctx.run_flags&DROP_R_F) {
                        LM_ERR("exit due to 'drop' in event route\n");
                        return -1;
                }
                set_route_type(rtb);
        }
 
+done:
        return 0;
 }
 
index 28aef44..0983e71 100644 (file)
@@ -67,6 +67,8 @@ Federico Cabiddu
               6.3. errors
               6.4. timeouts
 
+        7. Remarks
+
    List of Examples
 
    1.1. Set workers parameter
@@ -138,6 +140,8 @@ Chapter 1. Admin Guide
         6.3. errors
         6.4. timeouts
 
+   7. Remarks
+
 1. Overview
 
    This module performs asynchronous HTTP queries.
@@ -572,6 +576,20 @@ xlog("L_INFO", "received reply for query $http_query_id\n");
        http_set_tls_client_key())
      * tls_ca_path: sets the CA certificate path to use (see
        http_set_tls_ca_path())
+     * authmethod: Sets the preferred authentication mode for HTTP/HTTPS
+       requests. The value is a bitmap and multiple methods can be used.
+       Note that in this case, the CURL library will make an extra request
+       to discover server-supported authentication methods. You may want
+       to use a specific value.
+       Valid values are:
+          + 1 - BASIC authentication
+          + 2 - HTTP Digest authentication
+          + 4 - GSS-Negotiate authentication
+          + 8 - NTLM authentication
+          + 16 - HTTP Digest with IE flavour
+       Default value is 3 - BASIC and Digest authentication.
+     * username: sets the username to use for authenticated requests
+     * password: sets the password to use for authenticated requests
      * suspend: if set to 0 it doesn't suspend the current transaction
        before performing the query (see http_async_suspend())
 
@@ -624,3 +642,12 @@ http_async_query("https://example.com/test.php", "HTTP_REPLY");
 6.4. timeouts
 
    The number of timed out requests.
+
+7. Remarks
+
+   Note: libcurl leak in CentOS 6 - this module uses libcurl library and
+   in case if you are using CentOS 6, be aware that standard
+   libcurl-7.19.7-52 has a memory leak. To fix this memory, install
+   libcurl from city-fan repository. More details at:
+   https://www.digitalocean.com/community/questions/how-to-upgrade-curl-in
+   -centos6
index 67915f1..d4dda87 100644 (file)
@@ -45,7 +45,7 @@
 #include "../../dprint.h"
 #include "../../ut.h"
 #include "../../cfg/cfg_struct.h"
-#include "../../lib/kcore/faked_msg.h"
+#include "../../fmsg.h"
 #include "../../modules/tm/tm_load.h"
 
 #include "async_http.h"
@@ -213,7 +213,7 @@ void notification_socket_cb(int fd, short event, void *arg)
        const async_http_worker_t *worker = (async_http_worker_t *) arg;
 
        int received;
-       int i;
+       int i, len;
        async_query_t *aq;
 
        http_m_params_t query_params;
@@ -237,6 +237,8 @@ void notification_socket_cb(int fd, short event, void *arg)
        query_params.timeout = aq->query_params.timeout;
        query_params.tls_verify_peer = aq->query_params.tls_verify_peer;
        query_params.tls_verify_host = aq->query_params.tls_verify_host;
+       query_params.authmethod = aq->query_params.authmethod;
+
        query_params.headers = NULL;
        for (i = 0 ; i < aq->query_params.headers.len ; i++) {
                query_params.headers = curl_slist_append(query_params.headers, aq->query_params.headers.t[i]);
@@ -248,7 +250,7 @@ void notification_socket_cb(int fd, short event, void *arg)
        if (aq->query_params.tls_client_cert.s && aq->query_params.tls_client_cert.len > 0) {
                if (shm_str_dup(&query_params.tls_client_cert, &(aq->query_params.tls_client_cert)) < 0) {
                        LM_ERR("Error allocating query_params.tls_client_cert\n");
-                       return;
+                       goto done;
                }
        }
 
@@ -257,7 +259,7 @@ void notification_socket_cb(int fd, short event, void *arg)
        if (aq->query_params.tls_client_key.s && aq->query_params.tls_client_key.len > 0) {
                if (shm_str_dup(&query_params.tls_client_key, &(aq->query_params.tls_client_key)) < 0) {
                        LM_ERR("Error allocating query_params.tls_client_key\n");
-                       return;
+                       goto done;
                }
        }
 
@@ -266,7 +268,7 @@ void notification_socket_cb(int fd, short event, void *arg)
        if (aq->query_params.tls_ca_path.s && aq->query_params.tls_ca_path.len > 0) {
                if (shm_str_dup(&query_params.tls_ca_path, &(aq->query_params.tls_ca_path)) < 0) {
                        LM_ERR("Error allocating query_params.tls_ca_path\n");
-                       return;
+                       goto done;
                }
        }
 
@@ -275,9 +277,35 @@ void notification_socket_cb(int fd, short event, void *arg)
        if (aq->query_params.body.s && aq->query_params.body.len > 0) {
                if (shm_str_dup(&query_params.body, &(aq->query_params.body)) < 0) {
                        LM_ERR("Error allocating query_params.body\n");
-                       return;
+                       goto done;
                }
        }
+  
+       if (aq->query_params.username) {
+               len = strlen(aq->query_params.username);
+               query_params.username = shm_malloc(len+1);
+       
+               if(query_params.username == NULL) {
+                       LM_ERR("error in shm_malloc\n");
+                       goto done;
+               }
+
+               strncpy(query_params.username, aq->query_params.username, len);
+               query_params.username[len] = '\0';
+       }
+       
+       if (aq->query_params.password) {
+               len = strlen(aq->query_params.password);
+               query_params.password = shm_malloc(len+1);
+       
+               if(query_params.password == NULL) {
+                       LM_ERR("error in shm_malloc\n");
+                       goto done;
+               }
+
+               strncpy(query_params.password, aq->query_params.password, len);
+               query_params.password[len] = '\0';
+       }
 
        LM_DBG("query received: [%.*s] (%p)\n", query.len, query.s, aq);
 
@@ -286,6 +314,7 @@ void notification_socket_cb(int fd, short event, void *arg)
                free_async_query(aq);
        }
 
+done:
        if (query_params.tls_client_cert.s && query_params.tls_client_cert.len > 0) {
                shm_free(query_params.tls_client_cert.s);
                query_params.tls_client_cert.s = NULL;
@@ -307,6 +336,16 @@ void notification_socket_cb(int fd, short event, void *arg)
                query_params.body.len = 0;
        }
 
+       if (query_params.username) {
+               shm_free(query_params.username);
+               query_params.username = NULL;
+       }
+       
+       if (query_params.password) {
+               shm_free(query_params.password);
+               query_params.password = NULL;
+       }
+
        return;
 }
 
@@ -324,6 +363,7 @@ int async_send_query(sip_msg_t *msg, str *query, cfg_action_t *act)
        unsigned int tlabel = 0;
        short suspend = 0;
        int dsize;
+       int len;
        tm_cell_t *t = 0;
 
        if(query==0) {
@@ -375,7 +415,8 @@ int async_send_query(sip_msg_t *msg, str *query, cfg_action_t *act)
        aq->query_params.timeout = ah_params.timeout;
        aq->query_params.headers = ah_params.headers;
        aq->query_params.method = ah_params.method;
-
+       aq->query_params.authmethod = ah_params.authmethod;
+       
        q_idx++;
        snprintf(q_id, MAX_ID_LEN+1, "%u-%u", (unsigned int)getpid(), q_idx);
        strncpy(aq->id, q_id, strlen(q_id));
@@ -416,6 +457,34 @@ int async_send_query(sip_msg_t *msg, str *query, cfg_action_t *act)
                }
        }
 
+       aq->query_params.username = NULL;
+       if (ah_params.username) {
+               len = strlen(ah_params.username);
+               aq->query_params.username = shm_malloc(len+1);
+       
+               if(aq->query_params.username == NULL) {
+                       LM_ERR("error in shm_malloc\n");
+                       goto error;
+               }
+
+               strncpy(aq->query_params.username, ah_params.username, len);
+               aq->query_params.username[len] = '\0';
+       }
+
+       aq->query_params.password = NULL;
+       if (ah_params.password) {
+               len = strlen(ah_params.password);
+               aq->query_params.password = shm_malloc(len+1);
+       
+               if(aq->query_params.password == NULL) {
+                       LM_ERR("error in shm_malloc\n");
+                       goto error;
+               }
+
+               strncpy(aq->query_params.password, ah_params.password, len);
+               aq->query_params.password[len] = '\0';
+       }
+
        set_query_params(&ah_params);
 
        if(async_push_query(aq)<0) {
@@ -473,6 +542,7 @@ void set_query_params(struct query_params *p) {
        p->suspend_transaction = 1;
        p->timeout = http_timeout;
        p->method = AH_METH_DEFAULT;
+       p->authmethod = default_authmethod;
 
        if (p->tls_client_cert.s && p->tls_client_cert.len > 0) {
                shm_free(p->tls_client_cert.s);
@@ -515,6 +585,16 @@ void set_query_params(struct query_params *p) {
                p->body.s = NULL;
                p->body.len = 0;
        }
+       
+       if (p->username) {
+               shm_free(p->username);
+               p->username = NULL;
+       }
+       
+       if (p->password) {
+               shm_free(p->password);
+               p->password = NULL;
+       }
 }
 
 int header_list_add(struct header_list *hl, str* hdr) {
index 862ee2b..856e5d0 100644 (file)
@@ -59,6 +59,7 @@ extern str tls_client_cert;
 extern str tls_client_key;
 extern str tls_ca_path;
 
+extern unsigned int default_authmethod;
 
 typedef struct async_http_worker {
        int notication_socket[2];
@@ -92,6 +93,10 @@ struct query_params {
        str tls_client_key;
        str tls_ca_path;
        str body;
+
+       unsigned int authmethod;
+       char* username;
+       char* password;
 };
 
 extern struct query_params ah_params;
@@ -162,6 +167,16 @@ static inline void free_async_query(async_query_t *aq)
                aq->query_params.body.len = 0;
        }
 
+       if (aq->query_params.username) {
+               shm_free(aq->query_params.username);
+               aq->query_params.username = NULL;
+       }
+
+       if (aq->query_params.password) {
+               shm_free(aq->query_params.password);
+               aq->query_params.password = NULL;
+       }
+
        shm_free(aq);
 }
 
index 9a2c5a1..9e9cd2b 100644 (file)
@@ -589,6 +589,25 @@ xlog("L_INFO", "received reply for query $http_query_id\n");
                <listitem><para><emphasis>tls_client_cert</emphasis>: sets the client certificate to use (see <emphasis>http_set_tls_client_cert()</emphasis>)</para></listitem>
                <listitem><para><emphasis>tls_client_key</emphasis>: sets the client certificate key to use (see <emphasis>http_set_tls_client_key()</emphasis>)</para></listitem>
                <listitem><para><emphasis>tls_ca_path</emphasis>: sets the CA certificate path to use (see <emphasis>http_set_tls_ca_path()</emphasis>)</para></listitem>
+               <listitem><para><emphasis>authmethod</emphasis>: 
+                       Sets the preferred authentication mode for HTTP/HTTPS requests. The value is a bitmap
+                        and multiple methods can be used. Note that in this case, the CURL library will make an
+                        extra request to discover server-supported authentication methods. You may want to use
+                        a specific value.
+                        <para>
+                        Valid values are:
+                                <itemizedlist>
+                                <listitem><para>1 - BASIC authentication</para></listitem>
+                                <listitem><para>2 - HTTP Digest authentication</para></listitem>
+                                <listitem><para>4 - GSS-Negotiate authentication</para></listitem>
+                                <listitem><para>8 - NTLM authentication</para></listitem>
+                                <listitem><para>16 - HTTP Digest with IE flavour</para></listitem>
+                                </itemizedlist>
+                        Default value is 3 - BASIC and Digest authentication.
+                        </para>        
+               </para></listitem>
+               <listitem><para><emphasis>username</emphasis>: sets the username to use for authenticated requests</para></listitem>
+               <listitem><para><emphasis>password</emphasis>: sets the password to use for authenticated requests</para></listitem>
                <listitem><para><emphasis>suspend</emphasis>: if set to 0 it doesn't suspend the current transaction before performing the query (see <emphasis>http_async_suspend()</emphasis>)</para></listitem>
        </itemizedlist>
        <example>
@@ -659,5 +678,18 @@ http_async_query("https://example.com/test.php", "HTTP_REPLY");
        </section>
 
 </section>
+
+       <section id="http_async_client.s.remarks">
+               <title>Remarks</title>
+               <para>
+                       Note: libcurl leak in CentOS 6 - this module uses libcurl library
+                       and in case if you are using CentOS 6, be aware that standard
+                       libcurl-7.19.7-52 has a memory leak. To fix this memory, install
+                       libcurl from city-fan repository. More details at:
+                       <ulink url="https://www.digitalocean.com/community/questions/how-to-upgrade-curl-in-centos6">
+                               https://www.digitalocean.com/community/questions/how-to-upgrade-curl-in-centos6</ulink>
+               </para>
+       </section>
+
 </chapter>
 
index 6dd44ff..162605b 100644 (file)
@@ -74,6 +74,10 @@ typedef struct hm_params {
        str tls_client_key;
        str tls_ca_path;
        str body;
+       
+       unsigned int authmethod;
+       char* username;
+       char* password;
 } http_m_params_t;
 
 typedef struct http_m_cell
index 93389cd..0412a47 100644 (file)
@@ -49,7 +49,7 @@
 #include "../../mod_fix.h"
 #include "../../pvar.h"
 #include "../../cfg/cfg_struct.h"
-#include "../../lib/kcore/faked_msg.h"
+#include "../../fmsg.h"
 
 #include "../../modules/tm/tm_load.h"
 #include "../../modules/pv/pv_api.h"
@@ -75,6 +75,7 @@ str tls_client_key = STR_STATIC_INIT(""); // client SSL certificate key path, de
 str tls_ca_path = STR_STATIC_INIT(""); // certificate authority dir path, defaults to NULL
 static char *memory_manager = "shm";
 extern int curl_memory_manager;
+unsigned int default_authmethod = CURLAUTH_BASIC | CURLAUTH_DIGEST;
 
 static int  mod_init(void);
 static int  child_init(int);
@@ -128,7 +129,8 @@ enum http_req_name_t {
        E_HRN_HDR, E_HRN_METHOD, E_HRN_TIMEOUT,
        E_HRN_TLS_CA_PATH, E_HRN_TLS_CLIENT_KEY,
        E_HRN_TLS_CLIENT_CERT, E_HRN_SUSPEND,
-       E_HRN_BODY
+       E_HRN_BODY, E_HRN_AUTHMETHOD, E_HRN_USERNAME,
+       E_HRN_PASSWORD
 };
 
 static cmd_export_t cmds[]={
@@ -156,17 +158,18 @@ static cmd_export_t cmds[]={
 };
 
 static param_export_t params[]={
-{"workers",                                    INT_PARAM,   &num_workers},
-       {"connection_timeout",  INT_PARAM,   &http_timeout},
-       {"hash_size",                   INT_PARAM,   &hash_size},
-       {"tls_version",                 INT_PARAM,   &tls_version},
-       {"tls_verify_host",             INT_PARAM,   &tls_verify_host},
-       {"tls_verify_peer",             INT_PARAM,   &tls_verify_peer},
-       {"curl_verbose",                INT_PARAM,   &curl_verbose},
-       {"tls_client_cert",             PARAM_STR,   &tls_client_cert},
-       {"tls_client_key",              PARAM_STR,   &tls_client_key},
-       {"tls_ca_path",                 PARAM_STR,   &tls_ca_path},
-       {"memory_manager",      PARAM_STRING,&memory_manager},
+       {"workers",                             INT_PARAM,              &num_workers},
+       {"connection_timeout",  INT_PARAM,              &http_timeout},
+       {"hash_size",                   INT_PARAM,              &hash_size},
+       {"tls_version",                 INT_PARAM,              &tls_version},
+       {"tls_verify_host",             INT_PARAM,              &tls_verify_host},
+       {"tls_verify_peer",             INT_PARAM,              &tls_verify_peer},
+       {"curl_verbose",                INT_PARAM,              &curl_verbose},
+       {"tls_client_cert",             PARAM_STR,              &tls_client_cert},
+       {"tls_client_key",              PARAM_STR,              &tls_client_key},
+       {"tls_ca_path",                 PARAM_STR,              &tls_ca_path},
+       {"memory_manager",              PARAM_STRING,   &memory_manager},
+       {"authmethod",                  PARAM_INT,              &default_authmethod },
        {0, 0, 0}
 };
 
@@ -573,6 +576,33 @@ static int set_query_param(str* param, str input)
        return 1;
 }
 
+/*
+ * Helper to copy input string parameter into a query char* parameter
+ */
+static int set_query_cparam(char** param, str input)
+{
+       if (*param) {
+               shm_free(*param);
+               *param = NULL;
+       }
+
+       if (input.s && input.len > 0) {
+               *param = shm_malloc(input.len+1);
+       
+               if(*param == NULL) {
+                       LM_ERR("error in shm_malloc\n");
+                       return -1;
+               }
+
+               strncpy(*param, input.s, input.len);
+               (*param)[input.len] = '\0';
+               
+               LM_DBG("param set to '%s'\n", *param);
+       }
+
+       return 1;
+}
+
 /**
  *
  */
@@ -678,6 +708,18 @@ static int ah_parse_req_name(pv_spec_p sp, str *in) {
                                sp->pvp.pvn.u.isname.name.n = E_HRN_SUSPEND;
                        else goto error;
                        break;
+               case 8:
+                       if(strncmp(in->s, "username", 8)==0)
+                               sp->pvp.pvn.u.isname.name.n = E_HRN_USERNAME;
+                       else if(strncmp(in->s, "password", 8)==0)
+                               sp->pvp.pvn.u.isname.name.n = E_HRN_PASSWORD;
+                       else goto error;
+                       break;
+               case 10:
+                       if(strncmp(in->s, "authmethod", 10)==0)
+                               sp->pvp.pvn.u.isname.name.n = E_HRN_AUTHMETHOD;
+                       else goto error;
+                       break;
                case 11:
                        if(strncmp(in->s, "tls_ca_path", 11)==0)
                                sp->pvp.pvn.u.isname.name.n = E_HRN_TLS_CA_PATH;
@@ -808,6 +850,35 @@ static int ah_set_req(struct sip_msg* msg, pv_param_t *param,
                        set_query_param(&ah_params.body, tval->rs);
                }
                break;
+       case E_HRN_AUTHMETHOD:
+               if (tval) {
+                       if (!(tval->flags & PV_VAL_INT)) {
+                               LM_ERR("invalid value type for $http_req(authmethod)\n");
+                               return -1;
+                       }
+                       ah_params.authmethod = tval->ri;
+               } else {
+                       ah_params.authmethod = default_authmethod;
+               }
+               break;
+       case E_HRN_USERNAME:
+               if (tval) {
+                       if (!(tval->flags & PV_VAL_STR)) {
+                               LM_ERR("invalid value type for $http_req(username)\n");
+                               return -1;
+                       }
+                       set_query_cparam(&ah_params.username, tval->rs);
+               }
+               break;
+       case E_HRN_PASSWORD:
+               if (tval) {
+                       if (!(tval->flags & PV_VAL_STR)) {
+                               LM_ERR("invalid value type for $http_req(password)\n");
+                               return -1;
+                       }
+                       set_query_cparam(&ah_params.password, tval->rs);
+               }
+               break;
        }
 
        return 1;
index 6704c85..c4781b6 100644 (file)
@@ -511,6 +511,18 @@ int new_request(str *query, http_m_params_t *query_params, http_multi_cbe_t cb,
        default:
                break;
        }
+
+       if (cell->params.username) {
+               curl_easy_setopt(cell->easy, CURLOPT_USERNAME, cell->params.username);
+               curl_easy_setopt(cell->easy, CURLOPT_HTTPAUTH, cell->params.authmethod);
+
+               LM_DBG("set username to %s [authmethod %u]\n", cell->params.username, cell->params.authmethod);
+       }
+
+       if (cell->params.password) {
+               curl_easy_setopt(cell->easy, CURLOPT_PASSWORD, cell->params.password);
+       }
+
        LM_DBG("Adding easy %p to multi %p (%.*s)\n", cell->easy, g->multi, query->len, query->s);
        rc = curl_multi_add_handle(g->multi, cell->easy);
        if (check_mcode(rc, cell->error) < 0) {
index 9575b0a..15414b7 100644 (file)
@@ -14,7 +14,7 @@
 #include "../../lib/kcore/statistics.h"
 #include "../../action.h"
 #include "../../script_cb.h"
-#include "../../lib/kcore/faked_msg.h"
+#include "../../fmsg.h"
 #include "../../parser/parse_from.h"
 #include "../../parser/parse_cseq.h"
 #include "../../parser/contact/parse_contact.h"
index 81b3327..abb73ec 100644 (file)
@@ -13,7 +13,7 @@
 #include "../../pvar.h"
 #include "../../mod_fix.h"
 #include "../../script_cb.h"
-#include "../../lib/kcore/faked_msg.h"
+#include "../../fmsg.h"
 #include "../../lib/kcore/kstats_wrapper.h"
 #include "../../mem/mem.h"
 #include "../../lib/kmi/mi.h"
diff --git a/modules/ims_ocs/Makefile b/modules/ims_ocs/Makefile
new file mode 100644 (file)
index 0000000..3bd1003
--- /dev/null
@@ -0,0 +1,22 @@
+#
+# ims_qos make file
+#
+# 
+
+include ../../Makefile.defs
+auto_gen=
+NAME=ims_ocs.so
+LIBS=
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+SER_LIBS+=$(SERLIBPATH)/ims/kamailio_ims
+
+ifneq ($(OS),darwin)
+       LIBS += -lrt
+       LIBS += -lpthread
+endif
+
+include ../../Makefile.modules
diff --git a/modules/ims_ocs/README b/modules/ims_ocs/README
new file mode 100644 (file)
index 0000000..f6bd564
--- /dev/null
@@ -0,0 +1,221 @@
+IMS Online Charging Server (OCS) Module
+
+Carsten Bock
+
+   ng-voice GmbH
+
+   Copyright © 2016 ng-voice GmbH
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Faked Messages
+
+              3.1. Translating SIP to Diameter
+              3.2. SIP-Information in the faked messages
+
+        4. Functions
+
+              4.1. ccr_result(resultcode, grantedunits, finalunit)
+
+        5. Event routes
+
+              5.1. ocs:ccr-orig
+              5.2. ocs:ccr-term
+
+   List of Examples
+
+   1.1. ds_select_dst usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Faked Messages
+
+        3.1. Translating SIP to Diameter
+        3.2. SIP-Information in the faked messages
+
+   4. Functions
+
+        4.1. ccr_result(resultcode, grantedunits, finalunit)
+
+   5. Event routes
+
+        5.1. ocs:ccr-orig
+        5.2. ocs:ccr-term
+
+1. Overview
+
+   This module provides a simple Online Charging Server Module for working
+   with the ims_charging module. It communicates with the ims_charging
+   module via the Diameter-Ro Interface.
+
+   This module is dependent on the CDP (C Diameter Peer) modules for
+   communicating with a Charging-Server as specified in 3GPP specification
+   TS xx.xxx.
+
+   Please also refer to RFC 4006 (Diameter Credit-Control Application).
+
+   The module works will create fake SIP messages and provide them to an
+   event route for further operations. It up to the script writer to do
+   the processing.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The Following mouldes must be loaded before this module:
+     * CDP - C Diameter Peer
+     * CDP_AVP - CDP AVP Applications
+
+2.2. External Libraries or Applications
+
+   No external libraries are required.
+
+3. Faked Messages
+
+   3.1. Translating SIP to Diameter
+   3.2. SIP-Information in the faked messages
+
+3.1. Translating SIP to Diameter
+
+   The incoming Charging requests are translated into the following
+   methods:
+     * INVITE - For "Start" Charging Requests
+     * UPDATE - For "Interim" Charging Requests
+     * BYE - For "Stop" Charging Requests
+
+3.2. SIP-Information in the faked messages
+
+   The faked messages contain the following information:
+     * Method - See previous section
+     * Request-URI - The dialed number of the call
+     * From-Header - The originator of the session
+     * To-Header - Same as request-URI
+     * Call-ID - The Diameter-Charging-ID (not the SIP-Call-ID)
+     * P-Requested-Units - The requested units for this call
+     * P-Used-Units - The used units for this call (only useful for
+       Interim and Stop Events)
+     * P-Access-Network-Info - The used access network - if available
+     * P-Service-Identifier - The service identifier, if you want to
+       diffentiate different services (e.g. Audio and Video)
+
+4. Functions
+
+   4.1. ccr_result(resultcode, grantedunits, finalunit)
+
+4.1. ccr_result(resultcode, grantedunits, finalunit)
+
+   This method sets the response code of the Diameter Request.
+
+   Meaning of the parameters is as follows:
+     * resultcode - the Diameter Response code for the request. Typical
+       response codes are:
+          + "2001" - Ok
+          + "5030" - User unknown
+          + "5031" - Rating failed
+          + "4010" - End-User Service denied (e.g. Service blocked)
+          + "5006" - Ressources exceeded (e.g. too many concurrent calls)
+     * grantedunits - the number of granted units for this particular user
+     * finalunit - indication, that all following requests will be denied
+       (this is the final unit for the session)
+
+   This function can be used from the event route.
+
+   Example 1.1. ds_select_dst usage
+...
+ccr_result("2001", "600", "0");
+...
+$var(result) = 2001;
+$var(granted) = $hdr(P-Requested-Units);
+$var(final) = 0;
+ccr_result("$var(result)", "$var(granted)", "$var(final)");
+...
+ccr_result("2001", "$hdr(P-Requested-Units)", "0");
+...
+
+5. Event routes
+
+   5.1. ocs:ccr-orig
+   5.2. ocs:ccr-term
+
+5.1. ocs:ccr-orig
+
+   This route is called for Charging Requests with the session-case
+   "originating" - a call from a user to another destination.
+...
+event_route[ocs:ccr-orig] {
+        xlog("Session-Case: Originating\n");
+        xlog("----------------------------------------\n");
+        if (is_method("INVITE")) {
+                xlog("START - Request\n");
+        } else if (is_method("UPDATE")) {
+                xlog("INTERIM - Request\n");
+        } else if (is_method("BYE")) {
+                xlog("STOP - Request\n");
+        }
+        xlog("----------------------------------------\n");
+        xlog("From:               $fu\n");
+        xlog("To:                 $ru\n");
+        xlog("Call-ID:            $ci\n");
+        xlog("Requested Units:    $hdr(P-Requested-Units)\n");
+        xlog("Used Units:         $hdr(P-Used-Units)\n");
+        xlog("Access Network:     $hdr(P-Access-Network-Info)\n");
+        xlog("Service Identifier: $hdr(P-Service-Identifier)\n");
+
+        ccr_result("2001", "600", "0");
+}
+...
+
+5.2. ocs:ccr-term
+
+   This route is called for Charging Requests with the session-case
+   "terminating" - a call to a user from another destination.
+
+   You can have an "originating" and a "terminating" request for a single
+   call, e.g. if a user calls another user. Since the Diameter-Session-ID
+   is provided as "Call-ID", it will have a different Call-ID, since it is
+   logical two separate sessions.
+
+   This route is optional.
+...
+event_route[ocs:ccr-term] {
+        xlog("Session-Case: Terminating\n");
+        xlog("----------------------------------------\n");
+        if (is_method("INVITE")) {
+                xlog("START - Request\n");
+        } else if (is_method("UPDATE")) {
+                xlog("INTERIM - Request\n");
+        } else if (is_method("BYE")) {
+                xlog("STOP - Request\n");
+        }
+        xlog("----------------------------------------\n");
+        xlog("From:               $fu\n");
+        xlog("To:                 $ru\n");
+        xlog("Call-ID:            $ci\n");
+        xlog("Requested Units:    $hdr(P-Requested-Units)\n");
+        xlog("Used Units:         $hdr(P-Used-Units)\n");
+        xlog("Access Network:     $hdr(P-Access-Network-Info)\n");
+        xlog("Service Identifier: $hdr(P-Service-Identifier)\n");
+
+        ccr_result("2001", "600", "0");
+}
+...
diff --git a/modules/ims_ocs/doc/Makefile b/modules/ims_ocs/doc/Makefile
new file mode 100644 (file)
index 0000000..f1d515c
--- /dev/null
@@ -0,0 +1,4 @@
+docs = ims_ocs.xml
+
+docbook_dir=../../../docbook
+include $(docbook_dir)/Makefile.module
diff --git a/modules/ims_ocs/doc/ims_ocs.xml b/modules/ims_ocs/doc/ims_ocs.xml
new file mode 100644 (file)
index 0000000..d77e34d
--- /dev/null
@@ -0,0 +1,286 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<book id="print" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+        <title>IMS Online Charging Server (OCS) Module</title>
+       <authorgroup>
+           <author>
+               <firstname>Carsten</firstname>
+               <surname>Bock</surname>
+               <affiliation><orgname>ng-voice GmbH</orgname></affiliation>
+               <address>
+                   <email>carsten@ng-voice.com</email>
+               </address>
+           </author>
+       </authorgroup>
+       <copyright>
+           <year>2016</year>
+           <holder>ng-voice GmbH</holder>
+       </copyright>
+    </bookinfo>
+    <toc></toc>
+
+    <chapter>
+       <title>Admin Guide</title>
+    <section id="ims_ocs.overview">
+       <title>Overview</title>
+       <para>
+           This module provides a simple Online Charging Server Module for
+            working with the ims_charging module. It communicates with the 
+            ims_charging module via the Diameter-Ro Interface.
+       </para>
+       <para>
+            This module is dependent on the CDP (C Diameter Peer) modules for 
+            communicating with a Charging-Server as specified in 3GPP
+            specification TS xx.xxx.
+       </para>
+       <para>
+            Please also refer to RFC 4006 (Diameter Credit-Control Application).
+       </para>
+       <para>
+            The module works will create fake SIP messages and provide them
+            to an event route for further operations. It up to the script writer
+            to do the processing.
+       </para>
+    </section>
+
+  <section id="ims_ocs.dependencies">
+    <title>Dependencies</title>
+
+    <section>
+      <title>Kamailio Modules</title>
+
+      <para>The Following mouldes must be loaded before this module:</para>
+
+      <itemizedlist>
+        <listitem>
+          <para>CDP - C Diameter Peer</para>
+        </listitem>
+        <listitem>
+          <para>CDP_AVP - CDP AVP Applications</para>
+        </listitem>
+      </itemizedlist>
+    </section>
+    <section>
+      <title>External Libraries or Applications</title>
+      <para>No external libraries are required.</para>
+    </section>
+    </section>
+
+  <section id="ims_ocs.functionalities">
+    <title>Faked Messages</title>
+    <section id="ims_ocs.diameter2sip">
+      <title>Translating SIP to Diameter</title>
+      <para>The incoming Charging requests are translated into the following methods:</para>
+      <itemizedlist>
+        <listitem>
+          <para>INVITE - For "Start" Charging Requests</para>
+        </listitem>
+        <listitem>
+          <para>UPDATE - For "Interim" Charging Requests</para>
+        </listitem>
+        <listitem>
+          <para>BYE - For "Stop" Charging Requests</para>
+        </listitem>
+      </itemizedlist>
+    </section>
+    <section id="ims_ocs.sipheaders">
+      <title>SIP-Information in the faked messages</title>
+      <para>The faked messages contain the following information:</para>
+      <itemizedlist>
+        <listitem>
+          <para>Method - See previous section</para>
+        </listitem>
+        <listitem>
+          <para>Request-URI - The dialed number of the call</para>
+        </listitem>
+        <listitem>
+          <para>From-Header - The originator of the session</para>
+        </listitem>
+        <listitem>
+          <para>To-Header - Same as request-URI</para>
+        </listitem>
+        <listitem>
+          <para>Call-ID - The Diameter-Charging-ID (not the SIP-Call-ID)</para>
+        </listitem>
+        <listitem>
+          <para>P-Requested-Units - The requested units for this call</para>
+        </listitem>
+        <listitem>
+          <para>P-Used-Units - The used units for this call (only useful for Interim and Stop Events)</para>
+        </listitem>
+        <listitem>
+          <para>P-Access-Network-Info - The used access network - if available</para>
+        </listitem>
+        <listitem>
+          <para>P-Service-Identifier - The service identifier, if you want to diffentiate different services (e.g. Audio and Video)</para>
+        </listitem>
+      </itemizedlist>
+    </section>
+  </section>
+
+
+       <section>
+       <title>Functions</title>
+       <section id="ims_ocs.f.ccr_result">
+               <title>
+               <function moreinfo="none">ccr_result(resultcode, grantedunits, finalunit)</function>
+               </title>
+               <para>
+               This method sets the response code of the Diameter Request.
+               </para>
+               <para>Meaning of the parameters is as follows:</para>
+               <itemizedlist>
+               <listitem>
+                       <para>
+                       <emphasis>resultcode</emphasis> - the Diameter Response
+                       code for the request. Typical response codes are:       
+                       </para>
+                       <itemizedlist>
+                       <listitem>
+                               <para>
+                               <quote>2001</quote> - Ok
+                               </para>
+                       </listitem>
+                       <listitem>
+                               <para>
+                               <quote>5030</quote> - User unknown
+                               </para>
+                       </listitem>
+                       <listitem>
+                               <para>
+                               <quote>5031</quote> - Rating failed
+                               </para>
+                       </listitem>
+                       <listitem>
+                               <para>
+                               <quote>4010</quote> - End-User Service denied (e.g. Service blocked)
+                               </para>
+                       </listitem>
+                       <listitem>
+                               <para>
+                               <quote>5006</quote> - Ressources exceeded (e.g. too many concurrent calls)
+                               </para>
+                       </listitem>
+                       </itemizedlist>
+               </listitem>
+               <listitem>
+                       <para>
+                       <emphasis>grantedunits</emphasis> - the number of granted units for this particular user
+                       </para>
+               </listitem>
+               <listitem>
+                       <para>
+                       <emphasis>finalunit</emphasis> - indication, that all following requests will be denied (this is the final unit for the session)
+                        </para>
+                </listitem>
+               </itemizedlist>
+               <para>
+               This function can be used from the event route.
+               </para>
+               <example>
+               <title><function>ds_select_dst</function> usage</title>
+               <programlisting format="linespecific">
+...
+ccr_result("2001", "600", "0");
+...
+$var(result) = 2001;
+$var(granted) = $hdr(P-Requested-Units);
+$var(final) = 0;
+ccr_result("$var(result)", "$var(granted)", "$var(final)");
+...
+ccr_result("2001", "$hdr(P-Requested-Units)", "0");
+...
+</programlisting>
+               </example>
+       </section>
+       </section>
+
+        <section id="ims_ocs.ex.event_routes">
+        <title>Event routes</title>
+        <section>
+                <title>
+                <function moreinfo="none">ocs:ccr-orig</function>
+                </title>
+                <para>
+                       This route is called for Charging Requests with the
+                       session-case "originating" - a call from a user to
+                       another destination.
+                </para>
+        <programlisting  format="linespecific">
+...
+event_route[ocs:ccr-orig] {
+       xlog("Session-Case: Originating\n");
+       xlog("----------------------------------------\n");
+       if (is_method("INVITE")) {
+               xlog("START - Request\n");
+       } else if (is_method("UPDATE")) {
+               xlog("INTERIM - Request\n");
+       } else if (is_method("BYE")) {
+               xlog("STOP - Request\n");
+       }
+       xlog("----------------------------------------\n");
+       xlog("From:               $fu\n");
+       xlog("To:                 $ru\n");
+       xlog("Call-ID:            $ci\n");
+       xlog("Requested Units:    $hdr(P-Requested-Units)\n");
+       xlog("Used Units:         $hdr(P-Used-Units)\n");
+       xlog("Access Network:     $hdr(P-Access-Network-Info)\n");
+       xlog("Service Identifier: $hdr(P-Service-Identifier)\n");
+
+       ccr_result("2001", "600", "0");
+}
+...
+                </programlisting>
+       </section>
+        <section>
+                <title>
+                <function moreinfo="none">ocs:ccr-term</function>
+                </title>
+                <para>
+                       This route is called for Charging Requests with the
+                       session-case "terminating" - a call to a user from
+                       another destination.
+                </para>
+                <para>
+                       You can have an "originating" and a "terminating"
+                       request for a single call, e.g. if a user calls another
+                       user. Since the Diameter-Session-ID is provided as
+                       "Call-ID", it will have a different Call-ID, since it
+                       is logical two separate sessions.
+                </para>
+                <para>
+                       This route is optional.
+                </para>
+        <programlisting  format="linespecific">
+...
+event_route[ocs:ccr-term] {
+       xlog("Session-Case: Terminating\n");
+       xlog("----------------------------------------\n");
+       if (is_method("INVITE")) {
+               xlog("START - Request\n");
+       } else if (is_method("UPDATE")) {
+               xlog("INTERIM - Request\n");
+       } else if (is_method("BYE")) {
+               xlog("STOP - Request\n");
+       }
+       xlog("----------------------------------------\n");
+       xlog("From:               $fu\n");
+       xlog("To:                 $ru\n");
+       xlog("Call-ID:            $ci\n");
+       xlog("Requested Units:    $hdr(P-Requested-Units)\n");
+       xlog("Used Units:         $hdr(P-Used-Units)\n");
+       xlog("Access Network:     $hdr(P-Access-Network-Info)\n");
+       xlog("Service Identifier: $hdr(P-Service-Identifier)\n");
+
+       ccr_result("2001", "600", "0");
+}
+...
+                </programlisting>
+    </section>
+    </section>
+    </chapter>
+</book>
+
diff --git a/modules/ims_ocs/examples/full/kamailio.cfg b/modules/ims_ocs/examples/full/kamailio.cfg
new file mode 100644 (file)
index 0000000..fdb3c96
--- /dev/null
@@ -0,0 +1,617 @@
+#!KAMAILIO
+#
+# This config file implements an Online-Charging-Server
+#     - web: http://www.kamailio.org
+#     - git: http://sip-router.org
+#
+# Refer to the Core CookBook at http://www.kamailio.org/dokuwiki/doku.php
+# for an explanation of possible statements, functions and parameters.
+#
+# Direct your questions about this file to: <sr-users@lists.sip-router.org>.
+#
+# For more information about the various parameters, functions and statements
+# try http://sip-router.org/wiki/ .
+#
+
+import_file "ocs.cfg"
+
+#!ifndef GRANT
+#!define GRANT 600
+#!endif
+#!ifndef MULTIPLIER
+#!define MULTIPLIER 10000
+#!endif
+
+##!define WITH_DEBUG
+
+#!ifdef WITH_XMLRPC
+listen=tcp:127.0.0.1:5080
+#!endif
+
+
+####### Defined Values #########
+# *** Value defines - IDs used later in config
+
+# - flags
+#      FLT_ - per transaction (message) flags
+#      FLB_ - per branch flags
+
+
+####### Global Parameters #########
+
+#!ifdef WITH_DEBUG
+debug=4
+log_stderror=yes
+#!else
+debug=0
+log_stderror=no
+#!endif
+
+memdbg=5
+memlog=5
+
+log_stderror=no
+sip_warning=no
+
+rundir="/var/run/kamailio_ocs"
+
+user_agent_header="User-Agent: TelcoSuite OCS"
+server_header="Server: TelcoSuite OCS"
+
+/* comment the next line to enable the auto discovery of local aliases
+   based on reverse DNS on IPs (default on) */
+auto_aliases=no
+
+check_via=no    # (cmd. line: -v)
+dns=no          # (cmd. line: -r)
+rev_dns=no      # (cmd. line: -R)
+
+# Do SRV-Loadbalancing:
+dns_srv_lb=no
+# Always: Also try IPv6:
+dns_try_ipv6=no
+# Always prefer IPv6:
+dns_cache_flags=6
+# DNS-Based failover
+use_dns_failover = on
+# Query NAPTR-Records as well:
+dns_try_naptr=no
+
+#!ifdef WITH_XMLRPC
+tcp_accept_no_cl=yes
+tcp_rd_buf_size=16384
+tcp_children=3
+#!else
+disable_tcp=yes
+#!endif
+
+children=3
+log_name="[Charging]"
+
+system.shutdownmode = 0 desc "System shutdown mode"
+system.service = "Online-Charging-Server" desc "Function of this server"
+
+# ------------------ module loading ----------------------------------
+mpath="/usr/lib64/kamailio/modules_k/:/usr/lib64/kamailio/modules/:/usr/local/lib/kamailio/modules/"
+# (we try both the lib64 and the lib directory)
+
+loadmodule "cdp"
+loadmodule "cdp_avp"
+loadmodule "ims_ocs"
+loadmodule "xlog"
+loadmodule "pv"
+loadmodule "sqlops"
+loadmodule "cfgutils"
+loadmodule "db_mysql"
+loadmodule "textops"
+loadmodule "ctl"
+loadmodule "sl"
+loadmodule "tm"
+loadmodule "kex"
+loadmodule "corex"
+loadmodule "cfg_rpc"
+loadmodule "mi_rpc"
+
+# ----- db_cluster params -----
+
+# ----- ctl params -----
+modparam("ctl", "binrpc", "unix:/var/run/kamailio_ocs/kamailio_ctl")
+
+#!ifdef DB_URL2
+loadmodule "db_cluster"
+# ----- db_cluster params -----
+modparam("db_cluster", "connection", DB_URL)
+modparam("db_cluster", "connection", DB_URL2)
+modparam("db_cluster", "cluster", "cluster1=>con1=2s2s;con2=1s1s")
+#!endif
+
+#!ifdef WITH_DEBUG
+loadmodule "debugger"
+modparam("debugger", "mod_hash_size", 5)
+modparam("debugger", "mod_level_mode", 1)
+modparam("debugger", "mod_level", "ims_ocs=3")
+#!endif
+
+#!ifdef WITH_DMQ
+loadmodule "dmq"
+# ----- dmq params -----
+modparam("dmq", "server_address", DMQ_SERVER_ADDRESS)
+modparam("dmq", "notification_address", DMQ_NOTIFICATION_ADDRESS)
+#!endif
+
+loadmodule "htable"
+# ----- htable params -----
+
+#!ifdef WITH_DMQ
+modparam("htable", "enable_dmq", 1)
+# Per User:
+modparam("htable", "htable", "user=>size=16;autoexpire=14400;dmqreplicate=1")
+# Per Call:
+modparam("htable", "htable", "call=>size=16;autoexpire=14400;dmqreplicate=1")
+# Association: Calls/User
+modparam("htable", "htable", "cid=>size=8;autoexpire=14400;dmqreplicate=1")
+#!else
+modparam("htable", "enable_dmq", 0)
+# Per User:
+modparam("htable", "htable", "user=>size=16;autoexpire=14400")
+# Per Call:
+modparam("htable", "htable", "call=>size=16;autoexpire=14400")
+# Association: Calls/User
+modparam("htable", "htable", "cid=>size=8;autoexpire=14400")
+#!endif
+
+# ----- cdp params -----
+modparam("cdp","config_file","/etc/kamailio_ocs/ocs.xml")
+
+# ----- sqlops params -----
+#!ifdef DB_URL2
+modparam("sqlops","sqlcon", "hss_db=>cluster://cluster1")
+#!else
+modparam("sqlops","sqlcon", DB_URL)
+#!endif
+
+#!ifdef WITH_XMLRPC
+loadmodule "xmlrpc"
+# ----- xmlrpc params -----
+modparam("xmlrpc", "route", "XMLRPC");
+modparam("xmlrpc", "url_match", "^/RPC")
+#!endif
+
+route {
+#!ifdef WITH_DMQ
+       if(is_method("KDMQ")) {
+                dmq_handle_message();
+        }
+#!endif
+       drop();
+       exit;
+}
+
+# XMLRPC routing
+#!ifdef WITH_XMLRPC
+route[XMLRPC] {
+       # allow XMLRPC from localhost
+       if ((method=="POST" || method=="GET") && ((src_ip==127.0.0.1)
+#!ifdef XMLRPC_WHITELIST_1
+ || (src_ip == XMLRPC_WHITELIST_1)
+#!endif
+#!ifdef XMLRPC_WHITELIST_2
+ || (src_ip == XMLRPC_WHITELIST_2)
+#!endif
+#!ifdef XMLRPC_WHITELIST_3
+ || (src_ip == XMLRPC_WHITELIST_3)
+#!endif
+        )) {
+               # close connection only for xmlrpclib user agents (there is a bug in
+               # xmlrpclib: it waits for EOF before interpreting the response).
+               if ($hdr(User-Agent) =~ "xmlrpclib")
+                       set_reply_close();
+               set_reply_no_connect();
+               dispatch_rpc();
+               exit;
+       }
+       send_reply("403", "Forbidden");
+       exit;
+}
+#!endif
+
+event_route[ocs:ccr-orig] {
+#!ifdef WITH_DEBUG
+       xlog("ORIG: $rm $fu => $ru\n\n");
+#!endif
+       $var(result) = 5012;
+       $var(granted) = 0;
+       $var(final) = 0;
+       $var(multiplier) = MULTIPLIER;
+       $var(id) = 0;
+       
+       if (is_method("INVITE")) {
+               if ($fU =~ "^\+[1-9][0-9]+$")
+                       $var(from) = $(fU{s.substr,1,0});
+               else
+                       $var(from) = $fU;
+
+               sql_pvquery("hss_db", "SELECT impi.id AS impi_id, imsu.id, CAST(imsu.credit*$var(multiplier) as UNSIGNED), imsu.rate_plan_id, imsu.payment_method, imsu.max_concurrent_calls FROM impu LEFT JOIN impi_impu ON impu.id = impi_impu.id_impu LEFT JOIN impi ON impi.id = impi_impu.id_impi LEFT JOIN imsu ON impi.id_imsu = imsu.id WHERE ((impu.type = 0 AND impu.identity='sip:$var(from)@$fd') OR (impu.type = 0 AND impu.identity='sip:+$var(from)@$fd') OR (impu.type = 0 AND impu.identity='tel:$var(from)') OR (impu.type = 0 AND impu.identity='tel:+$var(from)') OR (impu.type = 0 AND impu.identity='tel:+$var(from)') OR (impu.type = 3 AND 'sip:$var(from)@$fd' REGEXP impu.identity) OR (impu.type = 3 AND 'tel:$var(from)' REGEXP impu.identity) OR (impu.type = 3 AND 'tel:+$var(from)' REGEXP impu.identity)) limit 1;", "$var(impi_id),$var(id),$var(credit),$var(ratecard),$var(payment),$var(concurrent_calls)");
+               if ($retcode != 1) {
+                       # User not found, send DIAMETER_USER_UNKNOWN
+                       $var(result) = 5030;
+               } else {
+                       if (($var(impi_id) == 0) || ($var(id) == 0)) {
+                               # User not found, send DIAMETER_USER_UNKNOWN
+                               $var(result) = 5030;
+                       } else {
+                               # Remove "+"
+                               if ($rU =~ "^\+[1-9][0-9]+$")
+                                       strip(1);
+
+                               $var(destination_impu_id) = 0;
+                               sql_pvquery("hss_db", "SELECT id FROM impu WHERE ((type = 0 AND identity='$ru') OR (type = 0 AND identity='tel:$rU') OR (type = 3 AND '$ru' REGEXP identity) OR (type = 3 AND 'tel:$rU' REGEXP identity)) limit 1;", "$var(destination_impu_id)");
+                               if ($retcode != 1) {
+                                       $var(destination_impu_id) = 0;
+                               }
+
+
+                               sht_lock("user=>$var(id)::current_calls");
+                               if ($sht(user=>$var(id)::current_calls) != $null)
+                                       $sht(user=>$var(id)::current_calls) = $(sht(user=>$var(id)::current_calls){s.int});
+                               sht_unlock("user=>$var(id)::current_calls");
+
+                               sht_lock("user=>$var(id)::reserved_credit");
+                               if ($sht(user=>$var(id)::reserved_credit) != $null)
+                                       $sht(user=>$var(id)::reserved_credit) = $(sht(user=>$var(id)::reserved_credit){s.int});
+                               sht_unlock("user=>$var(id)::reserved_credit");
+
+
+#!ifdef WITH_DEBUG
+                               xlog("IMPI:             $fu ($var(impi_id))\n");
+                               xlog("IMSU-ID:          $var(id)\n");
+                               xlog("Credit:           $var(credit) (Reserved: $sht(user=>$var(id)::reserved_credit))\n");
+                               xlog("Rate-Card:        $var(ratecard)\n");
+                               xlog("Payment:          $var(payment)\n");
+                               xlog("Concurrent-Calls: $var(concurrent_calls), currently ($sht(user=>$var(id)::current_calls))\n");
+                               if ($var(destination_impu_id) > 0) {
+                                       xlog(">>>>>>>>>>>>>>>>>>>>>> DESTINATION ($ru) IS ONNET ($var(destination_impu_id))!\n");
+                               }
+#!endif
+                               if (($sht(user=>$var(id)::current_calls) != $null) && ($var(concurrent_calls) >= 0)) {
+                                       if ($sht(user=>$var(id)::current_calls) >= $var(concurrent_calls)) {
+                                               $var(result) = 5006; # DIAMETER_RESOURCES_EXCEEDED
+                                       } else {
+                                               $sht(cid=>$ci) = $var(id);
+                                               $var(result) = 2001;
+                                       }
+                               } else {
+                                       sht_lock("user=>$var(id)::current_calls");
+                                       if ($sht(user=>$var(id)::current_calls) == $null) {
+                                               $sht(user=>$var(id)::current_calls) = 0;
+                                       }
+                                       sht_unlock("user=>$var(id)::current_calls");
+                                       $sht(cid=>$ci) = $var(id);
+                                       $var(result) = 2001;
+                               }
+                       }
+                       if ($var(result) == "2001") {
+                               $var(result) = 5031; # DIAMETER_RATING_FAILED
+                               if ($var(destination_impu_id) > 0) {
+                                       sql_pvquery("hss_db", "SELECT id, description, country_name, iso3166, type, CAST(rate_init*$var(multiplier) as UNSIGNED), rate_init_interval, CAST(rate_follow*$var(multiplier) as UNSIGNED), rate_follow_interval, blocked, max_call_duration FROM call_rates WHERE rate_plan_id = $var(ratecard) AND destination_prefix = 'ONNET' limit 1;", "$var(rate_id),$var(rate_name),$var(country),$var(iso),$var(type),$var(rate_init),$var(rate_init_interval),$var(rate_follow),$var(rate_follow_interval),$var(blocked),$var(maxduration)");
+                                       if ($retcode == 1) {
+                                               $var(result) = 2001;
+                                       }
+                               }
+                               if ($var(result) == 5031) {
+                                       if ($rU =~ "^[1-9][0-9]+$") {
+                                               sql_pvquery("hss_db", "SELECT id, description, country_name, iso3166, type, CAST(rate_init*$var(multiplier) as UNSIGNED), rate_init_interval, CAST(rate_follow*$var(multiplier) as UNSIGNED), rate_follow_interval, blocked, max_call_duration FROM call_rates WHERE rate_plan_id = $var(ratecard) AND destination_prefix = SUBSTRING('$rU', 1, LENGTH(destination_prefix)) ORDER BY LENGTH(destination_prefix) DESC limit 1;", "$var(rate_id),$var(rate_name),$var(country),$var(iso),$var(type),$var(rate_init),$var(rate_init_interval),$var(rate_follow),$var(rate_follow_interval),$var(blocked),$var(maxduration)");
+                                               if ($retcode == 1) {
+                                                       $var(result) = 2001;
+                                               } else {
+                                                       $var(result) = 5031; # DIAMETER_RATING_FAILED
+                                               }
+                                       }
+                               }
+                               if ($var(result) == "2001") {
+#!ifdef WITH_DEBUG
+                                       xlog("Rate-ID:      $var(rate_id)\n"); 
+                                       xlog("Country:      $var(iso): $var(country) ($var(type))\n"); 
+                                       xlog("Destination:  $var(rate_name) ($rU)\n"); 
+                                       xlog("Rate:         $var(rate_init)/$var(rate_init_interval), $var(rate_follow)/$var(rate_follow_interval)\n"); 
+                                       xlog("Blocked:      $var(blocked)\n");
+                                       xlog("Max-Duration: $var(maxduration)\n");
+#!endif
+                                       if ($var(blocked) == 1) {
+                                               $var(result) = 4010; # DIAMETER_END_USER_SERVICE_DENIED
+                                       } else {
+                                               $var(used_credit) = 0;
+                                               if ($var(payment) == 1) {
+                                                       # Prepaid
+                                                       if ($sht(user=>$var(id)::reserved_credit) != $null)
+                                                               $var(current_credit) = $var(credit) - $sht(user=>$var(id)::reserved_credit);
+                                                       else
+                                                               $var(current_credit) = $var(credit);
+                                                       if ($var(rate_init) > $var(current_credit)) {
+                                                               $var(result) = 4012; # DIAMETER_CREDIT_LIMIT_REACHED
+                                                               $var(granted) = 0;
+                                                               $var(used_credit) = 0;
+                                                       } else {
+                                                               $var(used_credit) = $var(rate_init);
+                                                               $var(granted) = $var(rate_init_interval);
+                                                               if (($var(rate_follow) == 0) || ($var(rate_follow_interval) <= 0)) {
+                                                                       $var(granted) = GRANT;
+                                                               } else {
+                                                                       $var(used_credit) = $var(rate_init);
+                                                                       while ($var(granted) < GRANT) {
+                                                                               if ($var(rate_follow) > ($var(current_credit) - $var(used_credit))) {
+                                                                                       $var(final) = 1;
+                                                                                       break;
+                                                                               }
+                                                                               $var(granted) = $var(granted) + $var(rate_follow_interval);
+                                                                               $var(used_credit) = $var(used_credit) + $var(rate_follow);
+                                                                       }
+                                                                       # If it's not sufficient for the next unit, it's the final unit:
+                                                                       if ($var(rate_follow) > ($var(current_credit) - $var(used_credit))) {
+                                                                               $var(final) = 1;
+                                                                       }
+                                                               }
+                                                       }
+                                               } else {
+                                                       # Postpaid
+                                                       $var(granted) = $var(rate_init_interval);
+                                                       $var(used_credit) = $var(rate_init);
+                                                       if (($var(rate_follow) == 0) || ($var(rate_follow_interval) <= 0)) {
+                                                               $var(granted) = GRANT;
+                                                       } else {
+                                                               while ($var(granted) < GRANT) {
+                                                                       $var(granted) = $var(granted) + $var(rate_follow_interval);
+                                                                       $var(used_credit) = $var(used_credit) + $var(rate_follow);
+                                                               }
+                                                       }
+                                               }
+                                               if ($var(result) == "2001") {
+                                                       $sht(call=>$ci::follow_rate) = $(var(rate_follow){s.int});
+                                                       $sht(call=>$ci::follow_unit) = $(var(rate_follow_interval){s.int});
+                                                       $sht(call=>$ci::initial_rate) = $(var(rate_init){s.int});
+                                                       $sht(call=>$ci::initial_unit) = $(var(rate_init_interval){s.int});
+
+                                                       $sht(call=>$ci::reserved_credit) = $(var(used_credit){s.int});
+                                                       $sht(call=>$ci::used_unit) = 0;
+                                                       $sht(call=>$ci::granted_unit) = $var(granted);
+
+                                                       sht_lock("user=>$var(id)::credit");
+                                                       if ($var(payment) == 1)
+                                                               $sht(user=>$var(id)::credit) = $var(credit);
+                                                       else
+                                                               $sht(user=>$var(id)::credit) = -1;
+                                                       sht_unlock("user=>$var(id)::credit");
+
+                                                       sht_lock("user=>$var(id)::reserved_credit");
+                                                       if ($sht(user=>$var(id)::reserved_credit) == $null) {
+                                                               $sht(user=>$var(id)::reserved_credit) = $(var(used_credit){s.int});                                             
+                                                       } else {
+                                                               $sht(user=>$var(id)::reserved_credit) = $sht(user=>$var(id)::reserved_credit) + $(var(used_credit){s.int});                                             
+                                                       }
+                                                       sht_unlock("user=>$var(id)::reserved_credit");
+
+                                                       $sht(call=>$ci::max_units) = $null;
+                                                       if ($var(maxduration) > 0) {
+                                                               $sht(call=>$ci::max_units) = $var(maxduration);
+                                                               if ($var(maxduration) < $var(granted)) {
+                                                                       $var(granted) = $var(maxduration);
+                                                                       $var(final) = 1;
+                                                               }
+                                                       }
+                                               }
+#!ifdef WITH_DEBUG
+                                               xlog("Granted $var(granted)s, reserved new credit $var(used_credit)\n");
+#!endif
+                                       }
+                               } else {
+                                       $var(result) = 5031; # DIAMETER_RATING_FAILED
+                               }
+                       }
+               }
+               if ($var(result) == "2001") {
+                       sht_lock("user=>$var(id)::current_calls");
+                       $sht(user=>$var(id)::current_calls) = $sht(user=>$var(id)::current_calls) + 1;
+                       sht_unlock("user=>$var(id)::current_calls");
+#!ifdef WITH_DEBUG
+                       xlog("User has now $sht(user=>$var(id)::current_calls) Calls, allowed is $var(concurrent_calls)\n");
+                       xlog("INSERT INTO tb_processed_cdrs (imsu_id, impi_id, dest_e164, callid, start, call_rate) VALUES ($var(id), $var(impi_id), '$fU', '$rU', '$ci', now(), $var(id), $var(rate_id))\n");
+#!endif
+                       sql_query("hss_db", "INSERT INTO tb_processed_cdrs (imsu_id, impi_id, src_e164, dest_e164, callid, start, call_rate) VALUES ($var(id), $var(impi_id), '$fU', '$rU', '$ci', now(), $var(rate_id));");
+                       if ($retcode == -1) {
+                               $var(result) = 5012;
+                       }
+               }
+       } else if (is_method("UPDATE")) {
+               $var(id) = $sht(cid=>$ci);
+               $var(used_credit) = 0;
+               $var(granted) = 0;
+               $var(credit) = $sht(user=>$var(id)::credit);
+               if ($var(credit) >= 0) {
+                       # Prepaid
+                       if (($sht(call=>$ci::follow_rate) == 0) || ($sht(call=>$ci::follow_unit) <= 0)) {
+                               $var(granted) = GRANT;
+                       } else {
+                               $var(current_credit) = $sht(user=>$var(id)::credit) - $sht(user=>$var(id)::reserved_credit);
+                               while ($var(granted) < GRANT) {
+                                       if ($sht(call=>$ci::follow_rate) > ($var(current_credit) - $var(used_credit))) {
+                                               $var(final) = 1;
+                                               break;
+                                       }
+                                       $var(granted) = $var(granted) + $sht(call=>$ci::follow_unit);
+                                       $var(used_credit) = $var(used_credit) + $sht(call=>$ci::follow_rate);
+                               }
+                               # If it's not sufficient for the next unit, it's the final unit:
+                               if ($sht(call=>$ci::follow_rate) > ($var(current_credit) - $var(used_credit))) {
+                                       $var(final) = 1;
+                               }
+                       }
+               } else {
+                       # Postpaid
+                       if (($sht(call=>$ci::follow_rate) == 0) || ($sht(call=>$ci::follow_unit) <= 0)) {
+                               $var(granted) = GRANT;
+                       } else {
+                               while ($var(granted) < GRANT) {
+                                       $var(granted) = $var(granted) + $sht(call=>$ci::follow_unit);
+                                       $var(used_credit) = $var(used_credit) + $sht(call=>$ci::follow_rate);
+                               }
+                       }
+               }
+               $sht(call=>$ci::used_unit) = $sht(call=>$ci::used_unit) + $(hdr(P-Used-Units){s.int});
+               if ($sht(call=>$ci::max_units) > 0) {
+                       $var(cur_granted) = $sht(call=>$ci::used_unit) + $var(granted);
+                       $var(max_granted) = $sht(call=>$ci::max_units);
+                       if ($var(max_granted) > $var(cur_granted)) {
+                               $var(granted) = $sht(call=>$ci::max_units) - $sht(call=>$ci::used_unit);
+                               $var(final) = 1;
+                       }
+               }
+               if ($var(granted) <= 0) {
+                       $var(result) = 4012; # DIAMETER_CREDIT_LIMIT_REACHED
+                       $var(granted) = 0;              
+               } else {
+                       $var(granted) = $var(granted) + ($sht(call=>$ci::granted_unit) - $(hdr(P-Used-Units){s.int}));
+                       $sht(call=>$ci::granted_unit) = $var(granted);
+                       $var(result) = 2001;
+               }
+
+               sht_lock("user=>$var(id)::reserved_credit");
+               $sht(user=>$var(id)::reserved_credit) = $sht(user=>$var(id)::reserved_credit) + $var(used_credit);
+               sht_unlock("user=>$var(id)::reserved_credit");
+
+               $sht(call=>$ci::reserved_credit) = $sht(call=>$ci::reserved_credit) + $var(used_credit);
+#!ifdef WITH_DEBUG
+               xlog("$var(result): Granted $var(granted)s (used $sht(call=>$ci::used_unit)), total granted $sht(call=>$ci::granted_unit), reserved credit $sht(user=>$var(id)::reserved_credit), new $var(used_credit)\n");
+#!endif
+       } else if (is_method("BYE")) {
+               $var(id) = $sht(cid=>$ci);
+#!ifdef WITH_DEBUG
+               xlog("$ci: User $var(id) has $sht(user=>$var(id)::current_calls) Calls, decrementing\n");
+#!endif
+               if ($var(id) != $null) {
+                       sht_lock("user=>$var(id)::current_calls");
+                       if ($sht(user=>$var(id)::current_calls) <= 0) {
+                               $sht(user=>$var(id)::current_calls) = $null;
+                       } else {
+                               $sht(user=>$var(id)::current_calls) = $sht(user=>$var(id)::current_calls) - 1;
+                       }
+                       sht_unlock("user=>$var(id)::current_calls");
+               }
+               $var(used) = $sht(call=>$ci::used_unit) + $(hdr(P-Used-Units){s.int});
+               $var(credit) = $sht(user=>$var(id)::credit);
+               if ($var(used) > 0) {
+                       $var(used_credit) = $sht(call=>$ci::initial_rate);
+                       $var(used) = $var(used) - $sht(call=>$ci::initial_unit);
+                       if (($sht(call=>$ci::follow_rate) != 0) && ($sht(call=>$ci::follow_unit) != 0) && ($var(used) > 0)) {
+                               while ($var(used) > 0) {
+                                       $var(used) = $var(used) - $sht(call=>$ci::follow_unit);
+                                       $var(used_credit) = $var(used_credit) + $sht(call=>$ci::follow_rate);
+                               }
+                       } else {
+                               $var(used) = $sht(call=>$ci::used_unit);
+                       }
+               } else {
+                       $var(used_credit) = 0;
+                       $var(used) = 0;
+               }
+               if (($var(used_credit) > 0) && ($var(credit) > 0)) {
+                       sht_lock("user=>$var(id)::credit");
+                       $sht(user=>$var(id)::credit) = $sht(user=>$var(id)::credit) - $var(used_credit);
+                       sht_unlock("user=>$var(id)::credit");
+                       sql_query("hss_db", "UPDATE imsu SET credit=credit - ($var(used_credit)/$var(multiplier)) WHERE id=$var(id);");
+                       if ($retcode == -1) {
+                               $var(result) = 5012;
+                       }
+               }
+               $var(duration) = $sht(call=>$ci::used_unit) + $(hdr(P-Used-Units){s.int});
+#!ifdef WITH_DEBUG
+               xlog("Call-Cost: $var(used_credit), Duration $var(duration)\n");
+               xlog("Credit: $sht(user=>$var(id)::credit)\n");
+#!endif
+               sht_lock("user=>$var(id)::reserved_credit");
+               $sht(user=>$var(id)::reserved_credit) = $sht(user=>$var(id)::reserved_credit) - $sht(call=>$ci::reserved_credit);
+               sht_unlock("user=>$var(id)::reserved_credit");
+
+               sql_query("hss_db", "UPDATE tb_processed_cdrs SET connect=DATE_SUB(now(), interval $var(duration) second), stop=now(), duration=$var(duration), price=($var(used_credit)/$var(multiplier)) WHERE callid='$ci';");
+               if ($retcode == -1) {
+                       $var(result) = 5012;
+               }
+
+               $var(result) = "2001";
+       }
+
+       ccr_result("$var(result)", "$var(granted)", "$var(final)");
+}
+
+event_route[ocs:ccr-term] {
+       # For the terminating case, we only check the user existence and Line-Limit
+       if ($fU =~ "^\+[1-9][0-9]+$")
+               $var(from) = $(fU{s.substr,1,0});
+       else
+               $var(from) = $fU;
+
+#!ifdef WITH_DEBUG
+       xlog("TERM: $rm $var(from) => $ru\n\n");
+#!endif
+       $var(result) = "5012";
+       $var(granted) = "0";
+       $var(final) = "0";
+
+       if (is_method("INVITE")) {
+               sql_pvquery("hss_db", "SELECT impi.id AS impi_id, imsu.id, imsu.max_concurrent_calls FROM impu LEFT JOIN impi_impu ON impu.id = impi_impu.id_impu LEFT JOIN impi ON impi.id = impi_impu.id_impi LEFT JOIN imsu ON impi.id_imsu = imsu.id WHERE ((impu.type = 0 AND impu.identity='sip:$var(from)@$fd') OR (impu.type = 0 AND impu.identity='sip:+$var(from)@$fd') OR (impu.type = 0 AND impu.identity='tel:$var(from)') OR (impu.type = 0 AND impu.identity='tel:+$var(from)') OR (impu.type = 3 AND 'sip:$var(from)@$fd' REGEXP impu.identity) OR (impu.type = 3 AND 'sip:+$var(from)@$fd' REGEXP impu.identity) OR (impu.type = 3 AND 'tel:$var(from)' REGEXP impu.identity) OR (impu.type = 3 AND 'tel:+$var(from)' REGEXP impu.identity)) limit 1;", "$var(impi_id),$var(id),$var(concurrent_calls)");
+               if ($retcode != 1) {
+                       # User not found, send DIAMETER_USER_UNKNOWN
+                       $var(result) = 5030;
+               } else {
+#!ifdef WITH_DEBUG
+                       xlog("IMPI:             $fu ($var(impi_id))\n");
+                       xlog("IMSU-ID:          $var(id)\n");
+                       xlog("Concurrent-Calls: $var(concurrent_calls), currently ($sht(user=>$var(id)::current_calls))\n");
+#!endif
+
+                       if (($sht(user=>$var(id)::current_calls) != $null) && ($var(concurrent_calls) >= 0)) {
+                               if ($sht(user=>$var(id)::current_calls) >= $var(concurrent_calls)) {
+                                       $var(result) = 5006; # DIAMETER_RESOURCES_EXCEEDED
+                               } else {
+                                       $sht(cid=>$ci) = $var(id);
+                                       sht_lock("user=>$var(id)::current_calls");
+                                       $sht(user=>$var(id)::current_calls) = $sht(user=>$var(id)::current_calls) + 1;
+                                       sht_unlock("user=>$var(id)::current_calls");
+                                       $var(result) = 2001;
+                                       $var(granted) = GRANT;
+                               }
+                       } else {
+                               sht_lock("user=>$var(id)::current_calls");
+                               if ($sht(user=>$var(id)::current_calls) == $null) {
+                                       $sht(user=>$var(id)::current_calls) = 1;
+                               } else {
+                                       $sht(user=>$var(id)::current_calls) = $sht(user=>$var(id)::current_calls) + 1;
+                               }
+                               sht_unlock("user=>$var(id)::current_calls");
+                               $sht(cid=>$ci) = $var(id);
+                               $var(result) = 2001;
+                               $var(granted) = GRANT;
+                       }
+               }
+       } else if (is_method("UPDATE")) {
+               $var(result) = 2001;
+               $var(granted) = GRANT;
+       } else if (is_method("BYE")) {
+               $var(id) = $sht(cid=>$ci);
+#!ifdef WITH_DEBUG
+               xlog("$ci: User $var(id) has $sht(user=>$var(id)::current_calls) Calls, decrementing\n");
+#!endif
+               if ($var(id) != $null) {
+                       sht_lock("user=>$var(id)::current_calls");
+                       if ($sht(user=>$var(id)::current_calls) <= 0) {
+                               $sht(user=>$var(id)::current_calls) = $null;
+                       } else {
+                               $sht(user=>$var(id)::current_calls) = $sht(user=>$var(id)::current_calls) - 1;
+                       }
+                       sht_unlock("user=>$var(id)::current_calls");
+               }
+               $var(result) = 2001;
+       }
+       // xlog("$rm: $var(result): $var(granted) units...\n");
+
+       ccr_result("$var(result)", "$var(granted)", "$var(final)");
+}
diff --git a/modules/ims_ocs/examples/full/ocs.cfg.sample b/modules/ims_ocs/examples/full/ocs.cfg.sample
new file mode 100644 (file)
index 0000000..a5eeafa
--- /dev/null
@@ -0,0 +1,36 @@
+# IP-Adress for incoming SIP-Traffic, in the following format:
+
+# SIP / UDP
+listen=udp:11.22.33.44:5080
+# SIP / TCP
+#listen=tcp:11.22.33.44:5060
+# SIP / TCP/TLS
+#listen=tls:11.22.33.44:5061
+
+# Connection URL for the database:
+#!define DB_URL "con1=>mysql://ocs:heslo@127.0.0.1/hss_db"
+##!define DB_URL2 "con2=>mysql://ocs:heslo@127.0.0.1/hss_db"
+
+# Allowed IPs for XML-RPC-Queries
+##!define XMLRPC_WHITELIST_1 "127.0.0.1"
+##!define XMLRPC_WHITELIST_2 "127.0.0.1"
+##!define XMLRPC_WHITELIST_3 "127.0.0.1"
+
+##!define DMQ_SERVER_ADDRESS "sip:11.22.33.44:5080"
+##!define DMQ_NOTIFICATION_ADDRESS "sip:11.22.33.44:5080"
+
+#
+# Several features can be enabled using '#!define WITH_FEATURE' directives:
+#
+# *** To run in debug mode: 
+#     - define WITH_DEBUG
+#
+# *** To enable XMLRPC support execute:
+#     - define WITH_XMLRPC
+#     - adjust route[XMLRPC] for access policy
+#
+# Enabled Features for this host:
+##!define WITH_XMLRPC
+##!define WITH_DMQ
+##!define WITH_DEBUG
+
diff --git a/modules/ims_ocs/examples/full/ocs.xml.sample b/modules/ims_ocs/examples/full/ocs.xml.sample
new file mode 100644 (file)
index 0000000..16489de
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<DiameterPeer 
+       FQDN="ocs.mnc001.mcc001.3gppnetwork.org"
+       Realm="ims.mnc001.mcc001.3gppnetwork.org"
+       Vendor_Id="10415"
+       Product_Name="CDiameterPeer"
+       AcceptUnknownPeers="1"
+       DropUnknownOnDisconnect="1"
+       Tc="30"
+       Workers="4"
+       QueueLength="8"
+       TransactionTimeout="5"
+       SessionsHashSize="128"
+       DefaultAuthSessionTimeout="3600"
+       MaxAuthSessionTimeout="3600"
+>
+       <Peer FQDN="scscf.mnc001.mcc001.3gppnetwork.org" Realm="ims.mnc001.mcc001.3gppnetwork.org" port="3870"/>
+               
+       <Acceptor port="3871" bind="11.22.33.44"/>
+
+        <Auth id="4" vendor="10415"/> <!--3GPP Ro -->              
+</DiameterPeer>
diff --git a/modules/ims_ocs/examples/simple/kamailio.cfg b/modules/ims_ocs/examples/simple/kamailio.cfg
new file mode 100644 (file)
index 0000000..2576706
--- /dev/null
@@ -0,0 +1,143 @@
+#!KAMAILIO
+#
+# This config file implements an Online-Charging-Server
+#     - web: http://www.kamailio.org
+#     - git: http://github.com/kamailio/kamailio
+#
+# Refer to the Core CookBook at http://www.kamailio.org/dokuwiki/doku.php
+# for an explanation of possible statements, functions and parameters.
+#
+# Direct your questions about this file to: <sr-users@lists.sip-router.org>.
+#
+# For more information about the various parameters, functions and statements
+# try http://sip-router.org/wiki/ .
+#
+
+import_file "ocs.cfg"
+
+####### Defined Values #########
+# *** Value defines - IDs used later in config
+
+# - flags
+#      FLT_ - per transaction (message) flags
+#      FLB_ - per branch flags
+
+
+####### Global Parameters #########
+
+#!ifdef WITH_DEBUG
+debug=4
+log_stderror=yes
+#!else
+debug=0
+log_stderror=no
+#!endif
+
+memdbg=5
+memlog=5
+
+log_stderror=no
+sip_warning=no
+
+rundir="/var/run/kamailio_ocs"
+
+user_agent_header="User-Agent: TelcoSuite OCS"
+server_header="Server: TelcoSuite OCS"
+
+/* comment the next line to enable the auto discovery of local aliases
+   based on reverse DNS on IPs (default on) */
+auto_aliases=no
+
+check_via=no    # (cmd. line: -v)
+dns=no          # (cmd. line: -r)
+rev_dns=no      # (cmd. line: -R)
+
+# Do SRV-Loadbalancing:
+dns_srv_lb=no
+# Always: Also try IPv6:
+dns_try_ipv6=no
+# Always prefer IPv6:
+dns_cache_flags=6
+# DNS-Based failover
+use_dns_failover = on
+# Query NAPTR-Records as well:
+dns_try_naptr=no
+
+children=3
+log_name="[Charging]"
+
+system.shutdownmode = 0 desc "System shutdown mode"
+system.service = "Online-Charging-Server" desc "Function of this server"
+
+# ------------------ module loading ----------------------------------
+mpath="/usr/lib64/kamailio/modules_k/:/usr/lib64/kamailio/modules/:/usr/local/lib/kamailio/modules/"
+# (we try both the lib64 and the lib directory)
+
+loadmodule "cdp"
+loadmodule "cdp_avp"
+loadmodule "ims_ocs"
+loadmodule "xlog"
+loadmodule "pv"
+loadmodule "cfgutils"
+loadmodule "textops"
+loadmodule "ctl"
+loadmodule "sl"
+loadmodule "tm"
+loadmodule "kex"
+loadmodule "corex"
+
+# ----- ctl params -----
+modparam("ctl", "binrpc", "unix:/var/run/kamailio_ocs/kamailio_ctl")
+
+# ----- cdp params -----
+modparam("cdp","config_file","/etc/kamailio_ocs/ocs.xml")
+
+route {
+       # Do nothing, we only do Diameter requests
+       drop();
+       exit;
+}
+
+event_route[ocs:ccr-orig] {
+       xlog("Session-Case: Originating\n");
+       xlog("----------------------------------------\n");
+       if (is_method("INVITE")) {
+               xlog("START - Request\n");
+       } else if (is_method("UPDATE")) {
+               xlog("INTERIM - Request\n");
+       } else if (is_method("BYE")) {
+               xlog("STOP - Request\n");
+       }
+       xlog("----------------------------------------\n");
+       xlog("From:               $fu\n");
+       xlog("To:                 $ru\n");
+       xlog("Call-ID:            $ci\n");
+       xlog("Requested Units:    $hdr(P-Requested-Units)\n");
+       xlog("Used Units:         $hdr(P-Used-Units)\n");
+       xlog("Access Network:     $hdr(P-Access-Network-Info)\n");
+       xlog("Service Identifier: $hdr(P-Service-Identifier)\n");
+
+       ccr_result("2001", "600", "0");
+}
+
+event_route[ocs:ccr-term] {
+       xlog("Session-Case: Terminating\n");
+       xlog("----------------------------------------\n");
+       if (is_method("INVITE")) {
+               xlog("START - Request\n");
+       } else if (is_method("UPDATE")) {
+               xlog("INTERIM - Request\n");
+       } else if (is_method("BYE")) {
+               xlog("STOP - Request\n");
+       }
+       xlog("----------------------------------------\n");
+       xlog("From:               $fu\n");
+       xlog("To:                 $ru\n");
+       xlog("Call-ID:            $ci\n");
+       xlog("Requested Units:    $hdr(P-Requested-Units)\n");
+       xlog("Used Units:         $hdr(P-Used-Units)\n");
+       xlog("Access Network:     $hdr(P-Access-Network-Info)\n");
+       xlog("Service Identifier: $hdr(P-Service-Identifier)\n");
+
+       ccr_result("2001", "600", "0");
+}
diff --git a/modules/ims_ocs/examples/simple/ocs.cfg.sample b/modules/ims_ocs/examples/simple/ocs.cfg.sample
new file mode 100644 (file)
index 0000000..2360028
--- /dev/null
@@ -0,0 +1,9 @@
+# IP-Adress for incoming SIP-Traffic, in the following format:
+
+# SIP / UDP
+listen=udp:11.22.33.44:5080
+# SIP / TCP
+#listen=tcp:11.22.33.44:5060
+# SIP / TCP/TLS
+#listen=tls:11.22.33.44:5061
+
diff --git a/modules/ims_ocs/examples/simple/ocs.xml.sample b/modules/ims_ocs/examples/simple/ocs.xml.sample
new file mode 100644 (file)
index 0000000..16489de
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<DiameterPeer 
+       FQDN="ocs.mnc001.mcc001.3gppnetwork.org"
+       Realm="ims.mnc001.mcc001.3gppnetwork.org"
+       Vendor_Id="10415"
+       Product_Name="CDiameterPeer"
+       AcceptUnknownPeers="1"
+       DropUnknownOnDisconnect="1"
+       Tc="30"
+       Workers="4"
+       QueueLength="8"
+       TransactionTimeout="5"
+       SessionsHashSize="128"
+       DefaultAuthSessionTimeout="3600"
+       MaxAuthSessionTimeout="3600"
+>
+       <Peer FQDN="scscf.mnc001.mcc001.3gppnetwork.org" Realm="ims.mnc001.mcc001.3gppnetwork.org" port="3870"/>
+               
+       <Acceptor port="3871" bind="11.22.33.44"/>
+
+        <Auth id="4" vendor="10415"/> <!--3GPP Ro -->              
+</DiameterPeer>
diff --git a/modules/ims_ocs/mod.c b/modules/ims_ocs/mod.c
new file mode 100644 (file)
index 0000000..79598ca
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2016 ng-voice GmbH, carsten@ng-voice.com
+ *
+ * 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 "../../sr_module.h"
+#include "../../route.h"
+#include "../cdp/cdp_load.h"
+#include "../cdp_avp/mod_export.h"
+#include "../../parser/msg_parser.h"
+#include "mod.h"
+#include "msg_faker.h"
+#include "ocs_avp_helper.h"
+
+MODULE_VERSION
+
+extern gen_lock_t* process_lock; /* lock on the process table */
+
+struct cdp_binds cdpb;
+
+cdp_avp_bind_t *cdp_avp;
+
+/** module functions */
+static int mod_init(void);
+static int mod_child_init(int);
+static void mod_destroy(void);
+
+int * callback_singleton; /*< Callback singleton */
+
+int result_code = 0;
+int granted_units = 0;
+int final_unit = 0;
+
+int event_route_ccr_orig = 0;
+int event_route_ccr_term = 0;
+
+static int w_ccr_result(struct sip_msg *msg, char* result, char* grantedunits, char* final);
+
+static cmd_export_t cmds[] = {
+       {"ccr_result", (cmd_function)w_ccr_result, 3, fixup_var_pve_str_12, 0, REQUEST_ROUTE},
+       { 0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[] = {
+    { 0, 0, 0}
+};
+
+
+/** module exports */
+struct module_exports exports = {"ims_ocs", DEFAULT_DLFLAGS, /* dlopen flags */
+    cmds, /* Exported functions */
+    params, 0, /* exported statistics */
+    0, /* exported MI functions */
+    0, /* exported pseudo-variables */
+    0, /* extra processes */
+    mod_init, /* module initialization function */
+    0, mod_destroy, mod_child_init /* per-child init function */};
+
+/**
+ * init module function
+ */
+static int mod_init(void) {
+       LM_DBG("Loading...\n");
+       event_route_ccr_orig = route_get(&event_rt, "ocs:ccr-orig");
+       if (event_route_ccr_orig < 0) {
+               LM_ERR("No ocs:ccr-orig event route found\n");
+               goto error;
+       }
+       LM_DBG("Found Route ocs:ccr-orig: %i\n", event_route_ccr_orig);
+
+       event_route_ccr_term = route_get(&event_rt, "ocs:ccr-term");
+       if (event_route_ccr_term < 0) {
+               LM_INFO("No ocs:ccr-term event route found\n");
+       }
+       LM_DBG("Found Route ocs:ccr-term: %i\n", event_route_ccr_term);
+
+
+       callback_singleton = shm_malloc(sizeof (int));
+       *callback_singleton = 0;
+
+       cdp_avp = 0;
+       /* load the CDP API */
+       if (load_cdp_api(&cdpb) != 0) {
+               LM_ERR("can't load CDP API\n");
+               goto error;
+           }
+
+       cdp_avp = load_cdp_avp();
+       if (!cdp_avp) {
+               LM_ERR("can't load CDP_AVP API\n");
+               goto error;
+       }
+
+       return 0;
+error:
+       LM_ERR("Failed to initialise ims_ocs module\n");
+       return -1;
+}
+
+/**
+ * Initializes the module in child.
+ */
+static int mod_child_init(int rank) {
+    LM_DBG("Initialization of module in child [%d] \n", rank);
+
+    /* don't do anything for main process and TCP manager process */
+    if (rank == PROC_MAIN || rank == PROC_TCP_MAIN) {
+        return 0;
+    }
+
+    lock_get(process_lock);
+    if ((*callback_singleton) == 0) {
+        *callback_singleton = 1;
+        cdpb.AAAAddRequestHandler(callback_cdp_request, NULL);
+    }
+    lock_release(process_lock);
+
+    return 0;
+}
+
+
+static void mod_destroy(void) {
+
+}
+
+static int w_ccr_result(struct sip_msg *msg, char* result, char* grantedunits, char* final) {
+       str s_result_code, s_granted_units, s_final_unit;
+       if (get_str_fparam(&s_result_code, msg, (fparam_t*)result) < 0) {
+           LM_ERR("failed to get Result\n");
+           return -1;
+       }
+       if (str2sint(&s_result_code, &result_code) != 0) {
+               LM_DBG("Invalid result-code (%.*s)\n", s_result_code.len, s_result_code.s);
+       }
+       LM_DBG("Got result: %i (%.*s)\n", result_code, s_result_code.len, s_result_code.s);
+
+       if (get_str_fparam(&s_granted_units, msg, (fparam_t*)grantedunits) < 0) {
+           LM_ERR("failed to get Granted Units\n");
+           return -1;
+       }
+       if (str2sint(&s_granted_units, &granted_units) != 0) {
+               LM_DBG("Invalid Granted Units (%.*s)\n", s_granted_units.len, s_granted_units.s);
+       }
+       LM_DBG("Got Granted Units: %i, %.*s\n", granted_units, s_granted_units.len, s_granted_units.s);
+
+       if (get_str_fparam(&s_final_unit, msg, (fparam_t*)final) < 0) {
+           LM_ERR("failed to get Final Unit\n");
+           return -1;
+       }
+       if (str2sint(&s_final_unit, &final_unit) != 0) {
+               LM_DBG("Invalid Granted Units (%.*s)\n", s_final_unit.len, s_final_unit.s);
+       }
+       LM_DBG("Got Final Unit: %i, %.*s\n", final_unit, s_final_unit.len, s_final_unit.s);
+       return 1;
+}
+
+AAAMessage* process_ccr(AAAMessage *ccr) {
+       int backup_rt;
+       struct run_act_ctx ctx;
+       struct sip_msg *msg;
+
+       // Initialize values:
+       result_code = 0;
+       granted_units = 0;
+
+       LM_DBG("Processing CCR");
+
+       if ((isOrig(ccr) != 0) && (event_route_ccr_term < 0)) {
+               result_code = DIAMETER_SUCCESS;
+               granted_units = 3600;
+               final_unit = 0;
+       } else {
+               if (faked_aaa_msg(ccr, &msg) != 0) {
+                       LM_ERR("Failed to build Fake-Message\n");
+               }
+
+               backup_rt = get_route_type();
+               set_route_type(REQUEST_ROUTE);
+               init_run_actions_ctx(&ctx);
+               if (isOrig(ccr) != 0) {
+                       run_top_route(event_rt.rlist[event_route_ccr_term], msg, 0);
+               } else {
+                       run_top_route(event_rt.rlist[event_route_ccr_orig], msg, 0);
+               }
+       
+               set_route_type(backup_rt);
+
+               free_sip_msg(msg);
+       }
+
+       LM_DBG("Result-Code is %i, Granted Units %i (Final: %i)\n", result_code, granted_units, final_unit);
+
+       if (result_code == 0) {
+               LM_ERR("event_route did not set Result-Code, aborting\n");
+               result_code = DIAMETER_UNABLE_TO_COMPLY;
+               granted_units = 0;
+               final_unit = 0;
+       }
+
+       AAAMessage *cca;
+       cca = cdpb.AAACreateResponse(ccr);
+       if (!cca) return 0;
+
+       ocs_build_answer(ccr, cca, result_code, granted_units, final_unit);
+
+       return cca;     
+}
+
+/**
+ * Handler for incoming Diameter requests.
+ * @param request - the received request
+ * @param param - generic pointer
+ * @returns the answer to this request
+ */
+AAAMessage* callback_cdp_request(AAAMessage *request, void *param) {
+    if (is_req(request)) {
+
+        switch (request->applicationId) {
+            case IMS_Ro:
+                switch (request->commandCode) {
+                   case IMS_CCR:
+                       return process_ccr(request);
+                       break;
+                    default:
+                        LM_ERR("Ro request handler(): - Received unknown request for Ro command %d, flags %#1x endtoend %u hopbyhop %u\n", request->commandCode, request->flags, request->endtoendId, request->hopbyhopId);
+                        return 0;
+                        break;
+                }
+                break;
+            default:
+                LM_ERR("Ro request handler(): - Received unknown request for app %d command %d\n", request->applicationId, request->commandCode);
+                return 0;
+                break;
+        }
+    }
+    return 0;
+}
+
diff --git a/modules/ims_ocs/mod.h b/modules/ims_ocs/mod.h
new file mode 100644 (file)
index 0000000..b46a29a
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *
+ * Copyright (C) 2015 ng-voice GmbH, Carsten Bock, carsten@ng-voice.com
+ *
+ * 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 IMS_OCS_MOD_H
+#define        IMS_OCS_MOD_H
+
+/** callback functions */
+
+struct cdp_binds cdpb;
+cdp_avp_bind_t *cdp_avp;
+
+struct AAAMessage;
+
+AAAMessage* callback_cdp_request(AAAMessage *request, void *param);
+
+#endif /* IMS_OCS_MOD_H */
+
diff --git a/modules/ims_ocs/msg_faker.c b/modules/ims_ocs/msg_faker.c
new file mode 100644 (file)
index 0000000..0d99194
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 ng-voice GmbH, carsten@ng-voice.com
+ * File is based on cnxcc: msg_faker, written by Carlos Ruiz Díaz (caruizdiaz.com)
+ *
+ * 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 "../../parser/msg_parser.h"
+#include "../../globals.h"
+#include "../cdp/cdp_load.h"
+#include "../cdp_avp/mod_export.h"
+#include "ocs_avp_helper.h"
+
+#include "msg_faker.h"
+
+#include <sys/socket.h>
+
+#define FAKED_SIP_SESSION_FORMAT "%.*s %.*s SIP/2.0\r\nVia: SIP/2.0/UDP 127.0.0.1\r\nFrom: %.*s%.*s\r\nTo: %.*s;tag=xyz\r\nCall-ID: %.*s\r\nCSeq: 1 %.*s\r\nContent-Length: 0\r\nP-Requested-Units: %i\r\nP-Used-Units: %i\r\nP-Access-Network-Info: %.*s\r\nP-Service-Identifier: %i\r\n\r\n"
+
+#define FAKED_SIP_SESSION_BUF_LEN      1024
+char _faked_sip_session_buf[FAKED_SIP_SESSION_BUF_LEN];
+
+str CC_INVITE = {"INVITE", 6};
+str CC_UPDATE = {"UPDATE", 6};
+str CC_BYE = {"BYE", 3};
+
+#define RO_CC_START    1
+#define RO_CC_INTERIM  2
+#define RO_CC_STOP     3
+
+static struct sip_msg _faked_msg;
+
+int getMethod(AAAMessage *msg, str ** method) {
+       str s;
+       s = get_avp(msg, AVP_IMS_CCR_Type, 0, __FUNCTION__);
+       if (!s.s) return -1;
+       switch (get_4bytes(s.s)) {
+               case RO_CC_START:
+                       *method = &CC_INVITE;
+                       break;
+               case RO_CC_INTERIM:
+                       *method = &CC_UPDATE;
+                       break;
+               case RO_CC_STOP:
+                       *method = &CC_BYE;
+                       break;
+               default:
+                       LM_ERR("Invalid CCR-Type\n");
+                       return -1;
+                       break;
+       }
+       
+       return 1;
+       
+}
+
+int faked_aaa_msg(AAAMessage *ccr, struct sip_msg **msg) {
+       int type, size;
+       str * method;
+       str prefix = {0, 0};
+       str from_uri = getSubscriptionId1(ccr, &type);
+       str to_uri = getCalledParty(ccr);
+       str callid = getSession(ccr);
+       str access_network_info = getAccessNetwork(ccr);
+       int used_units = 0;
+       int service = 0;
+       int group = 0;
+       int requested_units = getUnits(ccr, &used_units, &service, &group);
+
+       if (getMethod(ccr, &method) < 0) {
+               LM_ERR("Failed to get CCR-Type\n");
+               return -1;
+       }
+
+       if (type != AVP_Subscription_Id_Type_SIP_URI) {
+               prefix.s = "tel:";
+               prefix.len = 4;
+       }
+       
+
+       memset(_faked_sip_session_buf, 0, FAKED_SIP_SESSION_BUF_LEN);
+       memset(&_faked_msg, 0, sizeof(struct sip_msg));
+
+       size = snprintf(_faked_sip_session_buf, FAKED_SIP_SESSION_BUF_LEN, FAKED_SIP_SESSION_FORMAT,
+               /* First-Line METHOD sip:.... */
+               method->len, method->s, to_uri.len, to_uri.s,
+               /* Prefix */
+               prefix.len, prefix.s,
+               /* From-Header */
+               from_uri.len, from_uri.s,
+               /* To-Header */
+               to_uri.len, to_uri.s,
+               /* Call-ID */
+               callid.len, callid.s,
+               /* CSeq (Method) */
+               method->len, method->s,
+               /* Requested / Used Units */
+               requested_units, used_units,
+               /* P-Access-Network-Info */
+               access_network_info.len, access_network_info.s,
+                /* P-Access-Network-Info */
+               service
+       );
+       LM_DBG("fake msg:\n%s\n", _faked_sip_session_buf);
+
+       _faked_msg.buf = _faked_sip_session_buf;
+       _faked_msg.len = size;
+
+       _faked_msg.set_global_address   = default_global_address;
+       _faked_msg.set_global_port      = default_global_port;
+
+       if (parse_msg(_faked_msg.buf, _faked_msg.len, &_faked_msg) != 0) {
+               LM_ERR("parse_msg failed\n");
+               return -1;
+       }
+
+       _faked_msg.rcv.proto = PROTO_UDP;
+       _faked_msg.rcv.src_port = 5060;
+       _faked_msg.rcv.src_ip.u.addr32[0] = 0x7f000001;
+       _faked_msg.rcv.src_ip.af = AF_INET;
+       _faked_msg.rcv.src_ip.len = 4;
+       _faked_msg.rcv.dst_port = 5060;
+       _faked_msg.rcv.dst_ip.u.addr32[0] = 0x7f000001;
+       _faked_msg.rcv.dst_ip.af = AF_INET;
+       _faked_msg.rcv.dst_ip.len = 4;
+
+       *msg    = &_faked_msg;
+       return 0;
+}
diff --git a/modules/ims_ocs/msg_faker.h b/modules/ims_ocs/msg_faker.h
new file mode 100644 (file)
index 0000000..5694fbf
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 ng-voice GmbH, carsten@ng-voice.com
+ *
+ * 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 OCS_MSG_FAKER_H_
+#define OCS_MSG_FAKER_H_
+
+int faked_aaa_msg(AAAMessage *ccr, struct sip_msg **msg);
+
+#endif /* OCS_MSG_FAKER_H_ */
diff --git a/modules/ims_ocs/ocs_avp_helper.c b/modules/ims_ocs/ocs_avp_helper.c
new file mode 100644 (file)
index 0000000..649bda3
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ *
+ * Copyright (C) 2015 ng-voice GmbH, Carsten Bock, carsten@ng-voice.com
+ *
+ * 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 "../cdp/cdp_load.h"
+#include "../cdp_avp/mod_export.h"
+#include "ocs_avp_helper.h"
+#include "mod.h"
+
+/**
+ * Returns the value of a certain AVP from a Diameter message.
+ * @param m - Diameter message to look into
+ * @param avp_code - the code to search for
+ * @param vendorid - the value of the vendor id to look for or 0 if none
+ * @param func - the name of the calling function, for debugging purposes
+ * @returns the str with the payload on success or an empty string on failure
+ */
+str get_avp(AAAMessage *msg,int avp_code,int vendor_id, const char *func) {
+       AAA_AVP *avp;
+       str r={0,0};
+       avp = cdpb.AAAFindMatchingAVP(msg,0,avp_code,vendor_id,0);
+       if (avp==0) {
+               LM_INFO("%s: Failed finding avp\n",func);
+               return r;
+       } else return avp->data;
+}
+
+str getSession(AAAMessage *msg) {
+       AAA_AVP *avp;
+       str r={0,0};
+       avp = cdpb.AAAFindMatchingAVP(msg,0,AVP_Session_Id,0,0);
+       if (avp==0) {
+               LM_INFO("Failed finding avp\n");
+               return r;
+       } else return avp->data;
+}
+
+int getRecordNummber(AAAMessage *msg) {
+       AAA_AVP *avp;
+       avp = cdpb.AAAFindMatchingAVP(msg,0,AVP_Accounting_Record_Number,0,0);
+       if (avp==0) {
+               LM_DBG("Failed finding avp\n");
+               return 0;
+       } else return get_4bytes(avp->data.s);
+}
+
+str getSubscriptionId1(AAAMessage *msg, int * type) {
+       AAA_AVP *avp, *avp_type, *avp_value;
+       str r={0,0};
+       avp = cdpb.AAAFindMatchingAVP(msg,0,AVP_Subscription_Id,0,0);
+       AAA_AVP_LIST list;
+       list = cdp_avp->cdp->AAAUngroupAVPS(avp->data);
+       avp_type = cdpb.AAAFindMatchingAVPList(list, list.head, AVP_Subscription_Id_Type, 0, 0);
+       avp_value = cdpb.AAAFindMatchingAVPList(list, list.head, AVP_Subscription_Id_Data, 0, 0);
+       
+       if (avp_type) {
+               *type = get_4bytes(avp_type->data.s);
+       } else {
+               LM_DBG("Failed finding type\n");
+               *type = 0;
+       }
+       if (avp_value==0) {
+               LM_DBG("Failed finding value\n");
+       } else {
+               r = avp_value->data;
+       }
+       cdpb.AAAFreeAVPList(&list);
+       return r;
+}
+
+int isOrig(AAAMessage *msg) {
+       AAA_AVP *service, *imsinfo, *role;
+       AAA_AVP_LIST list, list2;
+
+       int result = 0;
+       service = cdpb.AAAFindMatchingAVP(msg,0,AVP_IMS_Service_Information,IMS_vendor_id_3GPP,0);
+       if (service) {
+               list = cdp_avp->cdp->AAAUngroupAVPS(service->data);
+               imsinfo = cdpb.AAAFindMatchingAVPList(list, list.head, AVP_IMS_IMS_Information, IMS_vendor_id_3GPP, 0);
+               if (imsinfo) {
+                       list2 = cdp_avp->cdp->AAAUngroupAVPS(imsinfo->data);
+                       role = cdpb.AAAFindMatchingAVPList(list2, list2.head, AVP_IMS_Role_Of_Node, IMS_vendor_id_3GPP, 0);
+                       if (role) {
+                               result = get_4bytes(role->data.s);
+                       }
+                       cdpb.AAAFreeAVPList(&list2);
+               } else {
+                       LM_DBG("Failed finding IMS-Info\n");
+               }
+               cdpb.AAAFreeAVPList(&list);
+       } else {
+               LM_DBG("Failed finding Service-Info\n");
+       }
+
+       return result;
+}
+
+str getCalledParty(AAAMessage *msg) {
+       AAA_AVP *service, *imsinfo, *calledparty;
+       str r={0,0};
+       service = cdpb.AAAFindMatchingAVP(msg,0,AVP_IMS_Service_Information,IMS_vendor_id_3GPP,0);
+       if (service) {
+               AAA_AVP_LIST list, list2;
+               list = cdp_avp->cdp->AAAUngroupAVPS(service->data);
+               imsinfo = cdpb.AAAFindMatchingAVPList(list, list.head, AVP_IMS_IMS_Information, IMS_vendor_id_3GPP, 0);
+               if (imsinfo) {
+                       list2 = cdp_avp->cdp->AAAUngroupAVPS(imsinfo->data);
+                       calledparty = cdpb.AAAFindMatchingAVPList(list2, list2.head, AVP_IMS_Called_Party_Address, IMS_vendor_id_3GPP, 0);
+                       if (calledparty) {
+                               r = calledparty->data;
+                       } else {
+                               LM_DBG("Failed finding value\n");
+                       }
+                       cdpb.AAAFreeAVPList(&list2);
+               } else {
+                       LM_DBG("Failed finding IMS-Info\n");
+               }
+               cdpb.AAAFreeAVPList(&list);
+       } else {
+               LM_DBG("Failed finding Service-Info\n");
+       }
+
+       return r;
+}
+
+str getAccessNetwork(AAAMessage *msg) {
+       AAA_AVP *service, *imsinfo, *access;
+       str r={0,0};
+       service = cdpb.AAAFindMatchingAVP(msg,0,AVP_IMS_Service_Information,IMS_vendor_id_3GPP,0);
+       if (service) {
+               AAA_AVP_LIST list, list2;
+               list = cdp_avp->cdp->AAAUngroupAVPS(service->data);
+               imsinfo = cdpb.AAAFindMatchingAVPList(list, list.head, AVP_IMS_IMS_Information, IMS_vendor_id_3GPP, 0);
+               if (imsinfo) {
+                       list2 = cdp_avp->cdp->AAAUngroupAVPS(imsinfo->data);
+                       access = cdpb.AAAFindMatchingAVPList(list2, list2.head, AVP_IMS_Access_Network_Information, IMS_vendor_id_3GPP, 0);
+                       if (access) {
+                               r = access->data;
+                       } else {
+                               LM_DBG("Failed finding value\n");
+                       }
+                       cdpb.AAAFreeAVPList(&list2);
+               } else {
+                       LM_DBG("Failed finding IMS-Info\n");
+               }
+               cdpb.AAAFreeAVPList(&list);
+       } else {
+               LM_DBG("Failed finding Service-Info\n");
+       }
+       return r;
+}
+
+int getUnits(AAAMessage *msg, int * used, int * service, int * group) {
+       AAA_AVP *avp, *req_units, *value, *used_units, *service_avp, *rating_group;
+       int units = 0;
+       *used = 0;
+       *service = 0;
+       avp = cdpb.AAAFindMatchingAVP(msg,0,AVP_Multiple_Services_Credit_Control,0,0);
+       if (avp) {
+               AAA_AVP_LIST list, list2;
+               list = cdp_avp->cdp->AAAUngroupAVPS(avp->data);
+               req_units = cdpb.AAAFindMatchingAVPList(list, list.head, AVP_Requested_Service_Unit, 0, 0);
+               if (req_units) {
+                       list2 = cdp_avp->cdp->AAAUngroupAVPS(req_units->data);
+                       value = cdpb.AAAFindMatchingAVPList(list2, list2.head, AVP_CC_Time, 0, 0);
+                       cdpb.AAAFreeAVPList(&list2);
+                       if (value)
+                               units = get_4bytes(value->data.s);
+                       cdpb.AAAFreeAVPList(&list2);
+               }
+               service_avp = cdpb.AAAFindMatchingAVPList(list, list.head, AVP_Service_Identifier, 0, 0);
+               if (service_avp) {
+                       *service = get_4bytes(service_avp->data.s);
+               }
+               used_units = cdpb.AAAFindMatchingAVPList(list, list.head, AVP_Used_Service_Unit, 0, 0);
+               if (used_units) {
+                       list2 = cdp_avp->cdp->AAAUngroupAVPS(used_units->data);
+                       value = cdpb.AAAFindMatchingAVPList(list2, list2.head, AVP_CC_Time, 0, 0);
+                       if (value)
+                               *used = get_4bytes(value->data.s);
+                       cdpb.AAAFreeAVPList(&list2);
+               }
+               rating_group = cdpb.AAAFindMatchingAVPList(list, list.head, AVP_Rating_Group, 0, 0);
+               if (rating_group) {
+                       *group = get_4bytes(rating_group->data.s);
+               }
+               cdpb.AAAFreeAVPList(&list);
+       }
+       if (*service == 0) LM_WARN("Failed to get service-identifier\n");
+       return units;
+}
+
+
+/**
+ * Create and add an AVP to a Diameter message.
+ * @param m - Diameter message to add to
+ * @param d - the payload data
+ * @param len - length of the payload data
+ * @param avp_code - the code of the AVP
+ * @param flags - flags for the AVP
+ * @param vendorid - the value of the vendor id or 0 if none
+ * @param data_do - what to do with the data when done
+ * @param func - the name of the calling function, for debugging purposes
+ * @returns 1 on success or 0 on failure
+ */
+int ocs_add_avp(AAAMessage *m, char *d, int len, int avp_code, int flags, int vendorid, int data_do, const char *func) {
+    AAA_AVP *avp;
+    if (vendorid != 0) flags |= AAA_AVP_FLAG_VENDOR_SPECIFIC;
+    avp = cdpb.AAACreateAVP(avp_code, flags, vendorid, d, len, data_do);
+    if (!avp) {
+        LM_ERR("%s: Failed creating avp\n", func);
+        return 0;
+    }
+    if (cdpb.AAAAddAVPToMessage(m, avp, m->avpList.tail) != AAA_ERR_SUCCESS) {
+        LM_ERR("%s: Failed adding avp to message\n", func);
+       cdpb.AAAFreeAVP(&avp);
+        return 0;
+    }
+    return 1;
+}
+
+
+/**
+ * Create and add an AVP to a list of AVPs.
+ * @param list - the AVP list to add to
+ * @param d - the payload data
+ * @param len - length of the payload data
+ * @param avp_code - the code of the AVP
+ * @param flags - flags for the AVP
+ * @param vendorid - the value of the vendor id or 0 if none
+ * @param data_do - what to do with the data when done
+ * @param func - the name of the calling function, for debugging purposes
+ * @returns 1 on success or 0 on failure
+ */
+int ocs_add_avp_list(AAA_AVP_LIST *list, char *d, int len, int avp_code,
+       int flags, int vendorid, int data_do, const char *func) {
+    AAA_AVP *avp;
+    if (vendorid != 0) flags |= AAA_AVP_FLAG_VENDOR_SPECIFIC;
+    avp = cdpb.AAACreateAVP(avp_code, flags, vendorid, d, len, data_do);
+    if (!avp) {
+       LM_ERR("%s: Failed creating avp\n", func);
+       return 0;
+    }
+    if (list->tail) {
+       avp->prev = list->tail;
+       avp->next = 0;
+       list->tail->next = avp;
+       list->tail = avp;
+    } else {
+       list->head = avp;
+       list->tail = avp;
+       avp->next = 0;
+       avp->prev = 0;
+    }
+
+    return 1;
+}
+
+int ocs_build_answer(AAAMessage *ccr, AAAMessage *cca, int result_code, int granted_units, int final_unit) {
+       AAA_AVP *avp;
+       AAA_AVP_LIST granted_list, mscc_list, final_list;
+       char x[4];
+       str granted_group, mscc_group, final_group;
+       int service, group, used;
+       
+       if (!ccr) return 0;
+       if (!cca) return 0;
+
+       // Set some basic data: Application-ID, CCR-Type, CCR-Request-Number
+       set_4bytes(x, IMS_Ro);
+       ocs_add_avp(cca, x, 4, AVP_Acct_Application_Id, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
+       
+       avp = cdpb.AAAFindMatchingAVP(ccr,0,AVP_IMS_CCR_Type,0,0);
+       ocs_add_avp(cca, avp->data.s, avp->data.len, AVP_IMS_CCR_Type, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
+
+       avp = cdpb.AAAFindMatchingAVP(ccr,0,AVP_CC_Request_Number,0,0);
+       ocs_add_avp(cca, avp->data.s, avp->data.len,AVP_CC_Request_Number, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
+       
+       // Result-Code:
+       set_4bytes(x, result_code);
+       ocs_add_avp(cca, x, 4, AVP_Result_Code, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
+
+       if (result_code == DIAMETER_SUCCESS) {
+               granted_list.head = 0;
+               granted_list.tail = 0;
+               final_list.head = 0;
+               final_list.tail = 0;
+               mscc_list.head = 0;
+               mscc_list.tail = 0;
+
+               getUnits(ccr, &used, &service, &group);
+       
+               set_4bytes(x, group);
+               ocs_add_avp_list(&mscc_list, x, 4, AVP_Rating_Group, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
+
+               set_4bytes(x, service);
+               ocs_add_avp_list(&mscc_list, x, 4, AVP_Service_Identifier, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
+
+               if (granted_units > 0) {
+                       set_4bytes(x, granted_units);
+                       ocs_add_avp_list(&granted_list, x, 4, AVP_CC_Time, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
+                       granted_group = cdpb.AAAGroupAVPS(granted_list);
+                       cdpb.AAAFreeAVPList(&granted_list);
+                       ocs_add_avp_list(&mscc_list, granted_group.s, granted_group.len, AVP_Granted_Service_Unit, AAA_AVP_FLAG_MANDATORY, 0, AVP_FREE_DATA, __FUNCTION__);
+               }
+
+               // Result-Code:
+               set_4bytes(x, result_code);
+               ocs_add_avp_list(&mscc_list, x, 4, AVP_Result_Code, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
+
+               set_4bytes(x, 86400);
+               ocs_add_avp_list(&mscc_list, x, 4, AVP_Validity_Time, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
+
+
+               if (final_unit > 0) {
+                       set_4bytes(x, 0);
+                       ocs_add_avp_list(&final_list, x, 4, AVP_Final_Unit_Action, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
+                       final_group = cdpb.AAAGroupAVPS(final_list);
+                       cdpb.AAAFreeAVPList(&final_list);
+                       ocs_add_avp_list(&mscc_list, final_group.s, final_group.len, AVP_Final_Unit_Indication, AAA_AVP_FLAG_MANDATORY, 0, AVP_FREE_DATA, __FUNCTION__);
+               }
+
+
+               mscc_group = cdpb.AAAGroupAVPS(mscc_list);
+               cdpb.AAAFreeAVPList(&mscc_list);
+
+               return ocs_add_avp(cca, mscc_group.s, mscc_group.len, AVP_Multiple_Services_Credit_Control, AAA_AVP_FLAG_MANDATORY, 0, AVP_FREE_DATA, __FUNCTION__);
+       }
+       return 1;
+}
diff --git a/modules/ims_ocs/ocs_avp_helper.h b/modules/ims_ocs/ocs_avp_helper.h
new file mode 100644 (file)
index 0000000..b581607
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright (C) 2015 ng-voice GmbH, Carsten Bock, carsten@ng-voice.com
+ *
+ * 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 OCS_AVP_HELPER_H
+#define OCS_AVP_HELPER_H
+
+#include "../cdp/diameter_api.h"
+
+str get_avp(AAAMessage *msg,int avp_code,int vendor_id, const char *func);
+str getSession(AAAMessage *msg);
+int getRecordNummber(AAAMessage *msg);
+str getSubscriptionId1(AAAMessage *msg, int * type);
+int isOrig(AAAMessage *msg);
+str getCalledParty(AAAMessage *msg);
+int getUnits(AAAMessage *msg, int * used, int * service, int * group);
+str getAccessNetwork(AAAMessage *msg);
+
+int ocs_build_answer(AAAMessage *ccr, AAAMessage *cca, int result_code, int granted_units, int final_unit);
+
+#endif /* OCS_AVP_HELPER_H */
diff --git a/modules/ims_ocs/sem.h b/modules/ims_ocs/sem.h
new file mode 100644 (file)
index 0000000..48a2431
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
+ * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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:
+ * --------
+ *  2011-02-02  initial version (jason.penton)
+ */
+
+#ifndef __SEM_H
+#define __SEM_H
+
+       #include <semaphore.h>
+
+       typedef sem_t gen_sem_t;
+
+       /**
+        * Create a new unnamed semaphore and initialize it
+        * @param value - 0 if it should be pre-locked, 1 if not, or how many locks until block
+        * @return
+        */
+    #define sem_new(sem_ptr,value)\
+       do {\
+               sem_ptr=shm_malloc(sizeof(gen_sem_t));\
+               if (!sem_ptr){\
+                       LM_ERR("Error allocating %lx bytes of shm!\n",sizeof(gen_sem_t));\
+               }       \
+               if (sem_init(sem_ptr, 1, value)<0) {\
+                       LM_ERR("Error > %s\n",strerror(errno));\
+               }\
+       } while(0)
+       
+    #define sem_free(sem)\
+       do {\
+               if (sem) {\
+                       sem_destroy(sem);\
+                       shm_free(sem);\
+                       sem=0;\
+               }\
+       } while(0)
+       
+       
+       #define sem_get(sem) sem_wait(sem)
+       #define sem_tryget(sem) sem_trywait(sem)
+       #define sem_timedget(sem,abs_timeout) sem_trywait(sem,abs_timeout)
+       
+       #define sem_release(sem) sem_post(sem)
+
+#endif
index aa3f209..b1423ee 100644 (file)
@@ -56,6 +56,7 @@ Carsten Bock
               3.13. regex_sdp_ip_prefix_to_maintain_in_fd (String)
               3.14. terminate_dialog_on_rx_failure integer
               3.15. delete_contact_on_rx_failure integer
+              3.16. include_rtcp_fd integer
 
         4. Functions
 
@@ -85,8 +86,9 @@ Carsten Bock
    1.13. regex_sdp_ip_prefix_to_maintain_in_fd parameter usage
    1.14. terminate_dialog_on_rx_failure parameter usage
    1.15. delete_contact_on_rx_failure parameter usage
-   1.16. Rx_AAR_Register
-   1.17. Rx_AAR
+   1.16. include_rtcp_fd parameter usage
+   1.17. Rx_AAR_Register
+   1.18. Rx_AAR
 
 Chapter 1. Admin Guide
 
@@ -115,6 +117,7 @@ Chapter 1. Admin Guide
         3.13. regex_sdp_ip_prefix_to_maintain_in_fd (String)
         3.14. terminate_dialog_on_rx_failure integer
         3.15. delete_contact_on_rx_failure integer
+        3.16. include_rtcp_fd integer
 
    4. Functions
 
@@ -170,6 +173,7 @@ Chapter 1. Admin Guide
    3.13. regex_sdp_ip_prefix_to_maintain_in_fd (String)
    3.14. terminate_dialog_on_rx_failure integer
    3.15. delete_contact_on_rx_failure integer
+   3.16. include_rtcp_fd integer
 
 3.1. rx_dest_realm (string)
 
@@ -367,6 +371,18 @@ modparam("ims_qos", "terminate_dialog_on_rx_failure", 0)
 modparam("ims_qos", "delete_contact_on_rx_failure", 0)
 ...
 
+3.16. include_rtcp_fd integer
+
+   If set then a flow description is added for media flows - next
+   available odd port is used as the default for RTCP traffic
+
+   Default value is 0, RTCP flow description not added
+
+   Example 1.16. include_rtcp_fd parameter usage
+...
+modparam("ims_qos", "include_rtcp_fd", 1)
+...
+
 4. Functions
 
    4.1. Rx_AAR_Register(route_block, domain)
@@ -397,7 +413,7 @@ modparam("ims_qos", "delete_contact_on_rx_failure", 0)
    p.s. this is executed asynchronously. See example on how to retrieve
    return value
 
-   Example 1.16. Rx_AAR_Register
+   Example 1.17. Rx_AAR_Register
 ...
 if(Rx_AAR_Register("REG_AAR_REPLY","location")==0){
     exit;
@@ -439,7 +455,7 @@ route[REG_AAR_REPLY]
    p.s. this is executed asynchronously. See example on how to retrieve
    return value
 
-   Example 1.17. Rx_AAR
+   Example 1.18. Rx_AAR
 ...
 if(Rx_AAR("ORIG_SESSION_AAR_REPLY","orig","",-1)==0){
     exit;
index 3c4b73f..640ae66 100644 (file)
@@ -365,6 +365,26 @@ modparam("ims_qos", "delete_contact_on_rx_failure", 0)
       </example>
     </section>
     
+    <section>
+      <title><varname>include_rtcp_fd</varname> integer</title>
+
+      <para>If set then a flow description is added for media flows - next available odd port 
+          is used as the default for RTCP traffic</para>
+
+      <para><emphasis> Default value is 0, RTCP flow description not added</emphasis></para>
+
+      <example>
+        <title><varname>include_rtcp_fd</varname> parameter
+        usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("ims_qos", "include_rtcp_fd", 1)
+...
+        </programlisting>
+      </example>
+    </section>
+    
   </section>
 
   <section>
index f088d1a..58891ae 100644 (file)
@@ -104,6 +104,9 @@ int video_default_bandwidth = 128;
 //Very useful for UEs that change ports mid way through call therefore breaking flow description filters
 str regex_sdp_ip_prefix_to_maintain_in_fd = {0, 0};
 
+//If set this will include an additional filter for all existing filters using the next odd port up - as this is the RTCP port
+int include_rtcp_fd = 0;
+
 int cdp_event_list_size_threshold = 0; /**Threshold for size of cdp event list after which a warning is logged */
 
 stat_var *aars;
@@ -204,6 +207,7 @@ static param_export_t params[] = {
                { "terminate_dialog_on_rx_failure", INT_PARAM, &terminate_dialog_on_rx_failure},
                { "delete_contact_on_rx_failure", INT_PARAM, &delete_contact_on_rx_failure},
                { "regex_sdp_ip_prefix_to_maintain_in_fd", PARAM_STR, &regex_sdp_ip_prefix_to_maintain_in_fd},
+               { "include_rtcp_fd", INT_PARAM, &include_rtcp_fd},
                { 0, 0, 0}
 };
 
index 8ba6f4e..f23740b 100644 (file)
@@ -436,8 +436,15 @@ int add_media_components(AAAMessage* aar, struct sip_msg *req,
     sdp_session_cell_t* req_sdp_session, *rpl_sdp_session;
     sdp_stream_cell_t* req_sdp_stream, *rpl_sdp_stream;
     int add_flow = 1;
-
-    if (!req || !rpl) {
+       str ttag = {0, 0};
+       str ftag = {0, 0};
+       int request_originated_from_callee = 0;  
+       str ipA, ipB;
+       
+       rx_authsessiondata_t* p_session_data = 0;
+    p_session_data = (rx_authsessiondata_t*) auth->u.auth.generic_data;
+       
+       if (!req || !rpl) {
                        goto error;
     }
 
@@ -497,23 +504,57 @@ int add_media_components(AAAMessage* aar, struct sip_msg *req,
 
                        if(add_flow) {
 
-                                               str ipA = req_sdp_session->ip_addr;
-                                               str ipB = rpl_sdp_session->ip_addr;
+                                       
+                                               if (cscf_get_to_tag(rpl, &ttag) && cscf_get_from_tag(rpl, &ftag)) {
+                                                               LM_DBG("Original ftag [%.*s] ttag [%.*s].  Current ftag [%.*s] ttag [%.*s]\n", 
+                                                                               p_session_data->ftag.len, p_session_data->ftag.s, p_session_data->ttag.len, p_session_data->ttag.s,
+                                                                               ftag.len, ftag.s, ttag.len, ttag.s);
+                                                       if (!(strncmp(p_session_data->ttag.s, ttag.s, p_session_data->ttag.len) == 0 && strncmp(p_session_data->ftag.s, ftag.s, p_session_data->ftag.len) == 0)) {
+                                                               LM_DBG("ftag and ttag of this response do not match initial response so this request came from callee\n");
+                                                               request_originated_from_callee = 1;
+                                                       }
+                                               } else {
+                                                       LM_ERR("Couldn't retrieve ftag so assume this request originated from caller\n");
+                                               }
+                                       
+                                               if (request_originated_from_callee) {
+                                                       LM_DBG("Request originated from callee so IPs are reversed\n"); 
+                                                       ipA = rpl_sdp_session->ip_addr;
+                                                       ipB = req_sdp_session->ip_addr;
+                                               } else {
+                                                       ipA = req_sdp_session->ip_addr;
+                                                       ipB = rpl_sdp_session->ip_addr;
+                                               }
+                                               
 
                                                if (ipA.len <= 0) {
-                                                               LM_DBG("Request SDP connection IP could not be retrieved, so we use SDP 1st stream IP");
-                                                               ipA = req_sdp_stream->ip_addr;
+                                                               LM_DBG("Request SDP connection IP could not be retrieved, so we use SDP 1st stream IP\n");
+                                                               if (request_originated_from_callee) {
+                                                                       LM_DBG("Request originated from callee so IPs are reversed\n"); 
+                                                                       ipA = rpl_sdp_stream->ip_addr;
+                                                               } else {
+                                                                       ipA = req_sdp_stream->ip_addr;
+                                                               }
+                                                               
+                                                               
                                                                if (ipA.len <= 0) {
-                                                                               LM_ERR("Requested SDP IP information could not be retrieved");
+                                                                               LM_ERR("Requested SDP IP information could not be retrieved\n");
                                                                                goto error;
                                                                }
                                                }
 
                                                if (ipB.len <= 0) {
-                                                               LM_DBG("Reply SDP connection IP could not be retrieved, so we use SDP 1st stream IP");
-                                                               ipB = rpl_sdp_stream->ip_addr;
+                                                               LM_DBG("Reply SDP connection IP could not be retrieved, so we use SDP 1st stream IP\n");
+                                                               if (request_originated_from_callee) {
+                                                                       LM_DBG("Request originated from callee so IPs are reversed\n"); 
+                                                                       ipB = req_sdp_stream->ip_addr;
+                                                               } else {
+                                                                       ipB = rpl_sdp_stream->ip_addr;
+                                                               }
+                                                               
+                                                               
                                                                if (ipB.len <= 0) {
-                                                                               LM_ERR("Request SDP IP information could not be retrieved");
+                                                                               LM_ERR("Request SDP IP information could not be retrieved\n");
                                                                                goto error;
                                                                }
                                                }
index 58f7e2e..a7b9020 100644 (file)
@@ -66,6 +66,8 @@ extern cdp_avp_bind_t *cdp_avp;
 
 extern str regex_sdp_ip_prefix_to_maintain_in_fd;
 
+extern int include_rtcp_fd;
+
 static const int prefix_length_ipv6 = 128;
 
 /**
@@ -675,8 +677,12 @@ AAA_AVP *rx_create_media_subcomponent_avp(int number, str* proto,
                str *ipB, str *portB, int flow_usage_type)
 {
                str data;
-               int len, len2, len3;
+               
+               int len, len2;
+               int int_port_rctp_a,int_port_rctp_b;
+               str port_rtcp_a, port_rtcp_b;
                AAA_AVP *flow_description1 = 0, *flow_description2 = 0, *flow_description3 = 0, *flow_description4 = 0, *flow_number = 0;
+               AAA_AVP *flow_description5 = 0, *flow_description6 = 0, *flow_description7 = 0, *flow_description8 = 0;
                AAA_AVP *flow_usage = 0;
 
                AAA_AVP_LIST list;
@@ -704,7 +710,145 @@ AAA_AVP *rx_create_media_subcomponent_avp(int number, str* proto,
 
                int intportA = atoi(portA->s);
                int intportB = atoi(portB->s);
+               
+               set_4bytes(x, number);
+               flow_number = cdpb.AAACreateAVP(AVP_IMS_Flow_Number,
+                               AAA_AVP_FLAG_MANDATORY | AAA_AVP_FLAG_VENDOR_SPECIFIC,
+                               IMS_vendor_id_3GPP, x, 4,
+                               AVP_DUPLICATE_DATA);
+               cdpb.AAAAddAVPToList(&list, flow_number);
+                               
+               /*IMS Flow descriptions*/
+               /*first flow is the receive flow*/
+               
+               len = (permit_out.len + from_s.len + to_s.len + ipB->len + ipA->len + 4 +
+                                               proto_len + portA->len + portB->len + 1/*nul terminator*/) * sizeof(char);
+               
+               if (!flowdata_buf.s || flowdata_buflen < len) {
+                               if (flowdata_buf.s)
+                                               pkg_free(flowdata_buf.s);
+                               flowdata_buf.s = (char*) pkg_malloc(len);
+                               if (!flowdata_buf.s) {
+                                               LM_ERR("PCC_create_media_component: out of memory \
+                                                        when allocating %i bytes in pkg\n", len);
+                                               return NULL;
+                               }
+                               flowdata_buflen = len;
+               }
+               
+               flowdata_buf.len = snprintf(flowdata_buf.s, len, permit_out_with_ports, proto_nr,
+                                               ipA->len, ipA->s, intportA,
+                                               ipB->len, ipB->s, intportB);
+               
+               flowdata_buf.len = strlen(flowdata_buf.s);
+               flow_description1 = cdpb.AAACreateAVP(AVP_IMS_Flow_Description,
+                               AAA_AVP_FLAG_MANDATORY | AAA_AVP_FLAG_VENDOR_SPECIFIC,
+                               IMS_vendor_id_3GPP, flowdata_buf.s, flowdata_buf.len,
+                               AVP_DUPLICATE_DATA);
+               cdpb.AAAAddAVPToList(&list, flow_description1);
+               
+               /*second flow*/
+               len2 = (permit_in.len + from_s.len + to_s.len + ipB->len + ipA->len + 4 +
+                                               proto_len + portA->len + portB->len + 1/*nul terminator*/) * sizeof(char);              
+               if (!flowdata_buf.s || len < len2) {
+                               len = len2;
+                               if (flowdata_buf.s)
+                                               pkg_free(flowdata_buf.s);
+                               flowdata_buf.s = (char*) pkg_malloc(len);
+                               if (!flowdata_buf.s) {
+                                               LM_ERR("PCC_create_media_component: out of memory \
+                                                                when allocating %i bytes in pkg\n", len);
+                                               return NULL;
+                               }
+                               flowdata_buflen = len;
+               }
+               
+               flowdata_buf.len = snprintf(flowdata_buf.s, len, permit_in_with_ports, proto_nr,
+                                               ipB->len, ipB->s, intportB,
+                                               ipA->len, ipA->s, intportA);
+               
+               flowdata_buf.len = strlen(flowdata_buf.s);
+               flow_description2 = cdpb.AAACreateAVP(AVP_IMS_Flow_Description,
+                               AAA_AVP_FLAG_MANDATORY | AAA_AVP_FLAG_VENDOR_SPECIFIC,
+                               IMS_vendor_id_3GPP, flowdata_buf.s, flowdata_buf.len,
+                               AVP_DUPLICATE_DATA);
+               cdpb.AAAAddAVPToList(&list, flow_description2);
+               
+               if (include_rtcp_fd) {
+                               LM_DBG("Need to add RTCP FD description - RTCP ports are by default next odd port number up from RTP ports\n");
+                               int_port_rctp_a = intportA + 1;
+                               if (int_port_rctp_a % 2 == 0) {
+                                               int_port_rctp_a ++;
+                               }
+                               int_port_rctp_b = intportB + 1;
+                               if (int_port_rctp_b % 2 == 0) {
+                                               int_port_rctp_b ++;
+                               }
+                               char c_port_rtcp_a[5];
+                               port_rtcp_a.len = sprintf(c_port_rtcp_a, "%d", int_port_rctp_a);
+                               port_rtcp_a.s = c_port_rtcp_a;
+                               char c_port_rtcp_b[5];
+                               port_rtcp_b.len = sprintf(c_port_rtcp_b, "%d", int_port_rctp_b);
+                               port_rtcp_b.s = c_port_rtcp_b;
+                               LM_DBG("RTCP A Port [%.*s] RCTP B Port [%.*s]\n", port_rtcp_a.len, port_rtcp_a.s, port_rtcp_b.len, port_rtcp_b.s);
+                               
+                               /*3rd (optional RTCP) flow*/
+                               len2 = (permit_out.len + from_s.len + to_s.len + ipB->len + ipA->len + 4 +
+                                               proto_len + port_rtcp_a.len + port_rtcp_b.len + 1/*nul terminator*/) * sizeof(char);
+               
+                               if (!flowdata_buf.s || len < len2) {
+                                               len = len2;
+                                               if (flowdata_buf.s)
+                                                               pkg_free(flowdata_buf.s);
+                                               flowdata_buf.s = (char*) pkg_malloc(len);
+                                               if (!flowdata_buf.s) {
+                                                               LM_ERR("PCC_create_media_component: out of memory \
+                                                                                                                               when allocating %i bytes in pkg\n", len);
+                                                               return NULL;
+                                               }
+                                               flowdata_buflen = len;
+                               }
+
+                               flowdata_buf.len = snprintf(flowdata_buf.s, len, permit_out_with_ports, proto_nr,
+                                                               ipA->len, ipA->s, int_port_rctp_a,
+                                                               ipB->len, ipB->s, int_port_rctp_b);
+
+                               flowdata_buf.len = strlen(flowdata_buf.s);
+                               flow_description3 = cdpb.AAACreateAVP(AVP_IMS_Flow_Description,
+                                               AAA_AVP_FLAG_MANDATORY | AAA_AVP_FLAG_VENDOR_SPECIFIC,
+                                               IMS_vendor_id_3GPP, flowdata_buf.s, flowdata_buf.len,
+                                               AVP_DUPLICATE_DATA);
+                               cdpb.AAAAddAVPToList(&list, flow_description3);
+                               
+                               /*4th (optional RTCP) flow*/
+                               len2 = (permit_in.len + from_s.len + to_s.len + ipB->len + ipA->len + 4 +
+                                               proto_len + port_rtcp_a.len + port_rtcp_b.len + 1/*nul terminator*/) * sizeof(char);            
+                               if (!flowdata_buf.s || len < len2) {
+                                               len = len2;
+                                               if (flowdata_buf.s)
+                                                               pkg_free(flowdata_buf.s);
+                                               flowdata_buf.s = (char*) pkg_malloc(len);
+                                               if (!flowdata_buf.s) {
+                                                               LM_ERR("PCC_create_media_component: out of memory \
+                                                                                                                                               when allocating %i bytes in pkg\n", len);
+                                                               return NULL;
+                                               }
+                                               flowdata_buflen = len;
+                               }
 
+                               flowdata_buf.len = snprintf(flowdata_buf.s, len, permit_in_with_ports, proto_nr,
+                                                               ipB->len, ipB->s, int_port_rctp_b,
+                                                               ipA->len, ipA->s, int_port_rctp_a);
+
+                               flowdata_buf.len = strlen(flowdata_buf.s);
+                               flow_description4 = cdpb.AAACreateAVP(AVP_IMS_Flow_Description,
+                                               AAA_AVP_FLAG_MANDATORY | AAA_AVP_FLAG_VENDOR_SPECIFIC,
+                                               IMS_vendor_id_3GPP, flowdata_buf.s, flowdata_buf.len,
+                                               AVP_DUPLICATE_DATA);
+                               cdpb.AAAAddAVPToList(&list, flow_description4);
+                               
+               }
+               
                int useAnyForIpA = 0;
                int useAnyForIpB = 0;
 
@@ -728,153 +872,210 @@ AAA_AVP *rx_create_media_subcomponent_avp(int number, str* proto,
                                }
 
                }
-
-               if (!useAnyForIpA && !useAnyForIpB) {
-                               len = (permit_out.len + from_s.len + to_s.len + ipB->len + ipA->len + 4 +
-                                               proto_len + portA->len + portB->len + 1/*nul terminator*/) * sizeof(char);
-               } else if (useAnyForIpA) {
-                               len = (permit_out.len + from_s.len + to_s.len + 3 /*for 'any'*/ + ipB->len + 4 +
+               
+               if (useAnyForIpA) {
+                               /*5th (optional replace IP A with ANY) flow*/
+                               len2 = (permit_out.len + from_s.len + to_s.len + 3 /*for 'any'*/ + ipB->len + 4 +
                                                proto_len + portB->len + 1/*nul terminator*/) * sizeof(char);
-                               len3 = (permit_out.len + from_s.len + to_s.len + ipB->len + ipA->len + 4 +
-                                               proto_len + portA->len + portB->len + 1/*nul terminator*/) * sizeof(char);
-               } else if (useAnyForIpB) {
-                               len = (permit_out.len + from_s.len + to_s.len + 3 /*for 'any'*/ + ipA->len + 4 +
-                                               proto_len + portA->len + 1/*nul terminator*/) * sizeof(char);
-                               len3 = (permit_out.len + from_s.len + to_s.len + ipB->len + ipA->len + 4 +
-                                               proto_len + portA->len + portB->len + 1/*nul terminator*/) * sizeof(char);
-               }
-
-               if (!flowdata_buf.s || flowdata_buflen < len) {
-                               if (flowdata_buf.s)
-                                               pkg_free(flowdata_buf.s);
-                               flowdata_buf.s = (char*) pkg_malloc(len);
-                               if (!flowdata_buf.s) {
-                                               LM_ERR("PCC_create_media_component: out of memory \
-                                                        when allocating %i bytes in pkg\n", len);
-                                               return NULL;
+                               if (!flowdata_buf.s || len < len2) {
+                                               len = len2;
+                                               if (flowdata_buf.s)
+                                                               pkg_free(flowdata_buf.s);
+                                               flowdata_buf.s = (char*) pkg_malloc(len);
+                                               if (!flowdata_buf.s) {
+                                                               LM_ERR("PCC_create_media_component: out of memory \
+                                                                                                                               when allocating %i bytes in pkg\n", len);
+                                                               return NULL;
+                                               }
+                                               flowdata_buflen = len;
                                }
-                               flowdata_buflen = len;
-               }
-
-               set_4bytes(x, number);
-
-               flow_number = cdpb.AAACreateAVP(AVP_IMS_Flow_Number,
-                               AAA_AVP_FLAG_MANDATORY | AAA_AVP_FLAG_VENDOR_SPECIFIC,
-                               IMS_vendor_id_3GPP, x, 4,
-                               AVP_DUPLICATE_DATA);
-               cdpb.AAAAddAVPToList(&list, flow_number);
-
-               /*IMS Flow descriptions*/
-               /*first flow is the receive flow*/
-               if (!useAnyForIpA && !useAnyForIpB) {
-                               flowdata_buf.len = snprintf(flowdata_buf.s, len, permit_out_with_ports, proto_nr,
-                                               ipA->len, ipA->s, intportA,
-                                               ipB->len, ipB->s, intportB);
-               } else if (useAnyForIpA) {
                                flowdata_buf.len = snprintf(flowdata_buf.s, len, permit_out_with_any_as_dst, proto_nr,
                                                ipB->len, ipB->s, intportB);
-               } else if (useAnyForIpB) {
-                               flowdata_buf.len = snprintf(flowdata_buf.s, len, permit_out_with_any_as_dst, proto_nr,
-                                               ipA->len, ipA->s, intportA);
-               }
-
-
-               flowdata_buf.len = strlen(flowdata_buf.s);
-               flow_description1 = cdpb.AAACreateAVP(AVP_IMS_Flow_Description,
-                               AAA_AVP_FLAG_MANDATORY | AAA_AVP_FLAG_VENDOR_SPECIFIC,
-                               IMS_vendor_id_3GPP, flowdata_buf.s, flowdata_buf.len,
-                               AVP_DUPLICATE_DATA);
-               cdpb.AAAAddAVPToList(&list, flow_description1);
-
-               //Extra flow for some devices - if you have any any filter also include the specific one
-               if (regex_sdp_ip_prefix_to_maintain_in_fd.len > 0 && regex_sdp_ip_prefix_to_maintain_in_fd.s && (useAnyForIpA || useAnyForIpB)) {
+                               flowdata_buf.len = strlen(flowdata_buf.s);
+                               flow_description5 = cdpb.AAACreateAVP(AVP_IMS_Flow_Description,
+                                               AAA_AVP_FLAG_MANDATORY | AAA_AVP_FLAG_VENDOR_SPECIFIC,
+                                               IMS_vendor_id_3GPP, flowdata_buf.s, flowdata_buf.len,
+                                               AVP_DUPLICATE_DATA);
+                               cdpb.AAAAddAVPToList(&list, flow_description5);
+                               
+                               if (include_rtcp_fd) {
+                                               /*7th (optional RTCP replace IP A with ANY) flow*/
+                                               len2 = (permit_out.len + from_s.len + to_s.len + 3 /*for 'any'*/ + ipB->len + 4 +
+                                                               proto_len + port_rtcp_b.len + 1/*nul terminator*/) * sizeof(char);
+                                               if (!flowdata_buf.s || len < len2) {
+                                                               len = len2;
+                                                               if (flowdata_buf.s)
+                                                                               pkg_free(flowdata_buf.s);
+                                                               flowdata_buf.s = (char*) pkg_malloc(len);
+                                                               if (!flowdata_buf.s) {
+                                                                               LM_ERR("PCC_create_media_component: out of memory \
+                                                                                                                                               when allocating %i bytes in pkg\n", len);
+                                                                               return NULL;
+                                                               }
+                                                               flowdata_buflen = len;
+                                               }
+                                               flowdata_buf.len = snprintf(flowdata_buf.s, len, permit_out_with_any_as_dst, proto_nr,
+                                                               ipB->len, ipB->s, int_port_rctp_b);
+                                               flowdata_buf.len = strlen(flowdata_buf.s);
+                                               flow_description7 = cdpb.AAACreateAVP(AVP_IMS_Flow_Description,
+                                                               AAA_AVP_FLAG_MANDATORY | AAA_AVP_FLAG_VENDOR_SPECIFIC,
+                                                               IMS_vendor_id_3GPP, flowdata_buf.s, flowdata_buf.len,
+                                                               AVP_DUPLICATE_DATA);
+                                               cdpb.AAAAddAVPToList(&list, flow_description7);
+                               }
                                
-                               LM_DBG("This is to fix some devices that want specific and any any filters");
                                
-                               len2 = len3;
-                               if (!flowdata_buf.s || flowdata_buflen <= len2) {
+                               /*6th (optional replace IP A with ANY) flow*/
+                               len2 = (permit_in.len + from_s.len + to_s.len + 3 /*for 'any'*/ + ipB->len + 4 +
+                                               proto_len + portB->len + 1/*nul terminator*/) * sizeof(char);
+                               if (!flowdata_buf.s || len < len2) {
+                                               len = len2;
                                                if (flowdata_buf.s)
                                                                pkg_free(flowdata_buf.s);
-                                               flowdata_buf.s = (char*) pkg_malloc(len2);
+                                               flowdata_buf.s = (char*) pkg_malloc(len);
                                                if (!flowdata_buf.s) {
                                                                LM_ERR("PCC_create_media_component: out of memory \
-                                                                                                                                               when allocating %i bytes in pkg\n", len2);
+                                                                                                                               when allocating %i bytes in pkg\n", len);
                                                                return NULL;
                                                }
-                                               flowdata_buflen = len2;
+                                               flowdata_buflen = len;
                                }
-                               
-                               flowdata_buf.len = snprintf(flowdata_buf.s, len3, permit_out_with_ports, proto_nr,
-                                               ipA->len, ipA->s, intportA,
+                               flowdata_buf.len = snprintf(flowdata_buf.s, len, permit_in_with_any_as_src, proto_nr,
                                                ipB->len, ipB->s, intportB);
                                flowdata_buf.len = strlen(flowdata_buf.s);
-                               flow_description3 = cdpb.AAACreateAVP(AVP_IMS_Flow_Description,
+                               flow_description6 = cdpb.AAACreateAVP(AVP_IMS_Flow_Description,
                                                AAA_AVP_FLAG_MANDATORY | AAA_AVP_FLAG_VENDOR_SPECIFIC,
                                                IMS_vendor_id_3GPP, flowdata_buf.s, flowdata_buf.len,
                                                AVP_DUPLICATE_DATA);
-                               cdpb.AAAAddAVPToList(&list, flow_description3);
-
-               }
-               
-               /*second flow*/
-               len2 = len - (permit_out.len - permit_in.len) * sizeof(char);
-               if (!flowdata_buf.s || flowdata_buflen <= len2) {
-                               if (flowdata_buf.s)
-                                               pkg_free(flowdata_buf.s);
-                               flowdata_buf.s = (char*) pkg_malloc(len2);
-                               if (!flowdata_buf.s) {
-                                               LM_ERR("PCC_create_media_component: out of memory \
-                                                                when allocating %i bytes in pkg\n", len2);
-                                               return NULL;
+                               cdpb.AAAAddAVPToList(&list, flow_description6);
+                               
+                               if (include_rtcp_fd) {
+                                               /*8th (optional RTCP replace IP A with ANY) flow*/
+                                               len2 = (permit_in.len + from_s.len + to_s.len + 3 /*for 'any'*/ + ipB->len + 4 +
+                                                               proto_len + port_rtcp_b.len + 1/*nul terminator*/) * sizeof(char);
+                                               if (!flowdata_buf.s || len < len2) {
+                                                               len = len2;
+                                                               if (flowdata_buf.s)
+                                                                               pkg_free(flowdata_buf.s);
+                                                               flowdata_buf.s = (char*) pkg_malloc(len);
+                                                               if (!flowdata_buf.s) {
+                                                                               LM_ERR("PCC_create_media_component: out of memory \
+                                                                                        &n