Explorar o código

modules_k/rtpproxy: new module

 - copy initial files from nathelper to rtpproxy
Ovidiu Sas %!s(int64=15) %!d(string=hai) anos
pai
achega
cef5d23c23

+ 19 - 0
modules_k/rtpproxy/Makefile

@@ -0,0 +1,19 @@
+# $Id$
+#
+# print example module makefile
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=nathelper.so
+LIBS=
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/kmi/kmi
+SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+include ../../Makefile.modules
+

+ 1027 - 0
modules_k/rtpproxy/README

@@ -0,0 +1,1027 @@
+nathelper Module
+
+Maxim Sobolev
+
+   Sippy Software, Inc.
+
+Juha Heinanen
+
+   TuTPro, Inc.
+
+Edited by
+
+Maxim Sobolev
+
+Edited by
+
+Bogdan-Andrei Iancu
+
+Edited by
+
+Juha Heinanen
+
+   Copyright © 2003-2008 Sippy Software, Inc.
+
+   Copyright © 2005 voice-system.ro
+
+   Copyright © 2009 TuTPro Inc.
+   Revision History
+   Revision $Revision$ $Date$
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. NAT pinging types
+        3. Multiple RTPProxy usage
+        4. Dependencies
+
+              4.1. Kamailio Modules
+              4.2. External Libraries or Applications
+
+        5. Exported Parameters
+
+              5.1. natping_interval (integer)
+              5.2. ping_nated_only (integer)
+              5.3. natping_processes (integer)
+              5.4. natping_socket (string)
+              5.5. received_avp (str)
+              5.6. rtpproxy_sock (string)
+              5.7. rtpproxy_disable_tout (integer)
+              5.8. rtpproxy_tout (integer)
+              5.9. rtpproxy_retr (integer)
+              5.10. force_socket (string)
+              5.11. sipping_bflag (integer)
+              5.12. sipping_from (string)
+              5.13. sipping_method (string)
+              5.14. nortpproxy_str (string)
+
+        6. Exported Functions
+
+              6.1. fix_nated_contact()
+              6.2. fix_nated_sdp(flags [, ip_address])
+              6.3. set_rtp_proxy_set()
+              6.4. force_rtp_proxy([flags [, ip_address]])
+              6.5. rtpproxy_offer([flags [, ip_address]])
+              6.6. rtpproxy_answer([flags [, ip_address]])
+              6.7. unforce_rtp_proxy()
+              6.8. rtpproxy_stream2uac(prompt_name, count),
+                      rtpproxy_stream2uas(prompt_name, count)
+
+              6.9. rtpproxy_stop_stream2uac(), rtpproxy_stop_stream2uas()
+              6.10. add_rcv_param([flag]),
+              6.11. fix_nated_register()
+              6.12. nat_uac_test(flags)
+              6.13. start_recording()
+              6.14. add_contact_alias()
+              6.15. handle_ruri_alias()
+
+        7. Exported Pseudo Variables
+
+              7.1. $rr_count
+              7.2. $rr_top_count
+
+        8. MI Commands
+
+              8.1. nh_enable_ping
+              8.2. nh_enable_rtpp
+              8.3. nh_show_rtpp
+
+   2. Frequently Asked Questions
+
+   List of Examples
+
+   1.1. Set natping_interval parameter
+   1.2. Set ping_nated_only parameter
+   1.3. Set natping_processes parameter
+   1.4. Set natping_socket parameter
+   1.5. Set received_avp parameter
+   1.6. Set rtpproxy_sock parameter
+   1.7. Set rtpproxy_disable_tout parameter
+   1.8. Set rtpproxy_tout parameter
+   1.9. Set rtpproxy_retr parameter
+   1.10. Set force_socket parameter
+   1.11. Set sipping_bflag parameter
+   1.12. Set sipping_from parameter
+   1.13. Set sipping_method parameter
+   1.14. Set nortpproxy_str parameter
+   1.15. fix_nated_contact usage
+   1.16. fix_nated_sdp usage
+   1.17. fix_nated_contact usage
+   1.18. force_rtp_proxy usage
+   1.19. rtpproxy_offer usage
+   1.20.
+   1.21. unforce_rtp_proxy usage
+   1.22. rtpproxy_stream2xxx usage
+   1.23. add_rcv_paramer usage
+   1.24. fix_nated_register usage
+   1.25. start_recording usage
+   1.26. add_contact_alias usage
+   1.27. handle_ruri_alias usage
+   1.28. $rr_count usage
+   1.29. $rr_top_count usage
+   1.30. nh_enable_ping usage
+   1.31. nh_enable_rtpp usage
+   1.32. nh_show_rtpp usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. NAT pinging types
+   3. Multiple RTPProxy usage
+   4. Dependencies
+
+        4.1. Kamailio Modules
+        4.2. External Libraries or Applications
+
+   5. Exported Parameters
+
+        5.1. natping_interval (integer)
+        5.2. ping_nated_only (integer)
+        5.3. natping_processes (integer)
+        5.4. natping_socket (string)
+        5.5. received_avp (str)
+        5.6. rtpproxy_sock (string)
+        5.7. rtpproxy_disable_tout (integer)
+        5.8. rtpproxy_tout (integer)
+        5.9. rtpproxy_retr (integer)
+        5.10. force_socket (string)
+        5.11. sipping_bflag (integer)
+        5.12. sipping_from (string)
+        5.13. sipping_method (string)
+        5.14. nortpproxy_str (string)
+
+   6. Exported Functions
+
+        6.1. fix_nated_contact()
+        6.2. fix_nated_sdp(flags [, ip_address])
+        6.3. set_rtp_proxy_set()
+        6.4. force_rtp_proxy([flags [, ip_address]])
+        6.5. rtpproxy_offer([flags [, ip_address]])
+        6.6. rtpproxy_answer([flags [, ip_address]])
+        6.7. unforce_rtp_proxy()
+        6.8. rtpproxy_stream2uac(prompt_name, count),
+                rtpproxy_stream2uas(prompt_name, count)
+
+        6.9. rtpproxy_stop_stream2uac(), rtpproxy_stop_stream2uas()
+        6.10. add_rcv_param([flag]),
+        6.11. fix_nated_register()
+        6.12. nat_uac_test(flags)
+        6.13. start_recording()
+        6.14. add_contact_alias()
+        6.15. handle_ruri_alias()
+
+   7. Exported Pseudo Variables
+
+        7.1. $rr_count
+        7.2. $rr_top_count
+
+   8. MI Commands
+
+        8.1. nh_enable_ping
+        8.2. nh_enable_rtpp
+        8.3. nh_show_rtpp
+
+1. Overview
+
+   This is a module to help with NAT traversal and reuse of tcp
+   connections. In particular, it helps symmetric UAs that don't advertise
+   they are symmetric and are not able to determine their public address.
+
+   Function fix_nated_contact() rewrites Contact header field with
+   request's source address:port pair. Function fix_nated_sdp() adds the
+   active direction ndication to SDP (flag 0x01) and updates source IP
+   address too (flag 0x02). Function fix_nated_register() exports exports
+   the request's source address:port into an AVP to be used during save()
+   and should be used for REGISTER requests.
+
+   Note: fix_nated_contact changes the Contact header, thus it breaks the
+   RFC. Although usually this is not an issue, it may cause problems with
+   strict SIP clients. An alternative is to use add_contact_alias() that
+   together with handle_ruri_alias() is standards conforming and also
+   supports reuse of TCP/TLS connections.
+
+   Known devices that get along over NATs with nathelper are ATAs (as
+   clients) and Cisco Gateways (since 12.2(T)) as servers. See
+   http://www.cisco.com/en/US/products/sw/iosswrel/ps1839/products_feature
+   _guide09186a0080110bf9.html">
+
+2. NAT pinging types
+
+   Currently, the nathelper module supports two types of NAT pings:
+     * UDP package - 4 bytes (zero filled) UDP packages are sent to the
+       contact address.
+          + Advantages: low bandwitdh traffic, easy to generate by
+            Kamailio;
+          + Disadvantages: unidirectional traffic through NAT (inbound -
+            from outside to inside); As many NATs do update the bind
+            timeout only on outbound traffic, the bind may expire and
+            closed.
+     * SIP request - a stateless SIP request is sent to the contact
+       address.
+          + Advantages: bidirectional traffic through NAT, since each PING
+            request from Kamailio (inbound traffic) will force the SIP
+            client to generate a SIP reply (outbound traffic) - the NAT
+            bind will be surely kept open.
+          + Disadvantages: higher bandwitdh traffic, more expensive (as
+            time) to generate by Kamailio;
+
+3. Multiple RTPProxy usage
+
+   Currently, the nathelper module can support multiple rtpproxies for
+   balancing/distribution and control/selection purposes.
+
+   The module allows the definition of several sets of rtpproxies -
+   load-balancing will be performed over a set and the user has the
+   ability to choose what set should be used. The set is selected via its
+   id - the id being defined along with the set. Refer to the
+   “rtpproxy_sock” module parameter definition for syntax description.
+
+   The balancing inside a set is done automatically by the module based on
+   the weight of each rtpproxy from the set.
+
+   The selection of the set is done from script prior using
+   [un]force_rtp_proxy(), rtpproxy_offer() or rtpproxy_answer() functions
+   - see the set_rtp_proxy_set() function.
+
+   For backward compatibility reasons, a set with no id take by default
+   the id 0. Also if no set is explicitly set before
+   [un]force_rtp_proxy(), rtpproxy_offer() or rtpproxy_answer() the 0 id
+   set will be used.
+
+   IMPORTANT: if you use multiple sets, take care and use the same set for
+   both force_ and unforce_rtpproxy()!!
+
+4. Dependencies
+
+   4.1. Kamailio Modules
+   4.2. External Libraries or Applications
+
+4.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * usrloc module - only if the NATed contacts are to be pinged.
+
+4.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * None.
+
+5. Exported Parameters
+
+   5.1. natping_interval (integer)
+   5.2. ping_nated_only (integer)
+   5.3. natping_processes (integer)
+   5.4. natping_socket (string)
+   5.5. received_avp (str)
+   5.6. rtpproxy_sock (string)
+   5.7. rtpproxy_disable_tout (integer)
+   5.8. rtpproxy_tout (integer)
+   5.9. rtpproxy_retr (integer)
+   5.10. force_socket (string)
+   5.11. sipping_bflag (integer)
+   5.12. sipping_from (string)
+   5.13. sipping_method (string)
+   5.14. nortpproxy_str (string)
+
+5.1. natping_interval (integer)
+
+   Period of time in seconds between sending the NAT pings to all
+   currently registered UAs to keep their NAT bindings alive. Value of 0
+   disables this functionality.
+
+Note
+
+   Enabling the NAT pinging functionality will force the module to bind
+   itself to USRLOC module.
+
+   Default value is 0.
+
+   Example 1.1. Set natping_interval parameter
+...
+modparam("nathelper", "natping_interval", 10)
+...
+
+5.2. ping_nated_only (integer)
+
+   If this variable is set then only contacts that have “behind_NAT” flag
+   in user location database set will get ping.
+
+   Default value is 0.
+
+   Example 1.2. Set ping_nated_only parameter
+...
+modparam("nathelper", "ping_nated_only", 1)
+...
+
+5.3. natping_processes (integer)
+
+   How many timer processes should be created by the module for the
+   exclusive task of sending the NAT pings.
+
+   Default value is 1.
+
+   Example 1.3. Set natping_processes parameter
+...
+modparam("nathelper", "natping_processes", 3)
+...
+
+5.4. natping_socket (string)
+
+   Spoof the natping's source-ip to this address. Works only for IPv4.
+
+   Default value is NULL.
+
+   Example 1.4. Set natping_socket parameter
+...
+modparam("nathelper", "natping_socket", "192.168.1.1:5006")
+...
+
+5.5. received_avp (str)
+
+   The name of the Attribute-Value-Pair (AVP) used to store the URI
+   containing the received IP, port, and protocol. The URI is created by
+   fix_nated_register function of nathelper module and the attribute is
+   then used by the registrar to store the received parameters. Do not
+   forget to change the value of corresponding parameter in registrar
+   module if you change the value of this parameter.
+
+Note
+
+   You must set this parameter if you use "fix_nated_register". In such
+   case you must set the parameter with same name of "registrar" module to
+   same value.
+
+   Default value is "NULL" (disabled).
+
+   Example 1.5. Set received_avp parameter
+...
+modparam("nathelper", "received_avp", "$avp(i:42)")
+...
+
+5.6. rtpproxy_sock (string)
+
+   Definition of socket(s) used to connect to (a set) RTPProxy. It may
+   specify a UNIX socket or an IPv4/IPv6 UDP socket.
+
+   Default value is “NONE” (disabled).
+
+   Example 1.6. Set rtpproxy_sock parameter
+...
+# single rtproxy
+modparam("nathelper", "rtpproxy_sock", "udp:localhost:12221")
+# multiple rtproxies for LB
+modparam("nathelper", "rtpproxy_sock",
+        "udp:localhost:12221 udp:localhost:12222")
+# multiple sets of multiple rtproxies
+modparam("nathelper", "rtpproxy_sock",
+        "1 == udp:localhost:12221 udp:localhost:12222")
+modparam("nathelper", "rtpproxy_sock",
+        "2 == udp:localhost:12225")
+...
+
+5.7. rtpproxy_disable_tout (integer)
+
+   Once RTPProxy was found unreachable and marked as disable, nathelper
+   will not attempt to establish communication to RTPProxy for
+   rtpproxy_disable_tout seconds.
+
+   Default value is “60”.
+
+   Example 1.7. Set rtpproxy_disable_tout parameter
+...
+modparam("nathelper", "rtpproxy_disable_tout", 20)
+...
+
+5.8. rtpproxy_tout (integer)
+
+   Timeout value in waiting for reply from RTPProxy.
+
+   Default value is “1”.
+
+   Example 1.8. Set rtpproxy_tout parameter
+...
+modparam("nathelper", "rtpproxy_tout", 2)
+...
+
+5.9. rtpproxy_retr (integer)
+
+   How many times nathelper should retry to send and receive after timeout
+   was generated.
+
+   Default value is “5”.
+
+   Example 1.9. Set rtpproxy_retr parameter
+...
+modparam("nathelper", "rtpproxy_retr", 2)
+...
+
+5.10. force_socket (string)
+
+   Socket to be forced in communicating to RTPProxy. It makes sense only
+   for UDP communication. If no one specified, the OS will choose.
+
+   Default value is “NULL”.
+
+   Example 1.10. Set force_socket parameter
+...
+modparam("nathelper", "force_socket", "localhost:33333")
+...
+
+5.11. sipping_bflag (integer)
+
+   What branch flag should be used by the module to identify NATed
+   contacts for which it should perform NAT ping via a SIP request instead
+   if dummy UDP package.
+
+   Default value is -1 (disabled).
+
+   Example 1.11. Set sipping_bflag parameter
+...
+modparam("nathelper", "sipping_bflag", 7)
+...
+
+5.12. sipping_from (string)
+
+   The parameter sets the SIP URI to be used in generating the SIP
+   requests for NAT ping purposes. To enable the SIP request pinging
+   feature, you have to set this parameter. The SIP request pinging will
+   be used only for requests marked so.
+
+   Default value is “NULL”.
+
+   Example 1.12. Set sipping_from parameter
+...
+modparam("nathelper", "sipping_from", "sip:[email protected]")
+...
+
+5.13. sipping_method (string)
+
+   The parameter sets the SIP method to be used in generating the SIP
+   requests for NAT ping purposes.
+
+   Default value is “OPTIONS”.
+
+   Example 1.13. Set sipping_method parameter
+...
+modparam("nathelper", "sipping_method", "INFO")
+...
+
+5.14. nortpproxy_str (string)
+
+   The parameter sets the SDP attribute used by nathelper to mark the
+   packet SDP informations have already been mangled.
+
+   If empty string, no marker will be added or checked.
+
+Note
+
+   The string must be a complete SDP line, including the EOH (\r\n).
+
+   Default value is “a=nortpproxy:yes\r\n”.
+
+   Example 1.14. Set nortpproxy_str parameter
+...
+modparam("nathelper", "nortpproxy_str", "a=sdpmangled:yes\r\n")
+...
+
+6. Exported Functions
+
+   6.1. fix_nated_contact()
+   6.2. fix_nated_sdp(flags [, ip_address])
+   6.3. set_rtp_proxy_set()
+   6.4. force_rtp_proxy([flags [, ip_address]])
+   6.5. rtpproxy_offer([flags [, ip_address]])
+   6.6. rtpproxy_answer([flags [, ip_address]])
+   6.7. unforce_rtp_proxy()
+   6.8. rtpproxy_stream2uac(prompt_name, count),
+          rtpproxy_stream2uas(prompt_name, count)
+
+   6.9. rtpproxy_stop_stream2uac(), rtpproxy_stop_stream2uas()
+   6.10. add_rcv_param([flag]),
+   6.11. fix_nated_register()
+   6.12. nat_uac_test(flags)
+   6.13. start_recording()
+   6.14. add_contact_alias()
+   6.15. handle_ruri_alias()
+
+6.1.  fix_nated_contact()
+
+   Rewrites Contact HF to contain request's source address:port.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   BRANCH_ROUTE.
+
+   Example 1.15. fix_nated_contact usage
+...
+if (search("User-Agent: Cisco ATA.*") {fix_nated_contact();};
+...
+
+6.2.  fix_nated_sdp(flags [, ip_address])
+
+   Alters the SDP information in orer to facilitate NAT traversal. What
+   changes to be performed may be controled via the “flags” parameter.
+
+   Meaning of the parameters is as follows:
+     * flags - the value may be a bitwise OR of the following flags:
+          + 0x01 - adds “a=direction:active” SDP line;
+          + 0x02 - rewrite media IP address (c=) with source address of
+            the message or the provided IP address (the provide IP address
+            take precedence over the source address).
+          + 0x04 - adds “a=nortpproxy:yes” SDP line;
+          + 0x08 - rewrite IP from origin description (o=) with source
+            address of the message or the provided IP address (the provide
+            IP address take precedence over the source address).
+     * ip_address - IP to be used for rewritting SDP. If not specified,
+       the received signalling IP will be used. The parameter allows
+       pseudo-variables usage. NOTE: For the IP to be used, you need to
+       use 0x02 or 0x08 flags, otherwise it will have no effect.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   FAILURE_ROUTE, BRANCH_ROUTE.
+
+   Example 1.16. fix_nated_sdp usage
+...
+if (search("User-Agent: Cisco ATA.*") {fix_nated_sdp("3");};
+...
+
+6.3.  set_rtp_proxy_set()
+
+   Sets the Id of the rtpproxy set to be used for the next
+   [un]force_rtp_proxy(), rtpproxy_offer() or rtpproxy_answer() command.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   BRANCH_ROUTE.
+
+   Example 1.17. fix_nated_contact usage
+...
+set_rtp_proxy_set("2");
+force_rtp_proxy();
+...
+
+6.4.  force_rtp_proxy([flags [, ip_address]])
+
+   Rewrites SDP body to ensure that media is passed through an RTP proxy.
+   It can have optional parameters to force additional features. If
+   ip_address is provided, it will be used to replace the one in SDP.
+
+   Note: Behavior of rtpproxy depends on the mode in which rtpproxy is
+   running: If rtpproxy listens only to a single interface, then rtpproxy
+   is default symmetric (port), if rtpproxy listens to two interfaces (so
+   called bridge-mode), then rtpproxy is default asymmetric (port).
+
+   Note: Regardless of symmetric/asymmetric rtpproxy mode, per default
+   rtpproxy accepts incoming RTP packets only from the same IP address as
+   the SIP signaling was received. Thus, if a SIP client uses different IP
+   for media and SIP (often seen with SBCs), the 'r' flag must be
+   specified.
+
+   The function is considered depreciated and provided for the
+   compatibility purposes. Use rtpproxy_offer() or rtpproxy_answer()
+   instead.
+
+   Meaning of the parameters is as follows:
+     * flags - flags to turn on some features.
+          + a - flags that UA from which message is received doesn't
+            support symmetric RTP. (automatically sets the 'r' flag)
+          + l - force “lookup”, that is, only rewrite SDP when
+            corresponding session is already exists in the RTP proxy. By
+            default is on when the session is to be completed (reply in
+            non-swap or ACK in swap mode).
+          + i, e - these flags specify the direction of the SIP message.
+            These flags only make sense when rtpproxy is running in bridge
+            mode. 'i' means internal network (LAN), 'e' means external
+            network (WAN). 'i' corresponds to rtpproxy's first interface,
+            'e' corresponds to rtpproxy's second interface. You always
+            have to specify two flags to define the incoming network and
+            the outgoing network. For example, 'ie' should be used for SIP
+            message received from the local interface and sent out on the
+            external interface, and 'ei' vice versa. Other options are
+            'ii' and 'ee'. So, for example if a SIP requests is processed
+            with 'ie' flags, the corresponding response must be processed
+            with 'ie' flags.
+            Note: As rtpproxy is in bridge mode per default asymmetric,
+            you have to specify the 'w' flag for clients behind NAT! See
+            also above notes!
+          + f - instructs nathelper to ignore marks inserted by another
+            nathelper in transit to indicate that the session is already
+            goes through another proxy. Allows creating chain of proxies.
+          + r - flags that IP address in SDP should be trusted. Without
+            this flag, nathelper ignores address in the SDP and uses
+            source address of the SIP message as media address which is
+            passed to the RTP proxy.
+          + o - flags that IP from the origin description (o=) should be
+            also changed.
+          + c - flags to change the session-level SDP connection (c=) IP
+            if media-description also includes connection information.
+          + s - flags to swap creation with confirmation between requests
+            and replies. By default, a request creates the RTP session and
+            a reply confirms it. If swapped, a reply will create the RTP
+            session and a request will confirm it. The flag is considered
+            depreciated and provided for the compatibility purposes. Use
+            rtpproxy_offer() or rtpproxy_answer() instead.
+          + w - flags that for the UA from which message is received,
+            support symmetric RTP must be forced.
+          + zNN - requests the RTPproxy to perform re-packetization of RTP
+            traffic coming from the UA which has sent the current message
+            to increase or decrease payload size per each RTP packet
+            forwarded if possible. The NN is the target payload size in
+            ms, for the most codecs its value should be in 10ms
+            increments, however for some codecs the increment could differ
+            (e.g. 30ms for GSM or 20ms for G.723). The RTPproxy would
+            select the closest value supported by the codec. This feature
+            could be used for significantly reducing bandwith overhead for
+            low bitrate codecs, for example with G.729 going from 10ms to
+            100ms saves two thirds of the network bandwith.
+     * ip_address - new SDP IP address.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   FAILURE_ROUTE, BRANCH_ROUTE.
+
+   Example 1.18. force_rtp_proxy usage
+...
+if (search("User-Agent: Cisco ATA.*") {force_rtp_proxy();};
+if (src_ip=1.2.3.4) {force_rtp_proxy("i");};
+if (search("User-Agent: Cisco ATA.*") {force_rtp_proxy("","1.2.3.4");};
+...
+
+6.5.  rtpproxy_offer([flags [, ip_address]])
+
+   Rewrites SDP body to ensure that media is passed through an RTP proxy.
+   Equivalent of force_rtp_proxy() function to be invoked on INVITE for
+   the cases the SDPs are in INVITE and 200 OK and on 200 OK when SDPs are
+   in 200 OK and ACK.
+
+   See force_rtp_proxy() function description above for the meaning of the
+   parameters.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   FAILURE_ROUTE, BRANCH_ROUTE.
+
+   Example 1.19. rtpproxy_offer usage
+route {
+...
+    if (is_method("INVITE")) {
+        if (has_sdp()) {
+            if (rtpproxy_offer())
+                t_on_reply("1");
+        } else {
+            t_on_reply("2");
+        }
+    }
+    if (is_method("ACK") && has_sdp())
+        rtpproxy_answer();
+...
+}
+
+onreply_route[1]
+{
+...
+    if (has_sdp())
+        rtpproxy_answer();
+...
+}
+
+onreply_route[2]
+{
+...
+    if (has_sdp())
+        rtpproxy_offer();
+...
+}
+
+6.6.  rtpproxy_answer([flags [, ip_address]])
+
+   Rewrites SDP body to ensure that media is passed through an RTP proxy.
+   Equivalent of force_rtp_proxy() function to be invoked on 200 OK for
+   the cases the SDPs are in INVITE and 200 OK and on ACK when SDPs are in
+   200 OK and ACK.
+
+   See force_rtp_proxy() function description above for the meaning of the
+   parameters.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   FAILURE_ROUTE, BRANCH_ROUTE.
+
+   Example 1.20.
+
+   See rtpproxy_offer() function example above for example.
+
+6.7.  unforce_rtp_proxy()
+
+   Tears down the RTPProxy session for the current call.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   FAILURE_ROUTE, BRANCH_ROUTE.
+
+   Example 1.21. unforce_rtp_proxy usage
+...
+unforce_rtp_proxy();
+...
+
+6.8.  rtpproxy_stream2uac(prompt_name, count),
+rtpproxy_stream2uas(prompt_name, count)
+
+   Instruct the RTPproxy to stream prompt/announcement pre-encoded with
+   the makeann command from the RTPproxy distribution. The uac/uas suffix
+   selects who will hear the announcement relatively to the current
+   transaction - UAC or UAS. For example invoking the rtpproxy_stream2uac
+   in the request processing block on ACK transaction will play the prompt
+   to the UA that has generated original INVITE and ACK while
+   rtpproxy_stop_stream2uas on 183 in reply processing block will play the
+   prompt to the UA that has generated 183.
+
+   Apart from generating announcements, another possible application of
+   this function is implementing music on hold (MOH) functionality. When
+   count is -1, the streaming will be in loop indefinitely until the
+   appropriate rtpproxy_stop_stream2xxx is issued.
+
+   In order to work correctly, functions require that the session in the
+   RTPproxy already exists. Also those functions don't alted SDP, so that
+   they are not substitute for calling rtpproxy_offer, rtpproxy_answer or
+   force_rtp_proxy.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE.
+
+   Meaning of the parameters is as follows:
+     * prompt_name - name of the prompt to stream. Should be either
+       absolute pathname or pathname relative to the directory where
+       RTPproxy runs.
+     * count - number of times the prompt should be repeated. The value of
+       -1 means that it will be streaming in loop indefinitely, until
+       appropriate rtpproxy_stop_stream2xxx is issued.
+
+   Example 1.22. rtpproxy_stream2xxx usage
+...
+    if (is_method("INVITE")) {
+        rtpproxy_offer();
+        if (detect_hold()) {
+            rtpproxy_stream2uas("/var/rtpproxy/prompts/music_on_hold", "-1");
+        } else {
+            rtpproxy_stop_stream2uas();
+        };
+    };
+...
+
+6.9.  rtpproxy_stop_stream2uac(), rtpproxy_stop_stream2uas()
+
+   Stop streaming of announcement/prompt/MOH started previously by the
+   respective rtpproxy_stream2xxx. The uac/uas suffix selects whose
+   announcement relatively to tha current transaction should be stopped -
+   UAC or UAS.
+
+   These functions can be used from REQUEST_ROUTE, ONREPLY_ROUTE.
+
+6.10.  add_rcv_param([flag]),
+
+   Add received parameter to Contact header fields or Contact URI. The
+   parameter will contain URI created from the source IP, port, and
+   protocol of the packet containing the SIP message. The parameter can be
+   then processed by another registrar, this is useful, for example, when
+   replicating register messages using t_replicate function to another
+   registrar.
+
+   Meaning of the parameters is as follows:
+     * flag - flags to indicate if the parameter should be added to
+       Contact URI or Contact header. If the flag is non-zero, the
+       parameter will be added to the Contact URI. If not used or equal to
+       zero, the parameter will go to the Contact header.
+
+   This function can be used from REQUEST_ROUTE.
+
+   Example 1.23. add_rcv_paramer usage
+...
+add_rcv_param(); # add the parameter to the Contact header
+....
+add_rcv_param("1"); # add the parameter to the Contact URI
+...
+
+6.11.  fix_nated_register()
+
+   The function creates a URI consisting of the source IP, port, and
+   protocol and stores the URI in an Attribute-Value-Pair. The URI will be
+   appended as "received" parameter to Contact in 200 OK and registrar
+   will store it in the received cloumn in the location table.
+
+   Note: You have to set the received_avp parameter of the nathelper
+   module and the registrar module (both module parameters must have the
+   same value) to use this function.
+
+   This function can be used from REQUEST_ROUTE.
+
+   Example 1.24. fix_nated_register usage
+...
+fix_nated_register();
+...
+
+6.12.  nat_uac_test(flags)
+
+   Tries to guess if client's request originated behind a nat. The
+   parameter determines what heuristics is used.
+
+   Meaning of the flags is as follows:
+     * 1 - Contact header field is searched for occurrence of RFC1918
+       addresses.
+     * 2 - the "received" test is used: address in Via is compared against
+       source IP address of signaling
+     * 4 - Top Most VIA is searched for occurrence of RFC1918 addresses
+     * 8 - SDP is searched for occurrence of RFC1918 addresses
+     * 16 - test if the source port is different from the port in Via
+
+   All flags can be bitwise combined, the test returns true if any of the
+   tests identified a NAT.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   FAILURE_ROUTE, BRANCH_ROUTE.
+
+6.13.  start_recording()
+
+   This command will send a signal to the RTP-Proxy to record the RTP
+   stream on the RTP-Proxy.
+
+   This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE.
+
+   Example 1.25. start_recording usage
+...
+start_recording();
+...
+
+6.14.  add_contact_alias()
+
+   Adds ;alias=ip:port parameter to contact URI containing received
+   ip:port if contact uri ip:port does not match received ip:port.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   BRANCH_ROUTE, and LOCAL_ROUTE.
+
+   Example 1.26. add_contact_alias usage
+...
+    if (!is_present_hf("Record-Route")) {
+        if (!add_contact_alias()) {
+            xlog("L_ERR", "Error in aliasing contact $ct\n");
+            send_reply("400", "Bad request");
+            exit;
+        };
+    };
+...
+
+6.15.  handle_ruri_alias()
+
+   Checks if Request URI has alias param and if so, removes it and sets
+   $du based on its value. Note that this means that routing of request is
+   based on ;alias parameter value of Request URI rather than Request URI
+   itself. If you call handle_ruri_alias() on a request, make thus sure
+   that you screen alias parameter value of Request URI the same way as
+   you would screen Request URI itself.
+
+   Returns 1 if ;alias param was found and $du was set and $ru rewritten,
+   2 if alias param was not found and nothing was done, or -1 in case of
+   error.
+
+   This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, and
+   LOCAL_ROUTE.
+
+   Example 1.27. handle_ruri_alias usage
+...
+    if ($du == "") {
+        handle_ruri_alias();
+        switch ($rc) {
+        case -1:
+            xlog("L_ERR", "Failed to handle alias of R-URI $ru\n");
+            send_reply("400", "Bad request");
+            exit;
+        case 1:
+            xlog("L_INFO", "Routing in-dialog $rm from $fu to $du\n");
+            break;
+        case 2:
+            xlog("L_INFO", "Routing in-dialog $rm from $fu to $ru\n");
+            break;
+         };
+    };
+...
+
+7. Exported Pseudo Variables
+
+   7.1. $rr_count
+   7.2. $rr_top_count
+
+7.1. $rr_count
+
+   Number of Record Routes in received SIP request or reply.
+
+   Example 1.28. $rr_count usage
+...
+    $avp(rr_count) = $rr_count;
+...
+
+7.2. $rr_top_count
+
+   If topmost Record Route in received SIP request or reply is a double
+   Record Route, value of $rr_top_count is 2. If it a single Record Route,
+   value of $rr_top_count is 1. If there is no Record Route(s), value of
+   $rr_top_count is 0.
+
+   Example 1.29. $rr_top_count usage
+...
+    if ($rr_count == $avp(rr_count) + $rr_top_count) {
+        route(ADD_CONTACT_ALIAS);
+    };
+...
+
+8. MI Commands
+
+   8.1. nh_enable_ping
+   8.2. nh_enable_rtpp
+   8.3. nh_show_rtpp
+
+8.1. nh_enable_ping
+
+   Enables natping if parameter value greater than 0. Disables natping if
+   parameter value is 0.
+
+   The function takes only one parameter - a number in decimal format.
+
+   Example 1.30. nh_enable_ping usage
+...
+$ kamctl fifo nh_enable_ping 1
+...
+
+8.2. nh_enable_rtpp
+
+   Enables a rtp proxy if parameter value is greater than 0. Disables it
+   if a zero value is given.
+
+   The first parameter is the rtp proxy url (exactly as defined in the
+   config file).
+
+   The second parameter value must be a number in decimal.
+
+   NOTE: if a rtpproxy is defined multiple times (in the same or diferente
+   sete), all its instances will be enables/disabled.
+
+   Example 1.31.  nh_enable_rtpp usage
+...
+$ kamctl fifo nh_enable_rtpp udp:192.168.2.133:8081 0
+...
+
+8.3. nh_show_rtpp
+
+   Displays all the rtp proxies and their information: set and status
+   (disabled or not, weight and recheck_ticks).
+
+   No parameter.
+
+   Example 1.32.  nh_show_rtpp usage
+...
+$ kamctl fifo nh_show_rtpp
+...
+
+Chapter 2. Frequently Asked Questions
+
+   2.1. What happend with “rtpproxy_disable” parameter?
+   2.2. Where can I find more about Kamailio?
+   2.3. Where can I post a question about this module?
+   2.4. How can I report a bug?
+
+   2.1.
+
+       What happend with “rtpproxy_disable” parameter?
+
+       It was removed as it became obsolete - now “rtpproxy_sock” can take
+       empty value to disable the rtpproxy functionality.
+
+   2.2.
+
+       Where can I find more about Kamailio?
+
+       Take a look at http://www.kamailio.org/.
+
+   2.3.
+
+       Where can I post a question about this module?
+
+       First at all check if your question was already answered on one of our
+       mailing lists:
+         * User Mailing List -
+           http://lists.kamailio.org/cgi-bin/mailman/listinfo/users
+         * Developer Mailing List -
+           http://lists.kamailio.org/cgi-bin/mailman/listinfo/devel
+
+       E-mails regarding any stable Kamailio release should be sent to
+       <[email protected]> and e-mails regarding development versions
+       should be sent to <[email protected]>.
+
+       If you want to keep the mail private, send it to
+       <[email protected]>.
+
+   2.4.
+
+       How can I report a bug?
+
+       Please follow the guidelines provided at:
+       http://sourceforge.net/tracker/?group_id=139143.

+ 4 - 0
modules_k/rtpproxy/doc/Makefile

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

+ 78 - 0
modules_k/rtpproxy/doc/nathelper.xml

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+	<bookinfo>
+	<title>nathelper Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+		<author>
+		<firstname>Maxim</firstname>
+		<surname>Sobolev</surname>
+		<affiliation><orgname>Sippy Software, Inc.</orgname></affiliation>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</author>
+		<author>
+		<firstname>Juha</firstname>
+		<surname>Heinanen</surname>
+		<affiliation><orgname>TuTPro, Inc.</orgname></affiliation>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</author>
+		<editor>
+		<firstname>Maxim</firstname>
+		<surname>Sobolev</surname>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</editor>
+		<editor>
+		<firstname>Bogdan-Andrei</firstname>
+		<surname>Iancu</surname>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</editor>
+		<editor>
+		<firstname>Juha</firstname>
+		<surname>Heinanen</surname>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</editor>
+	</authorgroup>
+	<copyright>
+		<year>2003-2008</year>
+		<holder>Sippy Software, Inc.</holder>
+	</copyright>
+	<copyright>
+		<year>2005</year>
+		<holder>&voicesystem;</holder>
+	</copyright>
+	<copyright>
+		<year>2009</year>
+		<holder>TuTPro Inc.</holder>
+	</copyright>
+	<revhistory>
+		<revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+		</revision>
+	</revhistory>
+	</bookinfo>
+	<toc></toc>
+	
+	<xi:include href="nathelper_admin.xml"/>
+	<xi:include href="nathelper_faq.xml"/>
+	
+</book>

+ 1208 - 0
modules_k/rtpproxy/doc/nathelper_admin.xml

@@ -0,0 +1,1208 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<!-- Module User's Guide -->
+
+<chapter>
+	
+	<title>&adminguide;</title>
+	
+	<section>
+	<title>Overview</title>
+	<para>
+		This is a module to help with &nat; traversal and reuse
+	of tcp connections.  In particular, 
+		it helps symmetric &ua;s that don't advertise they are symmetric 
+		and are not able to determine their public
+	address. 
+	</para>
+	<para>
+		Function fix_nated_contact() rewrites Contact header field with request's source
+		address:port pair. Function fix_nated_sdp() adds the active direction ndication 
+		to &sdp; (flag 0x01) and updates source &ip; address too (flag 0x02). Function
+		fix_nated_register() exports exports the request's source address:port into an AVP
+		to be used during save() and should be used for REGISTER requests.
+	</para>
+        <para>
+		Note: fix_nated_contact changes the Contact header, thus it breaks the RFC.
+		Although usually this is not an issue, it may cause problems with strict
+		SIP clients.  An alternative is to use add_contact_alias() that
+		together with handle_ruri_alias() is standards
+	conforming and also supports reuse of TCP/TLS connections.
+	</para>
+	<para>
+		Known devices that get along over &nat;s with nathelper are ATAs 
+		(as clients) and Cisco Gateways (since 12.2(T)) as servers.  See <ulink
+		url="http://www.cisco.com/en/US/products/sw/iosswrel/ps1839/products_feature_guide09186a0080110bf9.html">
+		http://www.cisco.com/en/US/products/sw/iosswrel/ps1839/products_feature_guide09186a0080110bf9.html"></ulink>
+	</para>
+	</section>
+
+	<section>
+	<title>NAT pinging types</title>
+	<para>
+		Currently, the nathelper module supports two types of NAT pings:
+	</para>
+	<itemizedlist>
+		<listitem>
+			<para>
+			<emphasis>UDP package</emphasis> - 4 bytes (zero filled) UDP 
+			packages are sent to the contact address.
+			</para>
+			<itemizedlist>
+				<listitem>
+				<para><emphasis>Advantages:</emphasis> low bandwitdh traffic,
+				easy to generate by &kamailio;;
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>Disadvantages:</emphasis> unidirectional 
+				traffic through NAT (inbound - from outside to inside); As 
+				many NATs do update the bind timeout only on outbound traffic,
+				the bind may expire and closed.
+				</para>
+				</listitem>
+			</itemizedlist>
+		</listitem>
+		<listitem>
+			<para>
+			<emphasis>SIP request</emphasis> - a stateless SIP request is 
+			sent to the contact address.
+			</para>
+			<itemizedlist>
+				<listitem>
+				<para><emphasis>Advantages:</emphasis> bidirectional traffic
+				through NAT, since each PING request from &kamailio; (inbound 
+				traffic) will force the SIP client to generate a SIP reply 
+				(outbound traffic) - the NAT bind will be surely kept open.
+				</para>
+				</listitem>
+				<listitem>
+				<para><emphasis>Disadvantages:</emphasis> higher bandwitdh 
+				traffic, more expensive (as time) to generate by &kamailio;;
+				</para>
+				</listitem>
+			</itemizedlist>
+		</listitem>
+	</itemizedlist>
+	</section>
+
+	<section>
+	<title>Multiple RTPProxy usage</title>
+	<para>
+		Currently, the nathelper module can support multiple rtpproxies for
+		balancing/distribution and control/selection purposes.
+	</para>
+	<para>
+		The module allows the definition of several sets of rtpproxies - 
+		load-balancing will be performed over a set and the user has the
+		ability to choose what set should be used. The set is selected via
+		its id - the id being defined along with the set. Refer to the 
+		<quote>rtpproxy_sock</quote> module parameter definition for syntax
+		description.
+	</para>
+	<para>
+		The balancing inside a set is done automatically by the module based on
+		the weight of each rtpproxy from the set.
+	</para>
+	<para>
+		The selection of the set is done from script prior using 
+		[un]force_rtp_proxy(), rtpproxy_offer() or rtpproxy_answer()
+		functions - see the set_rtp_proxy_set() function.
+	</para>
+	<para>
+		For backward compatibility reasons, a set with no id take by default 
+		the id 0. Also if no set is explicitly set before 
+		[un]force_rtp_proxy(), rtpproxy_offer() or rtpproxy_answer()
+		the 0 id set will be used.
+	</para>
+	<para>
+		IMPORTANT: if you use multiple sets, take care and use the same set for
+		both force_ and unforce_rtpproxy()!!
+	</para>
+	</section>
+
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>usrloc</emphasis> module - only if the NATed 
+				contacts are to be pinged.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before 
+		running &kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+
+	<section>
+	<title>Exported Parameters</title>
+	<section>
+		<title><varname>natping_interval</varname> (integer)</title>
+		<para>
+		Period of time in seconds between sending the NAT pings to all 
+		currently registered &ua;s to keep their &nat; bindings alive. 
+		Value of 0 disables this functionality.
+		</para>
+		<note><para>
+		Enabling the NAT pinging functionality will force the module to
+		bind itself to USRLOC module.
+		</para></note>
+		<para>
+		<emphasis>
+			Default value is 0.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>natping_interval</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "natping_interval", 10)
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>ping_nated_only</varname> (integer)</title>
+		<para>
+		If this variable is set then only contacts that have 
+		<quote>behind_NAT</quote> flag in user location database set will 
+		get ping.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 0.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>ping_nated_only</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "ping_nated_only", 1)
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>natping_processes</varname> (integer)</title>
+		<para>
+		How many timer processes should be created by the module for the
+		exclusive task of sending the NAT pings.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 1.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>natping_processes</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "natping_processes", 3)
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>natping_socket</varname> (string)</title>
+		<para>
+		Spoof the natping's source-ip to this address. Works only for IPv4.
+		</para>
+		<para>
+		<emphasis>
+			Default value is NULL.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>natping_socket</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "natping_socket", "192.168.1.1:5006")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>received_avp</varname> (str)</title>
+		<para>
+		The name of the Attribute-Value-Pair (AVP) used to store the URI 
+		containing the received IP, port, and protocol. The URI is created 
+		by fix_nated_register function of nathelper module and the attribute 
+		is then used by the registrar to store the received parameters. Do 
+		not forget to change the value of corresponding parameter in
+		registrar module if you change the value of this parameter.
+		</para>
+		<note>
+		<para>
+		You must set this parameter if you use "fix_nated_register". In such
+		case you must set the parameter with same name of "registrar"
+		module to same value.
+		</para>
+		</note>
+		<para>
+		<emphasis>
+			Default value is "NULL" (disabled).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>received_avp</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "received_avp", "$avp(i:42)")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>rtpproxy_sock</varname> (string)</title>
+		<para>
+		Definition of socket(s) used to connect to (a set) RTPProxy. It may 
+		specify a UNIX socket or an IPv4/IPv6 UDP socket.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>NONE</quote> (disabled).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>rtpproxy_sock</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+# single rtproxy
+modparam("nathelper", "rtpproxy_sock", "udp:localhost:12221")
+# multiple rtproxies for LB
+modparam("nathelper", "rtpproxy_sock",
+	"udp:localhost:12221 udp:localhost:12222")
+# multiple sets of multiple rtproxies
+modparam("nathelper", "rtpproxy_sock",
+	"1 == udp:localhost:12221 udp:localhost:12222")
+modparam("nathelper", "rtpproxy_sock",
+	"2 == udp:localhost:12225")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>rtpproxy_disable_tout</varname> (integer)</title>
+		<para>
+		Once RTPProxy was found unreachable and marked as disable, nathelper
+		will not attempt to establish communication to RTPProxy for 
+		rtpproxy_disable_tout seconds.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>60</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>rtpproxy_disable_tout</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "rtpproxy_disable_tout", 20)
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>rtpproxy_tout</varname> (integer)</title>
+		<para>
+		Timeout value in waiting for reply from RTPProxy.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>1</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>rtpproxy_tout</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "rtpproxy_tout", 2)
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>rtpproxy_retr</varname> (integer)</title>
+		<para>
+		How many times nathelper should retry to send and receive after
+		timeout was generated.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>5</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>rtpproxy_retr</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "rtpproxy_retr", 2)
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>force_socket</varname> (string)</title>
+		<para>
+		Socket to be forced in communicating to RTPProxy. It makes sense only
+		for UDP communication. If no one specified, the OS will choose.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>NULL</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>force_socket</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "force_socket", "localhost:33333")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>sipping_bflag</varname> (integer)</title>
+		<para>
+		What branch flag should be used by the module to identify NATed 
+		contacts for which it should perform NAT ping via a SIP request 
+		instead if dummy UDP package.
+		</para>
+		<para>
+		<emphasis>
+			Default value is -1 (disabled).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>sipping_bflag</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "sipping_bflag", 7)
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>sipping_from</varname> (string)</title>
+		<para>
+		The parameter sets the SIP URI to be used in generating the SIP
+		requests for NAT ping purposes. To enable the SIP request pinging
+		feature, you have to set this parameter. The SIP request pinging 
+		will be used only for requests marked so.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>NULL</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>sipping_from</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "sipping_from", "sip:[email protected]")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>sipping_method</varname> (string)</title>
+		<para>
+		The parameter sets the SIP method to be used in generating the SIP
+		requests for NAT ping purposes.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>OPTIONS</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>sipping_method</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "sipping_method", "INFO")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>nortpproxy_str</varname> (string)</title>
+		<para>
+		The parameter sets the SDP attribute used by nathelper to mark
+		the packet SDP informations have already been mangled.
+		</para>
+		<para>
+		If empty string, no marker will be added or checked.
+		</para>
+		<note><para>
+		The string must be a complete SDP line, including the EOH (\r\n).
+		</para></note>
+		<para>
+		<emphasis>
+			Default value is <quote>a=nortpproxy:yes\r\n</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>nortpproxy_str</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("nathelper", "nortpproxy_str", "a=sdpmangled:yes\r\n")
+...
+</programlisting>
+		</example>
+	</section>
+	</section>
+
+
+	<section>
+	<title>Exported Functions</title>
+	<section>
+		<title>
+		<function moreinfo="none">fix_nated_contact()</function>
+		</title>
+		<para>
+		Rewrites Contact <acronym>HF</acronym> to contain request's source 
+		address:port.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE.
+		</para>
+		<example>
+		<title><function>fix_nated_contact</function> usage</title>
+		<programlisting format="linespecific">
+...
+if (search("User-Agent: Cisco ATA.*") {fix_nated_contact();};
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title>
+		<function moreinfo="none">fix_nated_sdp(flags [, ip_address])</function>
+		</title>
+		<para>
+		Alters the SDP information in orer to facilitate NAT traversal. What
+		changes to be performed may be controled via the 
+		<quote>flags</quote> parameter.
+		</para>
+		<para>Meaning of the parameters is as follows:</para>
+		<itemizedlist>
+			<listitem><para>
+			<emphasis>flags</emphasis> - the value may be a bitwise OR of 
+			the following flags:
+			</para>
+			<itemizedlist>
+				<listitem>
+					<para><emphasis>0x01</emphasis> - adds 
+					<quote>a=direction:active</quote> SDP line;
+					</para>
+				</listitem>
+				<listitem>
+					<para><emphasis>0x02</emphasis> - rewrite media
+					&ip; address (c=) with source address of the message
+					or the provided IP address (the provide IP address take
+					precedence over the source address).</para>
+				</listitem>
+				<listitem>
+					<para><emphasis>0x04</emphasis> - adds 
+						<quote>a=nortpproxy:yes</quote> SDP line;</para>
+				</listitem>
+				<listitem>
+					<para><emphasis>0x08</emphasis> - rewrite IP from
+					origin description (o=) with source address of the message
+					or the provided IP address (the provide IP address take
+					precedence over the source address).</para>
+				</listitem>
+			</itemizedlist>
+			</listitem>
+			<listitem><para>
+			<emphasis>ip_address</emphasis> - IP to be used for rewritting SDP.
+			If not specified, the received signalling IP will be used. The
+			parameter allows pseudo-variables usage. NOTE: For the IP to be
+			used, you need to use 0x02 or 0x08 flags, otherwise it will have
+			no effect.
+			</para>
+			</listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, 
+		FAILURE_ROUTE, BRANCH_ROUTE.
+		</para>
+		<example>
+		<title><function>fix_nated_sdp</function> usage</title>
+		<programlisting format="linespecific">
+...
+if (search("User-Agent: Cisco ATA.*") {fix_nated_sdp("3");};
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title>
+		<function moreinfo="none">set_rtp_proxy_set()</function>
+		</title>
+		<para>
+		Sets the Id of the rtpproxy set to be used for the next 
+		[un]force_rtp_proxy(), rtpproxy_offer() or rtpproxy_answer()
+		command.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, 
+		BRANCH_ROUTE.
+		</para>
+		<example>
+		<title><function>fix_nated_contact</function> usage</title>
+		<programlisting format="linespecific">
+...
+set_rtp_proxy_set("2");
+force_rtp_proxy();
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title>
+		<function moreinfo="none">force_rtp_proxy([flags [, ip_address]])</function>
+		</title>
+		<para>
+		Rewrites &sdp; body to ensure that media is passed through 
+		an &rtp; proxy. It can have optional parameters to force additional
+		features. If ip_address is provided, it will be used to replace the
+		one in SDP.
+		</para>
+		<para>
+		Note: Behavior of rtpproxy depends on the mode in which rtpproxy is running: If
+		rtpproxy listens only to a single interface, then rtpproxy is default symmetric (port),
+		if rtpproxy listens to two interfaces (so called bridge-mode), then rtpproxy is
+		default asymmetric (port).
+		</para>
+		<para>
+		Note: Regardless of symmetric/asymmetric rtpproxy mode, per default rtpproxy accepts
+		incoming RTP packets only from the same IP address as the SIP signaling was received.
+		Thus, if a SIP client uses different IP for media and SIP (often seen with SBCs), the
+		'r' flag must be specified.
+		</para>
+		<para>
+		The function is considered depreciated and provided for the compatibility
+		purposes.  Use rtpproxy_offer() or rtpproxy_answer() instead.
+		</para>
+		<para>Meaning of the parameters is as follows:</para>
+		<itemizedlist>
+		<listitem>
+			<para>
+			<emphasis>flags</emphasis> - flags to turn on some features.
+			</para>
+			<itemizedlist>
+				<listitem><para>
+				<emphasis>a</emphasis> - flags that UA from which message is
+				received doesn't support symmetric RTP. (automatically sets the 'r' flag)
+				</para></listitem>
+				<listitem><para>
+				<emphasis>l</emphasis> - force <quote>lookup</quote>, that is,
+				only rewrite SDP when corresponding session is already exists 
+				in the RTP proxy. By default is on when the session is to be
+				completed (reply in non-swap or ACK in swap mode).
+				</para></listitem>
+				<listitem><para>
+				<emphasis>i, e</emphasis> - these flags specify the direction of the SIP
+				message. These flags only make sense when rtpproxy is running in bridge mode.
+				'i' means internal network (LAN), 'e' means external network (WAN). 'i'
+				corresponds to rtpproxy's first interface, 'e' corresponds to rtpproxy's
+				second interface. You always have to specify two flags to define
+				the incoming network and the outgoing network. For example, 'ie' should be
+				used for SIP message received from the local interface and sent out on the
+				external interface, and 'ei' vice versa. Other options are 'ii' and 'ee'.
+				So, for example if a SIP requests is processed with 'ie' flags, the corresponding
+				response must be processed with 'ie' flags.
+				</para><para>
+				Note: As rtpproxy is in bridge mode per default asymmetric, you have to specify
+				the 'w' flag for clients behind NAT! See also above notes!
+				</para></listitem>
+				<listitem><para>
+				<emphasis>f</emphasis> - instructs nathelper to ignore marks 
+				inserted by another nathelper in transit to indicate that the 
+				session is already goes through another proxy. Allows creating 
+				chain of proxies.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>r</emphasis> - flags that IP address in SDP should 
+				be trusted. Without this flag, nathelper ignores address in 
+				the SDP and uses source address of the SIP message as media 
+				address which is passed to the RTP proxy.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>o</emphasis> - flags that IP from the origin 
+				description (o=) should be also changed.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>c</emphasis> - flags to change the session-level 
+				SDP connection (c=) IP if media-description also includes 
+				connection information.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>s</emphasis> - flags to swap creation with 
+				confirmation between requests and replies. By default, a 
+				request creates the RTP session and a reply confirms it. If
+				swapped, a reply will create the RTP session and a request
+				will confirm it.  The flag is considered depreciated and
+				provided for the compatibility purposes.  Use
+				rtpproxy_offer() or rtpproxy_answer() instead.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>w</emphasis> - flags that for the UA from which 
+				message is received, support symmetric RTP must be forced.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>zNN</emphasis> - requests the RTPproxy to perform
+				re-packetization of RTP traffic coming from the UA which
+				has sent the current message to increase or decrease payload
+				size per each RTP packet forwarded if possible.  The NN is the
+				target payload size in ms, for the most codecs its value should
+				be in 10ms increments, however for some codecs the increment
+				could differ (e.g. 30ms for GSM or 20ms for G.723).  The
+				RTPproxy would select the closest value supported by the codec.
+				This feature could be used for significantly reducing bandwith
+				overhead for low bitrate codecs, for example with G.729 going
+				from 10ms to 100ms saves two thirds of the network bandwith.
+				</para></listitem>
+			</itemizedlist>
+		</listitem>
+		<listitem><para>
+		<emphasis>ip_address</emphasis> - new SDP IP address.
+		</para></listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, 
+		FAILURE_ROUTE, BRANCH_ROUTE.
+		</para>
+		<example>
+		<title><function>force_rtp_proxy</function> usage</title>
+		<programlisting format="linespecific">
+...
+if (search("User-Agent: Cisco ATA.*") {force_rtp_proxy();};
+if (src_ip=1.2.3.4) {force_rtp_proxy("i");};
+if (search("User-Agent: Cisco ATA.*") {force_rtp_proxy("","1.2.3.4");};
+...
+</programlisting>
+		</example>
+	</section>
+        <section>
+                <title>
+                <function moreinfo="none">rtpproxy_offer([flags [, ip_address]])</function>
+                </title>
+                <para>
+                Rewrites &sdp; body to ensure that media is passed through
+                an &rtp; proxy. Equivalent of force_rtp_proxy() function to be invoked
+		on INVITE for the cases the SDPs are in INVITE and 200 OK and on 200 OK
+		when SDPs are in 200 OK and ACK.
+                </para>
+                <para>
+		See force_rtp_proxy() function description above for the meaning of the
+		parameters.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+		FAILURE_ROUTE, BRANCH_ROUTE.
+                </para>
+		<example>
+		<title><function>rtpproxy_offer</function> usage</title>
+		<programlisting format="linespecific">
+route {
+...
+    if (is_method("INVITE")) {
+        if (has_sdp()) {
+            if (rtpproxy_offer())
+                t_on_reply("1");
+        } else {
+            t_on_reply("2");
+        }
+    }
+    if (is_method("ACK") &amp;&amp; has_sdp())
+        rtpproxy_answer();
+...
+}
+
+onreply_route[1]
+{
+...
+    if (has_sdp())
+        rtpproxy_answer();
+...
+}
+
+onreply_route[2]
+{
+...
+    if (has_sdp())
+        rtpproxy_offer();
+...
+}
+</programlisting>
+                </example>
+	</section>
+        <section>
+                <title>
+                <function moreinfo="none">rtpproxy_answer([flags [, ip_address]])</function>
+                </title>
+		<para>
+		Rewrites &sdp; body to ensure that media is passed through
+		an &rtp; proxy. Equivalent of force_rtp_proxy() function to be invoked
+		on 200 OK for the cases the SDPs are in INVITE and 200 OK and on ACK
+		when SDPs are in 200 OK and ACK.
+		</para>
+		<para>
+		See force_rtp_proxy() function description above for the meaning of the
+		parameters.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+		FAILURE_ROUTE, BRANCH_ROUTE.
+		</para>
+		<example>
+		<para>
+		See rtpproxy_offer() function example above for example.
+		</para>
+		</example>
+        </section>
+	<section>
+		<title>
+		<function moreinfo="none">unforce_rtp_proxy()</function>
+		</title>
+		<para>
+		Tears down the RTPProxy session for the current call.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE.
+		</para>
+		<example>
+		<title><function>unforce_rtp_proxy</function> usage</title>
+		<programlisting format="linespecific">
+...
+unforce_rtp_proxy();
+...
+</programlisting>
+		</example>
+	</section>
+	<section id="rtpproxy_stream2xxx">
+	<title>
+	    <function>rtpproxy_stream2uac(prompt_name, count)</function>,
+	    <function>rtpproxy_stream2uas(prompt_name, count)</function>
+	</title>
+	<para>
+	    Instruct the RTPproxy to stream prompt/announcement pre-encoded with
+	    the makeann command from the RTPproxy distribution. The uac/uas
+	    suffix selects who will hear the announcement relatively to the current
+	    transaction - UAC or UAS. For example invoking the
+	    <function>rtpproxy_stream2uac</function> in the request processing
+	    block on ACK transaction will play the prompt to the UA that has
+	    generated original INVITE and ACK while
+	    <function>rtpproxy_stop_stream2uas</function> on 183 in reply
+	    processing block will play the prompt to the UA that has generated 183.
+	</para>
+	<para>
+	    Apart from generating announcements, another possible application
+	    of this function is implementing music on hold (MOH) functionality.
+	    When count is -1, the streaming will be in loop indefinitely until
+	    the appropriate <function>rtpproxy_stop_stream2xxx</function> is issued.
+	</para>
+	<para>
+	    In order to work correctly, functions require that the session in the
+	    RTPproxy already exists. Also those functions don't alted SDP, so that
+	    they are not substitute for calling <function>rtpproxy_offer</function>,
+	    <function>rtpproxy_answer</function> or
+	    <function>force_rtp_proxy</function>.
+	</para>
+	<para>
+	    This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE.
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para>
+		    <emphasis>prompt_name</emphasis> - name of the prompt to
+		    stream.  Should be either absolute pathname or pathname
+		    relative to the directory where RTPproxy runs.
+		</para>
+	    </listitem>
+	    <listitem>
+		<para>
+		    <emphasis>count</emphasis> - number of times the prompt
+		    should be repeated.  The value of -1 means that it will
+		    be streaming in loop indefinitely, until appropriate
+		    <function>rtpproxy_stop_stream2xxx</function> is issued.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>rtpproxy_stream2xxx</function> usage</title>
+	    <programlisting>
+...
+    if (is_method("INVITE")) {
+        rtpproxy_offer();
+        if (detect_hold()) {
+            rtpproxy_stream2uas("/var/rtpproxy/prompts/music_on_hold", "-1");
+        } else {
+            rtpproxy_stop_stream2uas();
+        };
+    };
+...
+	    </programlisting>
+	</example>
+	</section>
+	<section id="rtpproxy_stop_stream2xxx">
+	<title>
+	    <function>rtpproxy_stop_stream2uac()</function>,
+	    <function>rtpproxy_stop_stream2uas()</function>
+	</title>
+	<para>
+	    Stop streaming of announcement/prompt/MOH started previously by the
+	    respective <function>rtpproxy_stream2xxx</function>.  The uac/uas
+	    suffix selects whose announcement relatively to tha current
+	    transaction should be stopped - UAC or UAS.
+	</para>
+	<para>
+	    These functions can be used from REQUEST_ROUTE, ONREPLY_ROUTE.
+	</para>
+	</section>
+	<section>
+		<title>
+		<function moreinfo="none">add_rcv_param([flag])</function>,
+		</title>
+		<para>
+		Add received parameter to Contact header fields or Contact URI.
+		The parameter will 
+		contain URI created from the source IP, port, and protocol of the 
+		packet containing the SIP message. The parameter can be then 
+		processed by another registrar, this is useful, for example, when 
+		replicating register messages using t_replicate function to
+		another registrar.
+		</para>
+		<para>Meaning of the parameters is as follows:</para>
+		<itemizedlist>
+			<listitem><para>
+			<emphasis>flag</emphasis> - flags to indicate if the parameter
+			should be added to Contact URI or Contact header. If the flag is
+			non-zero, the parameter will be added to the Contact URI. If not
+			used or equal to zero, the parameter will go to the Contact 
+			header.
+			</para></listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from REQUEST_ROUTE.
+		</para>
+		<example>
+		<title><function>add_rcv_paramer</function> usage</title>
+		<programlisting format="linespecific">
+...
+add_rcv_param(); # add the parameter to the Contact header
+....
+add_rcv_param("1"); # add the parameter to the Contact URI
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title>
+		<function moreinfo="none">fix_nated_register()</function>
+		</title>
+		<para>
+		The function creates a URI consisting of the source IP, port, and 
+		protocol and stores the URI in an Attribute-Value-Pair. The URI will 
+		be appended as "received" parameter to Contact in 200 OK and 
+		registrar will store it in the received cloumn in the location table.
+		</para>
+		<para>
+		Note: You have to set the received_avp parameter of the nathelper module
+		and the registrar module (both module parameters must have the same value)
+		to use this function.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE.
+		</para>
+		<example>
+		<title><function>fix_nated_register</function> usage</title>
+		<programlisting format="linespecific">
+...
+fix_nated_register();
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title>
+			<function>nat_uac_test(flags)</function>
+		</title>
+		<para>
+			Tries to guess if client's request originated behind a nat.
+			The parameter determines what heuristics is used.
+		</para>
+		<para>Meaning of the flags is as follows:</para>
+		<itemizedlist>
+			<listitem><para>
+			<emphasis>1</emphasis> -  Contact header field is searched 
+			for occurrence of RFC1918 addresses.
+			</para></listitem>
+			<listitem><para>
+			<emphasis>2</emphasis> -  the "received" test is used: address
+			in Via is compared against source IP address of signaling
+			</para></listitem>
+			<listitem><para>
+			<emphasis>4</emphasis> -  Top Most VIA is searched 
+			for occurrence of RFC1918 addresses
+			</para></listitem>
+			<listitem><para>
+			<emphasis>8</emphasis> -  SDP is searched for occurrence of 
+			RFC1918 addresses
+			</para></listitem>
+			<listitem><para>
+			<emphasis>16</emphasis> -  test if the source port is different
+			from the port in Via 
+			</para></listitem>
+		</itemizedlist>
+		<para>
+		All flags can be bitwise combined, the test returns true if any of 
+		the tests identified a NAT.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE.
+		</para>
+	</section>
+	<section>
+		<title>
+		<function moreinfo="none">start_recording()</function>
+		</title>
+		<para>
+		This command will send a signal to the RTP-Proxy to record 
+		the RTP stream on the RTP-Proxy.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE.
+		</para>
+		<example>
+		<title><function>start_recording</function> usage</title>
+		<programlisting format="linespecific">
+...
+start_recording();
+...
+		</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title>
+		<function moreinfo="none">add_contact_alias()</function>
+		</title>
+		<para>
+		Adds ;alias=ip:port parameter to contact URI containing
+		received ip:port if contact uri ip:port does not match
+		received ip:port.
+		</para>
+		<para>
+		This function can be used from
+		REQUEST_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE, and LOCAL_ROUTE.
+		</para>
+		<example>
+		<title><function>add_contact_alias</function> usage</title>
+		<programlisting format="linespecific">
+...
+    if (!is_present_hf("Record-Route")) {
+        if (!add_contact_alias()) {
+            xlog("L_ERR", "Error in aliasing contact $ct\n");
+            send_reply("400", "Bad request");
+            exit;
+        };
+    };
+...
+		</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title>
+		<function moreinfo="none">handle_ruri_alias()</function>
+		</title>
+		<para>
+		Checks if Request URI has alias param and if so, removes
+		it and sets $du based on its value.  Note that this
+		means that routing of request is based on ;alias
+		parameter value of Request URI rather than Request URI
+		itself. If you call handle_ruri_alias() on a request,
+		make thus sure that you screen alias parameter value of
+		Request URI the same way as you would screen
+		Request URI itself.
+		</para>
+		<para>
+		Returns 1 if ;alias param was found and $du was set and
+		$ru rewritten, 2 if alias param was not found and
+		nothing was done, or -1 in case of error.
+		</para>
+		<para>
+		This function can be used from
+		REQUEST_ROUTE, BRANCH_ROUTE, and LOCAL_ROUTE.
+		</para>
+		<example>
+		<title><function>handle_ruri_alias</function> usage</title>
+		<programlisting format="linespecific">
+...
+    if ($du == "") {
+        handle_ruri_alias();
+        switch ($rc) {
+        case -1:
+            xlog("L_ERR", "Failed to handle alias of R-URI $ru\n");
+            send_reply("400", "Bad request");
+            exit;
+        case 1:
+            xlog("L_INFO", "Routing in-dialog $rm from $fu to $du\n");
+            break;
+        case 2:
+            xlog("L_INFO", "Routing in-dialog $rm from $fu to $ru\n");
+            break;
+         };
+    };
+...
+		</programlisting>
+		</example>
+	</section>
+
+	</section>
+
+	<section>
+		<title>Exported Pseudo Variables</title>
+		<section>
+			<title><function moreinfo="none">$rr_count</function></title>
+			<para>
+			Number of Record Routes in received SIP request
+			or reply.
+			</para>
+		<example>
+		<title>$rr_count usage</title>
+		<programlisting format="linespecific">
+...
+    $avp(rr_count) = $rr_count;
+...
+		</programlisting>
+		</example>
+	        </section>
+
+		<section>
+			<title><function moreinfo="none">$rr_top_count</function></title>
+			<para>
+			If topmost Record Route in received SIP request
+			or reply is a double Record Route, value of
+			$rr_top_count is 2.  If it a single Record
+			Route, value of $rr_top_count 
+			is 1.  If there is no Record Route(s), value of
+			$rr_top_count is 0.
+			</para>
+		<example>
+		<title>$rr_top_count usage</title>
+		<programlisting format="linespecific">
+...
+    if ($rr_count == $avp(rr_count) + $rr_top_count) {
+        route(ADD_CONTACT_ALIAS);
+    };
+...
+		</programlisting>
+		</example>
+	        </section>
+
+	</section>
+
+	<section>
+		<title><acronym>MI</acronym> Commands</title>
+		<section>
+			<title><function moreinfo="none">nh_enable_ping</function></title>
+			<para>
+			Enables natping if parameter value greater than 0.
+			Disables natping if parameter value is 0.
+			</para>
+			<para>
+			The function takes only one parameter - a number in decimal format.
+			</para>
+			<example>
+			<title><function moreinfo="none">nh_enable_ping</function> usage</title>
+			<programlisting format="linespecific">
+...
+$ &ctltool; fifo nh_enable_ping 1
+...
+			</programlisting>
+			</example>
+		</section>
+
+		<section>
+			<title><function moreinfo="none">nh_enable_rtpp</function></title>
+			<para>
+			Enables a rtp proxy if parameter value is greater than 0.
+			Disables it if a zero value is given.
+			</para>
+			<para>
+			The first parameter is the rtp proxy url (exactly as defined in 
+			the config file).
+			</para>
+			<para>
+			The second parameter value must be a number in decimal.
+			</para>
+			<para>
+			NOTE: if a rtpproxy is defined multiple times (in the same or
+			diferente sete), all its instances will be enables/disabled.
+			</para>
+			<example>
+			<title>
+			<function moreinfo="none">nh_enable_rtpp</function> usage</title>
+			<programlisting format="linespecific">
+...
+$ &ctltool; fifo nh_enable_rtpp udp:192.168.2.133:8081 0
+...
+			</programlisting>
+			</example>
+		</section>
+				
+			<section>
+			<title><function moreinfo="none">nh_show_rtpp</function></title>
+			<para>
+			Displays all the rtp proxies and their information: set and 
+			status (disabled or not, weight and recheck_ticks).
+			</para>
+			<para>
+			No parameter.
+			</para>
+			<example>
+			<title>
+				<function moreinfo="none">nh_show_rtpp</function> usage</title>
+			<programlisting format="linespecific">
+...
+$ &ctltool; fifo nh_show_rtpp 
+...
+			</programlisting>
+			</example>
+		</section>
+	</section>
+
+</chapter>
+

+ 79 - 0
modules_k/rtpproxy/doc/nathelper_faq.xml

@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module FAQ -->
+
+<chapter>
+    
+    <title>&faqguide;</title>
+    <qandaset defaultlabel="number">
+	<qandaentry>
+	    <question>
+		<para>What happend with <quote>rtpproxy_disable</quote> parameter?</para>
+	    </question>
+	    <answer>
+		<para>
+			It was removed as it became obsolete - now 
+			<quote>rtpproxy_sock</quote> can take empty value to disable the
+			rtpproxy functionality.
+		</para>
+	    </answer>
+	</qandaentry>
+	<qandaentry>
+	    <question>
+		<para>Where can I find more about &kamailio;?</para>
+	    </question>
+	    <answer>
+		<para>
+			Take a look at &kamailiohomelink;.
+		</para>
+	    </answer>
+	</qandaentry>
+	<qandaentry>
+	    <question>
+		<para>Where can I post a question about this module?</para>
+	    </question>
+	    <answer>
+		<para>
+			First at all check if your question was already answered on one of
+			our mailing lists: 
+		</para>
+		<itemizedlist>
+		    <listitem>
+			<para>User Mailing List - &kamailiouserslink;</para>
+		    </listitem>
+		    <listitem>
+			<para>Developer Mailing List - &kamailiodevlink;</para>
+		    </listitem>
+		</itemizedlist>
+		<para>
+			E-mails regarding any stable &kamailio; release should be sent to 
+			&kamailiousersmail; and e-mails regarding development versions
+			should be sent to &kamailiodevmail;.
+		</para>
+		<para>
+			If you want to keep the mail private, send it to 
+			&kamailiohelpmail;.
+		</para>
+	    </answer>
+	</qandaentry>
+	<qandaentry>
+	    <question>
+		<para>How can I report a bug?</para>
+	    </question>
+	    <answer>
+		<para>
+			Please follow the guidelines provided at:
+			&kamailiobugslink;.
+		</para>
+	    </answer>
+	</qandaentry>
+    </qandaset>
+</chapter>
+

+ 113 - 0
modules_k/rtpproxy/examples/4to6.cfg

@@ -0,0 +1,113 @@
+# $Id$
+#
+# Simple application level gateway config script.
+#
+# Assumes that SER/rtpproxy run on a machine, which connected to
+# both IPv4 and IPv6 networks.
+#
+# Correspondingly, this machine has two IP addresses: one IPv4
+# and the second one IPv6
+#
+# For example:
+#
+# 192.168.0.1 - "internal" IPv4 address
+# 2002:1234:5678::1 - "external" IPv6 address
+#
+# rtpproxy started with `-l 192.168.0.1 -6 /2002:1234:5678::1' option.
+# ser started with `-l 192.168.0.1 -l [2002:1234:5678::1] option.
+#
+
+# ------------------ module loading ----------------------------------
+
+loadmodule "/usr/local/lib/ser/modules/sl.so"
+loadmodule "/usr/local/lib/ser/modules/tm.so"
+loadmodule "/usr/local/lib/ser/modules/rr.so"
+loadmodule "/usr/local/lib/ser/modules/maxfwd.so"
+loadmodule "/usr/local/lib/ser/modules/usrloc.so"
+loadmodule "/usr/local/lib/ser/modules/registrar.so"
+loadmodule "/usr/local/lib/ser/modules/nathelper.so"
+
+# ----------------- setting module-specific parameters ---------------
+
+# -- nathelper params --
+
+modparam("nathelper", "natping_interval", 15)
+
+# ------------------ main fun below ----------------------------------
+
+route {
+	# initial sanity checks -- messages with
+	# max_forwars == 0, or excessively long requests,
+	# or those that don't addressed to us
+	if (!mf_process_maxfwd_header("10")) {
+		sl_send_reply("483", "Too Many Hops");
+		break;
+	};
+	if (msg:len > max_len) {
+		sl_send_reply("513", "Message too big");
+		break;
+	};
+	if (!(uri == myself) && method == "INVITE") {
+		sl_send_reply("403", "Call cannot be served here");
+		break;
+	};
+
+	if (method == "REGISTER") {
+		if (af == inet) {
+			save("location-inet4");
+		} else if (af == inet6) {
+			save("location-inet6");
+		} else {
+			sl_send_reply("403", "Call cannot be served here");
+		};
+		break;
+	};
+
+	if (method == "INVITE") {
+		if (lookup("location-inet4")) {
+			# Comment out three lines below if you want
+			# RTP for IPv4->IPv4 calls to go directly
+			# between UAs
+			if (af == inet)
+				if (force_rtp_proxy("FAII"))
+					t_on_reply("1");
+			if (af == inet6)
+				if (force_rtp_proxy("FAEI"))
+					t_on_reply("1");
+		} else if (lookup("location-inet6")) {
+			if (af == inet)
+				if (force_rtp_proxy("FAIE"))
+					t_on_reply("1");
+			# Comment out three lines below if you want
+			# RTP for IPv6->IPv6 calls to go directly
+			# between UAs
+			if (af == inet6)
+				if (force_rtp_proxy("FAEE"))
+					t_on_reply("1");
+		} else {
+			sl_send_reply("403", "Call cannot be served here");
+			break;
+		};
+	}
+
+	if (method == "BYE" || method == "CANCEL")
+		unforce_rtp_proxy();
+
+	# Do strict routing if pre-loaded route headers present
+	if (loose_route()) {
+		t_relay();
+		break;
+	};
+
+	if (method == "INVITE")
+		record_route();
+
+        if (!t_relay())
+        	sl_reply_error();
+}
+
+onreply_route[1] {
+	if (!(status=~"183" || status=~"200"))
+		break;
+	force_rtp_proxy("FA");
+}

+ 104 - 0
modules_k/rtpproxy/examples/alg.cfg

@@ -0,0 +1,104 @@
+# $Id$
+#
+# Simple application level gateway config script.
+#
+# Assumes that SER/rtpproxy run on a machine, which connected to
+# two non-routable letworks: 192.168.0.0/24 and 192.168.1.1/24.
+#
+# Correspondingly, this machine has two IP addresses: 192.168.0.1
+# and 192.168.1.1.
+#
+# 192.168.0.0/24 - "internal" network
+# 192.168.1.0/24 - "external" network
+#
+# rtpproxy started with `-l 192.168.0.1/192.168.1.1' option.
+#
+
+# ------------------ module loading ----------------------------------
+
+loadmodule "/usr/local/lib/ser/modules/sl.so"
+loadmodule "/usr/local/lib/ser/modules/tm.so"
+loadmodule "/usr/local/lib/ser/modules/rr.so"
+loadmodule "/usr/local/lib/ser/modules/maxfwd.so"
+loadmodule "/usr/local/lib/ser/modules/usrloc.so"
+loadmodule "/usr/local/lib/ser/modules/registrar.so"
+loadmodule "/usr/local/lib/ser/modules/nathelper.so"
+
+# ----------------- setting module-specific parameters ---------------
+
+# -- nathelper params --
+
+modparam("nathelper", "natping_interval", 15)
+
+# ------------------ main fun below ----------------------------------
+
+route {
+	# initial sanity checks -- messages with
+	# max_forwars == 0, or excessively long requests,
+	# or those that don't addressed to us
+	if (!mf_process_maxfwd_header("10")) {
+		sl_send_reply("483", "Too Many Hops");
+		break;
+	};
+	if (msg:len > max_len) {
+		sl_send_reply("513", "Message too big");
+		break;
+	};
+	if (!(uri == myself) && method == "INVITE") {
+		sl_send_reply("403", "Call cannot be served here");
+		break;
+	};
+
+	if (method == "REGISTER") {
+		if (dst_ip == 192.168.0.1) {
+			save("location-internal");
+		} else if (dst_ip == 192.168.1.1) {
+			save("location-external");
+		} else {
+			sl_send_reply("403", "Call cannot be served here");
+		};
+		break;
+	};
+
+	if (method == "INVITE") {
+		if (lookup("location-internal")) {
+			if (dst_ip == 192.168.0.1)
+				if (force_rtp_proxy("FAII"))
+					t_on_reply("1");
+			if (dst_ip == 192.168.1.1)
+				if (force_rtp_proxy("FAEI"))
+					t_on_reply("1");
+		} else if (lookup("location-external")) {
+			if (dst_ip == 192.168.0.1)
+				if (force_rtp_proxy("FAIE"))
+					t_on_reply("1");
+			if (dst_ip == 192.168.1.1)
+				if (force_rtp_proxy("FAEE"))
+					t_on_reply("1");
+		} else {
+			sl_send_reply("403", "Call cannot be served here");
+			break;
+		};
+	}
+
+	if (method == "BYE" || method == "CANCEL")
+		unforce_rtp_proxy();
+
+	# Do strict routing if pre-loaded route headers present
+	if (loose_route()) {
+		t_relay();
+		break;
+	};
+
+	if (method == "INVITE")
+		record_route();
+
+        if (!t_relay())
+        	sl_reply_error();
+}
+
+onreply_route[1] {
+	if (!(status=~"183" || status=~"200"))
+		break;
+	force_rtp_proxy("FA");
+}

+ 3621 - 0
modules_k/rtpproxy/nathelper.c

@@ -0,0 +1,3621 @@
+/* $Id$
+ *
+ * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * ---------
+ * 2003-10-09	nat_uac_test introduced (jiri)
+ *
+ * 2003-11-06   nat_uac_test permitted from onreply_route (jiri)
+ *
+ * 2003-12-01   unforce_rtp_proxy introduced (sobomax)
+ *
+ * 2004-01-07	RTP proxy support updated to support new version of the
+ *		RTP proxy (20040107).
+ *
+ *		force_rtp_proxy() now inserts a special flag
+ *		into the SDP body to indicate that this session already
+ *		proxied and ignores sessions with such flag.
+ *
+ *		Added run-time check for version of command protocol
+ *		supported by the RTP proxy.
+ *
+ * 2004-01-16   Integrated slightly modified patch from Tristan Colgate,
+ *		force_rtp_proxy function with IP as a parameter (janakj)
+ *
+ * 2004-01-28	nat_uac_test extended to allow testing SDP body (sobomax)
+ *
+ *		nat_uac_test extended to allow testing top Via (sobomax)
+ *
+ * 2004-02-21	force_rtp_proxy now accepts option argument, which
+ *		consists of string of chars, each of them turns "on"
+ *		some feature, currently supported ones are:
+ *
+ *		 `a' - flags that UA from which message is received
+ *		       doesn't support symmetric RTP;
+ *		 `l' - force "lookup", that is, only rewrite SDP when
+ *		       corresponding session is already exists in the
+ *		       RTP proxy. Only makes sense for SIP requests,
+ *		       replies are always processed in "lookup" mode;
+ *		 `i' - flags that message is received from UA in the
+ *		       LAN. Only makes sense when RTP proxy is running
+ *		       in the bridge mode.
+ *
+ *		force_rtp_proxy can now be invoked without any arguments,
+ *		as previously, with one argument - in this case argument
+ *		is treated as option string and with two arguments, in
+ *		which case 1st argument is option string and the 2nd
+ *		one is IP address which have to be inserted into
+ *		SDP (IP address on which RTP proxy listens).
+ *
+ * 2004-03-12	Added support for IPv6 addresses in SDPs. Particularly,
+ *		force_rtp_proxy now can work with IPv6-aware RTP proxy,
+ *		replacing IPv4 address in SDP with IPv6 one and vice versa.
+ *		This allows creating full-fledged IPv4<->IPv6 gateway.
+ *		See 4to6.cfg file for example.
+ *
+ *		Two new options added into force_rtp_proxy:
+ *
+ *		 `f' - instructs nathelper to ignore marks inserted
+ *		       by another nathelper in transit to indicate
+ *		       that the session is already goes through another
+ *		       proxy. Allows creating chain of proxies.
+ *		 `r' - flags that IP address in SDP should be trusted.
+ *		       Without this flag, nathelper ignores address in the
+ *		       SDP and uses source address of the SIP message
+ *		       as media address which is passed to the RTP proxy.
+ *
+ *		Protocol between nathelper and RTP proxy in bridge
+ *		mode has been slightly changed. Now RTP proxy expects SER
+ *		to provide 2 flags when creating or updating session
+ *		to indicate direction of this session. Each of those
+ *		flags can be either `e' or `i'. For example `ei' means
+ *		that we received INVITE from UA on the "external" network
+ *		network and will send it to the UA on "internal" one.
+ *		Also possible `ie' (internal->external), `ii'
+ *		(internal->internal) and `ee' (external->external). See
+ *		example file alg.cfg for details.
+ *
+ * 2004-03-15	If the rtp proxy test failed (wrong version or not started)
+ *		retry test from time to time, when some *rtpproxy* function
+ *		is invoked. Minimum interval between retries can be
+ *		configured via rtpproxy_disable_tout module parameter (default
+ *		is 60 seconds). Setting it to -1 will disable periodic
+ *		rechecks completely, setting it to 0 will force checks
+ *		for each *rtpproxy* function call. (andrei)
+ *
+ * 2004-03-22	Fix assignment of rtpproxy_retr and rtpproxy_tout module
+ *		parameters.
+ *
+ * 2004-03-22	Fix get_body position (should be called before get_callid)
+ * 				(andrei)
+ *
+ * 2004-03-24	Fix newport for null ip address case (e.g onhold re-INVITE)
+ * 				(andrei)
+ *
+ * 2004-09-30	added received port != via port test (andrei)
+ *
+ * 2004-10-10   force_socket option introduced (jiri)
+ *
+ * 2005-02-24	Added support for using more than one rtp proxy, in which
+ *		case traffic will be distributed evenly among them. In addition,
+ *		each such proxy can be assigned a weight, which will specify
+ *		which share of the traffic should be placed to this particular
+ *		proxy.
+ *
+ *		Introduce failover mechanism, so that if SER detects that one
+ *		of many proxies is no longer available it temporarily decreases
+ *		its weight to 0, so that no traffic will be assigned to it.
+ *		Such "disabled" proxies are periodically checked to see if they
+ *		are back to normal in which case respective weight is restored
+ *		resulting in traffic being sent to that proxy again.
+ *
+ *		Those features can be enabled by specifying more than one "URI"
+ *		in the rtpproxy_sock parameter, optionally followed by the weight,
+ *		which if absent is assumed to be 1, for example:
+ *
+ *		rtpproxy_sock="unix:/foo/bar=4 udp:1.2.3.4:3456=3 udp:5.6.7.8:5432=1"
+ *
+ * 2005-02-25	Force for pinging the socket returned by USRLOC (bogdan)
+ *
+ * 2005-03-22	support for multiple media streams added (netch)
+ *
+ * 2005-07-11  SIP ping support added (bogdan)
+ *
+ * 2005-07-14  SDP origin (o=) IP may be also changed (bogdan)
+ *
+ * 2006-03-08  fix_nated_sdp() may take one more param to force a specific IP;
+ *             force_rtp_proxy() accepts a new flag 's' to swap creation/
+ *              confirmation between requests/replies; 
+ *             add_rcv_param() may take as parameter a flag telling if the
+ *              parameter should go to the contact URI or contact header;
+ *             (bogdan)
+ * 2006-03-28 Support for changing session-level SDP connection (c=) IP when
+ *            media-description also includes connection information (bayan)
+ * 2007-04-13 Support multiple sets of rtpproxies and set selection added
+ *            (ancuta)
+ * 2007-04-26 Added some MI commands:
+ *             nh_enable_ping used to enable or disable natping
+ *             nh_enable_rtpp used to enable or disable a specific rtp proxy
+ *             nh_show_rtpp   used to display information for all rtp proxies 
+ *             (ancuta)
+ * 2007-05-09 New function start_recording() allowing to start recording RTP 
+ *             session in the RTP proxy (Carsten Bock - ported from SER)
+ * 2007-09-11 Separate timer process and support for multiple timer processes
+ *             (bogdan)
+ * 2008-12-12 Support for RTCP attribute in the SDP
+ *              (Min Wang/BASIS AudioNet - ported from SER)
+ * 2010-08-05 Core SDP parser integrated into nathelper
+ *             (osas)
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#ifndef __USE_BSD
+#define  __USE_BSD
+#endif
+#include <netinet/ip.h>
+#ifndef __FAVOR_BSD
+#define __FAVOR_BSD
+#endif
+#include <netinet/udp.h>
+#include <arpa/inet.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../../flags.h"
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../data_lump.h"
+#include "../../data_lump_rpl.h"
+#include "../../error.h"
+#include "../../forward.h"
+#include "../../mem/mem.h"
+#include "../../parser/parse_from.h"
+#include "../../parser/parse_to.h"
+#include "../../parser/parse_uri.h"
+#include "../../parser/parser_f.h"
+#include "../../parser/sdp/sdp.h"
+#include "../../resolve.h"
+#include "../../timer.h"
+#include "../../trim.h"
+#include "../../ut.h"
+#include "../../pt.h"
+#include "../../timer_proc.h"
+#include "../../lib/kmi/attr.h"
+#include "../../lib/kcore/km_ut.h"
+#include "../../lib/kcore/parser_helpers.h"
+#include "../../pvar.h"
+#include "../../lvalue.h"
+#include "../../msg_translator.h"
+#include "../../usr_avp.h"
+#include "../../socket_info.h"
+#include "../../mod_fix.h"
+#include "../../dset.h"
+#include "../registrar/sip_msg.h"
+#include "../usrloc/usrloc.h"
+#include "nathelper.h"
+#include "nhelpr_funcs.h"
+#include "sip_pinger.h"
+#include "rtpproxy_stream.h"
+ 
+MODULE_VERSION
+
+#if !defined(AF_LOCAL)
+#define	AF_LOCAL AF_UNIX
+#endif
+#if !defined(PF_LOCAL)
+#define	PF_LOCAL PF_UNIX
+#endif
+
+/* NAT UAC test constants */
+#define	NAT_UAC_TEST_C_1918	0x01
+#define	NAT_UAC_TEST_RCVD	0x02
+#define	NAT_UAC_TEST_V_1918	0x04
+#define	NAT_UAC_TEST_S_1918	0x08
+#define	NAT_UAC_TEST_RPORT	0x10
+
+
+#define DEFAULT_RTPP_SET_ID		0
+
+#define MI_SET_NATPING_STATE		"nh_enable_ping"
+#define MI_DEFAULT_NATPING_STATE	1
+
+#define MI_ENABLE_RTP_PROXY			"nh_enable_rtpp"
+#define MI_MIN_RECHECK_TICKS		0
+#define MI_MAX_RECHECK_TICKS		(unsigned int)-1
+
+#define MI_SHOW_RTP_PROXIES			"nh_show_rtpp"
+
+#define MI_RTP_PROXY_NOT_FOUND		"RTP proxy not found"
+#define MI_RTP_PROXY_NOT_FOUND_LEN	(sizeof(MI_RTP_PROXY_NOT_FOUND)-1)
+#define MI_PING_DISABLED			"NATping disabled from script"
+#define MI_PING_DISABLED_LEN		(sizeof(MI_PING_DISABLED)-1)
+#define MI_SET						"set"
+#define MI_SET_LEN					(sizeof(MI_SET)-1)
+#define MI_INDEX					"index"
+#define MI_INDEX_LEN				(sizeof(MI_INDEX)-1)
+#define MI_DISABLED					"disabled"
+#define MI_DISABLED_LEN				(sizeof(MI_DISABLED)-1)
+#define MI_WEIGHT					"weight"
+#define MI_WEIGHT_LEN				(sizeof(MI_WEIGHT)-1)
+#define MI_RECHECK_TICKS			"recheck_ticks"
+#define MI_RECHECK_T_LEN			(sizeof(MI_RECHECK_TICKS)-1)
+
+
+
+/* Supported version of the RTP proxy command protocol */
+#define	SUP_CPROTOVER	20040107
+/* Required additional version of the RTP proxy command protocol */
+#define	REQ_CPROTOVER	"20050322"
+/* Additional version necessary for re-packetization support */
+#define	REP_CPROTOVER	"20071116"
+#define	PTL_CPROTOVER	"20081102"
+
+#define	CPORT		"22222"
+static int nat_uac_test_f(struct sip_msg* msg, char* str1, char* str2);
+static int fix_nated_contact_f(struct sip_msg *, char *, char *);
+static int add_contact_alias_f(struct sip_msg *, char *, char *);
+static int handle_ruri_alias_f(struct sip_msg *, char *, char *);
+static int pv_get_rr_count_f(struct sip_msg *, pv_param_t *, pv_value_t *);
+static int pv_get_rr_top_count_f(struct sip_msg *, pv_param_t *, pv_value_t *);
+static int fix_nated_sdp_f(struct sip_msg *, char *, char *);
+static int extract_mediaip(str *, str *, int *, char *);
+static int alter_mediaip(struct sip_msg *, str *, str *, int, str *, int, int);
+static int alter_mediaport(struct sip_msg *, str *, str *, str *, int);
+static int alter_rtcp(struct sip_msg *msg, str *body, str *oldport, str *newport);
+static char *gencookie();
+static int rtpp_test(struct rtpp_node*, int, int);
+static int unforce_rtp_proxy_f(struct sip_msg *, char *, char *);
+static int force_rtp_proxy0_f(struct sip_msg *, char *, char *);
+static int force_rtp_proxy1_f(struct sip_msg *, char *, char *);
+static int force_rtp_proxy2_f(struct sip_msg *, char *, char *);
+static int force_rtp_proxy(struct sip_msg *, char *, char *, int);
+static int fix_nated_register_f(struct sip_msg *, char *, char *);
+static int fixup_fix_nated_register(void** param, int param_no);
+static int fixup_fix_sdp(void** param, int param_no);
+static int add_rcv_param_f(struct sip_msg *, char *, char *);
+static int start_recording_f(struct sip_msg *, char *, char *);
+static int rtpproxy_answer1_f(struct sip_msg *, char *, char *);
+static int rtpproxy_answer2_f(struct sip_msg *, char *, char *);
+static int rtpproxy_offer1_f(struct sip_msg *, char *, char *);
+static int rtpproxy_offer2_f(struct sip_msg *, char *, char *);
+
+static int add_rtpproxy_socks(struct rtpp_set * rtpp_list, char * rtpproxy);
+static int fixup_set_id(void ** param, int param_no);
+static int set_rtp_proxy_set_f(struct sip_msg * msg, char * str1, char * str2);
+static struct rtpp_set * select_rtpp_set(int id_set);
+
+static int rtpproxy_set_store(modparam_t type, void * val);
+static int nathelper_add_rtpproxy_set( char * rtp_proxies);
+
+static void nh_timer(unsigned int, void *);
+static int mod_init(void);
+static int child_init(int);
+static void mod_destroy(void);
+
+/*mi commands*/
+static struct mi_root* mi_enable_natping(struct mi_root* cmd_tree,
+		void* param );
+static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree,
+		void* param );
+static struct mi_root* mi_show_rtpproxies(struct mi_root* cmd_tree,
+		void* param);
+
+
+static usrloc_api_t ul;
+
+static int cblen = 0;
+static int natping_interval = 0;
+struct socket_info* force_socket = 0;
+
+
+static struct {
+	const char *cnetaddr;
+	uint32_t netaddr;
+	uint32_t mask;
+} nets_1918[] = {
+	{"10.0.0.0",    0, 0xffffffffu << 24},
+	{"172.16.0.0",  0, 0xffffffffu << 20},
+	{"192.168.0.0", 0, 0xffffffffu << 16},
+	{NULL, 0, 0}
+};
+
+/*
+ * If this parameter is set then the natpinger will ping only contacts
+ * that have the NAT flag set in user location database
+ */
+static int ping_nated_only = 0;
+static const char sbuf[4] = {0, 0, 0, 0};
+static char *force_socket_str = 0;
+static int rtpproxy_disable_tout = 60;
+static int rtpproxy_retr = 5;
+static int rtpproxy_tout = 1;
+static pid_t mypid;
+static unsigned int myseqn = 0;
+static str nortpproxy_str = str_init("a=nortpproxy:yes");
+static int sipping_flag = -1;
+static int natping_processes = 1;
+
+static char* rcv_avp_param = NULL;
+static unsigned short rcv_avp_type = 0;
+static int_str rcv_avp_name;
+
+static char *natping_socket = 0;
+static int raw_sock = -1;
+static unsigned int raw_ip = 0;
+static unsigned short raw_port = 0;
+
+
+static char ** rtpp_strings=0;
+static int rtpp_sets=0; /*used in rtpproxy_set_store()*/
+static int rtpp_set_count = 0;
+static unsigned int current_msg_id = (unsigned int)-1;
+/* RTP proxy balancing list */
+struct rtpp_set_head * rtpp_set_list =0;
+struct rtpp_set * selected_rtpp_set =0;
+struct rtpp_set * default_rtpp_set=0;
+
+/* array with the sockets used by rtpporxy (per process)*/
+static unsigned int rtpp_no = 0;
+static int *rtpp_socks = 0;
+
+
+/*0-> disabled, 1 ->enabled*/
+unsigned int *natping_state=0;
+
+static cmd_export_t cmds[] = {
+	{"fix_nated_contact",  (cmd_function)fix_nated_contact_f,    0,
+		0, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"add_contact_alias",  (cmd_function)add_contact_alias_f,    0,
+		0, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"handle_ruri_alias",  (cmd_function)handle_ruri_alias_f,    0,
+		0, 0,
+		REQUEST_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"fix_nated_sdp",      (cmd_function)fix_nated_sdp_f,        1,
+		fixup_fix_sdp,  0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"fix_nated_sdp",      (cmd_function)fix_nated_sdp_f,        2,
+		fixup_fix_sdp, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"set_rtp_proxy_set",  (cmd_function)set_rtp_proxy_set_f,    1,
+		fixup_set_id, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"unforce_rtp_proxy",  (cmd_function)unforce_rtp_proxy_f,    0,
+		0, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"force_rtp_proxy",    (cmd_function)force_rtp_proxy0_f,     0,
+		0, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"force_rtp_proxy",    (cmd_function)force_rtp_proxy1_f,     1,
+		0, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"force_rtp_proxy",    (cmd_function)force_rtp_proxy2_f,     2,
+		0, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"nat_uac_test",       (cmd_function)nat_uac_test_f,         1,
+		fixup_uint_null, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"fix_nated_register", (cmd_function)fix_nated_register_f,   0,
+		fixup_fix_nated_register, 0,
+		REQUEST_ROUTE },
+	{"add_rcv_param",      (cmd_function)add_rcv_param_f,        0,
+		0, 0,
+		REQUEST_ROUTE },
+	{"add_rcv_param",      (cmd_function)add_rcv_param_f,        1,
+		fixup_uint_null, 0,
+		REQUEST_ROUTE },
+	{"start_recording",    (cmd_function)start_recording_f,      0,
+		0, 0,
+		REQUEST_ROUTE | ONREPLY_ROUTE },
+	{"rtpproxy_offer",	(cmd_function)rtpproxy_offer1_f,     0,
+		0, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"rtpproxy_offer",	(cmd_function)rtpproxy_offer1_f,     1,
+		0, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"rtpproxy_offer",	(cmd_function)rtpproxy_offer2_f,     2,
+		0, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"rtpproxy_answer",	(cmd_function)rtpproxy_answer1_f,    0,
+		0, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"rtpproxy_answer",	(cmd_function)rtpproxy_answer1_f,    1,
+		0, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"rtpproxy_answer",	(cmd_function)rtpproxy_answer2_f,    2,
+		0, 0,
+		REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE},
+	{"rtpproxy_stream2uac",(cmd_function)rtpproxy_stream2uac2_f, 2,
+		fixup_var_str_int, 0,
+		REQUEST_ROUTE | ONREPLY_ROUTE },
+	{"rtpproxy_stream2uas",(cmd_function)rtpproxy_stream2uas2_f, 2,
+		fixup_var_str_int, 0,
+		REQUEST_ROUTE | ONREPLY_ROUTE },
+	{"rtpproxy_stop_stream2uac",(cmd_function)rtpproxy_stop_stream2uac2_f,0,
+		NULL, 0,
+		REQUEST_ROUTE | ONREPLY_ROUTE },
+	{"rtpproxy_stop_stream2uas",(cmd_function)rtpproxy_stop_stream2uas2_f,0,
+		NULL, 0,
+		REQUEST_ROUTE | ONREPLY_ROUTE },
+	{0, 0, 0, 0, 0, 0}
+};
+
+static pv_export_t mod_pvs[] = {
+    {{"rr_count", (sizeof("rr_count")-1)}, /* number of records routes */
+     PVT_CONTEXT, pv_get_rr_count_f, 0, 0, 0, 0, 0},
+    {{"rr_top_count", (sizeof("rr_top_count")-1)}, /* number of topmost rrs */
+     PVT_CONTEXT, pv_get_rr_top_count_f, 0, 0, 0, 0, 0},
+    {{0, 0}, 0, 0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[] = {
+	{"natping_interval",      INT_PARAM, &natping_interval      },
+	{"ping_nated_only",       INT_PARAM, &ping_nated_only       },
+	{"nortpproxy_str",        STR_PARAM, &nortpproxy_str.s      },
+	{"rtpproxy_sock",         STR_PARAM|USE_FUNC_PARAM,
+	                         (void*)rtpproxy_set_store          },
+	{"rtpproxy_disable_tout", INT_PARAM, &rtpproxy_disable_tout },
+	{"rtpproxy_retr",         INT_PARAM, &rtpproxy_retr         },
+	{"rtpproxy_tout",         INT_PARAM, &rtpproxy_tout         },
+	{"received_avp",          STR_PARAM, &rcv_avp_param         },
+	{"force_socket",          STR_PARAM, &force_socket_str      },
+	{"sipping_from",          STR_PARAM, &sipping_from.s        },
+	{"sipping_method",        STR_PARAM, &sipping_method.s      },
+	{"sipping_bflag",         INT_PARAM, &sipping_flag          },
+	{"natping_processes",     INT_PARAM, &natping_processes     },
+	{"natping_socket",        STR_PARAM, &natping_socket        },
+	{0, 0, 0}
+};
+
+static mi_export_t mi_cmds[] = {
+	{MI_SET_NATPING_STATE,    mi_enable_natping,    0,                0, 0},
+	{MI_ENABLE_RTP_PROXY,     mi_enable_rtp_proxy,  0,                0, 0},
+	{MI_SHOW_RTP_PROXIES,     mi_show_rtpproxies,   MI_NO_INPUT_FLAG, 0, 0},
+	{ 0, 0, 0, 0, 0}
+};
+
+
+struct module_exports exports = {
+	"nathelper",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	params,
+	0,           /* exported statistics */
+	mi_cmds,     /* exported MI functions */
+	mod_pvs,     /* exported pseudo-variables */
+	0,           /* extra processes */
+	mod_init,
+	0,           /* reply processing */
+	mod_destroy, /* destroy function */
+	child_init
+};
+
+
+static int rtpproxy_set_store(modparam_t type, void * val){
+
+	char * p;
+	int len;
+
+	p = (char* )val;
+
+	if(p==0 || *p=='\0'){
+		return 0;
+	}
+
+	if(rtpp_sets==0){
+		rtpp_strings = (char**)pkg_malloc(sizeof(char*));
+		if(!rtpp_strings){
+			LM_ERR("no pkg memory left\n");
+			return -1;
+		}
+	} else {/*realloc to make room for the current set*/
+		rtpp_strings = (char**)pkg_realloc(rtpp_strings, 
+										  (rtpp_sets+1)* sizeof(char*));
+		if(!rtpp_strings){
+			LM_ERR("no pkg memory left\n");
+			return -1;
+		}
+	}
+	
+	/*allocate for the current set of urls*/
+	len = strlen(p);
+	rtpp_strings[rtpp_sets] = (char*)pkg_malloc((len+1)*sizeof(char));
+
+	if(!rtpp_strings[rtpp_sets]){
+		LM_ERR("no pkg memory left\n");
+		return -1;
+	}
+	
+	memcpy(rtpp_strings[rtpp_sets], p, len);
+	rtpp_strings[rtpp_sets][len] = '\0';
+	rtpp_sets++;
+
+	return 0;
+}
+
+
+static int add_rtpproxy_socks(struct rtpp_set * rtpp_list, 
+										char * rtpproxy){
+	/* Make rtp proxies list. */
+	char *p, *p1, *p2, *plim;
+	struct rtpp_node *pnode;
+	int weight;
+
+	p = rtpproxy;
+	plim = p + strlen(p);
+
+	for(;;) {
+			weight = 1;
+		while (*p && isspace((int)*p))
+			++p;
+		if (p >= plim)
+			break;
+		p1 = p;
+		while (*p && !isspace((int)*p))
+			++p;
+		if (p <= p1)
+			break; /* may happen??? */
+		/* Have weight specified? If yes, scan it */
+		p2 = memchr(p1, '=', p - p1);
+		if (p2 != NULL) {
+			weight = strtoul(p2 + 1, NULL, 10);
+		} else {
+			p2 = p;
+		}
+		pnode = shm_malloc(sizeof(struct rtpp_node));
+		if (pnode == NULL) {
+			LM_ERR("no shm memory left\n");
+			return -1;
+		}
+		memset(pnode, 0, sizeof(*pnode));
+		pnode->idx = rtpp_no++;
+		pnode->rn_recheck_ticks = 0;
+		pnode->rn_weight = weight;
+		pnode->rn_umode = 0;
+		pnode->rn_disabled = 0;
+		pnode->rn_url.s = shm_malloc(p2 - p1 + 1);
+		if (pnode->rn_url.s == NULL) {
+			shm_free(pnode);
+			LM_ERR("no shm memory left\n");
+			return -1;
+		}
+		memmove(pnode->rn_url.s, p1, p2 - p1);
+		pnode->rn_url.s[p2 - p1] 	= 0;
+		pnode->rn_url.len 			= p2-p1;
+
+		LM_DBG("url is %s, len is %i\n", pnode->rn_url.s, pnode->rn_url.len);
+		/* Leave only address in rn_address */
+		pnode->rn_address = pnode->rn_url.s;
+		if (strncasecmp(pnode->rn_address, "udp:", 4) == 0) {
+			pnode->rn_umode = 1;
+			pnode->rn_address += 4;
+		} else if (strncasecmp(pnode->rn_address, "udp6:", 5) == 0) {
+			pnode->rn_umode = 6;
+			pnode->rn_address += 5;
+		} else if (strncasecmp(pnode->rn_address, "unix:", 5) == 0) {
+			pnode->rn_umode = 0;
+			pnode->rn_address += 5;
+		}
+
+		if (rtpp_list->rn_first == NULL) {
+			rtpp_list->rn_first = pnode;
+		} else {
+			rtpp_list->rn_last->rn_next = pnode;
+		}
+
+		rtpp_list->rn_last = pnode;
+		rtpp_list->rtpp_node_count++;
+	}
+	return 0;
+}
+
+
+/*	0-succes
+ *  -1 - erorr
+ * */
+static int nathelper_add_rtpproxy_set( char * rtp_proxies)
+{
+	char *p,*p2;
+	struct rtpp_set * rtpp_list;
+	unsigned int my_current_id;
+	str id_set;
+	int new_list;
+
+	/* empty definition? */
+	p= rtp_proxies;
+	if(!p || *p=='\0'){
+		return 0;
+	}
+
+	for(;*p && isspace(*p);p++);
+	if(*p=='\0'){
+		return 0;
+	}
+
+	rtp_proxies = strstr(p, "==");
+	if(rtp_proxies){
+		if(*(rtp_proxies +2)=='\0'){
+			LM_ERR("script error -invalid rtp proxy list!\n");
+			return -1;
+		}
+			
+		*rtp_proxies = '\0';
+		p2 = rtp_proxies-1;
+		for(;isspace(*p2); *p2 = '\0',p2--);
+		id_set.s = p;	id_set.len = p2 - p+1;
+			
+		if(id_set.len <= 0 ||str2int(&id_set, &my_current_id)<0 ){
+		LM_ERR("script error -invalid set_id value!\n");
+			return -1;
+		}
+			
+		rtp_proxies+=2;
+	}else{
+		rtp_proxies = p;
+		my_current_id = DEFAULT_RTPP_SET_ID;
+	}
+
+	for(;*rtp_proxies && isspace(*rtp_proxies);rtp_proxies++);
+
+	if(!(*rtp_proxies)){
+		LM_ERR("script error -empty rtp_proxy list\n");
+		return -1;;
+	}
+
+	/*search for the current_id*/
+	rtpp_list = rtpp_set_list ? rtpp_set_list->rset_first : 0;
+	while( rtpp_list != 0 && rtpp_list->id_set!=my_current_id)
+		rtpp_list = rtpp_list->rset_next;
+
+	if(rtpp_list==NULL){	/*if a new id_set : add a new set of rtpp*/
+		rtpp_list = shm_malloc(sizeof(struct rtpp_set));
+		if(!rtpp_list){
+			LM_ERR("no shm memory left\n");
+			return -1;
+		}
+		memset(rtpp_list, 0, sizeof(struct rtpp_set));
+		rtpp_list->id_set = my_current_id;
+		new_list = 1;
+	} else {
+		new_list = 0;
+	}
+
+	if(add_rtpproxy_socks(rtpp_list, rtp_proxies)!= 0){
+		/*if this list will not be inserted, clean it up*/
+		goto error;
+	}
+
+	if (new_list) {
+		if(!rtpp_set_list){/*initialize the list of set*/
+			rtpp_set_list = shm_malloc(sizeof(struct rtpp_set_head));
+			if(!rtpp_set_list){
+				LM_ERR("no shm memory left\n");
+				return -1;
+			}
+			memset(rtpp_set_list, 0, sizeof(struct rtpp_set_head));
+		}
+
+		/*update the list of set info*/
+		if(!rtpp_set_list->rset_first){
+			rtpp_set_list->rset_first = rtpp_list;
+		}else{
+			rtpp_set_list->rset_last->rset_next = rtpp_list;
+		}
+
+		rtpp_set_list->rset_last = rtpp_list;
+		rtpp_set_count++;
+
+		if(my_current_id == DEFAULT_RTPP_SET_ID){
+			default_rtpp_set = rtpp_list;
+		}
+	}
+
+	return 0;
+error:
+	return -1;
+}
+
+
+static int fixup_set_id(void ** param, int param_no)
+{
+	int int_val, err;
+	struct rtpp_set* rtpp_list;
+
+	int_val = str2s(*param, strlen(*param), &err);
+	if (err == 0) {
+		pkg_free(*param);
+		if((rtpp_list = select_rtpp_set(int_val)) ==0){
+			LM_ERR("rtpp_proxy set %i not configured\n", int_val);
+			return E_CFG;
+		}
+		*param = (void *)rtpp_list;
+	
+		return 0;
+	} else {
+		LM_ERR("bad number <%s>\n",	(char *)(*param));
+		return E_CFG;
+	}
+}
+
+static int
+fixup_fix_sdp(void** param, int param_no)
+{
+	pv_elem_t *model;
+	str s;
+
+	if (param_no==1) {
+		/* flags */
+		return fixup_uint_null( param, param_no);
+	}
+	/* new IP */
+	model=NULL;
+	s.s = (char*)(*param); s.len = strlen(s.s);
+	if(pv_parse_format(&s,&model)<0) {
+		LM_ERR("wrong format[%s]!\n", (char*)(*param));
+		return E_UNSPEC;
+	}
+	if (model==NULL) {
+		LM_ERR("empty parameter!\n");
+		return E_UNSPEC;
+	}
+	*param = (void*)model;
+	return 0;
+}
+
+static int fixup_fix_nated_register(void** param, int param_no)
+{
+	if (rcv_avp_name.n == 0) {
+		LM_ERR("you must set 'received_avp' parameter. Must be same value as"
+				" parameter 'received_avp' of registrar module\n");
+		return -1;
+	}
+	return 0;
+}
+
+static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree, 
+												void* param )
+{	struct mi_node* node;
+	str rtpp_url;
+	unsigned int enable;
+	struct rtpp_set * rtpp_list;
+	struct rtpp_node * crt_rtpp;
+	int found;
+
+	found = 0;
+
+	if(rtpp_set_list ==NULL)
+		goto end;
+
+	node = cmd_tree->node.kids;
+	if(node == NULL)
+		return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	if(node->value.s == NULL || node->value.len ==0)
+		return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+
+	rtpp_url = node->value;
+
+	node = node->next;
+	if(node == NULL)
+		return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	enable = 0;
+	if( strno2int( &node->value, &enable) <0)
+		goto error;
+
+	for(rtpp_list = rtpp_set_list->rset_first; rtpp_list != NULL; 
+					rtpp_list = rtpp_list->rset_next){
+
+		for(crt_rtpp = rtpp_list->rn_first; crt_rtpp != NULL;
+						crt_rtpp = crt_rtpp->rn_next){
+			/*found a matching rtpp*/
+			
+			if(crt_rtpp->rn_url.len == rtpp_url.len){
+				
+				if(strncmp(crt_rtpp->rn_url.s, rtpp_url.s, rtpp_url.len) == 0){
+					/*set the enabled/disabled status*/
+					found = 1;
+					crt_rtpp->rn_recheck_ticks = 
+						enable? MI_MIN_RECHECK_TICKS : MI_MAX_RECHECK_TICKS;
+					crt_rtpp->rn_disabled = enable?0:1;
+				}
+			}
+		}
+	}
+
+end:
+	if(found)
+		return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+	return init_mi_tree(404,MI_RTP_PROXY_NOT_FOUND,MI_RTP_PROXY_NOT_FOUND_LEN);
+error:
+	return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+}
+
+
+
+static struct mi_root* mi_enable_natping(struct mi_root* cmd_tree, 
+											void* param )
+{
+	unsigned int value;
+	struct mi_node* node;
+
+	if (natping_state==NULL)
+		return init_mi_tree( 400, MI_PING_DISABLED, MI_PING_DISABLED_LEN);
+
+	node = cmd_tree->node.kids;
+	if(node == NULL)
+		return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	value = 0;
+	if( strno2int( &node->value, &value) <0)
+		goto error;
+
+	(*natping_state) = value?1:0;
+	
+	return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+error:
+	return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+}
+
+
+
+#define add_rtpp_node_int_info(_parent, _name, _name_len, _value, _child,\
+								_len, _string, _error)\
+	do {\
+		(_string) = int2str((_value), &(_len));\
+		if((_string) == 0){\
+			LM_ERR("cannot convert int value\n");\
+				goto _error;\
+		}\
+		if(((_child) = add_mi_node_child((_parent), MI_DUP_VALUE, (_name), \
+				(_name_len), (_string), (_len))   ) == 0)\
+			goto _error;\
+	}while(0);
+
+static struct mi_root* mi_show_rtpproxies(struct mi_root* cmd_tree, 
+												void* param)
+{
+	struct mi_node* node, *crt_node, *child;
+	struct mi_root* root;
+	struct mi_attr * attr;
+	struct rtpp_set * rtpp_list;
+	struct rtpp_node * crt_rtpp;
+	char * string, *id;
+	int id_len, len;
+
+	string = id = 0;
+
+	root = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+	if (!root) {
+		LM_ERR("the MI tree cannot be initialized!\n");
+		return 0;
+	}
+
+	if(rtpp_set_list ==NULL)
+		return root;
+
+	node = &root->node;
+
+	for(rtpp_list = rtpp_set_list->rset_first; rtpp_list != NULL; 
+					rtpp_list = rtpp_list->rset_next){
+
+		for(crt_rtpp = rtpp_list->rn_first; crt_rtpp != NULL;
+						crt_rtpp = crt_rtpp->rn_next){
+
+			id =  int2str(rtpp_list->id_set, &id_len);
+			if(!id){
+				LM_ERR("cannot convert set id\n");
+				goto error;
+			}
+
+			if(!(crt_node = add_mi_node_child(node, 0, crt_rtpp->rn_url.s, 
+					crt_rtpp->rn_url.len, 0,0)) ) {
+				LM_ERR("cannot add the child node to the tree\n");
+				goto error;
+			}
+
+			LM_DBG("adding node name %s \n",crt_rtpp->rn_url.s );
+
+			if((attr = add_mi_attr(crt_node, MI_DUP_VALUE, MI_SET, MI_SET_LEN, 
+									id, id_len))== 0){
+				LM_ERR("cannot add attributes to the node\n");
+				goto error;
+			}
+
+			add_rtpp_node_int_info(crt_node, MI_INDEX, MI_INDEX_LEN,
+				crt_rtpp->idx, child, len,string,error);
+			add_rtpp_node_int_info(crt_node, MI_DISABLED, MI_DISABLED_LEN,
+				crt_rtpp->rn_disabled, child, len,string,error);
+			add_rtpp_node_int_info(crt_node, MI_WEIGHT, MI_WEIGHT_LEN,
+				crt_rtpp->rn_weight,  child, len, string,error);
+			add_rtpp_node_int_info(crt_node, MI_RECHECK_TICKS,MI_RECHECK_T_LEN,
+				crt_rtpp->rn_recheck_ticks, child, len, string, error);
+		}
+	}
+
+	return root;
+error:
+	if (root)
+		free_mi_tree(root);
+	return 0;
+}
+
+
+static int init_raw_socket(void)
+{
+	int on = 1;
+
+	raw_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+	if (raw_sock ==-1) {
+		LM_ERR("cannot create raw socket\n");
+		return -1;
+	}
+
+	if (setsockopt(raw_sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) == -1) {
+		LM_ERR("cannot set socket options\n");
+		return -1;
+	}
+
+	return raw_sock;
+}
+
+
+static int get_natping_socket(char *socket, 
+										unsigned int *ip, unsigned short *port)
+{
+	struct hostent* he;
+	str host;
+	int lport;
+	int lproto;
+
+	if (parse_phostport( socket, &host.s, &host.len, &lport, &lproto)!=0){
+		LM_CRIT("invalid natping_socket parameter <%s>\n",natping_socket);
+		return -1;
+	}
+
+	if (lproto!=PROTO_UDP && lproto!=PROTO_NONE) {
+		LM_CRIT("natping_socket can be only UDP <%s>\n",natping_socket);
+		return 0;
+	}
+	lproto = PROTO_UDP;
+	*port = lport?(unsigned short)lport:SIP_PORT;
+
+	he = sip_resolvehost( &host, port, (char*)(void*)&lproto);
+	if (he==0) {
+		LM_ERR("could not resolve hostname:\"%.*s\"\n", host.len, host.s);
+		return -1;
+	}
+	if (he->h_addrtype != AF_INET) {
+		LM_ERR("only ipv4 addresses allowed in natping_socket\n");
+		return -1;
+	}
+
+	memcpy( ip, he->h_addr_list[0], he->h_length);
+
+	return 0;
+}
+
+
+static int
+mod_init(void)
+{
+	int i;
+	bind_usrloc_t bind_usrloc;
+	struct in_addr addr;
+	str socket_str;
+	pv_spec_t avp_spec;
+	str s;
+
+	if(register_mi_mod(exports.name, mi_cmds)!=0)
+	{
+		LM_ERR("failed to register MI commands\n");
+		return -1;
+	}
+
+	if (rcv_avp_param && *rcv_avp_param) {
+		s.s = rcv_avp_param; s.len = strlen(s.s);
+		if (pv_parse_spec(&s, &avp_spec)==0
+				|| avp_spec.type!=PVT_AVP) {
+			LM_ERR("malformed or non AVP %s AVP definition\n", rcv_avp_param);
+			return -1;
+		}
+
+		if(pv_get_avp_name(0, &avp_spec.pvp, &rcv_avp_name, &rcv_avp_type)!=0)
+		{
+			LM_ERR("[%s]- invalid AVP definition\n", rcv_avp_param);
+			return -1;
+		}
+	} else {
+		rcv_avp_name.n = 0;
+		rcv_avp_type = 0;
+	}
+
+	if (force_socket_str) {
+		socket_str.s=force_socket_str;
+		socket_str.len=strlen(socket_str.s);
+		force_socket=grep_sock_info(&socket_str,0,0);
+	}
+
+	/* create raw socket? */
+	if (natping_socket && natping_socket[0]) {
+		if (get_natping_socket( natping_socket, &raw_ip, &raw_port)!=0)
+			return -1;
+		if (init_raw_socket() < 0)
+			return -1;
+	}
+
+	/* any rtpproxy configured? */
+	if(rtpp_set_list)
+		default_rtpp_set = select_rtpp_set(DEFAULT_RTPP_SET_ID);
+
+	if (nortpproxy_str.s==NULL || nortpproxy_str.s[0]==0) {
+		nortpproxy_str.len = 0;
+		nortpproxy_str.s = NULL;
+	} else {
+		nortpproxy_str.len = strlen(nortpproxy_str.s);
+		while (nortpproxy_str.len > 0 && (nortpproxy_str.s[nortpproxy_str.len - 1] == '\r' ||
+		    nortpproxy_str.s[nortpproxy_str.len - 1] == '\n'))
+			nortpproxy_str.len--;
+		if (nortpproxy_str.len == 0)
+			nortpproxy_str.s = NULL;
+	}
+
+	if (natping_interval > 0) {
+		bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0);
+		if (!bind_usrloc) {
+			LM_ERR("can't find usrloc module\n");
+			return -1;
+		}
+
+		if (bind_usrloc(&ul) < 0) {
+			return -1;
+		}
+
+		natping_state =(unsigned int *) shm_malloc(sizeof(unsigned int));
+		if (!natping_state) {
+			LM_ERR("no shmem left\n");
+			return -1;
+		}
+		*natping_state = MI_DEFAULT_NATPING_STATE;
+
+		if (ping_nated_only && ul.nat_flag==0) {
+			LM_ERR("bad config - ping_nated_only enabled, but no nat bflag"
+				" set in usrloc module\n");
+			return -1;
+		}
+		if (natping_processes>=8) {
+			LM_ERR("too many natping processes (%d) max=8\n",
+				natping_processes);
+			return -1;
+		}
+
+		sipping_flag = (sipping_flag==-1)?0:(1<<sipping_flag);
+
+		/* set reply function if SIP natping is enabled */
+		if (sipping_flag) {
+			if (sipping_from.s==0 || sipping_from.s[0]==0) {
+				LM_ERR("SIP ping enabled, but SIP ping FROM is empty!\n");
+				return -1;
+			}
+			if (sipping_method.s==0 || sipping_method.s[0]==0) {
+				LM_ERR("SIP ping enabled, but SIP ping method is empty!\n");
+				return -1;
+			}
+			sipping_method.len = strlen(sipping_method.s);
+			sipping_from.len = strlen(sipping_from.s);
+			exports.response_f = sipping_rpl_filter;
+			init_sip_ping();
+		}
+
+		register_procs(natping_processes);
+	}
+
+	/* Prepare 1918 networks list */
+	for (i = 0; nets_1918[i].cnetaddr != NULL; i++) {
+		if (inet_aton(nets_1918[i].cnetaddr, &addr) != 1)
+			abort();
+		nets_1918[i].netaddr = ntohl(addr.s_addr) & nets_1918[i].mask;
+	}
+
+	/* storing the list of rtp proxy sets in shared memory*/
+	for(i=0;i<rtpp_sets;i++){
+		if(nathelper_add_rtpproxy_set(rtpp_strings[i]) !=0){
+			for(;i<rtpp_sets;i++)
+				if(rtpp_strings[i])
+					pkg_free(rtpp_strings[i]);
+			pkg_free(rtpp_strings);
+			return -1;
+		}
+		if(rtpp_strings[i])
+			pkg_free(rtpp_strings[i]);
+	}
+	if (rtpp_strings)
+		pkg_free(rtpp_strings);
+
+	return 0;
+}
+
+
+static int
+child_init(int rank)
+{
+	int n, i;
+	char *cp;
+	struct addrinfo hints, *res;
+	struct rtpp_set  *rtpp_list;
+	struct rtpp_node *pnode;
+
+	if (rank==PROC_MAIN && natping_interval > 0) {
+		for( i=0 ; i<natping_processes ; i++ ) {
+			if(fork_dummy_timer(PROC_TIMER, "TIMER NH", 1 /*socks flag*/,
+								nh_timer, (void*)(unsigned long)i,
+								1 /*sec*/)<0) {
+				LM_ERR("failed to register timer routine as process\n");
+				return -1;
+				/* error */
+			}
+		}
+	}
+
+	if (rank<=0 && rank!=PROC_TIMER)
+		return 0;
+
+	if(rtpp_set_list==NULL )
+		return 0;
+
+	/* Iterate known RTP proxies - create sockets */
+	mypid = getpid();
+
+	rtpp_socks = (int*)pkg_malloc( sizeof(int)*rtpp_no );
+	if (rtpp_socks==NULL) {
+		LM_ERR("no more pkg memory\n");
+		return -1;
+	}
+
+	for(rtpp_list = rtpp_set_list->rset_first; rtpp_list != 0;
+		rtpp_list = rtpp_list->rset_next){
+
+		for (pnode=rtpp_list->rn_first; pnode!=0; pnode = pnode->rn_next){
+			char *hostname;
+
+			if (pnode->rn_umode == 0) {
+				rtpp_socks[pnode->idx] = -1;
+				goto rptest;
+			}
+
+			/*
+			 * This is UDP or UDP6. Detect host and port; lookup host;
+			 * do connect() in order to specify peer address
+			 */
+			hostname = (char*)pkg_malloc(sizeof(char) * (strlen(pnode->rn_address) + 1));
+			if (hostname==NULL) {
+				LM_ERR("no more pkg memory\n");
+				return -1;
+			}
+			strcpy(hostname, pnode->rn_address);
+
+			cp = strrchr(hostname, ':');
+			if (cp != NULL) {
+				*cp = '\0';
+				cp++;
+			}
+			if (cp == NULL || *cp == '\0')
+				cp = CPORT;
+
+			memset(&hints, 0, sizeof(hints));
+			hints.ai_flags = 0;
+			hints.ai_family = (pnode->rn_umode == 6) ? AF_INET6 : AF_INET;
+			hints.ai_socktype = SOCK_DGRAM;
+			if ((n = getaddrinfo(hostname, cp, &hints, &res)) != 0) {
+				LM_ERR("%s\n", gai_strerror(n));
+				pkg_free(hostname);
+				return -1;
+			}
+			pkg_free(hostname);
+
+			rtpp_socks[pnode->idx] = socket((pnode->rn_umode == 6)
+			    ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
+			if ( rtpp_socks[pnode->idx] == -1) {
+				LM_ERR("can't create socket\n");
+				freeaddrinfo(res);
+				return -1;
+			}
+
+			if (connect( rtpp_socks[pnode->idx], res->ai_addr, res->ai_addrlen) == -1) {
+				LM_ERR("can't connect to a RTP proxy\n");
+				close( rtpp_socks[pnode->idx] );
+				rtpp_socks[pnode->idx] = -1;
+				freeaddrinfo(res);
+				return -1;
+			}
+			freeaddrinfo(res);
+rptest:
+			pnode->rn_disabled = rtpp_test(pnode, 0, 1);
+		}
+	}
+
+	return 0;
+}
+
+
+static void mod_destroy(void)
+{
+	struct rtpp_set * crt_list, * last_list;
+	struct rtpp_node * crt_rtpp, *last_rtpp;
+
+	/*free the shared memory*/
+	if (natping_state)
+		shm_free(natping_state);
+
+	if(rtpp_set_list == NULL)
+		return;
+
+	for(crt_list = rtpp_set_list->rset_first; crt_list != NULL; ){
+
+		for(crt_rtpp = crt_list->rn_first; crt_rtpp != NULL;  ){
+
+			if(crt_rtpp->rn_url.s)
+				shm_free(crt_rtpp->rn_url.s);
+
+			last_rtpp = crt_rtpp;
+			crt_rtpp = last_rtpp->rn_next;
+			shm_free(last_rtpp);
+		}
+
+		last_list = crt_list;
+		crt_list = last_list->rset_next;
+		shm_free(last_list);
+	}
+
+	shm_free(rtpp_set_list);
+}
+
+
+
+static int
+isnulladdr(str *sx, int pf)
+{
+	char *cp;
+
+	if (pf == AF_INET6) {
+		for(cp = sx->s; cp < sx->s + sx->len; cp++)
+			if (*cp != '0' && *cp != ':')
+				return 0;
+		return 1;
+	}
+	return (sx->len == 7 && memcmp("0.0.0.0", sx->s, 7) == 0);
+}
+
+/*
+ * Replaces ip:port pair in the Contact: field with the source address
+ * of the packet.
+ */
+static int
+fix_nated_contact_f(struct sip_msg* msg, char* str1, char* str2)
+{
+	int offset, len, len1;
+	char *cp, *buf, temp[2];
+	contact_t *c;
+	struct lump *anchor;
+	struct sip_uri uri;
+	str hostport;
+
+	if (get_contact_uri(msg, &uri, &c) == -1)
+		return -1;
+	if ((c->uri.s < msg->buf) || (c->uri.s > (msg->buf + msg->len))) {
+		LM_ERR("you can't call fix_nated_contact twice, "
+		    "check your config!\n");
+		return -1;
+	}
+
+	offset = c->uri.s - msg->buf;
+	anchor = del_lump(msg, offset, c->uri.len, HDR_CONTACT_T);
+	if (anchor == 0)
+		return -1;
+
+	hostport = uri.host;
+	if (uri.port.len > 0)
+		hostport.len = uri.port.s + uri.port.len - uri.host.s;
+
+	cp = ip_addr2a(&msg->rcv.src_ip);
+	len = c->uri.len + strlen(cp) + 6 /* :port */ - hostport.len + 1;
+	buf = pkg_malloc(len);
+	if (buf == NULL) {
+		LM_ERR("out of pkg memory\n");
+		return -1;
+	}
+	temp[0] = hostport.s[0];
+	temp[1] = c->uri.s[c->uri.len];
+	c->uri.s[c->uri.len] = hostport.s[0] = '\0';
+	len1 = snprintf(buf, len, "%s%s:%d%s", c->uri.s, cp, msg->rcv.src_port,
+	    hostport.s + hostport.len);
+	if (len1 < len)
+		len = len1;
+	hostport.s[0] = temp[0];
+	c->uri.s[c->uri.len] = temp[1];
+	if (insert_new_lump_after(anchor, buf, len, HDR_CONTACT_T) == 0) {
+		pkg_free(buf);
+		return -1;
+	}
+	c->uri.s = buf;
+	c->uri.len = len;
+
+	return 1;
+}
+
+
+#define SALIAS        ";alias="
+#define SALIAS_LEN (sizeof(SALIAS) - 1)
+
+/*
+ * Adds ;alias=ip:port param to cotact uri containing received ip:port
+ * if contact uri ip:port does not match received ip:port.
+ */
+static int
+add_contact_alias_f(struct sip_msg* msg, char* str1, char* str2)
+{
+    int len, param_len, ip_len;
+    contact_t *c;
+    struct lump *anchor;
+    struct sip_uri uri;
+    struct ip_addr *ip;
+    char *param, *at, *port, *start;
+
+    /* Do nothing if Contact header does not exist */
+    if (!msg->contact) {
+	if (parse_headers(msg, HDR_CONTACT_F, 0) == -1)  {
+	    LM_ERR("while parsing headers\n");
+	    return -1;
+	}
+	if (!msg->contact) {
+	    LM_DBG("no contact header\n");
+	    return 2;
+	}
+    }
+    if (get_contact_uri(msg, &uri, &c) == -1) {
+	LM_ERR("failed to get contact uri\n");
+	return -1;
+    }
+
+    /* Compare source ip and port against contact uri */
+    if ((ip = str2ip(&(uri.host))) == NULL) {
+	LM_ERR("contact uri host is not an ip address\n");
+	return -1;
+    }
+    if (ip_addr_cmp(ip, &(msg->rcv.src_ip)) &&
+	((msg->rcv.src_port == uri.port_no) ||
+	 ((uri.port.len == 0) && (msg->rcv.src_port == 5060)))) {
+	LM_DBG("no need to add alias param\n");
+	return 2;
+    }
+	
+    /* Add alias param */
+    if ((c->uri.s < msg->buf) || (c->uri.s > (msg->buf + msg->len))) {
+	LM_ERR("you can't call alias_contact twice, check your config!\n");
+	return -1;
+    }
+    param_len = SALIAS_LEN + IP6_MAX_STR_SIZE + 1 /* : */ + 5 /* port */ +
+	1 /* t */ + 1 /* proto */;
+    param = (char*)pkg_malloc(param_len);
+    if (!param) {
+	LM_ERR("no pkg memory left for alias param\n");
+	return -1;
+    }
+    at = param;
+    append_str(at, SALIAS, SALIAS_LEN);
+    ip_len = ip_addr2sbuf(&(msg->rcv.src_ip), at, param_len - SALIAS_LEN);
+    if (ip_len <= 0) {
+	pkg_free(param);
+	LM_ERR("failed to copy source ip\n");
+	return -1;
+    }
+    at = at + ip_len;
+    append_chr(at, ':');
+    port = int2str(msg->rcv.src_port, &len);
+    append_str(at, port, len);
+    append_chr(at, 't');
+    if ((msg->rcv.proto < PROTO_UDP) || (msg->rcv.proto > PROTO_SCTP)) {
+	pkg_free(param);
+	LM_ERR("invalid transport protocol\n");
+	return -1;
+    }
+    append_chr(at, msg->rcv.proto + '0');
+    param_len = at - param;
+    LM_DBG("adding param <%.*s>\n", param_len, param);
+    if (uri.port.len > 0) {
+	start = uri.port.s + uri.port.len;
+    } else {
+	start = uri.host.s + uri.host.len;
+    }
+    anchor = anchor_lump(msg, start - msg->buf, 0, 0);
+    if (anchor == NULL) {
+	pkg_free(param);
+	LM_ERR("anchor_lump failed\n");
+	return -1;
+    }
+    if (insert_new_lump_after(anchor, param, param_len, 0) == 0) {
+	LM_ERR("insert_new_lump_after failed\n");
+	pkg_free(param);
+	return -1;
+    }
+    return 1;
+}
+
+
+#define ALIAS        "alias="
+#define ALIAS_LEN (sizeof(ALIAS) - 1)
+
+/*
+ * Checks if r-uri has alias param and if so, removes it and sets $du
+ * based on its value.
+ */
+static int
+handle_ruri_alias_f(struct sip_msg* msg, char* str1, char* str2)
+{
+    str uri, proto;
+    char buf[MAX_URI_SIZE], *val, *sep, *trans, *at, *next, *cur_uri, *rest;
+    unsigned int len, rest_len, val_len, alias_len, proto_type, cur_uri_len,
+	ip_port_len;
+
+    if (parse_sip_msg_uri(msg) < 0) {
+	LM_ERR("while parsing Request-URI\n");
+	return -1;
+    }
+    rest = msg->parsed_uri.sip_params.s;
+    rest_len = msg->parsed_uri.sip_params.len;
+    if (rest_len == 0) {
+	LM_DBG("no params\n");
+	return 2;
+    }
+    while (rest_len >= ALIAS_LEN) {
+	if (strncmp(rest, ALIAS, ALIAS_LEN) == 0) break;
+	sep = memchr(rest, 59 /* ; */, rest_len);
+	if (sep == NULL) {
+	    LM_DBG("no alias param\n");
+	    return 2;
+	} else {
+	    rest_len = rest_len - (sep - rest + 1);
+	    rest = sep + 1;
+	}
+    }
+
+    if (rest_len < ALIAS_LEN) {
+	LM_DBG("no alias param\n");
+	return 2;
+    }
+
+    /* set dst uri based on alias param value */
+    val = rest + ALIAS_LEN;
+    val_len = rest_len - ALIAS_LEN;
+    sep = memchr(val, 116 /* t */, val_len);
+    if (sep == NULL) {
+	LM_ERR("no 't' in alias param value\n");
+	return -1;
+    }
+    at = &(buf[0]);
+    append_str(at, "sip:", 4);
+    ip_port_len = sep - val;
+    alias_len = SALIAS_LEN + ip_port_len + 2 /* tn */;
+    memcpy(at, val, ip_port_len);
+    at = at + ip_port_len;
+    trans = sep + 1;
+    if ((ip_port_len + 2 > val_len) || (*trans == ';') || (*trans == '?')) {
+	LM_ERR("no proto in alias param\n");
+	return -1;
+    }
+    proto_type = *trans - 48 /* char 0 */;
+    if (proto_type != PROTO_UDP) {
+	proto_type_to_str(proto_type, &proto);
+	if (proto.len == 0) {
+	    LM_ERR("unknown proto in alias param\n");
+	    return -1;
+	}
+	append_str(at, ";transport=", 11);
+	memcpy(at, proto.s, proto.len);
+	at = at + proto.len;
+    }
+    next = trans + 1;
+    if ((ip_port_len + 2 < val_len) && (*next != ';') && (*next != '?')) {
+	LM_ERR("invalid alias param value\n");
+	return -1;
+    }
+    uri.s = &(buf[0]);
+    uri.len = at - &(buf[0]);
+    LM_DBG("setting dst_uri to <%.*s>\n", uri.len, uri.s);
+    if (set_dst_uri(msg, &uri) == -1) {
+	LM_ERR("failed to set dst uri\n");
+	return -1;
+    }
+    
+    /* remove alias param */
+    if (msg->new_uri.s) {
+	cur_uri = msg->new_uri.s;
+	cur_uri_len = msg->new_uri.len;
+    } else {
+	cur_uri = msg->first_line.u.request.uri.s;
+	cur_uri_len = msg->first_line.u.request.uri.len;
+    }
+    at = &(buf[0]);
+    len = rest - 1 /* ; */ - cur_uri;
+    memcpy(at, cur_uri, len);
+    at = at + len;
+    len = cur_uri_len - alias_len - len;
+    memcpy(at, rest + alias_len - 1, len);
+    uri.s = &(buf[0]);
+    uri.len = cur_uri_len - alias_len;
+    LM_DBG("rewriting r-uri to <%.*s>\n", uri.len, uri.s);
+    return rewrite_uri(msg, &uri);
+}
+
+
+/*
+ * Counts and return the number of record routes in rr headers of the message.
+ */
+static int
+pv_get_rr_count_f(struct sip_msg *msg, pv_param_t *param,
+		  pv_value_t *res)
+{
+    unsigned int count;
+    struct hdr_field *header;
+    rr_t *body;
+
+    if (msg == NULL) return -1;
+
+    if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
+	LM_ERR("while parsing message\n");
+	return -1;
+    }
+
+    count = 0;
+    header = msg->record_route;
+
+    while (header) {
+	if (header->type == HDR_RECORDROUTE_T) {
+	    if (parse_rr(header) == -1) {
+		LM_ERR("while parsing rr header\n");
+		return -1;
+	    }
+	    body = (rr_t *)header->parsed;
+	    while (body) {
+		count++;
+		body = body->next;
+	    }
+	}
+	header = header->next;
+    }
+
+    return pv_get_uintval(msg, param, res, (unsigned int)count);
+}
+
+/*
+ * Return count of topmost record routes in rr headers of the message.
+ */
+static int
+pv_get_rr_top_count_f(struct sip_msg *msg, pv_param_t *param,
+		      pv_value_t *res)
+{
+    str uri;
+    struct sip_uri puri;
+
+    if (msg == NULL) return -1;
+
+    if (!msg->record_route &&
+	(parse_headers(msg, HDR_RECORDROUTE_F, 0) == -1)) {
+	LM_ERR("while parsing Record-Route header\n");
+	return -1;
+    }
+
+    if (!msg->record_route) {
+	return pv_get_uintval(msg, param, res, 0);
+    }
+    
+    if (parse_rr(msg->record_route) == -1) {
+	LM_ERR("while parsing rr header\n");
+	return -1;
+    }
+
+    uri = ((rr_t *)msg->record_route->parsed)->nameaddr.uri;
+    if (parse_uri(uri.s, uri.len, &puri) < 0) {
+	LM_ERR("while parsing rr uri\n");
+	return -1;
+    }
+	
+    if (puri.r2.len > 0) {
+	return pv_get_uintval(msg, param, res, 2);
+    } else {
+	return pv_get_uintval(msg, param, res, 1);
+    }
+}
+
+
+/*
+ * Test if IP address pointed to by saddr belongs to RFC1918 networks
+ */
+static inline int
+is1918addr(str *saddr)
+{
+	struct in_addr addr;
+	uint32_t netaddr;
+	int i, rval;
+	char backup;
+
+	rval = -1;
+	backup = saddr->s[saddr->len];
+	saddr->s[saddr->len] = '\0';
+	if (inet_aton(saddr->s, &addr) != 1)
+		goto theend;
+	netaddr = ntohl(addr.s_addr);
+	for (i = 0; nets_1918[i].cnetaddr != NULL; i++) {
+		if ((netaddr & nets_1918[i].mask) == nets_1918[i].netaddr) {
+			rval = 1;
+			goto theend;
+		}
+	}
+	rval = 0;
+
+theend:
+	saddr->s[saddr->len] = backup;
+	return rval;
+}
+
+/*
+ * test for occurrence of RFC1918 IP address in Contact HF
+ */
+static int
+contact_1918(struct sip_msg* msg)
+{
+	struct sip_uri uri;
+	contact_t* c;
+
+	if (get_contact_uri(msg, &uri, &c) == -1)
+		return -1;
+
+	return (is1918addr(&(uri.host)) == 1) ? 1 : 0;
+}
+
+/*
+ * test for occurrence of RFC1918 IP address in SDP
+ */
+static int
+sdp_1918(struct sip_msg* msg)
+{
+	str body, ip;
+	int pf;
+
+	if (extract_body(msg, &body) == -1) {
+		LM_ERR("cannot extract body from msg!\n");
+		return 0;
+	}
+	if (extract_mediaip(&body, &ip, &pf,"c=") == -1) {
+		LM_ERR("can't extract media IP from the SDP\n");
+		return 0;
+	}
+	if (pf != AF_INET || isnulladdr(&ip, pf))
+		return 0;
+
+	return (is1918addr(&ip) == 1) ? 1 : 0;
+}
+
+/*
+ * test for occurrence of RFC1918 IP address in top Via
+ */
+static int
+via_1918(struct sip_msg* msg)
+{
+
+	return (is1918addr(&(msg->via1->host)) == 1) ? 1 : 0;
+}
+
+static int
+nat_uac_test_f(struct sip_msg* msg, char* str1, char* str2)
+{
+	int tests;
+
+	tests = (int)(long)str1;
+
+	/* return true if any of the NAT-UAC tests holds */
+
+	/* test if the source port is different from the port in Via */
+	if ((tests & NAT_UAC_TEST_RPORT) &&
+		 (msg->rcv.src_port!=(msg->via1->port?msg->via1->port:SIP_PORT)) ){
+		return 1;
+	}
+	/*
+	 * test if source address of signaling is different from
+	 * address advertised in Via
+	 */
+	if ((tests & NAT_UAC_TEST_RCVD) && received_test(msg))
+		return 1;
+	/*
+	 * test for occurrences of RFC1918 addresses in Contact
+	 * header field
+	 */
+	if ((tests & NAT_UAC_TEST_C_1918) && (contact_1918(msg)>0))
+		return 1;
+	/*
+	 * test for occurrences of RFC1918 addresses in SDP body
+	 */
+	if ((tests & NAT_UAC_TEST_S_1918) && sdp_1918(msg))
+		return 1;
+	/*
+	 * test for occurrences of RFC1918 addresses top Via
+	 */
+	if ((tests & NAT_UAC_TEST_V_1918) && via_1918(msg))
+		return 1;
+
+	/* no test succeeded */
+	return -1;
+
+}
+
+#define	ADD_ADIRECTION	0x01
+#define	FIX_MEDIP	0x02
+#define	ADD_ANORTPPROXY	0x04
+#define	FIX_ORGIP	0x08
+
+#define	ADIRECTION	"a=direction:active"
+#define	ADIRECTION_LEN	(sizeof(ADIRECTION) - 1)
+
+#define	AOLDMEDIP	"a=oldmediaip:"
+#define	AOLDMEDIP_LEN	(sizeof(AOLDMEDIP) - 1)
+
+#define	AOLDMEDIP6	"a=oldmediaip6:"
+#define	AOLDMEDIP6_LEN	(sizeof(AOLDMEDIP6) - 1)
+
+#define	AOLDMEDPRT	"a=oldmediaport:"
+#define	AOLDMEDPRT_LEN	(sizeof(AOLDMEDPRT) - 1)
+
+
+static inline int 
+replace_sdp_ip(struct sip_msg* msg, str *org_body, char *line, str *ip)
+{
+	str body1, oldip, newip;
+	str body = *org_body;
+	unsigned hasreplaced = 0;
+	int pf, pf1 = 0;
+	str body2;
+	char *bodylimit = body.s + body.len;
+
+	/* Iterate all lines and replace ips in them. */
+	if (!ip) {
+		newip.s = ip_addr2a(&msg->rcv.src_ip);
+		newip.len = strlen(newip.s);
+	} else {
+		newip = *ip;
+	}
+	body1 = body;
+	for(;;) {
+		if (extract_mediaip(&body1, &oldip, &pf,line) == -1)
+			break;
+		if (pf != AF_INET) {
+			LM_ERR("not an IPv4 address in '%s' SDP\n",line);
+				return -1;
+			}
+		if (!pf1)
+			pf1 = pf;
+		else if (pf != pf1) {
+			LM_ERR("mismatching address families in '%s' SDP\n",line);
+			return -1;
+		}
+		body2.s = oldip.s + oldip.len;
+		body2.len = bodylimit - body2.s;
+		if (alter_mediaip(msg, &body1, &oldip, pf, &newip, pf,1) == -1) {
+			LM_ERR("can't alter '%s' IP\n",line);
+			return -1;
+		}
+		hasreplaced = 1;
+		body1 = body2;
+	}
+	if (!hasreplaced) {
+		LM_ERR("can't extract '%s' IP from the SDP\n",line);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+fix_nated_sdp_f(struct sip_msg* msg, char* str1, char* str2)
+{
+	str body;
+	str ip;
+	int level;
+	char *buf;
+	struct lump* anchor;
+
+	level = (int)(long)str1;
+	if (str2 && pv_printf_s( msg, (pv_elem_p)str2, &ip)!=0)
+		return -1;
+
+	if (extract_body(msg, &body) == -1) {
+		LM_ERR("cannot extract body from msg!\n");
+		return -1;
+	}
+
+	if (level & (ADD_ADIRECTION | ADD_ANORTPPROXY)) {
+		msg->msg_flags |= FL_FORCE_ACTIVE;
+		anchor = anchor_lump(msg, body.s + body.len - msg->buf, 0, 0);
+		if (anchor == NULL) {
+			LM_ERR("anchor_lump failed\n");
+			return -1;
+		}
+		if (level & ADD_ADIRECTION) {
+			buf = pkg_malloc((ADIRECTION_LEN + CRLF_LEN) * sizeof(char));
+			if (buf == NULL) {
+				LM_ERR("out of pkg memory\n");
+				return -1;
+			}
+			memcpy(buf, CRLF, CRLF_LEN);
+			memcpy(buf + CRLF_LEN, ADIRECTION, ADIRECTION_LEN);
+			if (insert_new_lump_after(anchor, buf, ADIRECTION_LEN + CRLF_LEN, 0) == NULL) {
+				LM_ERR("insert_new_lump_after failed\n");
+				pkg_free(buf);
+				return -1;
+			}
+		}
+		if ((level & ADD_ANORTPPROXY) && nortpproxy_str.len) {
+			buf = pkg_malloc((nortpproxy_str.len + CRLF_LEN) * sizeof(char));
+			if (buf == NULL) {
+				LM_ERR("out of pkg memory\n");
+				return -1;
+			}
+			memcpy(buf, CRLF, CRLF_LEN);
+			memcpy(buf + CRLF_LEN, nortpproxy_str.s, nortpproxy_str.len);
+			if (insert_new_lump_after(anchor, buf, nortpproxy_str.len + CRLF_LEN, 0) == NULL) {
+				LM_ERR("insert_new_lump_after failed\n");
+				pkg_free(buf);
+				return -1;
+			}
+		}
+	}
+
+	if (level & FIX_MEDIP) {
+		/* Iterate all c= and replace ips in them. */
+		if (replace_sdp_ip(msg, &body, "c=", str2?&ip:0)==-1)
+			return -1;
+	}
+
+	if (level & FIX_ORGIP) {
+		/* Iterate all o= and replace ips in them. */
+		if (replace_sdp_ip(msg, &body, "o=", str2?&ip:0)==-1)
+			return -1;
+	}
+
+	return 1;
+}
+
+static int
+extract_mediaip(str *body, str *mediaip, int *pf, char *line)
+{
+	char *cp, *cp1;
+	int len, nextisip;
+
+	cp1 = NULL;
+	for (cp = body->s; (len = body->s + body->len - cp) > 0;) {
+		cp1 = ser_memmem(cp, line, len, 2);
+		if (cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r')
+			break;
+		cp = cp1 + 2;
+	}
+	if (cp1 == NULL)
+		return -1;
+
+	mediaip->s = cp1 + 2;
+	mediaip->len = eat_line(mediaip->s, body->s + body->len - mediaip->s) - mediaip->s;
+	trim_len(mediaip->len, mediaip->s, *mediaip);
+
+	nextisip = 0;
+	for (cp = mediaip->s; cp < mediaip->s + mediaip->len;) {
+		len = eat_token_end(cp, mediaip->s + mediaip->len) - cp;
+		if (nextisip == 1) {
+			mediaip->s = cp;
+			mediaip->len = len;
+			nextisip++;
+			break;
+		}
+		if (len == 3 && memcmp(cp, "IP", 2) == 0) {
+			switch (cp[2]) {
+			case '4':
+				nextisip = 1;
+				*pf = AF_INET;
+				break;
+
+			case '6':
+				nextisip = 1;
+				*pf = AF_INET6;
+				break;
+
+			default:
+				break;
+			}
+		}
+		cp = eat_space_end(cp + len, mediaip->s + mediaip->len);
+	}
+	if (nextisip != 2 || mediaip->len == 0) {
+		LM_ERR("no `IP[4|6]' in `%s' field\n",line);
+		return -1;
+	}
+	return 1;
+}
+
+static int
+alter_mediaip(struct sip_msg *msg, str *body, str *oldip, int oldpf,
+  str *newip, int newpf, int preserve)
+{
+	char *buf;
+	int offset;
+	struct lump* anchor;
+	str omip, nip, oip;
+
+	/* check that updating mediaip is really necessary */
+	if (oldpf == newpf && isnulladdr(oldip, oldpf))
+		return 0;
+	if (newip->len == oldip->len &&
+	    memcmp(newip->s, oldip->s, newip->len) == 0)
+		return 0;
+
+	if (preserve != 0) {
+		anchor = anchor_lump(msg, body->s + body->len - msg->buf, 0, 0);
+		if (anchor == NULL) {
+			LM_ERR("anchor_lump failed\n");
+			return -1;
+		}
+		if (oldpf == AF_INET6) {
+			omip.s = AOLDMEDIP6;
+			omip.len = AOLDMEDIP6_LEN;
+		} else {
+			omip.s = AOLDMEDIP;
+			omip.len = AOLDMEDIP_LEN;
+		}
+		buf = pkg_malloc(omip.len + oldip->len + CRLF_LEN);
+		if (buf == NULL) {
+			LM_ERR("out of pkg memory\n");
+			return -1;
+		}
+		memcpy(buf, CRLF, CRLF_LEN);
+		memcpy(buf + CRLF_LEN, omip.s, omip.len);
+		memcpy(buf + CRLF_LEN + omip.len, oldip->s, oldip->len);
+		if (insert_new_lump_after(anchor, buf,
+		    omip.len + oldip->len + CRLF_LEN, 0) == NULL) {
+			LM_ERR("insert_new_lump_after failed\n");
+			pkg_free(buf);
+			return -1;
+		}
+	}
+
+	if (oldpf == newpf) {
+		nip.len = newip->len;
+		nip.s = pkg_malloc(nip.len);
+		if (nip.s == NULL) {
+			LM_ERR("out of pkg memory\n");
+			return -1;
+		}
+		memcpy(nip.s, newip->s, newip->len);
+	} else {
+		nip.len = newip->len + 2;
+		nip.s = pkg_malloc(nip.len);
+		if (nip.s == NULL) {
+			LM_ERR("out of pkg memory\n");
+			return -1;
+		}
+		memcpy(nip.s + 2, newip->s, newip->len);
+		nip.s[0] = (newpf == AF_INET6) ? '6' : '4';
+		nip.s[1] = ' ';
+	}
+
+	oip = *oldip;
+	if (oldpf != newpf) {
+		do {
+			oip.s--;
+			oip.len++;
+		} while (*oip.s != '6' && *oip.s != '4');
+	}
+	offset = oip.s - msg->buf;
+	anchor = del_lump(msg, offset, oip.len, 0);
+	if (anchor == NULL) {
+		LM_ERR("del_lump failed\n");
+		pkg_free(nip.s);
+		return -1;
+	}
+
+	if (insert_new_lump_after(anchor, nip.s, nip.len, 0) == 0) {
+		LM_ERR("insert_new_lump_after failed\n");
+		pkg_free(nip.s);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+alter_mediaport(struct sip_msg *msg, str *body, str *oldport, str *newport,
+  int preserve)
+{
+	char *buf;
+	int offset;
+	struct lump* anchor;
+
+	/* check that updating mediaport is really necessary */
+	if (newport->len == oldport->len &&
+	    memcmp(newport->s, oldport->s, newport->len) == 0)
+		return 0;
+
+	/*
+	 * Since rewriting the same info twice will mess SDP up,
+	 * apply simple anti foot shooting measure - put flag on
+	 * messages that have been altered and check it when
+	 * another request comes.
+	 */
+#if 0
+	/* disabled: - it propagates to the reply and we don't want this
+	 *  -- andrei */
+	if (msg->msg_flags & FL_SDP_PORT_AFS) {
+		LM_ERR("you can't rewrite the same SDP twice, check your config!\n");
+		return -1;
+	}
+#endif
+
+	if (preserve != 0) {
+		anchor = anchor_lump(msg, body->s + body->len - msg->buf, 0, 0);
+		if (anchor == NULL) {
+			LM_ERR("anchor_lump failed\n");
+			return -1;
+		}
+		buf = pkg_malloc(AOLDMEDPRT_LEN + oldport->len + CRLF_LEN);
+		if (buf == NULL) {
+			LM_ERR("out of pkg memory\n");
+			return -1;
+		}
+		memcpy(buf, CRLF, CRLF_LEN);
+		memcpy(buf + CRLF_LEN, AOLDMEDPRT, AOLDMEDPRT_LEN);
+		memcpy(buf + CRLF_LEN + AOLDMEDPRT_LEN, oldport->s, oldport->len);
+		if (insert_new_lump_after(anchor, buf,
+		    AOLDMEDPRT_LEN + oldport->len + CRLF_LEN, 0) == NULL) {
+			LM_ERR("insert_new_lump_after failed\n");
+			pkg_free(buf);
+			return -1;
+		}
+	}
+
+	buf = pkg_malloc(newport->len);
+	if (buf == NULL) {
+		LM_ERR("out of pkg memory\n");
+		return -1;
+	}
+	offset = oldport->s - msg->buf;
+	anchor = del_lump(msg, offset, oldport->len, 0);
+	if (anchor == NULL) {
+		LM_ERR("del_lump failed\n");
+		pkg_free(buf);
+		return -1;
+	}
+	memcpy(buf, newport->s, newport->len);
+	if (insert_new_lump_after(anchor, buf, newport->len, 0) == 0) {
+		LM_ERR("insert_new_lump_after failed\n");
+		pkg_free(buf);
+		return -1;
+	}
+
+#if 0
+	msg->msg_flags |= FL_SDP_PORT_AFS;
+#endif
+	return 0;
+}
+
+/*
+ * this function is ported from SER 
+ */
+static int
+alter_rtcp(struct sip_msg *msg, str *body, str *oldport, str *newport)
+{
+	char *buf;
+	int offset;
+	struct lump* anchor;
+
+	/* check that updating rtcpport is really necessary */
+	if (newport->len == oldport->len &&
+	    memcmp(newport->s, oldport->s, newport->len) == 0)
+		return 0;
+
+	buf = pkg_malloc(newport->len);
+	if (buf == NULL) {
+		LM_ERR("alter_rtcp: out of memory\n");
+		return -1;
+	}
+	offset = oldport->s - msg->buf;
+	anchor = del_lump(msg, offset, oldport->len, 0);
+	if (anchor == NULL) {
+		LM_ERR("alter_rtcp: del_lump failed\n");
+		pkg_free(buf);
+		return -1;
+	}
+	memcpy(buf, newport->s, newport->len);
+	if (insert_new_lump_after(anchor, buf, newport->len, 0) == 0) {
+		LM_ERR("alter_rtcp: insert_new_lump_after failed\n");
+		pkg_free(buf);
+		return -1;
+	}
+
+	return 0;
+}
+
+static char * gencookie(void)
+{
+	static char cook[34];
+
+	sprintf(cook, "%d_%u ", (int)mypid, myseqn);
+	myseqn++;
+	return cook;
+}
+
+static int
+rtpp_checkcap(struct rtpp_node *node, char *cap, int caplen)
+{
+	char *cp;
+	struct iovec vf[4] = {{NULL, 0}, {"VF", 2}, {" ", 1}, {NULL, 0}};
+
+	vf[3].iov_base = cap;
+	vf[3].iov_len = caplen;
+
+	cp = send_rtpp_command(node, vf, 4);
+	if (cp == NULL)
+		return -1;
+	if (cp[0] == 'E' || atoi(cp) != 1)
+		return 0;
+	return 1;
+}
+
+static int
+rtpp_test(struct rtpp_node *node, int isdisabled, int force)
+{
+	int rtpp_ver, rval;
+	char *cp;
+	struct iovec v[2] = {{NULL, 0}, {"V", 1}};
+
+	if(node->rn_recheck_ticks == MI_MAX_RECHECK_TICKS){
+	    LM_DBG("rtpp %s disabled for ever\n", node->rn_url.s);
+		return 1;
+	}
+
+	if (force == 0) {
+		if (isdisabled == 0)
+			return 0;
+		if (node->rn_recheck_ticks > get_ticks())
+			return 1;
+	}
+	cp = send_rtpp_command(node, v, 2);
+	if (cp == NULL) {
+		LM_WARN("can't get version of the RTP proxy\n");
+		goto error;
+	}
+	rtpp_ver = atoi(cp);
+	if (rtpp_ver != SUP_CPROTOVER) {
+		LM_WARN("unsupported version of RTP proxy <%s> found: %d supported,"
+				"%d present\n", node->rn_url.s, SUP_CPROTOVER, rtpp_ver);
+		goto error;
+	}
+	rval = rtpp_checkcap(node, REQ_CPROTOVER, sizeof(REQ_CPROTOVER) - 1);
+	if (rval == -1) {
+		LM_WARN("RTP proxy went down during version query\n");
+		goto error;
+	}
+	if (rval == 0) {
+		LM_WARN("of RTP proxy <%s> doesn't support required protocol version"
+				"%s\n", node->rn_url.s, REQ_CPROTOVER);
+		goto error;
+	}
+	LM_INFO("rtp proxy <%s> found, support for it %senabled\n",
+	    node->rn_url.s, force == 0 ? "re-" : "");
+	/* Check for optional capabilities */
+	rval = rtpp_checkcap(node, REP_CPROTOVER, sizeof(REP_CPROTOVER) - 1);
+	if (rval != -1) {
+		node->rn_rep_supported = rval;
+	} else {
+		node->rn_rep_supported = 0;
+	}
+	rval = rtpp_checkcap(node, PTL_CPROTOVER, sizeof(PTL_CPROTOVER) - 1);
+	if (rval != -1) {
+		node->rn_ptl_supported = rval;
+	} else {
+		node->rn_ptl_supported = 0;
+	}
+	return 0;
+error:
+	LM_WARN("support for RTP proxy <%s> has been disabled%s\n", node->rn_url.s,
+	    rtpproxy_disable_tout < 0 ? "" : " temporarily");
+	if (rtpproxy_disable_tout >= 0)
+		node->rn_recheck_ticks = get_ticks() + rtpproxy_disable_tout;
+
+	return 1;
+}
+
+char *
+send_rtpp_command(struct rtpp_node *node, struct iovec *v, int vcnt)
+{
+	struct sockaddr_un addr;
+	int fd, len, i;
+	char *cp;
+	static char buf[256];
+	struct pollfd fds[1];
+
+	len = 0;
+	cp = buf;
+	if (node->rn_umode == 0) {
+		memset(&addr, 0, sizeof(addr));
+		addr.sun_family = AF_LOCAL;
+		strncpy(addr.sun_path, node->rn_address,
+		    sizeof(addr.sun_path) - 1);
+#ifdef HAVE_SOCKADDR_SA_LEN
+		addr.sun_len = strlen(addr.sun_path);
+#endif
+
+		fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+		if (fd < 0) {
+			LM_ERR("can't create socket\n");
+			goto badproxy;
+		}
+		if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+			close(fd);
+			LM_ERR("can't connect to RTP proxy\n");
+			goto badproxy;
+		}
+
+		do {
+			len = writev(fd, v + 1, vcnt - 1);
+		} while (len == -1 && errno == EINTR);
+		if (len <= 0) {
+			close(fd);
+			LM_ERR("can't send command to a RTP proxy\n");
+			goto badproxy;
+		}
+		do {
+			len = read(fd, buf, sizeof(buf) - 1);
+		} while (len == -1 && errno == EINTR);
+		close(fd);
+		if (len <= 0) {
+			LM_ERR("can't read reply from a RTP proxy\n");
+			goto badproxy;
+		}
+	} else {
+		fds[0].fd = rtpp_socks[node->idx];
+		fds[0].events = POLLIN;
+		fds[0].revents = 0;
+		/* Drain input buffer */
+		while ((poll(fds, 1, 0) == 1) &&
+		    ((fds[0].revents & POLLIN) != 0)) {
+			recv(rtpp_socks[node->idx], buf, sizeof(buf) - 1, 0);
+			fds[0].revents = 0;
+		}
+		v[0].iov_base = gencookie();
+		v[0].iov_len = strlen(v[0].iov_base);
+		for (i = 0; i < rtpproxy_retr; i++) {
+			do {
+				len = writev(rtpp_socks[node->idx], v, vcnt);
+			} while (len == -1 && (errno == EINTR || errno == ENOBUFS));
+			if (len <= 0) {
+				LM_ERR("can't send command to a RTP proxy\n");
+				goto badproxy;
+			}
+			while ((poll(fds, 1, rtpproxy_tout * 1000) == 1) &&
+			    (fds[0].revents & POLLIN) != 0) {
+				do {
+					len = recv(rtpp_socks[node->idx], buf, sizeof(buf)-1, 0);
+				} while (len == -1 && errno == EINTR);
+				if (len <= 0) {
+					LM_ERR("can't read reply from a RTP proxy\n");
+					goto badproxy;
+				}
+				if (len >= (v[0].iov_len - 1) &&
+				    memcmp(buf, v[0].iov_base, (v[0].iov_len - 1)) == 0) {
+					len -= (v[0].iov_len - 1);
+					cp += (v[0].iov_len - 1);
+					if (len != 0) {
+						len--;
+						cp++;
+					}
+					goto out;
+				}
+				fds[0].revents = 0;
+			}
+		}
+		if (i == rtpproxy_retr) {
+			LM_ERR("timeout waiting reply from a RTP proxy\n");
+			goto badproxy;
+		}
+	}
+
+out:
+	cp[len] = '\0';
+	return cp;
+badproxy:
+	LM_ERR("proxy <%s> does not respond, disable it\n", node->rn_url.s);
+	node->rn_disabled = 1;
+	node->rn_recheck_ticks = get_ticks() + rtpproxy_disable_tout;
+	
+	return NULL;
+}
+
+/*
+ * select the set with the id_set id
+ */
+
+static struct rtpp_set * select_rtpp_set(int id_set ){
+
+	struct rtpp_set * rtpp_list;
+	/*is it a valid set_id?*/
+	
+	if(!rtpp_set_list || !rtpp_set_list->rset_first){
+		LM_ERR("no rtp_proxy configured\n");
+		return 0;
+	}
+
+	for(rtpp_list=rtpp_set_list->rset_first; rtpp_list!=0 && 
+		rtpp_list->id_set!=id_set; rtpp_list=rtpp_list->rset_next);
+	if(!rtpp_list){
+		LM_ERR(" script error-invalid id_set to be selected\n");
+	}
+
+	return rtpp_list;
+}
+/*
+ * Main balancing routine. This does not try to keep the same proxy for
+ * the call if some proxies were disabled or enabled; proxy death considered
+ * too rare. Otherwise we should implement "mature" HA clustering, which is
+ * too expensive here.
+ */
+struct rtpp_node *
+select_rtpp_node(str callid, int do_test)
+{
+	unsigned sum, sumcut, weight_sum;
+	struct rtpp_node* node;
+	int was_forced;
+
+	if(!selected_rtpp_set){
+		LM_ERR("script error -no valid set selected\n");
+		return NULL;
+	}
+	/* Most popular case: 1 proxy, nothing to calculate */
+	if (selected_rtpp_set->rtpp_node_count == 1) {
+		node = selected_rtpp_set->rn_first;
+		if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks())
+			node->rn_disabled = rtpp_test(node, 1, 0);
+		return node->rn_disabled ? NULL : node;
+	}
+
+	/* XXX Use quick-and-dirty hashing algo */
+	for(sum = 0; callid.len > 0; callid.len--)
+		sum += callid.s[callid.len - 1];
+	sum &= 0xff;
+
+	was_forced = 0;
+retry:
+	weight_sum = 0;
+	for (node=selected_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
+
+		if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks()){
+			/* Try to enable if it's time to try. */
+			node->rn_disabled = rtpp_test(node, 1, 0);
+		}
+		if (!node->rn_disabled)
+			weight_sum += node->rn_weight;
+	}
+	if (weight_sum == 0) {
+		/* No proxies? Force all to be redetected, if not yet */
+		if (was_forced)
+			return NULL;
+		was_forced = 1;
+		for(node=selected_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
+			node->rn_disabled = rtpp_test(node, 1, 1);
+		}
+		goto retry;
+	}
+	sumcut = sum % weight_sum;
+	/*
+	 * sumcut here lays from 0 to weight_sum-1.
+	 * Scan proxy list and decrease until appropriate proxy is found.
+	 */
+	for (node=selected_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
+		if (node->rn_disabled)
+			continue;
+		if (sumcut < node->rn_weight)
+			goto found;
+		sumcut -= node->rn_weight;
+	}
+	/* No node list */
+	return NULL;
+found:
+	if (do_test) {
+		node->rn_disabled = rtpp_test(node, node->rn_disabled, 0);
+		if (node->rn_disabled)
+			goto retry;
+	}
+	return node;
+}
+
+static int
+unforce_rtp_proxy_f(struct sip_msg* msg, char* str1, char* str2)
+{
+	str callid, from_tag, to_tag;
+	struct rtpp_node *node;
+	struct iovec v[1 + 4 + 3] = {{NULL, 0}, {"D", 1}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}};
+						/* 1 */   /* 2 */   /* 3 */    /* 4 */   /* 5 */    /* 6 */   /* 1 */
+
+	if (get_callid(msg, &callid) == -1 || callid.len == 0) {
+		LM_ERR("can't get Call-Id field\n");
+		return -1;
+	}
+	to_tag.s = 0;
+	if (get_to_tag(msg, &to_tag) == -1) {
+		LM_ERR("can't get To tag\n");
+		return -1;
+	}
+	if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) {
+		LM_ERR("can't get From tag\n");
+		return -1;
+	}
+	STR2IOVEC(callid, v[3]);
+	STR2IOVEC(from_tag, v[5]);
+	STR2IOVEC(to_tag, v[7]);
+	
+	if(msg->id != current_msg_id){
+		selected_rtpp_set = default_rtpp_set;
+	}
+	
+	node = select_rtpp_node(callid, 1);
+	if (!node) {
+		LM_ERR("no available proxies\n");
+		return -1;
+	}
+	send_rtpp_command(node, v, (to_tag.len > 0) ? 8 : 6);
+
+	return 1;
+}
+
+/* This function assumes p points to a line of requested type. */
+
+static int
+set_rtp_proxy_set_f(struct sip_msg * msg, char * str1, char * str2)
+{
+	current_msg_id = msg->id;
+	selected_rtpp_set = (struct rtpp_set *)str1;
+	return 1;
+}
+
+static int
+rtpproxy_offer1_f(struct sip_msg *msg, char *str1, char *str2)
+{
+        char *cp;
+        char newip[IP_ADDR_MAX_STR_SIZE];
+
+        cp = ip_addr2a(&msg->rcv.dst_ip);
+        strcpy(newip, cp);
+	return rtpproxy_offer2_f(msg, str1, newip);
+}
+
+static int
+rtpproxy_offer2_f(struct sip_msg *msg, char *param1, char *param2)
+{
+
+	if (msg->first_line.type == SIP_REQUEST)
+		if (msg->first_line.u.request.method_value != METHOD_INVITE)
+			return -1;
+
+	return force_rtp_proxy(msg, param1, param2, 1);
+}
+
+static int
+rtpproxy_answer1_f(struct sip_msg *msg, char *str1, char *str2)
+{
+        char *cp;
+        char newip[IP_ADDR_MAX_STR_SIZE];
+
+        cp = ip_addr2a(&msg->rcv.dst_ip);
+        strcpy(newip, cp);
+        return rtpproxy_answer2_f(msg, str1, newip);
+}
+
+static int
+rtpproxy_answer2_f(struct sip_msg *msg, char *param1, char *param2)
+{
+
+	if (msg->first_line.type == SIP_REQUEST)
+		if (msg->first_line.u.request.method_value != METHOD_ACK)
+			return -1;
+
+	return force_rtp_proxy(msg, param1, param2, 0);
+}
+
+static int
+force_rtp_proxy2_f(struct sip_msg *msg, char *param1, char *param2)
+{
+	int offer;
+
+	if (msg->first_line.type == SIP_REQUEST &&
+	    msg->first_line.u.request.method_value == METHOD_INVITE) {
+		offer = 1;
+	} else if (msg->first_line.type == SIP_REPLY) {
+		offer = 0;
+	} else {
+		return -1;
+	}
+
+	return force_rtp_proxy(msg, param1, param2, offer);
+}
+
+struct options {
+	str s;
+	int oidx;
+};
+
+static int
+append_opts(struct options *op, char ch)
+{
+	void *p;
+
+	if (op->s.len <= op->oidx) {
+		p = pkg_realloc(op->s.s, op->oidx + 32);
+		if (p == NULL) {
+			return (-1);
+		}
+		op->s.s = p;
+		op->s.len = op->oidx + 32;
+	}
+	op->s.s[op->oidx++] = ch;
+	return (0);
+}
+
+static void
+free_opts(struct options *op1, struct options *op2, struct options *op3)
+{
+
+	if (op1->s.len > 0 && op1->s.s != NULL) {
+		pkg_free(op1->s.s);
+		op1->s.len = 0;
+	}
+	if (op2->s.len > 0 && op2->s.s != NULL) {
+		pkg_free(op2->s.s);
+		op2->s.len = 0;
+	}
+	if (op3->s.len > 0 && op3->s.s != NULL) {
+		pkg_free(op3->s.s);
+		op3->s.len = 0;
+	}
+}
+
+#define FORCE_RTP_PROXY_RET(e) \
+    do { \
+	free_opts(&opts, &rep_opts, &pt_opts); \
+	return (e); \
+    } while (0);
+
+static int
+force_rtp_proxy(struct sip_msg* msg, char* str1, char* str2, int offer)
+{
+	str body, body1, oldport, oldip, newport, newip;
+	str callid, from_tag, to_tag, tmp, payload_types;
+	str newrtcp;
+	int create, port, len, flookup, argc, proxied, real;
+	int orgip, commip;
+	int pf, pf1, force, swap;
+	struct options opts, rep_opts, pt_opts;
+	char *cp, *cp1;
+	char  *cpend, *next;
+	char **ap, *argv[10];
+	struct lump* anchor;
+	struct rtpp_node *node;
+	struct iovec v[] = {
+		{NULL, 0},	/* reserved (cookie) */
+		{NULL, 0},	/* command & common options */
+		{NULL, 0},	/* per-media/per-node options 1 */
+		{NULL, 0},	/* per-media/per-node options 2 */
+		{" ", 1},	/* separator */
+		{NULL, 0},	/* callid */
+		{" ", 1},	/* separator */
+		{NULL, 7},	/* newip */
+		{" ", 1},	/* separator */
+		{NULL, 1},	/* oldport */
+		{" ", 1},	/* separator */
+		{NULL, 0},	/* from_tag */
+		{";", 1},	/* separator */
+		{NULL, 0},	/* medianum */
+		{" ", 1},	/* separator */
+		{NULL, 0},	/* to_tag */
+		{";", 1},	/* separator */
+		{NULL, 0}	/* medianum */
+	};
+	char *c1p, *c2p, *bodylimit, *o1p;
+	char medianum_buf[20];
+	int medianum, media_multi;
+	str medianum_str;
+	int c1p_altered;
+	static int swap_warned = 0;
+
+	int sdp_session_num, sdp_stream_num;
+	sdp_session_cell_t* sdp_session;
+	sdp_stream_cell_t* sdp_stream;
+
+	memset(&opts, '\0', sizeof(opts));
+	memset(&rep_opts, '\0', sizeof(rep_opts));
+	memset(&pt_opts, '\0', sizeof(pt_opts));
+	/* Leave space for U/L prefix TBD later */
+	if (append_opts(&opts, '?') == -1) {
+		LM_ERR("out of pkg memory\n");
+		FORCE_RTP_PROXY_RET (-1);
+	}
+	flookup = force = real = orgip = commip = swap = 0;
+	for (cp = str1; cp != NULL && *cp != '\0'; cp++) {
+		switch (*cp) {
+		case 'a':
+		case 'A':
+			if (append_opts(&opts, 'A') == -1) {
+				LM_ERR("out of pkg memory\n");
+				FORCE_RTP_PROXY_RET (-1);
+			}
+			real = 1;
+			break;
+
+		case 'i':
+		case 'I':
+			if (append_opts(&opts, 'I') == -1) {
+				LM_ERR("out of pkg memory\n");
+				FORCE_RTP_PROXY_RET (-1);
+			}
+			break;
+
+		case 'e':
+		case 'E':
+			if (append_opts(&opts, 'E') == -1) {
+				LM_ERR("out of pkg memory\n");
+				FORCE_RTP_PROXY_RET (-1);
+			}
+			break;
+
+		case 'l':
+		case 'L':
+			if (offer == 0) {
+				FORCE_RTP_PROXY_RET (-1);
+			}
+			flookup = 1;
+			break;
+
+		case 'f':
+		case 'F':
+			force = 1;
+			break;
+
+		case 'r':
+		case 'R':
+			real = 1;
+			break;
+
+		case 'c':
+		case 'C':
+			commip = 1;
+			break;
+
+		case 'o':
+		case 'O':
+			orgip = 1;
+			break;
+
+		case 's':
+		case 'S':
+			swap = 1;
+			if (swap_warned != 0)
+				break;
+			LM_WARN("swap flag (`%c') is depreciated, use "
+			    "rtpproxy_offer() and rtpproxy_answer() "
+			    "instead\n", *cp);
+			swap_warned = 1;
+			break;
+
+		case 'w':
+		case 'W':
+			if (append_opts(&opts, 'S') == -1) {
+				LM_ERR("out of pkg memory\n");
+				FORCE_RTP_PROXY_RET (-1);
+			}
+			break;
+
+		case 'z':
+		case 'Z':
+			if (append_opts(&rep_opts, 'Z') == -1) {
+				LM_ERR("out of pkg memory\n");
+				FORCE_RTP_PROXY_RET (-1);
+			}
+			/* If there are any digits following Z copy them into the command */
+			for (; cp[1] != '\0' && isdigit(cp[1]); cp++) {
+				if (append_opts(&rep_opts, cp[1]) == -1) {
+					LM_ERR("out of pkg memory\n");
+					FORCE_RTP_PROXY_RET (-1);
+				}
+			}
+			break;
+
+		default:
+			LM_ERR("unknown option `%c'\n", *cp);
+			FORCE_RTP_PROXY_RET (-1);
+		}
+	}
+
+	if (offer != 0) {
+		create = swap?0:1;
+	} else {
+		create = swap?1:0;
+	}
+	/* extract_body will also parse all the headers in the message as
+	 * a side effect => don't move get_callid/get_to_tag in front of it
+	 * -- andrei */
+	if (extract_body(msg, &body) == -1) {
+		LM_ERR("can't extract body from the message\n");
+		FORCE_RTP_PROXY_RET (-1);
+	}
+	if (get_callid(msg, &callid) == -1 || callid.len == 0) {
+		LM_ERR("can't get Call-Id field\n");
+		FORCE_RTP_PROXY_RET (-1);
+	}
+	to_tag.s = 0;
+	if (get_to_tag(msg, &to_tag) == -1) {
+		LM_ERR("can't get To tag\n");
+		FORCE_RTP_PROXY_RET (-1);
+	}
+	if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) {
+		LM_ERR("can't get From tag\n");
+		FORCE_RTP_PROXY_RET (-1);
+	}
+	/*  LOGIC
+	 *  ------
+	 *  1) NO SWAP (create on request, lookup on reply):
+	 *       req -> create = 1
+	 *       rpl -> create = 0
+	 *       if (forced_lookup) -> create = 0;
+	 *
+	 *  2) SWAP (create on reply, lookup on request):
+	 *       req -> create = 0
+	 *       rpl -> create = 1
+	 *       swap_tags
+	 *       if (forced_lookup) -> create = 0;
+	 */
+	if (flookup != 0) {
+		if (to_tag.len == 0) {
+			FORCE_RTP_PROXY_RET (-1);
+		}
+		create = 0;
+		if (swap != 0 || (msg->first_line.type == SIP_REPLY && offer != 0)
+				|| (msg->first_line.type == SIP_REQUEST && offer == 0)) {
+			tmp = from_tag;
+			from_tag = to_tag;
+			to_tag = tmp;
+		}
+	} else if (swap != 0 || (msg->first_line.type == SIP_REPLY && offer != 0)
+			|| (msg->first_line.type == SIP_REQUEST && offer == 0)) {
+		if (to_tag.len == 0) {
+			FORCE_RTP_PROXY_RET (-1);
+		}
+		tmp = from_tag;
+		from_tag = to_tag;
+		to_tag = tmp;
+	}
+	proxied = 0;
+	if (nortpproxy_str.len) {
+		for ( cp=body.s ; (len=body.s+body.len-cp) >= nortpproxy_str.len ; ) {
+			cp1 = ser_memmem(cp, nortpproxy_str.s, len, nortpproxy_str.len);
+			if (cp1 == NULL)
+				break;
+			if (cp1[-1] == '\n' || cp1[-1] == '\r') {
+				proxied = 1;
+				break;
+			}
+			cp = cp1 + nortpproxy_str.len;
+		}
+	}
+	if (proxied != 0 && force == 0) {
+		FORCE_RTP_PROXY_RET (-1);
+	}
+	/*
+	 * Parsing of SDP body.
+	 * It can contain a few session descriptions (each starts with
+	 * v-line), and each session may contain a few media descriptions
+	 * (each starts with m-line).
+	 * We have to change ports in m-lines, and also change IP addresses in
+	 * c-lines which can be placed either in session header (fallback for
+	 * all medias) or media description.
+	 * Ports should be allocated for any media. IPs all should be changed
+	 * to the same value (RTP proxy IP), so we can change all c-lines
+	 * unconditionally.
+	 */
+	if(0 != parse_sdp(msg)) {
+		LM_ERR("Unable to parse sdp\n");
+		FORCE_RTP_PROXY_RET (-1);
+	}
+	print_sdp((sdp_info_t*)msg->body, L_INFO);
+
+	bodylimit = body.s + body.len;
+
+	if(msg->id != current_msg_id){
+		selected_rtpp_set = default_rtpp_set;
+	}
+
+	opts.s.s[0] = (create == 0) ? 'L' : 'U';
+	v[1].iov_base = opts.s.s;
+	v[1].iov_len = opts.oidx;
+	STR2IOVEC(callid, v[5]);
+	STR2IOVEC(from_tag, v[11]);
+	STR2IOVEC(to_tag, v[15]);
+
+	/* check if this is a single or a multi stream SDP offer/answer */
+	sdp_stream_num = get_sdp_stream_num(msg);
+	switch (sdp_stream_num) {
+	case 0:
+		LM_ERR("sdp w/o streams\n");
+		FORCE_RTP_PROXY_RET (-1);
+		break;
+	case 1:
+		media_multi = 0;
+		break;
+	default:
+		media_multi = 1;
+	}
+#ifdef EXTRA_DEBUG
+	LM_DBG("my new media_multi=%d\n", media_multi);
+#endif
+	medianum = 0; 
+	sdp_session_num = 0;
+	for(;;) {
+		sdp_session = get_sdp_session(msg, sdp_session_num);
+		if(!sdp_session) break;
+		sdp_stream_num = 0;
+		c1p_altered = 0;
+		o1p = sdp_session->o_ip_addr.s;
+		for(;;) {
+			sdp_stream = get_sdp_stream(msg, sdp_session_num, sdp_stream_num);
+			if(!sdp_stream) break;
+
+			if (sdp_stream->ip_addr.s && !sdp_stream->ip_addr.len) {
+				oldip = sdp_stream->ip_addr;
+				pf = sdp_stream->pf;
+			} else {
+				oldip = sdp_session->ip_addr;
+				pf = sdp_session->pf;
+			}
+			oldport = sdp_stream->port;
+			payload_types = sdp_stream->payloads;
+			medianum++;
+
+			if (real != 0) {
+				newip = oldip;
+			} else {
+				newip.s = ip_addr2a(&msg->rcv.src_ip);
+				newip.len = strlen(newip.s);
+			}
+			/* XXX must compare address families in all addresses */
+			if (pf == AF_INET6) {
+				if (append_opts(&opts, '6') == -1) {
+					LM_ERR("out of pkg memory\n");
+					FORCE_RTP_PROXY_RET (-1);
+				}
+			}
+			STR2IOVEC(newip, v[7]);
+			STR2IOVEC(oldport, v[9]);
+#ifdef EXTRA_DEBUG
+			LM_DBG("STR2IOVEC(newip[%.*s], v[7])", newip.len, newip.s);
+			LM_DBG("STR2IOVEC(oldport[%.*s], v[9])", oldport.len, oldport.s);
+#endif
+			if (1 || media_multi) /* XXX netch: can't choose now*/
+			{
+				snprintf(medianum_buf, sizeof medianum_buf, "%d", medianum);
+				medianum_str.s = medianum_buf;
+				medianum_str.len = strlen(medianum_buf);
+				STR2IOVEC(medianum_str, v[13]);
+				STR2IOVEC(medianum_str, v[17]);
+#ifdef EXTRA_DEBUG
+				LM_DBG("STR2IOVEC(medianum_str, v[13])\n");
+				LM_DBG("STR2IOVEC(medianum_str, v[17])\n");
+#endif
+			} else {
+				v[12].iov_len = v[13].iov_len = 0;
+				v[16].iov_len = v[17].iov_len = 0;
+			}
+			do {
+				node = select_rtpp_node(callid, 1);
+				if (!node) {
+					LM_ERR("no available proxies\n");
+					FORCE_RTP_PROXY_RET (-1);
+				}
+				if (rep_opts.oidx > 0) {
+					if (node->rn_rep_supported == 0) {
+						LM_WARN("re-packetization is requested but is not "
+						    "supported by the selected RTP proxy node\n");
+						v[2].iov_len = 0;
+					} else {
+						v[2].iov_base = rep_opts.s.s;
+						v[2].iov_len += rep_opts.oidx;
+					}
+				}
+#ifdef EXTRA_DEBUG
+				LM_DBG("payload_types='%.*s'\n", payload_types.len, payload_types.s);
+#endif
+				if (sdp_stream->is_rtp && payload_types.len > 0 && node->rn_ptl_supported != 0) {
+					pt_opts.oidx = 0;
+					if (append_opts(&pt_opts, 'c') == -1) {
+						LM_ERR("out of pkg memory\n");
+						FORCE_RTP_PROXY_RET (-1);
+					}
+					/*
+					 * Convert space-separated payload types list into
+					 * a comma-separated list.
+					 */
+					for (cp = payload_types.s;
+					    cp < payload_types.s + payload_types.len; cp++) {
+						if (isdigit(*cp)) {
+							if (append_opts(&pt_opts, *cp) == -1) {
+								LM_ERR("out of pkg memory\n");
+								FORCE_RTP_PROXY_RET (-1);
+							}
+							continue;
+						}
+						do {
+							cp++;
+						} while (!isdigit(*cp) &&
+						    cp < payload_types.s + payload_types.len);
+						/* Check EOL */
+						if (cp >= payload_types.s + payload_types.len)
+							break;
+						if (append_opts(&pt_opts, ',') == -1) {
+							LM_ERR("out of pkg memory\n");
+							FORCE_RTP_PROXY_RET (-1);
+						}
+						cp--;
+					}
+					v[3].iov_base = pt_opts.s.s;
+					v[3].iov_len = pt_opts.oidx;
+				} else {
+					v[3].iov_len = 0;
+				}
+				cp = send_rtpp_command(node, v, (to_tag.len > 0) ? 18 : 14);
+			} while (cp == NULL);
+			LM_DBG("proxy reply: %s\n", cp);
+			/* Parse proxy reply to <argc,argv> */
+			argc = 0;
+			memset(argv, 0, sizeof(argv));
+			cpend=cp+strlen(cp);
+			next=eat_token_end(cp, cpend);
+			for (ap=argv; cp<cpend; cp=next+1, next=eat_token_end(cp, cpend)){
+				*next=0;
+				if (*cp != '\0') {
+					*ap=cp;
+					argc++;
+					if ((char*)++ap >= ((char*)argv+sizeof(argv)))
+						break;
+				}
+			}
+			if (argc < 1) {
+				LM_ERR("no reply from rtp proxy\n");
+				FORCE_RTP_PROXY_RET (-1);
+			}
+			port = atoi(argv[0]);
+			if (port <= 0 || port > 65535) {
+				if (port != 0 || flookup == 0)
+					LM_ERR("incorrect port %i in reply "
+						"from rtp proxy\n",port);
+				FORCE_RTP_PROXY_RET (-1);
+			}
+
+			pf1 = (argc >= 3 && argv[2][0] == '6') ? AF_INET6 : AF_INET;
+
+			if (isnulladdr(&oldip, pf)) {
+				if (pf1 == AF_INET6) {
+					newip.s = "::";
+					newip.len = 2;
+				} else {
+					newip.s = "0.0.0.0";
+					newip.len = 7;
+				}
+			} else {
+				newip.s = (argc < 2) ? str2 : argv[1];
+				newip.len = strlen(newip.s);
+			}
+			/* marker to double check : newport goes: str -> int -> str ?!?! */
+			newport.s = int2str(port, &newport.len); /* beware static buffer */
+			/* Alter port. */
+			body1.s = sdp_stream->media.s;
+			body1.len = bodylimit - body1.s;
+#ifdef EXTRA_DEBUG
+			LM_DBG("alter port body1='%.*s'\n", body1.len, body1.s);
+#endif
+			/* do not do it if old port was 0 (means media disable)
+			 * - check if actually should be better done in rtpptoxy,
+			 *   by returning also 0
+			 * - or by not sending to rtpproxy the old port if 0
+			 */
+			if(oldport.len!=1 || oldport.s[0]!='0')
+			{
+				if (alter_mediaport(msg, &body1, &oldport, &newport, 0) == -1) {
+					FORCE_RTP_PROXY_RET (-1);
+				}
+			}
+			
+			/*
+			 * Alter RTCP attribute if present. Inserting RTP port + 1 (as allocated
+			 * by RTP proxy). No IP-address is needed in the new RTCP attribute as the
+			 * 'c' attribute (altered below) will contain the RTP proxy IP address.
+			 * See RFC 3605 for definition of RTCP attribute.
+			 * ported from ser
+			 */
+			if (sdp_stream->rtcp_port.s && sdp_stream->rtcp_port.len) {
+				newrtcp.s = int2str(port+1, &newrtcp.len); /* beware static buffer */
+				/* Alter port. */
+				body1.s = sdp_stream->rtcp_port.s;
+				body1.len = bodylimit - body1.s;
+#ifdef EXTRA_DEBUG
+				LM_DBG("alter rtcp body1='%.*s'\n", body1.len, body1.s);
+#endif
+				if (alter_rtcp(msg, &body1, &sdp_stream->rtcp_port, &newrtcp) == -1) {
+					FORCE_RTP_PROXY_RET (-1);
+				}
+			}
+
+			c1p = sdp_session->ip_addr.s;
+			c2p = sdp_stream->ip_addr.s;
+			/*
+			 * Alter IP. Don't alter IP common for the session
+			 * more than once.
+			 */
+			if (c2p != NULL || !c1p_altered) {
+				body1.s = c2p ? c2p : c1p;
+				body1.len = bodylimit - body1.s;
+#ifdef EXTRA_DEBUG
+				LM_DBG("alter ip body1='%.*s'\n", body1.len, body1.s);
+#endif
+				if (alter_mediaip(msg, &body1, &oldip, pf, &newip, pf1, 0)==-1) {
+					FORCE_RTP_PROXY_RET (-1);
+				}
+				if (!c2p)
+					c1p_altered = 1;
+			}
+			/*
+			 * Alter common IP if required, but don't do it more than once.
+			 */
+			if (commip && c1p && !c1p_altered) {
+				body1.s = c1p;
+				body1.len = bodylimit - body1.s;
+#ifdef EXTRA_DEBUG
+				LM_DBG("alter common ip body1='%.*s'\n", body1.len, body1.s);
+#endif
+				if (alter_mediaip(msg, &body1, &sdp_session->ip_addr, sdp_session->pf, &newip, pf1, 0)==-1) {
+					FORCE_RTP_PROXY_RET (-1);
+				}
+				c1p_altered = 1;
+			}
+			/*
+			 * Alter the IP in "o=", but only once per session
+			 */
+			if (o1p) {
+				body1.s = o1p;
+				body1.len = bodylimit - body1.s;
+#ifdef EXTRA_DEBUG
+				LM_DBG("alter media ip body1='%.*s'\n", body1.len, body1.s);
+#endif
+				if (alter_mediaip(msg, &body1, &sdp_session->o_ip_addr, sdp_session->o_pf, &newip, pf1, 0)==-1) {
+					FORCE_RTP_PROXY_RET (-1);
+				}
+				o1p = 0;
+			}
+			sdp_stream_num++;
+		}
+		sdp_session_num++;
+	}
+
+
+
+	free_opts(&opts, &rep_opts, &pt_opts);
+
+	if (proxied == 0 && nortpproxy_str.len) {
+		cp = pkg_malloc((nortpproxy_str.len + CRLF_LEN) * sizeof(char));
+		if (cp == NULL) {
+			LM_ERR("out of pkg memory\n");
+			return -1;
+		}
+		anchor = anchor_lump(msg, body.s + body.len - msg->buf, 0, 0);
+		if (anchor == NULL) {
+			LM_ERR("anchor_lump failed\n");
+			pkg_free(cp);
+			return -1;
+		}
+		memcpy(cp, CRLF, CRLF_LEN);
+		memcpy(cp + CRLF_LEN, nortpproxy_str.s, nortpproxy_str.len);
+		if (insert_new_lump_after(anchor, cp, nortpproxy_str.len + CRLF_LEN, 0) == NULL) {
+			LM_ERR("insert_new_lump_after failed\n");
+			pkg_free(cp);
+			return -1;
+		}
+	}
+
+	return 1;
+}
+
+static int
+force_rtp_proxy1_f(struct sip_msg* msg, char* str1, char* str2)
+{
+	char *cp;
+	char newip[IP_ADDR_MAX_STR_SIZE];
+
+	cp = ip_addr2a(&msg->rcv.dst_ip);
+	strcpy(newip, cp);
+
+	return force_rtp_proxy2_f(msg, str1, newip);
+}
+
+static int
+force_rtp_proxy0_f(struct sip_msg* msg, char* str1, char* str2)
+{
+	char arg[1] = {'\0'};
+
+	return force_rtp_proxy1_f(msg, arg, NULL);
+}
+
+
+static u_short raw_checksum(unsigned char *buffer, int len)
+{
+	u_long sum = 0;
+
+	while (len > 1) {
+		sum += *buffer << 8;
+		buffer++;
+		sum += *buffer;
+		buffer++;
+		len -= 2;
+	}
+	if (len) {
+		sum += *buffer << 8;
+	}
+	sum = (sum >> 16) + (sum & 0xffff);
+	sum = (sum >> 16) + (sum);
+
+	return (u_short) ~sum;
+}
+
+
+static int send_raw(const char *buf, int buf_len, union sockaddr_union *to,
+							const unsigned int s_ip, const unsigned int s_port)
+{
+	struct ip *ip;
+	struct udphdr *udp;
+	unsigned char packet[50];
+	int len = sizeof(struct ip) + sizeof(struct udphdr) + buf_len;
+
+	if (len > sizeof(packet)) {
+		LM_ERR("payload too big\n");
+		return -1;
+	}
+
+	ip = (struct ip*) packet;
+	udp = (struct udphdr *) (packet + sizeof(struct ip));
+	memcpy(packet + sizeof(struct ip) + sizeof(struct udphdr), buf, buf_len);
+
+	ip->ip_v = 4;
+	ip->ip_hl = sizeof(struct ip) / 4; // no options
+	ip->ip_tos = 0;
+	ip->ip_len = htons(len);
+	ip->ip_id = 23;
+	ip->ip_off = 0;
+	ip->ip_ttl = 69;
+	ip->ip_p = 17;
+	ip->ip_src.s_addr = s_ip;
+	ip->ip_dst.s_addr = to->sin.sin_addr.s_addr;
+
+	ip->ip_sum = raw_checksum((unsigned char *) ip, sizeof(struct ip));
+
+	udp->uh_sport = htons(s_port);
+	udp->uh_dport = to->sin.sin_port;
+	udp->uh_ulen = htons((unsigned short) sizeof(struct udphdr) + buf_len);
+	udp->uh_sum = 0;
+
+	return sendto(raw_sock, packet, len, 0, (struct sockaddr *) to, sizeof(struct sockaddr_in));
+}
+
+
+static void
+nh_timer(unsigned int ticks, void *timer_idx)
+{
+	static unsigned int iteration = 0;
+	int rval;
+	void *buf, *cp;
+	str c;
+	str opt;
+	str path;
+	struct sip_uri curi;
+	struct hostent* he;
+	struct socket_info* send_sock;
+	unsigned int flags;
+	char proto;
+	struct dest_info dst;
+
+	if((*natping_state) == 0)
+		goto done;
+
+	buf = NULL;
+	if (cblen > 0) {
+		buf = pkg_malloc(cblen);
+		if (buf == NULL) {
+			LM_ERR("out of pkg memory\n");
+			goto done;
+		}
+	}
+	rval = ul.get_all_ucontacts(buf, cblen, (ping_nated_only?ul.nat_flag:0),
+		((unsigned int)(unsigned long)timer_idx)*natping_interval+iteration,
+		natping_processes*natping_interval);
+	if (rval<0) {
+		LM_ERR("failed to fetch contacts\n");
+		goto done;
+	}
+	if (rval > 0) {
+		if (buf != NULL)
+			pkg_free(buf);
+		cblen = rval * 2;
+		buf = pkg_malloc(cblen);
+		if (buf == NULL) {
+			LM_ERR("out of pkg memory\n");
+			goto done;
+		}
+		rval = ul.get_all_ucontacts(buf,cblen,(ping_nated_only?ul.nat_flag:0),
+		   ((unsigned int)(unsigned long)timer_idx)*natping_interval+iteration,
+		   natping_processes*natping_interval);
+		if (rval != 0) {
+			pkg_free(buf);
+			goto done;
+		}
+	}
+
+	if (buf == NULL)
+		goto done;
+
+	cp = buf;
+	while (1) {
+		memcpy(&(c.len), cp, sizeof(c.len));
+		if (c.len == 0)
+			break;
+		c.s = (char*)cp + sizeof(c.len);
+		cp =  (char*)cp + sizeof(c.len) + c.len;
+		memcpy( &send_sock, cp, sizeof(send_sock));
+		cp = (char*)cp + sizeof(send_sock);
+		memcpy( &flags, cp, sizeof(flags));
+		cp = (char*)cp + sizeof(flags);
+		memcpy( &(path.len), cp, sizeof(path.len));
+		path.s = path.len ? ((char*)cp + sizeof(path.len)) : NULL ;
+		cp =  (char*)cp + sizeof(path.len) + path.len;
+
+		/* determin the destination */
+		if ( path.len && (flags&sipping_flag)!=0 ) {
+			/* send to first URI in path */
+			if (get_path_dst_uri( &path, &opt) < 0) {
+				LM_ERR("failed to get dst_uri for Path\n");
+				continue;
+			}
+			/* send to the contact/received */
+			if (parse_uri(opt.s, opt.len, &curi) < 0) {
+				LM_ERR("can't parse contact dst_uri\n");
+				continue;
+			}
+		} else {
+			/* send to the contact/received */
+			if (parse_uri(c.s, c.len, &curi) < 0) {
+				LM_ERR("can't parse contact uri\n");
+				continue;
+			}
+		}
+		if (curi.proto != PROTO_UDP && curi.proto != PROTO_NONE)
+			continue;
+		if (curi.port_no == 0)
+			curi.port_no = SIP_PORT;
+		proto = curi.proto;
+		/* we sholud get rid of this resolve (to ofen and to slow); for the
+		 * moment we are lucky since the curi is an IP -bogdan */
+		he = sip_resolvehost(&curi.host, &curi.port_no, &proto);
+		if (he == NULL){
+			LM_ERR("can't resolve_host\n");
+			continue;
+		}
+		init_dest_info(&dst);
+		hostent2su(&dst.to, he, 0, curi.port_no);
+		if (send_sock==0) {
+			send_sock=force_socket ? force_socket : 
+					get_send_socket(0, &dst.to, PROTO_UDP);
+		}
+		if (send_sock == NULL) {
+			LM_ERR("can't get sending socket\n");
+			continue;
+		}
+		dst.proto=PROTO_UDP;
+		dst.send_sock=send_sock;
+
+		if ( (flags&sipping_flag)!=0 &&
+		(opt.s=build_sipping( &c, send_sock, &path, &opt.len))!=0 ) {
+			if (udp_send(&dst, opt.s, opt.len)<0){
+				LM_ERR("sip udp_send failed\n");
+			}
+		} else if (raw_ip) {
+			if (send_raw((char*)sbuf, sizeof(sbuf), &dst.to, raw_ip, 
+						 raw_port)<0) {
+				LM_ERR("send_raw failed\n");
+			}
+		} else {
+			if (udp_send(&dst, (char *)sbuf, sizeof(sbuf))<0 ) {
+				LM_ERR("udp_send failed\n");
+			}
+		}
+	}
+	pkg_free(buf);
+done:
+	iteration++;
+	if (iteration==natping_interval)
+		iteration = 0;
+}
+
+
+/*
+ * Create received SIP uri that will be either
+ * passed to registrar in an AVP or apended
+ * to Contact header field as a parameter
+ */
+static int
+create_rcv_uri(str* uri, struct sip_msg* m)
+{
+	static char buf[MAX_URI_SIZE];
+	char* p;
+	str ip, port;
+	int len;
+	str proto;
+
+	if (!uri || !m) {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+
+	ip.s = ip_addr2a(&m->rcv.src_ip);
+	ip.len = strlen(ip.s);
+
+	port.s = int2str(m->rcv.src_port, &port.len);
+
+	switch(m->rcv.proto) {
+	case PROTO_NONE:
+	case PROTO_UDP:
+		proto.s = 0; /* Do not add transport parameter, UDP is default */
+		proto.len = 0;
+		break;
+
+	case PROTO_TCP:
+		proto.s = "TCP";
+		proto.len = 3;
+		break;
+
+	case PROTO_TLS:
+		proto.s = "TLS";
+		proto.len = 3;
+		break;
+
+	case PROTO_SCTP:
+		proto.s = "SCTP";
+		proto.len = 4;
+		break;
+
+	default:
+		LM_ERR("unknown transport protocol\n");
+		return -1;
+	}
+
+	len = 4 + ip.len + 2*(m->rcv.src_ip.af==AF_INET6)+ 1 + port.len;
+	if (proto.s) {
+		len += TRANSPORT_PARAM_LEN;
+		len += proto.len;
+	}
+
+	if (len > MAX_URI_SIZE) {
+		LM_ERR("buffer too small\n");
+		return -1;
+	}
+
+	p = buf;
+	memcpy(p, "sip:", 4);
+	p += 4;
+	
+	if (m->rcv.src_ip.af==AF_INET6)
+		*p++ = '[';
+	memcpy(p, ip.s, ip.len);
+	p += ip.len;
+	if (m->rcv.src_ip.af==AF_INET6)
+		*p++ = ']';
+
+	*p++ = ':';
+	
+	memcpy(p, port.s, port.len);
+	p += port.len;
+
+	if (proto.s) {
+		memcpy(p, TRANSPORT_PARAM, TRANSPORT_PARAM_LEN);
+		p += TRANSPORT_PARAM_LEN;
+
+		memcpy(p, proto.s, proto.len);
+		p += proto.len;
+	}
+
+	uri->s = buf;
+	uri->len = len;
+
+	return 0;
+}
+
+
+/*
+ * Add received parameter to Contacts for further
+ * forwarding of the REGISTER requuest
+ */
+static int
+add_rcv_param_f(struct sip_msg* msg, char* str1, char* str2)
+{
+	contact_t* c;
+	struct lump* anchor;
+	char* param;
+	str uri;
+	int hdr_param;
+
+	hdr_param = str1?0:1;
+
+	if (create_rcv_uri(&uri, msg) < 0) {
+		return -1;
+	}
+
+	if (contact_iterator(&c, msg, 0) < 0) {
+		return -1;
+	}
+
+	while(c) {
+		param = (char*)pkg_malloc(RECEIVED_LEN + 2 + uri.len);
+		if (!param) {
+			LM_ERR("no pkg memory left\n");
+			return -1;
+		}
+		memcpy(param, RECEIVED, RECEIVED_LEN);
+		param[RECEIVED_LEN] = '\"';
+		memcpy(param + RECEIVED_LEN + 1, uri.s, uri.len);
+		param[RECEIVED_LEN + 1 + uri.len] = '\"';
+
+		if (hdr_param) {
+			/* add the param as header param */
+			anchor = anchor_lump(msg, c->name.s + c->len - msg->buf, 0, 0);
+		} else {
+			/* add the param as uri param */
+			anchor = anchor_lump(msg, c->uri.s + c->uri.len - msg->buf, 0, 0);
+		}
+		if (anchor == NULL) {
+			LM_ERR("anchor_lump failed\n");
+			return -1;
+		}		
+
+		if (insert_new_lump_after(anchor, param, RECEIVED_LEN + 1 + uri.len + 1, 0) == 0) {
+			LM_ERR("insert_new_lump_after failed\n");
+			pkg_free(param);
+			return -1;
+		}
+
+		if (contact_iterator(&c, msg, c) < 0) {
+			return -1;
+		}
+	}
+
+	return 1;
+}
+
+
+static int start_recording_f(struct sip_msg* msg, char *foo, char *bar)
+{
+	int nitems;
+	str callid = {0, 0};
+	str from_tag = {0, 0};
+	str to_tag = {0, 0};
+	struct rtpp_node *node;
+	struct iovec v[1 + 4 + 3] = {{NULL, 0}, {"R", 1}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}, {" ", 1}, {NULL, 0}};
+	                             /* 1 */   /* 2 */   /* 3 */    /* 4 */   /* 5 */    /* 6 */   /* 1 */
+
+	if (get_callid(msg, &callid) == -1 || callid.len == 0) {
+		LM_ERR("can't get Call-Id field\n");
+		return -1;
+	}
+
+	if (get_to_tag(msg, &to_tag) == -1) {
+		LM_ERR("can't get To tag\n");
+		return -1;
+	}
+
+	if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) {
+		LM_ERR("can't get From tag\n");
+		return -1;
+	}
+
+	if(msg->id != current_msg_id){
+		selected_rtpp_set = default_rtpp_set;
+	}
+
+	STR2IOVEC(callid, v[3]);
+	STR2IOVEC(from_tag, v[5]);
+	STR2IOVEC(to_tag, v[7]);
+	node = select_rtpp_node(callid, 1);
+	if (!node) {
+		LM_ERR("no available proxies\n");
+		return -1;
+	}
+
+	nitems = 8;
+	if (msg->first_line.type == SIP_REPLY) {
+		if (to_tag.len == 0)
+			return -1;
+		STR2IOVEC(to_tag, v[5]);
+		STR2IOVEC(from_tag, v[7]);
+	} else {
+		STR2IOVEC(from_tag, v[5]);
+		STR2IOVEC(to_tag, v[7]);
+		if (to_tag.len <= 0)
+			nitems = 6;
+	}
+	send_rtpp_command(node, v, nitems);
+
+	return 1;
+}
+
+
+
+/*
+ * Create an AVP to be used by registrar with the source IP and port
+ * of the REGISTER
+ */
+static int
+fix_nated_register_f(struct sip_msg* msg, char* str1, char* str2)
+{
+	str uri;
+	int_str val;
+
+	if(rcv_avp_name.n==0)
+		return 1;
+
+	if (create_rcv_uri(&uri, msg) < 0) {
+		return -1;
+	}
+
+	val.s = uri;
+
+	if (add_avp(AVP_VAL_STR|rcv_avp_type, rcv_avp_name, val) < 0) {
+		LM_ERR("failed to create AVP\n");
+		return -1;
+	}
+
+	return 1;
+}

