Explorar el Código

* Module: mediaproxy

Moved modules_k/mediaproxy to modules and removed modules_s/mediaproxy.
Juha Heinanen hace 16 años
padre
commit
ee7c37af07

+ 0 - 0
modules_k/mediaproxy/Makefile → modules/mediaproxy/Makefile


+ 0 - 0
modules_k/mediaproxy/README → modules/mediaproxy/README


+ 0 - 0
modules_k/mediaproxy/doc/mediaproxy.xml → modules/mediaproxy/doc/mediaproxy.xml


+ 0 - 0
modules_k/mediaproxy/doc/mediaproxy_admin.xml → modules/mediaproxy/doc/mediaproxy_admin.xml


+ 0 - 0
modules_k/mediaproxy/mediaproxy.c → modules/mediaproxy/mediaproxy.c


+ 0 - 16
modules_s/mediaproxy/Makefile

@@ -1,16 +0,0 @@
-# $Id$
-#
-# mediaproxy module makefile
-#
-# 
-# WARNING: do not run this directly, it should be run by the master Makefile
-
-include ../../Makefile.defs
-auto_gen=
-NAME=mediaproxy.so
-LIBS=
-
-DEFS+=-DSER_MOD_INTERFACE
-
-include ../../Makefile.modules
-

+ 0 - 259
modules_s/mediaproxy/Readme