+ 71 - 0
modules_k/rtpproxy/nathelper.h

@@ -0,0 +1,71 @@
+/* $Id: nathelper.c 1808 2007-03-10 17:36:19Z bogdan_iancu $
+ *
+ * Copyright (C) 2003 Porta Software Ltd
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * ---------
+ * 2007-04-13   splitted from nathelper.c (ancuta)
+*/
+
+
+#ifndef _NATHELPER_NATHELPER_H
+#define _NATHELPER_NATHELPER_H
+
+#include "../../str.h"
+
+/* Handy macros */
+#define STR2IOVEC(sx, ix)       do {(ix).iov_base = (sx).s; (ix).iov_len = (sx).len;} while(0)
+#define SZ2IOVEC(sx, ix)        do {(ix).iov_base = (sx); (ix).iov_len = strlen(sx);} while(0)
+
+struct rtpp_node {
+	unsigned int		idx;			/* overall index */
+	str					rn_url;			/* unparsed, deletable */
+	int					rn_umode;
+	char				*rn_address;	/* substring of rn_url */
+	int					rn_disabled;	/* found unaccessible? */
+	unsigned			rn_weight;		/* for load balancing */
+	unsigned int		rn_recheck_ticks;
+        int                     rn_rep_supported;
+        int                     rn_ptl_supported;
+	struct rtpp_node	*rn_next;
+};
+
+
+struct rtpp_set{
+	unsigned int 		id_set;
+	unsigned			weight_sum;
+	unsigned int		rtpp_node_count;
+	int 				set_disabled;
+	unsigned int		set_recheck_ticks;
+	struct rtpp_node	*rn_first;
+	struct rtpp_node	*rn_last;
+	struct rtpp_set     *rset_next;
+};
+
+
+struct rtpp_set_head{
+	struct rtpp_set		*rset_first;
+	struct rtpp_set		*rset_last;
+};
+
+/* Functions from nathelper */
+struct rtpp_node *select_rtpp_node(str, int);
+char *send_rtpp_command(struct rtpp_node *, struct iovec *, int);
+
+#endif