@@ -1,259 +0,0 @@
-
-                       Mediaproxy SER module
-
-                        Copyright Dan Pascu
-    		       2002-2004 AG Projects
-
-
-  Mediaproxy is a SER module that is designed to allow automatic NAT
-traversal for the majority of existing SIP clients. This means that there
-will be no need to configure anything in particular on the NAT box to allow
-these clients to work behind NAT when using the mediaproxy module.
-
-Section 1 for a description of the modus operandi.
-Section 2 describes the types of SIP phones that work with mediaproxy and a
-          short description about how the NAT traversal is working.
-Section 3 is about mediaproxy features.
-Section 4 describes the exported module parameters and functions.
-Section 5 is a comparison with nathelper.
-
-1. Principle of operation
-   ----------------------
-
-  This NAT traversal solution operates by placing a mediaproxy server in the
-middle between 2 SIP user-agents. It mangles the SDP messages for both of
-them in a way that will make the parties talk with mediaproxy while they
-think they talk directly with each other.
-
-To achieve this, mediaproxy is actually composed by 2 components:
-  - the SER mediaproxy module itself
-  - an external proxy server called SER MediaProxy (available from
-    http://mediaproxy.ag-projects.com/ )
-
-To avoid confusion in this document the mediaproxy module will be called
-'module' or 'mediaproxy module', while the mediaproxy server will be called
-'proxy server' from here on.
-
-  The proxy server can be run on the same machine as the module or on a
-remote host. Moreover it is possible for a single module to control multiple
-proxy servers running on multiple geographically distributed hosts. To find
-out more about the architecture of SER MediaProxy please read the
-documentation that comes with it.
-
-  To be able to act as a proxy between the 2 talking parties, the machine(s)
-running the module/proxy server must have a public IP address.
-
-  The module will ask the proxy server to allocate as many sockets as there
-are media streams in the SDP body of the SIP INVITE/Ok messages. The proxy
-server will send back to the module the address and port(s) for them. Then
-the module will replace the original contact IP and RTP ports from the SDP
-messages with the ones provided by the proxy server. By doing this both
-clients will try to contact the proxy server instead of talking directly
-with each other. Once the clients contact the proxy server, it will record
-the addresses they came from and will know where to forward packets received
-from the other party This is needed because the address/port the NAT box
-will allocate for the leaving streams is not known before they actually
-leave the NAT box. However the address of the proxy server is always known
-(being a public one) so the 2 parties know where to connect and then after
-they did so, the proxy learns the addresses they came from and can forward
-packets between them.
-
-
-2. Types of SIP clients
-   --------------------
-
-  The SIP clients that will work transparently behind NAT when using the
-mediaproxy module are the so-called symmetric clients. The symmetric clients
-have the particularity that use the same port to send the data as the one
-they use to receive it. In other words, if they are for example configured
-to use port 5060 for SIP signaling, they will use the same port when sending
-data as well as when receiving it. This must be true for both the SIP
-signaling as well as the RTP streams for a client to work transparently with
-the mediaproxy module without any additional configuration on the NAT box.
-
-  This ability is important because the only way to get back to a client
-behind NAT is to send to the IP address and port the packet was received
-from. Once a packet is sent from the client behind NAT to the outside world,
-it opens a communication channel in the NAT box that is open in both
-directions for a while (it will timeout after a while after no more data is
-sent through it, but it can be kept active by sending data through it at
-certain regular time intervals). While this channel is open, any data sent
-to the public address and port that the NAT box assigned for the address and
-port the client behind NAT is sending from (and this mapping is guaranteed
-to be unique), will go back straight to the address and port the client has
-sent from. This is why is necessary for the clients to be symmetric. If they
-listen on the same port they sent from, the data sent back to the public
-address that the NAT box assigned to the leaving packets will actually reach
-the listening port of the client behind NAT.
-
-  Some SIP clients implement particular algorithms to detect if they are
-actually behind a NAT box and try to act smart by detecting the IP address
-of the NAT box (or simply allowing one to manually configure it), and then
-use this IP address in the SIP and SDP messages instead of their own private
-IP address. This situation can be confusing for a module that tries to
-perform transparent NAT traversal as it can wrongly mistake such a client
-that is behind NAT with a client that is actually in the public address
-space. However for the mediaproxy module it is not important if the clients
-apply or not this kind of behavior, as it is able to cope with both
-situations gracefully.
-
-  This doesn't mean that mediaproxy is not able to work with asymmetric
-clients behind NAT, but in their case special static forwarding routes need
-to be configured on the NAT box.
-
-  Mediaproxy has special support for asymmetric clients, can detect them and
-send the data to the ports they expect it to, however they can work behind
-NAT only if static routes are configured on the NAT box since there is no
-way of getting back to an address/port that has not previously opened a data
-channel in the NAT box by sending something out first. Nevertheless the
-support for asymmetric clients is important, because without it they won't
-be able to work even when they have public Internet addresses. Also this
-support allows one to use an asymmetric client behind NAT if he can
-configure the NAT box to forward the packets meant to that client.
-
-  The only requirement a symmetric SIP client must met to be able to work
-transparently behind NAT when using the mediaproxy module is to accept to be
-configured to use a so called outbound proxy and this proxy must be the one
-running with the mediaproxy module loaded.
-
-
-3. Features
-   --------
-
-- make symmetric clients work behind NAT transparently if they use the SIP
-  server as the outbound SIP server.
-- handle all media streams specified in the SDP body. There is a limit of 64
-  RTP streams per session in the code now, but we hardly find this to be a
-  limitation for the time being.
-- able to distribute RTP traffic load on multiple proxy servers running on
-  multiple hosts.
-- able to specify which proxy server to use based on the SIP domain of the
-  caller/destination (done by the proxy server's dispatcher module)
-- handle asymmetric clients properly. They can even work behind NAT if a
-  proper port forwarding is done for them on the NAT box.
-
-
-4. Module parameters and exported functions
-   ----------------------------------------
-
-  The module exports the following parameters:
-
-  - mediaproxy_socket
-
-    it is the path to the filesystem socket where the proxy server
-    listens for commands from the module.
-
-  - sip_asymmetrics
-
-    it is the path to a file that lists regular expressions that match
-    'User-Agent' or 'Server' fields from clients that are asymmetric
-    regarding SIP signaling. Needed to detect when a client is asymmetric
-    regarding SIP signaling. An example file is in the config/ subdirectory.
-
-  - rtp_asymmetrics
-
-    it is the path to a file that lists regular expressions that match
-    'User-Agent' or 'Server' fields from clients that are asymmetric
-    regarding the RTP media. Needed to detect when a client is asymmetric
-    regarding the RTP media. An example file is in the config/ subdirectory.
-
-  - natping_interval
-
-    it holds an integer value representing how often the module will
-    send packets to all registered clients that are behind NAT to keep
-    their opened channels alive. Represents an interval in seconds.
-
-Parameters are set in the SER configuration file by using the modparam
-command. Below are examples, which contain the actual default values of the
-parameters. If you are Ok with them it is not necessary to specify them in the
-configuration file at all.
-
-modparam("mediaproxy", "mediaproxy_socket", "/var/run/proxydispatcher.sock")
-modparam("mediaproxy", "sip_asymmetrics", "/etc/ser/sip-asymmetrics-clients")
-modparam("mediaproxy", "rtp_asymmetrics", "/etc/ser/rtp-asymmetrics-clients")
-modparam("mediaproxy", "natping_interval", 20)
-
-  The module exports the following functions:
-
-  - client_nat_test(type)
-
-    tests if the client is behind NAT or not. The types of tests are
-    specified by the type parameter which represents a sum of the 
-    following numbers (add the values of the ones you wish to perform
-    tests for):
-
-      1 - tests if client has a private IP address (as defined by RFC1918)
-          in the Contact field of the SIP message.
-
-      2 - tests if client has contacted SER from an address that is different
-          from the one in the 1st Via field. Both IP and port are checked,
-          except for asymmetric clients for which the port is ignored.
-
-      4 - tests if client has a private IP address (as defined by RFC1918)
-          in the top Via field of the SIP message.
-
-    for example calling client_nat_test("3") in ser.cfg will perform
-    first 2 tests listen above and return true as soon as one succeeds
-    if both fail will return false.
-
-  - fix_contact()
-
-    will replace the IP:Port in the Contact field of the SIP message
-    with the ones the SIP message was received from. For clients that
-    are asymmetric regarding SIP signaling (as determined from the
-    sip_asymmetrics file) will preserve the port.
-    usually called after an if (client_nat_test(type)) has succeded
-
-  - use_media_proxy()
-
-    will make a call to the proxy server and replace the IPs and ports
-    in the SDP body with the ones returned by the proxy server for
-    each media stream that the SDP message describes. This will force
-    the media streams to be routed through the proxy server.
-    called when you want to make the session go through a proxy server
-
-  - end_media_session()
-
-    will call on the proxy server to end the media session for that call
-    this is done at the end of the call to instruct the proxy server to
-    free the resources allocated to that call as well as to save log
-    information about the call.
-    called when a session should end (BYE or CANCEL received)
-
-
-5. Comparison with the nathelper module
-   ------------------------------------
-
-  After reading all this you may wonder what this module can offer you that
-the nathelper module (a similar nat traversal solution) can't and why was
-necessary to develop this module.
-
-While at surface they seem to offer about the same functionality, there are
-a few core differences that make them quite different.
-
-  The main and most notable difference is that mediaproxy offers a
-distributed environment, where the mediaproxy module can control multiple
-mediaproxy servers. The mediaproxy servers can be local or remote and they
-can be specified per domain or as defaults for domains that don't have their
-own mediaproxy servers defined. These mediaproxy servers can be arranged in
-load balancing and fallback schemes allowing the platform to scale up easily
-and also offer redundancy to keep the service running even if some of the
-mediaproxies go offline. Mediaproxy is able to detect the dead proxies and
-redistribute the calls among the other mediaproxies that are available.
-(More details about this can be found in the SER MediaProxy documentation)
-
-  Another important difference is that mediaproxy tries to move the complex
-logic of decision from the ser configuration file to the module and the
-proxy servers themselves. This is why there are very few functions in this
-module that take any parameters. Instead, control is achieved by modifying
-resources outside of ser.cfg. This includes for example specifying the
-mediaproxy servers using DNS SRV records, or declaring asymmetric clients
-in external files that are automatically re-read as soon as they change.
-This allows SER to run without interruption or restarts. If one wants to
-change SER's behavior, instead of changing ser.cfg and restarting SER, one
-will change these external resources and SER will adapt it's behavior on the
-fly without any need for restart.
-Another advantage of this is that ser.cfg becomes simpler and easier to
-maintain.
-
-

+ 0 - 8
modules_s/mediaproxy/config/Readme

@@ -1,8 +0,0 @@
-
-sip-asymmetric-clients and rtp-asymmetric-clients list clients that are
-asymmetric regarding the SIP signaling respective the RTP media streams.
-
-By default these files should go in /etc/ser/ (unless specifically
-configured to be somewhere else using the sip_asymmetrics and/or
-rtp_asymmetrics options available with the mediaproxy module.
-

+ 0 - 12
modules_s/mediaproxy/config/rtp-asymmetric-clients

@@ -1,12 +0,0 @@
-#
-# Lines starting with a '#' character or empty lines are ignored.
-# Put a User-Agent name or regular expression per line.
-# Check is case insensitive.
-#
-# This file should only list SIP clients that are asymmetric regarding
-# the RTP media streams. For clients that are asymmetric regarding the SIP
-# signaling, use the equivalent SIP version of this file.
-# Clients that are asymmetric for both SIP signaling as well as RTP media
-# streams should go in both files.
-#
-

+ 0 - 118
modules_s/mediaproxy/config/ser.cfg

@@ -1,118 +0,0 @@
-# Example ser.cfg for mediaproxy functionality
-
-loadmodule "/usr/lib/ser/modules/registrar.so"
-loadmodule "/usr/lib/ser/modules/domain.so"
-loadmodule "/usr/lib/ser/modules/mediaproxy.so"
-
-modparam("mediaproxy", "natping_interval", 60)
-modparam("registrar",  "nat_flag",         2)
-
-route{
-    if (!mf_process_maxfwd_header("10")) {
-        if (method!="ACK") {
-            sl_send_reply("483", "Too many hops");
-        };
-        break;
-    };
-
-    if (msg:len >= max_len) {
-        if (method!="ACK") {
-            sl_send_reply("513", "Message too big");
-        };
-        break;
-    };
-
-    if (method=="REGISTER") {
-        if (is_from_local()) {
-            # Mark as NAT'ed
-            if (client_nat_test("3")) {
-                setflag(2);
-                force_rport();
-                fix_contact();
-            };
-
-            if (!www_authorize("", "subscriber")) {
-                www_challenge("", "0");
-                break;
-            } else if (!check_to()) {
-                sl_send_reply("403", "Username!=To not allowed");
-                break;
-            };
-
-            if (!save("location")) {
-                sl_reply_error();  
-            };
-        } else {
-            sl_send_reply("403", "This domain is not served here");
-        };
-
-        break;
-    };
-
-    if (method=="INVITE") {
-        if (!(is_from_local() || is_uri_host_local())) {
-            sl_send_reply("403", "Relaying is forbidden");
-            break;
-        };
-        t_on_failure("1");
-    } else if (method == "BYE" || method == "CANCEL") {
-        end_media_session();
-    };
-
-    if (loose_route()) { 
-        if (method=="INVITE" || method=="ACK") {
-            use_media_proxy();
-        };
-        # end media session for BYE and CANCEL is done above
-        # before entering the loose route. no need to call it here
-        t_relay();
-        break;
-    };
-
-    # Force subsequent messages to pass trough this proxy
-    if (method == "INVITE") {
-        record_route();
-    };
-
-    if (client_nat_test("3") && !search("^Record-Route:")) {
-        # Mark as NAT'ed
-        force_rport();
-        fix_contact();
-    };
-
-    if (method=="INVITE") {
-        t_on_reply("1");
-    };
-
-    if (is_uri_host_local()) { # join with next if?
-        if (!lookup("location")) {
-            sl_send_reply("404", "User not found");
-            break;
-        };
-    };
-
-    if (method=="INVITE" || method=="ACK") {
-        use_media_proxy();
-    };
-
-    if (!t_relay()) {
-        if (method=="INVITE" || method=="ACK") {
-            end_media_session();
-        };
-        sl_reply_error();
-    };
-}
-
-failure_route[1] {
-    end_media_session();
-}
-
-onreply_route[1] {
-    if (status=~"(183)|(2[0-9][0-9])") {
-        if (client_nat_test("1")) {
-            fix_contact();
-        };
-        use_media_proxy();
-    };
-}
-

+ 0 - 14
modules_s/mediaproxy/config/sip-asymmetric-clients

@@ -1,14 +0,0 @@
-#
-# Lines starting with a '#' character or empty lines are ignored.
-# Put a User-Agent name or regular expression per line.
-# Check is case insensitive.
-#
-# This file should only list SIP clients that are asymmetric regarding
-# the SIP signaling. For clients that are asymmetric regarding the RTP
-# media streams, use the equivalent RTP version of this file.
-# Clients that are asymmetric for both SIP signaling as well as RTP media
-# streams should go in both files.
-#
-
-ipDialog SipTone.*
-

+ 0 - 194
modules_s/mediaproxy/functions.h

@@ -1,194 +0,0 @@
-/* $Id$
- *
- * Copyright (C) 2004 Dan Pascu
- * Copyright (C) 2003 Porta Software Ltd
- *
- * 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
- *
- */
-
-static void
-pingClients(unsigned int ticks, void *param)
-{
-    static char pingbuf[4] = "\0\0\0\0";
-    static int length = 256;
-    struct hostent* hostent;
-    struct dest_info dst;
-    struct sip_uri uri;
-    void *buf, *ptr;
-    str contact;
-    int needed;
-    char proto;
-
-    buf = pkg_malloc(length);
-    if (buf == NULL) {
-        LOG(L_ERR, "error: mediaproxy/pingClients(): out of memory\n");
-        return;
-    }
-    needed = userLocation.get_all_ucontacts(buf, length, FL_NAT);
-    if (needed > 0) {
-        // make sure we alloc more than actually we were told is missing
-        // (some clients may register while we are making these calls)
-        length = (length + needed) * 2;
-        ptr = pkg_realloc(buf, length);
-        if (ptr == NULL) {
-            LOG(L_ERR, "error: mediaproxy/pingClients(): out of memory\n");
-            pkg_free(buf);
-            return;
-        } else {
-            buf = ptr;
-        }
-        // try again. we may fail again if _many_ clients register in between
-        needed = userLocation.get_all_ucontacts(buf, length, FL_NAT);
-        if (needed != 0) {
-            pkg_free(buf);
-            return;
-        }
-    }
-
-    ptr = buf;
-    while (1) {
-        memcpy(&(contact.len), ptr, sizeof(contact.len));
-        if (contact.len == 0)
-            break;
-        contact.s = (char*)ptr + sizeof(contact.len);
-        ptr = contact.s + contact.len;
-		init_dest_info(&dst);
-	memcpy(&dst.send_sock, ptr, sizeof(dst.send_sock));
-	ptr += sizeof(dst.send_sock);
-        if (parse_uri(contact.s, contact.len, &uri) < 0) {
-            LOG(L_ERR, "error: mediaproxy/pingClients(): can't parse contact uri\n");
-            continue;
-        }
-        if (uri.proto != PROTO_UDP && uri.proto != PROTO_NONE)
-            continue;
-        if (uri.port_no == 0)
-            uri.port_no = SIP_PORT;
-        proto=PROTO_UDP;
-        hostent = sip_resolvehost(&uri.host, &uri.port_no, &proto);
-        if (hostent == NULL){
-            LOG(L_ERR, "error: mediaproxy/pingClients(): can't resolve host\n");
-            continue;
-        }
-        hostent2su(&dst.to, hostent, 0, uri.port_no);
-	if (dst.send_sock==0) {
- 		dst.send_sock = get_send_socket(0, &dst.to, PROTO_UDP);
-        	if (dst.send_sock == NULL) {
-			LOG(L_ERR, "error: mediaproxy/pingClients(): can't get "
-			    "sending socket\n");
-			continue;
-		}
-        }
-		dst.proto=PROTO_UDP;
-        udp_send(&dst, pingbuf, sizeof(pingbuf));
-    }
-    pkg_free(buf);
-}
-
-
-// Replace IP:Port in Contact field with the source address of the packet.
-// Preserve port for SIP asymmetric clients
-static int
-FixContact(struct sip_msg* msg, char* str1, char* str2)
-{
-    str beforeHost, after, agent;
-    contact_t* contact;
-    struct lump* anchor;
-    struct sip_uri uri;
-    char *newip, *buf;
-    int len, newiplen, offset;
-    Bool asymmetric;
-
-    if (!getContactURI(msg, &uri, &contact))
-        return -1;
-
-    newip = ip_addr2a(&msg->rcv.src_ip);
-    newiplen = strlen(newip);
-
-    /* Don't do anything if the IP's are the same. Return success. */
-    if (newiplen==uri.host.len && memcmp(uri.host.s, newip, newiplen)==0) {
-        return 1;
-    }
-
-    if (uri.port.len == 0)
-        uri.port.s = uri.host.s + uri.host.len;
-
-    agent = getUserAgent(msg);
-    asymmetric = isSIPAsymmetric(agent);
-
-    beforeHost.s   = contact->uri.s;
-    beforeHost.len = uri.host.s - contact->uri.s;
-    if (asymmetric) {
-        // for asymmetrics we preserve the original port
-        after.s   = uri.port.s;
-        after.len = contact->uri.s + contact->uri.len - after.s;
-    } else {
-        after.s   = uri.port.s + uri.port.len;
-        after.len = contact->uri.s + contact->uri.len - after.s;
-    }
-
-    len = beforeHost.len + newiplen + after.len + 20;
-
-    // first try to alloc mem. if we fail we don't want to have the lump
-    // deleted and not replaced. at least this way we keep the original.
-    buf = pkg_malloc(len);
-    if (buf == NULL) {
-        LOG(L_ERR, "error: fix_contact(): out of memory\n");
-        return -1;
-    }
-
-    offset = contact->uri.s - msg->buf;
-    anchor = del_lump(msg, offset, contact->uri.len, HDR_CONTACT_T);
-
-    if (!anchor) {
-        pkg_free(buf);
-        return -1;
-    }
-
-    if (asymmetric && uri.port.len==0) {
-        len = sprintf(buf, "%.*s%s%.*s", beforeHost.len, beforeHost.s,
-                      newip, after.len, after.s);
-    } else if (asymmetric) {
-        len = sprintf(buf, "%.*s%s:%.*s", beforeHost.len, beforeHost.s,
-                      newip, after.len, after.s);
-    } else {
-        len = sprintf(buf, "%.*s%s:%d%.*s", beforeHost.len, beforeHost.s,
-                      newip, msg->rcv.src_port, after.len, after.s);
-    }
-
-    if (insert_new_lump_after(anchor, buf, len, HDR_CONTACT_T) == 0) {
-        pkg_free(buf);
-        return -1;
-    }
-
-    contact->uri.s   = buf;
-    contact->uri.len = len;
-
-    if (asymmetric) {
-        LOG(L_INFO, "info: fix_contact(): preserved port for SIP "
-            "asymmetric client: `%.*s'\n", agent.len, agent.s);
-    }
-
-    return 1;
-}
-
-

+ 0 - 1604
modules_s/mediaproxy/mediaproxy.c

@@ -1,1604 +0,0 @@
-/* $Id$
- *
- * Copyright (C) 2004 Dan Pascu
- *
- * 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
- *
- */
-
-// TODO
-//
-// - make the asymmetric files use CFG_DIR
-// - find a way to install the config files with make install
-
-#include "../../sr_module.h"
-#include "../../dprint.h"
-#include "../../str.h"
-#include "../../error.h"
-#include "../../data_lump.h"
-#include "../../forward.h"
-#include "../../mem/mem.h"
-#include "../../resolve.h"
-#include "../../timer.h"
-#include "../../ut.h"
-#include "../../parser/parse_from.h"
-#include "../../parser/parse_to.h"
-#include "../../parser/parse_uri.h"
-#include "../../msg_translator.h"
-#include "../../id.h"
-#include "../registrar/sip_msg.h"
-#include "../usrloc/usrloc.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-#include <regex.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/un.h>
-
-
-MODULE_VERSION
-
-
-// Although `AF_LOCAL' is mandated by POSIX.1g, `AF_UNIX' is portable to
-// more systems.  `AF_UNIX' was the traditional name stemming from BSD, so
-// even most POSIX systems support it.  It is also the name of choice in
-// the Unix98 specification. So if there's no AF_LOCAL fallback to AF_UNIX
-#ifndef AF_LOCAL
-# define AF_LOCAL AF_UNIX
-#endif
-
-
-#define min(x, y)         (((x) < (y)) ? (x) : (y))
-
-#define isAnyAddress(adr) ((adr).len==7 && memcmp("0.0.0.0", (adr).s, 7)==0)
-
-
-typedef int Bool;
-#define True  1
-#define False 0
-
-
-typedef int  (*CheckLocalPartyProc)(struct sip_msg* msg, char* s1, char* s2);
-
-typedef Bool (*NatTestProc)(struct sip_msg* msg);
-
-
-typedef enum {
-    NTNone=0,
-    NTPrivateContact=1,
-    NTSourceAddress=2,
-    NTPrivateVia=4
-} NatTestType;
-
-typedef enum {
-    STUnknown=0,
-    STAudio,
-    STVideo,
-    STAudioVideo
-} StreamType;
-
-typedef struct {
-    const char *name;
-    uint32_t address;
-    uint32_t mask;
-} NetInfo;
-
-typedef struct {
-    str ip;
-    str port;
-    str type;    // stream type (`audio', `video', ...)
-    int localIP; // true if the IP is locally defined inside this media stream
-} StreamInfo;
-
-typedef struct {
-    NatTestType test;
-    NatTestProc proc;
-} NatTest;
-
-typedef struct {
-    char *file;        // the file which lists the asymmetric clients
-    long timestamp;    // for checking if it was modified
-
-    regex_t **clients; // the asymmetric clients regular expressions
-    int size;          // size of array above
-    int count;         // how many clients are in array above
-} AsymmetricClients;
-
-
-/* Function prototypes */
-static int ClientNatTest(struct sip_msg *msg, char *str1, char *str2);
-static int FixContact(struct sip_msg *msg, char *str1, char *str2);
-static int UseMediaProxy(struct sip_msg *msg, char *str1, char *str2);
-static int EndMediaSession(struct sip_msg *msg, char *str1, char *str2);
-
-static int mod_init(void);
-
-static Bool testPrivateContact(struct sip_msg* msg);
-static Bool testSourceAddress(struct sip_msg* msg);
-static Bool testPrivateVia(struct sip_msg* msg);
-
-
-/* Local global variables */
-static char *mediaproxySocket = "/var/run/proxydispatcher.sock";
-
-static int natpingInterval = 60; // 60 seconds
-
-static usrloc_api_t userLocation;
-
-static AsymmetricClients sipAsymmetrics = {
-    "/etc/ser/sip-asymmetric-clients",
-    //CFG_DIR"/sip-asymmetric-clients",
-    0,
-    NULL,
-    0,
-    0
-};
-
-static AsymmetricClients rtpAsymmetrics = {
-    "/etc/ser/rtp-asymmetric-clients",
-    0,
-    NULL,
-    0,
-    0
-};
-
-//static CheckLocalPartyProc isToLocal;
-
-NetInfo rfc1918nets[] = {
-    {"10.0.0.0",    0x0a000000UL, 0xff000000UL},
-    {"172.16.0.0",  0xac100000UL, 0xfff00000UL},
-    {"192.168.0.0", 0xc0a80000UL, 0xffff0000UL},
-    {NULL,          0UL,          0UL}
-};
-
-NatTest natTests[] = {
-    {NTPrivateContact, testPrivateContact},
-    {NTSourceAddress,  testSourceAddress},
-    {NTPrivateVia,     testPrivateVia},
-    {NTNone,           NULL}
-};
-
-static cmd_export_t commands[] = {
-    {"fix_contact",       FixContact,      0, 0,             REQUEST_ROUTE | ONREPLY_ROUTE },
-    {"use_media_proxy",   UseMediaProxy,   0, 0,             REQUEST_ROUTE | ONREPLY_ROUTE },
-    {"end_media_session", EndMediaSession, 0, 0,             REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE },
-    {"client_nat_test",   ClientNatTest,   1, fixup_int_1, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE },
-    {0, 0, 0, 0, 0}
-};
-
-static param_export_t parameters[] = {
-    {"mediaproxy_socket", PARAM_STRING, &mediaproxySocket},
-    {"sip_asymmetrics",   PARAM_STRING, &(sipAsymmetrics.file)},
-    {"rtp_asymmetrics",   PARAM_STRING, &(rtpAsymmetrics.file)},
-    {"natping_interval",  PARAM_INT,    &natpingInterval},
-    {0, 0, 0}
-};
-
-struct module_exports exports = {
-    "mediaproxy", // module name
-    commands,     // module exported functions
-    0,            // RPC methods
-    parameters,   // module exported parameters
-    mod_init,     // module init (before any kid is created. kids will inherit)
-    NULL,         // reply processing
-    NULL,         // destroy function
-    NULL,         // on_break
-    NULL          // child_init
-};
-
-
-
-/* Helper functions */
-
-// Functions dealing with strings
-
-/*
- * strfind() finds the start of the first occurrence of the substring needle
- * of length nlen in the memory area haystack of length len.
- */
-static void*
-strfind(const void *haystack, size_t len, const void *needle, size_t nlen)
-{
-    char *sp;
-
-    /* Sanity check */
-    if(!(haystack && needle && nlen && len>=nlen))
-        return NULL;
-
-    for (sp = (char*)haystack; sp <= (char*)haystack + len - nlen; sp++) {
-        if (*sp == *(char*)needle && memcmp(sp, needle, nlen)==0) {
-            return sp;
-        }
-    }
-
-    return NULL;
-}
-
-/*
- * strcasefind() finds the start of the first occurrence of the substring
- * needle of length nlen in the memory area haystack of length len by doing
- * a case insensitive search
- */
-static void*
-strcasefind(const char *haystack, size_t len, const char *needle, size_t nlen)
-{
-    char *sp;
-
-    /* Sanity check */
-    if(!(haystack && needle && nlen && len>=nlen))
-        return NULL;
-
-    for (sp = (char*)haystack; sp <= (char*)haystack + len - nlen; sp++) {
-        if (tolower(*sp) == tolower(*(char*)needle) &&
-            strncasecmp(sp, needle, nlen)==0) {
-            return sp;
-        }
-    }
-
-    return NULL;
-}
-
-/* returns string with whitespace trimmed from left end */
-static inline void
-ltrim(str *string)
-{
-    while (string->len>0 && isspace((int)*(string->s))) {
-        string->len--;
-        string->s++;
-    }
-}
-
-/* returns string with whitespace trimmed from right end */
-static inline void
-rtrim(str *string)
-{
-    char *ptr;
-
-    ptr = string->s + string->len - 1;
-    while (string->len>0 && (*ptr==0 || isspace((int)*ptr))) {
-        string->len--;
-        ptr--;
-    }
-}
-
-/* returns string with whitespace trimmed from both ends */
-static inline void
-trim(str *string)
-{
-    ltrim(string);
-    rtrim(string);
-}
-
-/* returns a pointer to first CR or LF char found or the end of string */
-static char*
-findendline(char *string, int len)
-{
-    char *ptr = string;
-
-    while(ptr - string < len && *ptr != '\n' && *ptr != '\r')
-        ptr++;
-
-    return ptr;
-}
-
-
-static int
-strtoint(str *data)
-{
-    long int result;
-    char c;
-
-    // hack to avoid copying the string
-    c = data->s[data->len];
-    data->s[data->len] = 0;
-    result = strtol(data->s, NULL, 10);
-    data->s[data->len] = c;
-
-    return (int)result;
-}
-
-
-/* Free the returned string with pkg_free() after you've done with it */
-static char*
-encodeQuopri(str buf)
-{
-    char *result;
-    int i, j;
-    char c;
-
-    result = pkg_malloc(buf.len*3+1);
-    if (!result) {
-        LOG(L_ERR, "error: mediaproxy/encodeQuopri(): out of memory\n");
-        return NULL;
-    }
-
-    for (i=0, j=0; i<buf.len; i++) {
-        c = buf.s[i];
-        if ((c>0x20 && c<0x7f && c!='=') || c=='\n' || c=='\r') {
-            result[j++] = c;
-        } else {
-            result[j++] = '=';
-            sprintf(&result[j], "%02X", (c & 0xff));
-            j += 2;
-        }
-    }
-    result[j] = 0;
-
-    return result;
-}
-
-
-/* Find a line in str `block' that starts with `start'. */
-static char*
-findLineStartingWith(str *block, char *start, int ignoreCase)
-{
-    char *ptr, *bend;
-    str zone;
-    int tlen;
-
-    bend = block->s + block->len;
-    tlen = strlen(start);
-    ptr = NULL;
-
-    for (zone = *block; zone.len > 0; zone.len = bend - zone.s) {
-        if (ignoreCase)
-            ptr = strcasefind(zone.s, zone.len, start, tlen);
-        else
-            ptr = strfind(zone.s, zone.len, start, tlen);
-        if (!ptr || ptr==zone.s || ptr[-1]=='\n' || ptr[-1]=='\r')
-            break;
-        zone.s = ptr + tlen;
-    }
-
-    return ptr;
-}
-
-
-/* get up to `limit' whitespace separated tokens from `char *string' */
-static int
-getTokens(char *string, str *tokens, int limit)
-{
-    int i, len, size;
-    char *ptr;
-
-    if (!string) {
-        return 0;
-    }
-
-    len  = strlen(string);
-
-    for (ptr=string, i=0; i<limit && len>0; i++) {
-        size = strspn(ptr, " \t\n\r");
-        ptr += size;
-        len -= size;
-        if (len <= 0)
-            break;
-        size = strcspn(ptr, " \t\n\r");
-        if (size==0)
-            break;
-        tokens[i].s = ptr;
-        tokens[i].len = size;
-        ptr += size;
-        len -= size;
-    }
-
-    return i;
-}
-
-/* get up to `limit' whitespace separated tokens from `str *string' */
-static int
-getStrTokens(str *string, str *tokens, int limit)
-{
-    int count;
-    char c;
-
-    if (!string || !string->s) {
-        return 0;
-    }
-
-    c = string->s[string->len];
-    string->s[string->len] = 0;
-
-    count = getTokens(string->s, tokens, limit);
-
-    string->s[string->len] = c;
-
-    return count;
-}
-
-
-/* Functions to extract the info we need from the SIP/SDP message */
-
-/* Extract Call-ID value. */
-static Bool
-getCallId(struct sip_msg* msg, str *cid)
-{
-    if (msg->callid == NULL) {
-        if (parse_headers(msg, HDR_CALLID_F, 0) == -1) {
-            return False;
-        }
-        if (msg->callid == NULL) {
-            return False;
-        }
-    }
-
-    *cid = msg->callid->body;
-
-    trim(cid);
-
-    return True;
-}
-
-
-/* Get caller domain */
-static str
-getFromDomain(char** type, struct sip_msg* msg)
-{
-    static char buf[16] = "unknown"; // buf is here for a reason. don't
-    static str notfound = {buf, 7};  // use the constant string directly!
-    static struct sip_uri puri;
-    str uri, did;
-
-    if (get_from_did(&did, msg) == 1) {
-		*type = "local";
-		return did;
-    }
-
-    *type = "remote";
-
-    if (parse_from_header(msg) < 0) {
-        LOG(L_ERR, "error: mediaproxy/getFromDomain(): error parsing `From' header\n");
-        return notfound;
-    }
-
-    uri = get_from(msg)->uri;
-
-    if (parse_uri(uri.s, uri.len, &puri) < 0) {
-        LOG(L_ERR, "error: mediaproxy/getFromDomain(): error parsing `From' URI\n");
-        return notfound;
-    } else if (puri.host.len == 0) {
-        return notfound;
-    }
-
-    return puri.host;
-}
-
-/* Get called domain */
-static str
-getToDomain(char** type, struct sip_msg* msg)
-{
-    static char buf[16] = "unknown"; // buf is here for a reason. don't
-    static str notfound = {buf, 7};  // use the constant string directly!
-    static struct sip_uri puri;
-    str uri, did;
-
-    if (get_to_did(&did, msg) == 0) {
-	*type = "local";
-	return did;
-    }
-
-    *type = "remote";
-
-    uri = get_to(msg)->uri;
-
-    if (parse_uri(uri.s, uri.len, &puri) < 0) {
-        LOG(L_ERR, "error: mediaproxy/getToDomain(): error parsing `To' URI\n");
-        return notfound;
-    } else if (puri.host.len == 0) {
-        return notfound;
-    }
-
-    return puri.host;
-
-}
-
-/* Get destination domain */
-// This function only works when called for a request although it's more
-// reliable than the getToDomain function.
-static str
-getDestinationDomain(char** type, struct sip_msg* msg)
-{
-    static char buf[16] = "unknown"; // buf is here for a reason. don't
-    static str notfound = {buf, 7};  // use the constant string directly!
-    str did;
-
-    if (get_to_did(&did, msg) == 0) {
-	*type = "local";
-	return did;
-    }
-
-    *type = "remote";
-
-    if (parse_sip_msg_uri(msg) < 0) {
-        LOG(L_ERR, "error: mediaproxy/getDestinationDomain(): error parsing destination URI\n");
-        return notfound;
-    } else if (msg->parsed_uri.host.len==0) {
-        return notfound;
-    }
-
-    return msg->parsed_uri.host;
-}
-
-
-/* Get From tag */
-static str
-getFromAddress(struct sip_msg *msg)
-{
-    static char buf[16] = "unknown"; // buf is here for a reason. don't
-    static str notfound = {buf, 7};  // use the constant string directly!
-    str uri;
-    char *ptr;
-
-    if (parse_from_header(msg) == -1) {
-        LOG(L_ERR, "error: mediaproxy/getFromAddress(): error parsing From: field\n");
-        return notfound;
-    }
-
-    uri = get_from(msg)->uri;
-
-    if (uri.len == 0)
-        return notfound;
-
-    if (strncmp(uri.s, "sip:", 4)==0) {
-        uri.s += 4;
-        uri.len -= 4;
-    }
-
-    if ((ptr = strfind(uri.s, uri.len, ";", 1))!=NULL) {
-        uri.len = ptr - uri.s;
-    }
-
-    return uri;
-}
-
-
-/* Get To tag */
-static str
-getToAddress(struct sip_msg *msg)
-{
-    static char buf[16] = "unknown"; // buf is here for a reason. don't
-    static str notfound = {buf, 7};  // use the constant string directly!
-    str uri;
-    char *ptr;
-
-    if (!msg->to) {
-        LOG(L_ERR, "error: mediaproxy/getToAddress(): missing To: field\n");
-        return notfound;
-    }
-
-    uri = get_to(msg)->uri;
-
-    if (uri.len == 0)
-        return notfound;
-
-    if (strncmp(uri.s, "sip:", 4)==0) {
-        uri.s += 4;
-        uri.len -= 4;
-    }
-
-    if ((ptr = strfind(uri.s, uri.len, ";", 1))!=NULL) {
-        uri.len = ptr - uri.s;
-    }
-
-    return uri;
-}
-
-
-/* Get From tag */
-static str
-getFromTag(struct sip_msg *msg)
-{
-    static char buf[4] = "";        // buf is here for a reason. don't
-    static str notfound = {buf, 0}; // use the constant string directly!
-    str tag;
-
-    if (parse_from_header(msg) == -1) {
-        LOG(L_ERR, "error: mediaproxy/getFromTag(): error parsing From: field\n");
-        return notfound;
-    }
-
-    tag = get_from(msg)->tag_value;
-
-    if (tag.len == 0)
-        return notfound;
-
-    return tag;
-}
-
-
-/* Get To tag */
-static str
-getToTag(struct sip_msg *msg)
-{
-    static char buf[4] = "";        // buf is here for a reason. don't
-    static str notfound = {buf, 0}; // use the constant string directly!
-    str tag;
-
-    if (!msg->to) {
-        LOG(L_ERR, "error: mediaproxy/getToTag(): missing To: field\n");
-        return notfound;
-    }
-
-    tag = get_to(msg)->tag_value;
-
-    if (tag.len == 0)
-        return notfound;
-
-    return tag;
-}
-
-
-/* Extract User-Agent */
-static str
-getUserAgent(struct sip_msg* msg)
-{
-    static char buf[16] = "unknown-agent"; // buf is here for a reason. don't
-    static str notfound = {buf, 13};       // use the constant string directly!
-    str block, server;
-    char *ptr;
-
-    if ((parse_headers(msg, HDR_USERAGENT_F, 0)!=-1) && msg->user_agent &&
-        msg->user_agent->body.len>0) {
-        return msg->user_agent->body;
-    }
-
-    // If we can't find user-agent, look after the Server: field
-
-    // This is a temporary hack. Normally it should be extracted by ser
-    // (either as the Server field, or if User-Agent is missing in place
-    // of the User-Agent field)
-
-    block.s   = msg->buf;
-    block.len = msg->len;
-
-    ptr = findLineStartingWith(&block, "Server:", True);
-    if (!ptr)
-        return notfound;
-
-    server.s   = ptr + 7;
-    server.len = findendline(server.s, block.s+block.len-server.s) - server.s;
-
-    trim(&server);
-    if (server.len == 0)
-        return notfound;
-
-    return server;
-}
-
-// Get URI from the Contact: field.
-static Bool
-getContactURI(struct sip_msg* msg, struct sip_uri *uri, contact_t** _c)
-{
-
-    if ((parse_headers(msg, HDR_CONTACT_F, 0) == -1) || !msg->contact)
-        return False;
-
-    if (!msg->contact->parsed && parse_contact(msg->contact) < 0) {
-        LOG(L_ERR, "error: mediaproxy/getContactURI(): cannot parse Contact header\n");
-        return False;
-    }
-
-    *_c = ((contact_body_t*)msg->contact->parsed)->contacts;
-
-    if (*_c == NULL) {
-        return False;
-    }
-
-    if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) {
-        LOG(L_ERR, "error: mediaproxy/getContactURI(): cannot parse Contact URI\n");
-        return False;
-    }
-
-    return True;
-}
-
-
-// Functions to manipulate the SDP message body
-
-static Bool
-checkContentType(struct sip_msg *msg)
-{
-    str type;
-
-    if (!msg->content_type) {
-        LOG(L_WARN, "warning: mediaproxy/checkContentType(): Content-Type "
-            "header missing! Let's assume the content is text/plain ;-)\n");
-        return True;
-    }
-
-    type = msg->content_type->body;
-    trim(&type);
-
-    if (strncasecmp(type.s, "application/sdp", 15) != 0) {
-        LOG(L_ERR, "error: mediaproxy/checkContentType(): invalid Content-Type "
-            "for SDP message\n");
-        return False;
-    }
-
-    if (!(isspace((int)type.s[15]) || type.s[15] == ';' || type.s[15] == 0)) {
-        LOG(L_ERR,"error: mediaproxy/checkContentType(): invalid character "
-            "after Content-Type!\n");
-        return False;
-    }
-
-    return True;
-}
-
-
-// Get the SDP message from SIP message and check it's Content-Type
-// return -1 on error, 0 if empty message, 1 if message present and not empty
-static int
-getSDPMessage(struct sip_msg *msg, str *sdp)
-{
-    sdp->s = get_body(msg);
-    if (sdp->s==NULL) {
-        LOG(L_ERR, "error: mediaproxy/getSDPMessage(): cannot get the SDP body from SIP message\n");
-        return -1;
-    }
-    sdp->len = msg->buf + msg->len - sdp->s;
-    if (sdp->len==0) {
-        // 0 length body is ok for ACK messages
-        if (!(msg->first_line.type == SIP_REQUEST &&
-              msg->first_line.u.request.method_value == METHOD_ACK)) {
-            LOG(L_ERR, "error: mediaproxy/getSDPMessage(): SDP message has zero length\n");
-        }
-        return 0;
-    }
-
-    if (!checkContentType(msg)) {
-        LOG(L_ERR, "error: mediaproxy/getSDPMessage(): content type is not `application/sdp'\n");
-        return -1;
-    }
-
-    return 1;
-}
-
-
-// will return the ip address present in a `c=' line in the given block
-// returns: -1 on error, 0 if not found, 1 if found
-static int
-getMediaIPFromBlock(str *block, str *mediaip)
-{
-    str tokens[3], zone;
-    char *ptr;
-    int count;
-
-    ptr = findLineStartingWith(block, "c=", False);
-
-    if (!ptr) {
-        mediaip->s   = NULL;
-        mediaip->len = 0;
-        return 0;
-    }
-
-    zone.s = ptr + 2;
-    zone.len = findendline(zone.s, block->s + block->len - zone.s) - zone.s;
-
-    count = getStrTokens(&zone, tokens, 3);
-
-    if (count != 3) {
-        LOG(L_ERR, "error: mediaproxy/getMediaIPFromBlock(): invalid `c=' "
-            "line in SDP body\n");
-        return -1;
-    }
-
-    // can also check if tokens[1] == 'IP4'
-    *mediaip = tokens[2];
-
-    return 1;
-}
-
-
-// Get the session-level media IP defined by the SDP message
-static Bool
-getSessionLevelMediaIP(str *sdp, str *mediaip)
-{
-    str block;
-    char *ptr;
-
-    // session IP can be found from the beginning up to the first media block
-    ptr = findLineStartingWith(sdp, "m=", False);
-    if (ptr) {
-        block.s   = sdp->s;
-        block.len = ptr - block.s;
-    } else {
-        block = *sdp;
-    }
-
-    if (getMediaIPFromBlock(&block, mediaip) == -1) {
-        LOG(L_ERR, "error: mediaproxy/getSessionLevelMediaIP(): parse error "
-            "while getting session-level media IP from SDP body\n");
-        return False;
-    }
-
-    // it's not an error to be missing. it can be locally defined
-    // by each media stream. thus we return true even if not found
-    return True;
-}
-
-
-// will get all media streams
-static int
-getMediaStreams(str *sdp, str *sessionIP, StreamInfo *streams, int limit)
-{
-    str tokens[2], block, zone;
-    char *ptr, *sdpEnd;
-    int i, count, streamCount, result;
-
-    sdpEnd = sdp->s + sdp->len;
-
-    for (i=0, block=*sdp; i<limit; i++) {
-        ptr = findLineStartingWith(&block, "m=", False);
-
-        if (!ptr)
-            break;
-
-        zone.s = ptr + 2;
-        zone.len = findendline(zone.s, sdpEnd - zone.s) - zone.s;
-        count = getStrTokens(&zone, tokens, 2);
-
-        if (count != 2) {
-            LOG(L_ERR, "error: mediaproxy/getMediaStreams(): invalid `m=' "
-                "line in SDP body\n");
-            return -1;
-        }
-
-        streams[i].type = tokens[0];
-        streams[i].port = tokens[1];
-
-        block.s   = zone.s + zone.len;
-        block.len = sdpEnd - block.s;
-    }
-
-    streamCount = i;
-
-    for (i=0; i<streamCount; i++) {
-        block.s = streams[i].port.s;
-        if (i < streamCount-1)
-            block.len = streams[i+1].port.s - block.s;
-        else
-            block.len = sdpEnd - block.s;
-
-        result = getMediaIPFromBlock(&block, &(streams[i].ip));
-        if (result == -1) {
-            LOG(L_ERR, "error: mediaproxy/getMediaStreams(): parse error in "
-                "getting the contact IP for the media stream nr. %d\n", i+1);
-            return -1;
-        } else if (result == 0) {
-            if (sessionIP->s == NULL) {
-                LOG(L_ERR, "error: mediaproxy/getMediaStreams(): media stream "
-                    "doesn't define a contact IP and the session-level IP "
-                    "is missing\n");
-                return -1;
-            }
-            streams[i].ip = *sessionIP;
-            streams[i].localIP = 0;
-        } else {
-            streams[i].localIP = 1;
-        }
-    }
-
-    return streamCount;
-}
-
-
-static Bool
-replaceElement(struct sip_msg *msg, str *oldElem, str *newElem)
-{
-    struct lump* anchor;
-    char *buf;
-
-    if (newElem->len==oldElem->len &&
-        memcmp(newElem->s, oldElem->s, newElem->len)==0) {
-        return True;
-    }
-
-    buf = pkg_malloc(newElem->len);
-    if (!buf) {
-        LOG(L_ERR, "error: mediaproxy/replaceElement(): out of memory\n");
-        return False;
-    }
-
-    anchor = del_lump(msg, oldElem->s - msg->buf, oldElem->len, 0);
-    if (!anchor) {
-        LOG(L_ERR, "error: mediaproxy/replaceElement(): failed to delete old element\n");
-        pkg_free(buf);
-        return False;
-    }
-
-    memcpy(buf, newElem->s, newElem->len);
-
-    if (insert_new_lump_after(anchor, buf, newElem->len, 0)==0) {
-        LOG(L_ERR, "error: mediaproxy/replaceElement(): failed to insert new element\n");
-        pkg_free(buf);
-        return False;
-    }
-
-    return True;
-}
-
-
-// Functions dealing with the external mediaproxy helper
-
-static inline size_t
-uwrite(int fd, const void *buf, size_t count)
-{
-    int len;
-
-    do
-        len = write(fd, buf, count);
-    while (len == -1 && errno == EINTR);
-
-    return len;
-}
-
-static inline size_t
-uread(int fd, void *buf, size_t count)
-{
-    int len;
-
-    do
-        len = read(fd, buf, count);
-    while (len == -1 && errno == EINTR);
-
-    return len;
-}
-
-static inline size_t
-readall(int fd, void *buf, size_t count)
-{
-    int len, total;
-
-    for (len=0, total=0; count-total>0; total+=len) {
-        len = uread(fd, (char*)buf+total, count-total);
-        if (len == -1) {
-            return -1;
-        }
-        if (len == 0) {
-            break;
-        }
-    }
-
-    return total;
-}
-
-static char*
-sendMediaproxyCommand(char *command)
-{
-    struct sockaddr_un addr;
-    int smpSocket, len;
-    static char buf[1024];
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sun_family = AF_LOCAL;
-    strncpy(addr.sun_path, mediaproxySocket, sizeof(addr.sun_path) - 1);
-#ifdef HAVE_SOCKADDR_SA_LEN
-    addr.sun_len = strlen(addr.sun_path);
-#endif
-
-    smpSocket = socket(AF_LOCAL, SOCK_STREAM, 0);
-    if (smpSocket < 0) {
-        LOG(L_ERR, "error: mediaproxy/sendMediaproxyCommand(): can't create socket\n");
-        return NULL;
-    }
-    if (connect(smpSocket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
-        close(smpSocket);
-        LOG(L_ERR, "error: mediaproxy/sendMediaproxyCommand(): can't connect to MediaProxy\n");
-        return NULL;
-    }
-
-    len = uwrite(smpSocket, command, strlen(command));
-
-    if (len <= 0) {
-        close(smpSocket);
-        LOG(L_ERR, "error: mediaproxy/sendMediaproxyCommand(): can't send command to MediaProxy\n");
-        return NULL;
-    }
-
-    len = readall(smpSocket, buf, sizeof(buf)-1);
-
-    close(smpSocket);
-
-    if (len < 0) {
-        LOG(L_ERR, "error: mediaproxy/sendMediaproxyCommand(): can't read reply from MediaProxy\n");
-        return NULL;
-    }
-
-    buf[len] = 0;
-
-    return buf;
-}
-
-
-// Miscellaneous helper functions
-
-/* Test if IP in `address' belongs to a RFC1918 network */
-static inline int
-rfc1918address(str *address)
-{
-    struct in_addr inaddr;
-    uint32_t netaddr;
-    int i, result;
-    char c;
-
-    c = address->s[address->len];
-    address->s[address->len] = 0;
-
-    result = inet_aton(address->s, &inaddr);
-
-    address->s[address->len] = c;
-
-    if (result==0)
-        return -1; /* invalid address to test */
-
-    netaddr = ntohl(inaddr.s_addr);
-
-    for (i=0; rfc1918nets[i].name!=NULL; i++) {
-        if ((netaddr & rfc1918nets[i].mask)==rfc1918nets[i].address) {
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
-#define isPrivateAddress(x) (rfc1918address(x)==1 ? 1 : 0)
-// test for a public address is more complex (also need to test for
-// address not in 0.0.0.0/8, 127.0.0.0/8, 224.0.0.0/4).
-// #define isPublicAddress(x)  (rfc1918address(x)==0 ? 1 : 0)
-
-
-// Check if the requested asymmetrics file has changed and reload it if needed
-static void
-checkAsymmetricFile(AsymmetricClients *aptr)
-{
-    char buf[512], errbuf[256], *which;
-    regex_t *re, **regs;
-    int i, size, code;
-    Bool firstTime = False;
-    struct stat statbuf;
-    FILE *file;
-    str line;
-
-    if (stat(aptr->file, &statbuf) < 0)
-        return; // ignore missing file
-
-    if (statbuf.st_mtime <= aptr->timestamp)
-        return; // not changed
-
-    // now we have work to do
-
-    which = (aptr == &sipAsymmetrics ? "SIP" : "RTP");
-
-    if (!aptr->clients) {
-        // if we are here the first time allocate memory to hold the regexps
-
-        // initial size of array
-        // (hopefully not reached so we won't need to realloc)
-        size = 32;
-        aptr->clients = (regex_t**)pkg_malloc(size*sizeof(regex_t**));
-        if (!aptr->clients) {
-            LOG(L_WARN, "warning: mediaproxy/checkAsymmetricFile() cannot "
-                "allocate memory for the %s asymmetric client list. "
-                "%s asymmetric clients will not be handled properly.\n",
-                which, which);
-            return; // ignore as it is not fatal
-        }
-        aptr->size  = size;
-        aptr->count = 0;
-        firstTime = True;
-    } else {
-        // else clean old stuff
-        for (i=0; i<aptr->count; i++) {
-            regfree(aptr->clients[i]);
-            pkg_free(aptr->clients[i]);
-            aptr->clients[i] = NULL;
-        }
-        aptr->count = 0;
-    }
-
-    // read new
-    file = fopen(aptr->file, "r");
-    i = 0; // this will count on which line are we in the file
-    while (!feof(file)) {
-        if (!fgets(buf, 512, file))
-            break;
-        i++;
-
-        line.s = buf;
-        line.len = strlen(buf);
-        trim(&line);
-        if (line.len == 0 || line.s[0] == '#')
-            continue; // comment or empty line. ignore
-        line.s[line.len] = 0;
-
-        re = (regex_t*)pkg_malloc(sizeof(regex_t));
-        if (!re) {
-            LOG(L_WARN, "warning: mediaproxy/checkAsymmetricFile(): "
-                "cannot allocate memory for all the %s asymmetric "
-                "clients listed in file. Some of them will not be "
-                "handled properly.\n", which);
-            break;
-        }
-        code = regcomp(re, line.s, REG_EXTENDED|REG_ICASE|REG_NOSUB);
-        if (code == 0) {
-            if (aptr->count+1 > aptr->size) {
-                size = aptr->size * 2;
-                regs = aptr->clients;
-                regs = (regex_t**)pkg_realloc(regs, size*sizeof(regex_t**));
-                if (!regs) {
-                    LOG(L_WARN, "warning: mediaproxy/checkAsymmetricFile(): "
-                        "cannot allocate memory for all the %s asymmetric "
-                        "clients listed in file. Some of them will not be "
-                        "handled properly.\n", which);
-                    break;
-                }
-                aptr->clients = regs;
-                aptr->size = size;
-            }
-            aptr->clients[aptr->count] = re;
-            aptr->count++;
-        } else {
-            regerror(code, re, errbuf, 256);
-            LOG(L_WARN, "warning: mediaproxy/checkAsymmetricFile(): cannot "
-                "compile line %d of the %s asymmetric clients file into a "
-                "regular expression (will be ignored): %s", i, which, errbuf);
-            pkg_free(re);
-        }
-    }
-
-    aptr->timestamp = statbuf.st_mtime;
-
-    LOG(L_INFO, "info: mediaproxy: %sloaded %s asymmetric clients file "
-        "containing %d entr%s.\n", firstTime ? "" : "re",
-        which, aptr->count, aptr->count==1 ? "y" : "ies");
-
-}
-
-
-//
-// This is a hack. Until a I find a better way to allow all children to update
-// the asymmetric client list when the files change on disk, it stays as it is
-// A timer registered from mod_init() only runs in one of the ser processes
-// and the others will always see the file that was read at startup.
-//
-#include <time.h>
-#define CHECK_INTERVAL 5
-static void
-periodicAsymmetricsCheck(void)
-{
-    static time_t last = 0;
-    time_t now;
-
-    // this is not guaranteed to run at every CHECK_INTERVAL.
-    // it is only guaranteed that the files won't be checked more often.
-    now = time(NULL);
-    if (now > last + CHECK_INTERVAL) {
-        checkAsymmetricFile(&sipAsymmetrics);
-        checkAsymmetricFile(&rtpAsymmetrics);
-        last = now;
-    }
-}
-
-static Bool
-isSIPAsymmetric(str userAgent)
-{
-    int i, code;
-    char c;
-
-    periodicAsymmetricsCheck();
-
-    if (!sipAsymmetrics.clients || sipAsymmetrics.count==0)
-        return False;
-
-    c = userAgent.s[userAgent.len];
-    userAgent.s[userAgent.len] = 0;
-
-    for (i=0; i<sipAsymmetrics.count; i++) {
-        code = regexec(sipAsymmetrics.clients[i], userAgent.s, 0, NULL, 0);
-        if (code == 0) {
-            userAgent.s[userAgent.len] = c;
-            return True;
-        } else if (code != REG_NOMATCH) {
-            char errbuf[256];
-            regerror(code, sipAsymmetrics.clients[i], errbuf, 256);
-            LOG(L_WARN, "warning: mediaproxy/isSIPAsymmetric() failed to "
-                "match regexp: %s\n", errbuf);
-        }
-    }
-
-    userAgent.s[userAgent.len] = c;
-
-    return False;
-}
-
-
-static Bool
-isRTPAsymmetric(str userAgent)
-{
-    int i, code;
-    char c;
-
-    periodicAsymmetricsCheck();
-
-    if (!rtpAsymmetrics.clients || rtpAsymmetrics.count==0)
-        return False;
-
-    c = userAgent.s[userAgent.len];
-    userAgent.s[userAgent.len] = 0;
-
-    for (i=0; i<rtpAsymmetrics.count; i++) {
-        code = regexec(rtpAsymmetrics.clients[i], userAgent.s, 0, NULL, 0);
-        if (code == 0) {
-            userAgent.s[userAgent.len] = c;
-            return True;
-        } else if (code != REG_NOMATCH) {
-            char errbuf[256];
-            regerror(code, rtpAsymmetrics.clients[i], errbuf, 256);
-            LOG(L_WARN, "warning: mediaproxy/isRTPAsymmetric() failed to "
-                "match regexp: %s\n", errbuf);
-        }
-    }
-
-    userAgent.s[userAgent.len] = c;
-
-    return False;
-}
-
-
-// NAT tests
-
-/* tests if address of signaling is different from address in 1st Via field */
-static Bool
-testSourceAddress(struct sip_msg* msg)
-{
-    Bool diffIP, diffPort;
-    int via1Port;
-
-    diffIP = received_test(msg);
-    if (isSIPAsymmetric(getUserAgent(msg))) {
-        // ignore port test for asymmetric clients (it's always different)
-        diffPort = False;
-    } else {
-        via1Port = (msg->via1->port ? msg->via1->port : SIP_PORT);
-        diffPort = (msg->rcv.src_port != via1Port);
-    }
-
-    return (diffIP || diffPort);
-}
-
-/* tests if Contact field contains a private IP address as defined in RFC1918 */
-static Bool
-testPrivateContact(struct sip_msg* msg)
-{
-    struct sip_uri uri;
-    contact_t* contact;
-
-    if (!getContactURI(msg, &uri, &contact))
-        return False;
-
-    return isPrivateAddress(&(uri.host));
-}
-
-/* tests if top Via field contains a private IP address as defined in RFC1918 */
-static Bool
-testPrivateVia(struct sip_msg* msg)
-{
-    return isPrivateAddress(&(msg->via1->host));
-}
-
-
-#include "functions.h"
-
-/* The public functions that are exported by this module */
-
-static int
-ClientNatTest(struct sip_msg* msg, char* str1, char* str2)
-{
-    int tests, i;
-
-	if (get_int_fparam(&tests, msg, (fparam_t*) str1) < 0) {
-		return -1;
-	}
-
-    for (i=0; natTests[i].test!=NTNone; i++) {
-        if ((tests & natTests[i].test)!=0 && natTests[i].proc(msg)) {
-            return 1;
-        }
-    }
-
-    return -1; // all failed
-}
-
-
-static int
-EndMediaSession(struct sip_msg* msg, char* str1, char* str2)
-{
-    char *command, *result;
-    str callId;
-
-    if (!getCallId(msg, &callId)) {
-        LOG(L_ERR, "error: end_media_session(): can't get Call-Id\n");
-        return -1;
-    }
-
-    command = pkg_malloc(callId.len + 20);
-    if (command == NULL) {
-        LOG(L_ERR, "error: end_media_session(): out of memory\n");
-        return -1;
-    }
-
-    sprintf(command, "delete %.*s info=\n", callId.len, callId.s);
-    result = sendMediaproxyCommand(command);
-
-    pkg_free(command);
-
-    return result==NULL ? -1 : 1;
-}
-
-
-#define MSG_UNKNOWN 0
-#define MSG_INVITE  1
-#define MSG_ACK     2
-#define MSG_REPLY   3
-
-
-static int
-UseMediaProxy(struct sip_msg* msg, char* str1, char* str2)
-{
-    str sdp, sessionIP, callId, fromDomain, toDomain, userAgent, tokens[64];
-    str fromAddr, toAddr, fromTag, toTag;
-    char *clientIP, *ptr, *command, *result, *agent, *fromType, *toType, *info;
-    int streamCount, i, port, count, portCount, cmdlen, infolen, success, type;
-    StreamInfo streams[64], stream;
-    Bool request;
-
-    if (msg->first_line.type == SIP_REQUEST) {
-        if (msg->first_line.u.request.method_value == METHOD_INVITE)
-            type = MSG_INVITE;
-        else if (msg->first_line.u.request.method_value == METHOD_ACK)
-            type = MSG_ACK;
-        else
-            type = MSG_UNKNOWN;
-    } else if (msg->first_line.type == SIP_REPLY) {
-        type = MSG_REPLY;
-    } else {
-        type = MSG_UNKNOWN;
-    }
-
-    if (type==MSG_INVITE || type==MSG_ACK) {
-        request = True;
-    } else if (type==MSG_REPLY) {
-        request = False;
-    } else {
-        return -1;
-    }
-
-    if (!getCallId(msg, &callId)) {
-        LOG(L_ERR, "error: use_media_proxy(): can't get Call-Id\n");
-        return -1;
-    }
-
-    success = getSDPMessage(msg, &sdp);
-    if (success==0 && type==MSG_ACK) {
-        return 1; // nothing to do. it's ok for ACK to not have a SDP body
-    } else if (success <= 0) {
-        LOG(L_ERR, "error: use_media_proxy(): failed to get the SDP message\n");
-        return -1;
-    }
-
-    if (!getSessionLevelMediaIP(&sdp, &sessionIP)) {
-        LOG(L_ERR, "error: use_media_proxy(): error parsing the SDP message\n");
-        return -1;
-    }
-
-    streamCount = getMediaStreams(&sdp, &sessionIP, streams, 64);
-    if (streamCount == -1) {
-        LOG(L_ERR, "error: use_media_proxy(): can't extract media streams "
-            "from the SDP message\n");
-        return -1;
-    }
-
-    if (streamCount == 0) {
-        // there are no media streams. we have nothing to do.
-        return 1;
-    }
-
-    fromDomain = getFromDomain(&fromType, msg);
-    fromAddr   = getFromAddress(msg);
-    toAddr     = getToAddress(msg);
-    fromTag    = getFromTag(msg);
-    toTag      = getToTag(msg);
-    userAgent  = getUserAgent(msg);
-    if (request) {
-        toDomain = getDestinationDomain(&toType, msg); // call only for requests
-    } else {
-        toDomain = getToDomain(&toType, msg);
-    }
-
-    clientIP = ip_addr2a(&msg->rcv.src_ip);
-
-    infolen = fromAddr.len + toAddr.len + fromTag.len + toTag.len + 64;
-
-    cmdlen = callId.len + strlen(clientIP) + fromDomain.len + toDomain.len +
-        userAgent.len*3 + infolen + 128;
-
-    for (i=0; i<streamCount; i++) {
-        stream = streams[i];
-        cmdlen += stream.ip.len + stream.port.len + stream.type.len + 4;
-    }
-
-    command = pkg_malloc(cmdlen);
-    if (!command) {
-        LOG(L_ERR, "error: use_media_proxy(): out of memory\n");
-        return -1;
-    }
-
-    if (request)
-        count = sprintf(command, "request %.*s", callId.len, callId.s);
-    else
-        count = sprintf(command, "lookup %.*s", callId.len, callId.s);
-
-    for (i=0, ptr=command+count; i<streamCount; i++) {
-        char c = (i==0 ? ' ' : ',');
-        count = sprintf(ptr, "%c%.*s:%.*s:%.*s", c,
-                        streams[i].ip.len, streams[i].ip.s,
-                        streams[i].port.len, streams[i].port.s,
-                        streams[i].type.len, streams[i].type.s);
-        ptr += count;
-    }
-
-    agent = encodeQuopri(userAgent);
-    if (!agent) {
-        LOG(L_ERR, "error: use_media_proxy(): out of memory\n");
-        pkg_free(command);
-        return -1;
-    }
-
-    info = pkg_malloc(infolen);
-    if (!info) {
-        LOG(L_ERR, "error: use_media_proxy(): out of memory\n");
-        pkg_free(command);
-        pkg_free(agent);
-        return -1;
-    }
-
-    sprintf(info, "from:%.*s,to:%.*s,fromtag:%.*s,totag:%.*s",
-            fromAddr.len, fromAddr.s, toAddr.len, toAddr.s,
-            fromTag.len, fromTag.s, toTag.len, toTag.s);
-    if (isRTPAsymmetric(userAgent)) {
-        strcat(info, ",asymmetric");
-    }
-
-    snprintf(ptr, command + cmdlen - ptr, " %s %.*s %s %.*s %s %s info=%s\n",
-             clientIP, fromDomain.len, fromDomain.s, fromType,
-             toDomain.len, toDomain.s, toType, agent, info);
-
-    pkg_free(info);
-    pkg_free(agent);
-
-    result = sendMediaproxyCommand(command);
-
-    pkg_free(command);
-
-    if (result == NULL)
-        return -1;
-
-    count = getTokens(result, tokens, sizeof(tokens)/sizeof(str));
-
-    if (count == 0) {
-        LOG(L_ERR, "error: use_media_proxy(): empty response from mediaproxy\n");
-        return -1;
-    } else if (count<streamCount+1) {
-        if (request) {
-            LOG(L_ERR, "error: use_media_proxy(): insufficient ports returned "
-                "from mediaproxy: got %d, expected %d\n", count-1, streamCount);
-            return -1;
-        } else {
-            LOG(L_WARN, "warning: use_media_proxy(): broken client. Called UA "
-                "added extra media stream(s) in the OK reply\n");
-        }
-    }
-
-    if (sessionIP.s && !isAnyAddress(sessionIP)) {
-        success = replaceElement(msg, &sessionIP, &tokens[0]);
-        if (!success) {
-            LOG(L_ERR, "error: use_media_proxy(): failed to replace "
-                "session-level media IP in SDP body\n");
-            return -1;
-        }
-    }
-
-    portCount = min(count-1, streamCount);
-
-    for (i=0; i<portCount; i++) {
-        // check. is this really necessary?
-        port = strtoint(&tokens[i+1]);
-        if (port <= 0 || port > 65535) {
-            LOG(L_ERR, "error: use_media_proxy(): invalid port returned "
-                "by mediaproxy: %.*s\n", tokens[i+1].len, tokens[i+1].s);
-            //return -1;
-            continue;
-        }
-
-        if (streams[i].port.len!=1 || streams[i].port.s[0]!='0') {
-            success = replaceElement(msg, &(streams[i].port), &tokens[i+1]);
-            if (!success) {
-                LOG(L_ERR, "error: use_media_proxy(): failed to replace "
-                    "port in media stream nr. %d\n", i+1);
-                return -1;
-            }
-        }
-
-        if (streams[i].localIP && !isAnyAddress(streams[i].ip)) {
-            success = replaceElement(msg, &(streams[i].ip), &tokens[0]);
-            if (!success) {
-                LOG(L_ERR, "error: use_media_proxy(): failed to replace "
-                    "IP address in media stream nr. %d\n", i+1);
-                return -1;
-            }
-        }
-    }
-
-    return 1;
-}
-
-
-/* Module management: initialization/destroy/function-parameter-fixing/... */
-
-static int
-mod_init(void)
-{
-    bind_usrloc_t ul_bind_usrloc;
-
-    if (natpingInterval > 0) {
-        ul_bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0);
-        if (!ul_bind_usrloc) {
-            LOG(L_ERR, "error: mediaproxy/mod_init(): can't find the usrloc "
-                "module. Check if usrloc.so is loaded.\n");
-            return -1;
-        }
-
-        if (ul_bind_usrloc(&userLocation) < 0) {
-            LOG(L_ERR, "error: mediaproxy/mod_init(): can't access the usrloc module.\n");
-            return -1;
-        }
-
-        register_timer(pingClients, NULL, natpingInterval);
-    }
-
-    checkAsymmetricFile(&sipAsymmetrics);
-    checkAsymmetricFile(&rtpAsymmetrics);
-
-    // children won't benefit from this. figure another way
-    //register_timer(checkAsymmetricFiles, NULL, 5);
-
-    return 0;
-}