+ 420 - 0
modules_k/rtpproxy/nhelpr_funcs.c

@@ -0,0 +1,420 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ *  2003-11-06  body len is computed using the message len (it's
+ *               not taken any more from the msg. content-length) (andrei)
+ *  2008-08-30  body len is taken from Conent-length header as it is more 
+ *               reliable (UDP packages may contain garbage at the end)(bogdan)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "nhelpr_funcs.h"
+#include "../../dprint.h"
+#include "../../config.h"
+#include "../../ut.h"
+#include "../../forward.h"
+#include "../../resolve.h"
+#include "../../globals.h"
+#include "../../udp_server.h"
+#include "../../pt.h"
+#include "../../parser/msg_parser.h"
+#include "../../trim.h"
+#include "../../parser/parse_from.h"
+#include "../../parser/contact/parse_contact.h"
+#include "../../parser/parse_uri.h"
+#include "../../parser/parse_content.h"
+#include "../../parser/parser_f.h"
+#include "../../parser/sdp/sdp_helpr_funcs.h"
+
+#define READ(val) \
+	(*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24))
+#define advance(_ptr,_n,_str,_error) \
+	do{\
+		if ((_ptr)+(_n)>(_str).s+(_str).len)\
+			goto _error;\
+		(_ptr) = (_ptr) + (_n);\
+	}while(0);
+#define one_of_16( _x , _t ) \
+	(_x==_t[0]||_x==_t[15]||_x==_t[8]||_x==_t[2]||_x==_t[3]||_x==_t[4]\
+	||_x==_t[5]||_x==_t[6]||_x==_t[7]||_x==_t[1]||_x==_t[9]||_x==_t[10]\
+	||_x==_t[11]||_x==_t[12]||_x==_t[13]||_x==_t[14])
+#define one_of_8( _x , _t ) \
+	(_x==_t[0]||_x==_t[7]||_x==_t[1]||_x==_t[2]||_x==_t[3]||_x==_t[4]\
+	||_x==_t[5]||_x==_t[6])
+
+
+
+/**
+ * return:
+ * -1: error
+ *  1: text or sdp
+ *  2: multipart
+ */
+int check_content_type(struct sip_msg *msg)
+{
+	static unsigned int appl[16] = {
+		0x6c707061/*appl*/,0x6c707041/*Appl*/,0x6c705061/*aPpl*/,
+		0x6c705041/*APpl*/,0x6c507061/*apPl*/,0x6c507041/*ApPl*/,
+		0x6c505061/*aPPl*/,0x6c505041/*APPl*/,0x4c707061/*appL*/,
+		0x4c707041/*AppL*/,0x4c705061/*aPpL*/,0x4c705041/*APpL*/,
+		0x4c507061/*apPL*/,0x4c507041/*ApPL*/,0x4c505061/*aPPL*/,
+		0x4c505041/*APPL*/};
+	static unsigned int icat[16] = {
+		0x74616369/*icat*/,0x74616349/*Icat*/,0x74614369/*iCat*/,
+		0x74614349/*ICat*/,0x74416369/*icAt*/,0x74416349/*IcAt*/,
+		0x74414369/*iCAt*/,0x74414349/*ICAt*/,0x54616369/*icaT*/,
+		0x54616349/*IcaT*/,0x54614369/*iCaT*/,0x54614349/*ICaT*/,
+		0x54416369/*icAT*/,0x54416349/*IcAT*/,0x54414369/*iCAT*/,
+		0x54414349/*ICAT*/};
+	static unsigned int ion_[8] = {
+		0x006e6f69/*ion_*/,0x006e6f49/*Ion_*/,0x006e4f69/*iOn_*/,
+		0x006e4f49/*IOn_*/,0x004e6f69/*ioN_*/,0x004e6f49/*IoN_*/,
+		0x004e4f69/*iON_*/,0x004e4f49/*ION_*/};
+	static unsigned int sdp_[8] = {
+		0x00706473/*sdp_*/,0x00706453/*Sdp_*/,0x00704473/*sDp_*/,
+		0x00704453/*SDp_*/,0x00506473/*sdP_*/,0x00506453/*SdP_*/,
+		0x00504473/*sDP_*/,0x00504453/*SDP_*/};
+	str           str_type;
+	unsigned int  x;
+	char          *p;
+
+	if (!msg->content_type)
+	{
+		LM_WARN("the header Content-TYPE is absent!"
+			"let's assume the content is text/plain ;-)\n");
+		return 1;
+	}
+
+	trim_len(str_type.len,str_type.s,msg->content_type->body);
+	if (str_type.len>=15 && (*str_type.s=='m' || *str_type.s=='M')
+			&& strncasecmp(str_type.s, "multipart/mixed", 15) == 0) {
+		return 2;
+    }
+	p = str_type.s;
+	advance(p,4,str_type,error_1);
+	x = READ(p-4);
+	if (!one_of_16(x,appl))
+		goto other;
+	advance(p,4,str_type,error_1);
+	x = READ(p-4);
+	if (!one_of_16(x,icat))
+		goto other;
+	advance(p,3,str_type,error_1);
+	x = READ(p-3) & 0x00ffffff;
+	if (!one_of_8(x,ion_))
+		goto other;
+
+	/* skip spaces and tabs if any */
+	while (*p==' ' || *p=='\t')
+		advance(p,1,str_type,error_1);
+	if (*p!='/')
+	{
+		LM_ERR("no / found after primary type\n");
+		goto error;
+	}
+	advance(p,1,str_type,error_1);
+	while ((*p==' ' || *p=='\t') && p+1<str_type.s+str_type.len)
+		advance(p,1,str_type,error_1);
+
+	advance(p,3,str_type,error_1);
+	x = READ(p-3) & 0x00ffffff;
+	if (!one_of_8(x,sdp_))
+		goto other;
+
+	if (*p==';'||*p==' '||*p=='\t'||*p=='\n'||*p=='\r'||*p==0) {
+		LM_DBG("type <%.*s> found valid\n", (int)(p-str_type.s), str_type.s);
+		return 1;
+	} else {
+		LM_ERR("bad end for type!\n");
+		return -1;
+	}
+
+error_1:
+	LM_ERR("body ended :-(!\n");
+error:
+	return -1;
+other:
+	LM_ERR("invalid type for a message\n");
+	return -1;
+}
+
+
+/*
+ * Get message body and check Content-Type header field
+ */
+int extract_body(struct sip_msg *msg, str *body )
+{
+	char c;
+	int skip;
+	int ret;
+	str mpdel;
+	char *rest, *p1, *p2;
+	struct hdr_field hf;
+	unsigned int mime;
+	
+	body->s = get_body(msg);
+	if (body->s==0) {
+		LM_ERR("failed to get the message body\n");
+		goto error;
+	}
+	
+	/*
+	 * Better use the content-len value - no need of any explicit
+	 * parcing as get_body() parsed all headers and Conten-Length
+	 * body header is automaticaly parsed when found.
+	 */
+	if (msg->content_length==0) {
+		LM_ERR("failed to get the content length in message\n");
+		goto error;
+	}
+
+	body->len = get_content_length(msg);
+	if (body->len==0) {
+		LM_ERR("message body has length zero\n");
+		goto error;
+	}
+
+	if (body->len + body->s > msg->buf + msg->len) {
+		LM_ERR("content-length exceeds packet-length by %d\n",
+				(body->len + body->s) - (msg->buf + msg->len));
+		goto error;
+	}
+
+	/* no need for parse_headers(msg, EOH), get_body will 
+	 * parse everything */
+	/*is the content type correct?*/
+	if((ret = check_content_type(msg))==-1)
+	{
+		LM_ERR("content type mismatching\n");
+		goto error;
+	}
+
+	if(ret!=2)
+		goto done;
+
+	/* multipart body */
+	if(get_mixed_part_delimiter(&msg->content_type->body,&mpdel) < 0) {
+		goto error;
+	}
+	p1 = find_sdp_line_delimiter(body->s, body->s+body->len, mpdel);
+	if (p1 == NULL) {
+		LM_ERR("empty multipart content\n");
+		return -1;
+	}
+	p2=p1;
+	c = 0;
+	for(;;)
+	{
+		p1 = p2;
+		if (p1 == NULL || p1 >= body->s+body->len)
+			break; /* No parts left */
+		p2 = find_next_sdp_line_delimiter(p1, body->s+body->len,
+				mpdel, body->s+body->len);
+		/* p2 is text limit for application parsing */
+		rest = eat_line(p1 + mpdel.len + 2, p2 - p1 - mpdel.len - 2);
+		if ( rest > p2 ) {
+			LM_ERR("Unparsable <%.*s>\n", (int)(p1-p1), p1);
+			return -1;
+		}
+		while( rest<p2 ) {
+			memset(&hf,0, sizeof(struct hdr_field));
+			rest = get_sdp_hdr_field(rest, p2, &hf);
+			if(hf.type==HDR_EOH_T)
+				break;
+			if(hf.type==HDR_ERROR_T)
+				return -1;
+			if(hf.type==HDR_CONTENTTYPE_T) {
+				if(decode_mime_type(hf.body.s, hf.body.s + hf.body.len,
+						&mime)==NULL)
+					return -1;
+				if (((((unsigned int)mime)>>16) == TYPE_APPLICATION)
+						&& ((mime&0x00ff) == SUBTYPE_SDP)) {
+					c = 1;
+				}
+			}
+		} /* end of while */
+		if(c==1)
+		{
+			body->s = rest;
+			body->len = p2-rest;
+			goto done;
+		}
+	}
+
+error:
+	return -1;
+
+done:
+	for (skip = 0; skip < body->len; skip++) {
+		c = body->s[body->len - skip - 1];
+		if (c != '\r' && c != '\n')
+			break;
+	}
+	if (skip == body->len) {
+		LM_ERR("empty body");
+		goto error;
+	}
+	body->len -= skip;
+
+	/*LM_DBG("DEBUG:extract_body:=|%.*s|\n",body->len,body->s);*/
+	return 1;
+}
+
+/*
+ * ser_memmem() returns the location of the first occurrence of data
+ * pattern b2 of size len2 in memory block b1 of size len1 or
+ * NULL if none is found. Obtained from NetBSD.
+ */
+void *
+ser_memmem(const void *b1, const void *b2, size_t len1, size_t len2)
+{
+        /* Initialize search pointer */
+        char *sp = (char *) b1;
+
+        /* Initialize pattern pointer */
+        char *pp = (char *) b2;
+
+        /* Initialize end of search address space pointer */
+        char *eos = sp + len1 - len2;
+
+        /* Sanity check */
+        if(!(b1 && b2 && len1 && len2))
+                return NULL;
+
+        while (sp <= eos) {
+                if (*sp == *pp)
+                        if (memcmp(sp, pp, len2) == 0)
+                                return sp;
+
+                        sp++;
+        }
+
+        return NULL;
+}
+
+/*
+ * Some helper functions taken verbatim from tm module.
+ */
+
+/*
+ * Extract Call-ID value
+ * assumes the callid header is already parsed
+ * (so make sure it is, before calling this function or
+ *  it might fail even if the message _has_ a callid)
+ */
+int
+get_callid(struct sip_msg* _m, str* _cid)
+{
+
+        if ((parse_headers(_m, HDR_CALLID_F, 0) == -1)) {
+                LM_ERR("failed to parse call-id header\n");
+                return -1;
+        }
+
+        if (_m->callid == NULL) {
+                LM_ERR("call-id not found\n");
+                return -1;
+        }
+
+        _cid->s = _m->callid->body.s;
+        _cid->len = _m->callid->body.len;
+        trim(_cid);
+        return 0;
+}
+
+/*
+ * Extract tag from To header field of a response
+ * assumes the to header is already parsed, so
+ * make sure it really is before calling this function
+ */
+int
+get_to_tag(struct sip_msg* _m, str* _tag)
+{
+
+        if (!_m->to) {
+                LM_ERR("To header field missing\n");
+                return -1;
+        }
+
+        if (get_to(_m)->tag_value.len) {
+                _tag->s = get_to(_m)->tag_value.s;
+                _tag->len = get_to(_m)->tag_value.len;
+        } else {
+                _tag->s = NULL; /* fixes gcc 4.0 warnings */
+                _tag->len = 0;
+        }
+
+        return 0;
+}
+
+/*
+ * Extract tag from From header field of a request
+ */
+int
+get_from_tag(struct sip_msg* _m, str* _tag)
+{
+
+        if (parse_from_header(_m)<0) {
+                LM_ERR("failed to parse From header\n");
+                return -1;
+        }
+
+        if (get_from(_m)->tag_value.len) {
+                _tag->s = get_from(_m)->tag_value.s;
+                _tag->len = get_from(_m)->tag_value.len;
+        } else {
+                _tag->s = NULL; /* fixes gcc 4.0 warnings */
+                _tag->len = 0;
+        }
+
+        return 0;
+}
+
+/*
+ * Extract URI from the Contact header field
+ */
+int
+get_contact_uri(struct sip_msg* _m, struct sip_uri *uri, contact_t** _c)
+{
+
+        if ((parse_headers(_m, HDR_CONTACT_F, 0) == -1) || !_m->contact)
+                return -1;
+        if (!_m->contact->parsed && parse_contact(_m->contact) < 0) {
+                LM_ERR("failed to parse Contact body\n");
+                return -1;
+        }
+        *_c = ((contact_body_t*)_m->contact->parsed)->contacts;
+        if (*_c == NULL)
+                /* no contacts found */
+                return -1;
+
+        if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) {
+                LM_ERR("failed to parse Contact URI [%.*s]\n",
+                        (*_c)->uri.len, ((*_c)->uri.s)?(*_c)->uri.s:"");
+                return -1;
+        }
+        return 0;
+}

+ 39 - 0
modules_k/rtpproxy/nhelpr_funcs.h

@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _NHLPR_FUNCS_H
+#define  _NHLPR_FUNCS_H
+
+#include "../../str.h"
+#include "../../parser/msg_parser.h"
+#include "../../parser/contact/contact.h"
+
+int extract_body(struct sip_msg * , str *);
+int check_content_type(struct sip_msg * );
+void *ser_memmem(const void *, const void *, size_t, size_t);
+int get_callid(struct sip_msg *, str *);
+int get_to_tag(struct sip_msg *, str *);
+int get_from_tag(struct sip_msg *, str *);
+int get_contact_uri(struct sip_msg *, struct sip_uri *, contact_t **);
+
+#endif

+ 234 - 0
modules_k/rtpproxy/rtpproxy_stream.c

@@ -0,0 +1,234 @@
+/* $Id: rtpproxy_stream.c,v 1.2 2008/11/04 22:28:04 sobomax Exp $
+ *
+ * Copyright (C) 2008 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <sys/uio.h>
+
+#include "../../ip_addr.h"
+#include "../../parser/msg_parser.h"
+#include "../../sr_module.h"
+#include "../../ut.h"
+#include "nathelper.h"
+#include "nhelpr_funcs.h"
+
+int
+fixup_var_str_int(void **param, int param_no)
+{
+    int ret;
+    pv_elem_t *model;
+    str s;
+
+    if (param_no == 1) {
+        model = NULL;
+        s.s = (char *)(*param);
+        s.len = strlen(s.s);
+        if (pv_parse_format(&s, &model) < 0) {
+            LM_ERR("wrong format[%s]!\n", (char *)(*param));
+            return E_UNSPEC;
+        }
+        if (model == NULL) {
+            LM_ERR("empty parameter!\n");
+            return E_UNSPEC;
+        }
+        *param = (void *)model;
+    } else if (param_no == 2) {
+	/* According to
+	 * http://www.kamailio.org/docs/modules/1.5.x/nathelper.html#rtpproxy_stream2xxx
+	 * this could be -1 */
+	s.s = (char *)(*param);
+	s.len = strlen(s.s);
+	if (str2sint(&s, &ret)==0) {
+		pkg_free(*param);
+		*param = (void *)ret;
+        } else {
+            LM_ERR("bad number <%s>\n", (char *)(*param));
+            return E_CFG;
+        }
+    }
+    return 0;
+}
+
+static int
+rtpproxy_stream(struct sip_msg* msg, str *pname, int count, int stream2uac)
+{
+    int nitems;
+    str callid, from_tag, to_tag;
+    struct rtpp_node *node;
+    char cbuf[16];
+    struct iovec v[] = {
+        {NULL,        0},
+        {cbuf,        0}, /* 1 P<count> */
+        {" ",         1},
+        {NULL,        0}, /* 3 callid */
+        {" ",         1},
+        {NULL,        0}, /* 5 pname */
+        {" session ", 9},
+        {NULL,        0}, /* 7 from tag */
+        {";1 ",       3},
+        {NULL,        0}, /* 9 to tag */
+        {";1",        2}
+    };
+
+    if (get_callid(msg, &callid) == -1 || callid.len == 0) {
+        LM_ERR("can't get Call-Id field\n");
+        return -1;
+    }
+    if (get_to_tag(msg, &to_tag) == -1) {
+        LM_ERR("can't get To tag\n");
+        return -1;
+    }
+    if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) {
+        LM_ERR("can't get From tag\n");
+        return -1;
+    }
+    v[1].iov_len = sprintf(cbuf, "P%d", count);
+    STR2IOVEC(callid, v[3]);
+    STR2IOVEC(*pname, v[5]);
+    node = select_rtpp_node(callid, 1);
+    if (!node) {
+        LM_ERR("no available proxies\n");
+        return -1;
+    }
+    if (node->rn_ptl_supported == 0) {
+        LM_ERR("required functionality is not "
+          "supported by the version of the RTPproxy running on the selected "
+          "node.  Please upgrade the RTPproxy and try again.\n");
+        return -1;
+    }
+    nitems = 11;
+    if (stream2uac == 0) {
+        if (to_tag.len == 0)
+            return -1;
+        STR2IOVEC(to_tag, v[7]);
+        STR2IOVEC(from_tag, v[9]);
+    } else {
+        STR2IOVEC(from_tag, v[7]);
+        STR2IOVEC(to_tag, v[9]);
+        if (to_tag.len <= 0)
+            nitems -= 2;
+    }
+    send_rtpp_command(node, v, nitems);
+
+    return 1;
+}
+
+static int
+rtpproxy_stream2_f(struct sip_msg *msg, char *str1, int count, int stream2uac)
+{
+    str pname;
+
+    if (str1 == NULL || pv_printf_s(msg, (pv_elem_p)str1, &pname) != 0)
+	return -1;
+    return rtpproxy_stream(msg, &pname, count, stream2uac);
+}
+
+int
+rtpproxy_stream2uac2_f(struct sip_msg* msg, char* str1, char* str2)
+{
+
+    return rtpproxy_stream2_f(msg, str1, (int)(long)str2, 1);
+}
+
+int
+rtpproxy_stream2uas2_f(struct sip_msg* msg, char* str1, char* str2)
+{
+
+    return rtpproxy_stream2_f(msg, str1, (int)(long)str2, 0);
+}
+
+static int
+rtpproxy_stop_stream(struct sip_msg* msg, int stream2uac)
+{
+    int nitems;
+    str callid, from_tag, to_tag;
+    struct rtpp_node *node;
+    struct iovec v[] = {
+        {NULL,        0},
+        {"S",         1}, /* 1 */
+        {" ",         1},
+        {NULL,        0}, /* 3 callid */
+        {" ",         1},
+        {NULL,        0}, /* 5 from tag */
+        {";1 ",       3},
+        {NULL,        0}, /* 7 to tag */
+        {";1",        2}
+    };
+
+    if (get_callid(msg, &callid) == -1 || callid.len == 0) {
+        LM_ERR("can't get Call-Id field\n");
+        return -1;
+    }
+    if (get_to_tag(msg, &to_tag) == -1) {
+        LM_ERR("can't get To tag\n");
+        return -1;
+    }
+    if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) {
+        LM_ERR("can't get From tag\n");
+        return -1;
+    }
+    STR2IOVEC(callid, v[3]);
+    node = select_rtpp_node(callid, 1);
+    if (!node) {
+        LM_ERR("no available proxies\n");
+        return -1;
+    }
+    if (node->rn_ptl_supported == 0) {
+        LM_ERR("required functionality is not "
+          "supported by the version of the RTPproxy running on the selected "
+          "node.  Please upgrade the RTPproxy and try again.\n");
+        return -1;
+    }
+    nitems = 9;
+    if (stream2uac == 0) {
+        if (to_tag.len == 0)
+            return -1;
+        STR2IOVEC(to_tag, v[5]);
+        STR2IOVEC(from_tag, v[7]);
+    } else {
+        STR2IOVEC(from_tag, v[5]);
+        STR2IOVEC(to_tag, v[7]);
+        if (to_tag.len <= 0)
+            nitems -= 2;
+    }
+    send_rtpp_command(node, v, nitems);
+
+    return 1;
+}
+
+int
+rtpproxy_stop_stream2uac2_f(struct sip_msg* msg, char* str1, char* str2)
+{
+
+    return rtpproxy_stop_stream(msg, 1);
+}
+
+int
+rtpproxy_stop_stream2uas2_f(struct sip_msg* msg, char* str1, char* str2)
+{
+
+    return rtpproxy_stop_stream(msg, 0);
+}

+ 37 - 0
modules_k/rtpproxy/rtpproxy_stream.h

@@ -0,0 +1,37 @@
+/* $Id: rtpproxy_stream.h,v 1.1 2008/11/04 21:10:07 sobomax Exp $
+ *
+ * Copyright (C) 2008 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _RTPPROXY_STREAM_H
+#define  _RTPPROXY_STREAM_H
+
+int fixup_var_str_int(void **, int);
+int rtpproxy_stream2uac2_f(struct sip_msg *, char *, char *);
+int rtpproxy_stream2uas2_f(struct sip_msg *, char *, char *);
+int rtpproxy_stop_stream2uac2_f(struct sip_msg *, char *, char *);
+int rtpproxy_stop_stream2uas2_f(struct sip_msg *, char *, char *);
+
+#endif