Browse Source

added support for network io intercept

Muhammad Shahzad Shafi 11 years ago
parent
commit
0640f7cd97

+ 264 - 17
modules/corex/README

@@ -10,7 +10,13 @@ Daniel-Constantin Mierla
 
    <[email protected]>
 
-   Copyright © 2012 asipto.com
+Edited by
+
+Muhammad Shahzad Shafi
+
+   <[email protected]>
+
+   Copyright © 2012 asipto.com
      __________________________________________________________________
 
    Table of Contents
@@ -26,6 +32,9 @@ Daniel-Constantin Mierla
         3. Parameters
 
               3.1. alias_subdomains (string)
+              3.2. network_io_intercept (int)
+              3.3. msg_min_len (int)
+              3.4. msg_avp (string)
 
         4. Functions
 
@@ -33,6 +42,7 @@ Daniel-Constantin Mierla
               4.2. send([ host [ :port ] ])
               4.3. send_tcp([ host [ :port ] ])
               4.4. send_data(uri, data)
+              4.5. is_incoming()
 
         5. RPC Commands
 
@@ -41,12 +51,24 @@ Daniel-Constantin Mierla
               5.3. corex.shm_status
               5.4. corex.shm_summary
 
+        6. Event Routes
+
+              6.1. event_route[network:msg]
+
+        7. Examples of Usage
+
    List of Examples
 
    1.1. Set alias_subdomains parameter
-   1.2. append_branch usage
-   1.3. send usage
-   1.4. send_data usage
+   1.2. Set network_io_intercept parameter
+   1.3. Set msg_min_len parameter
+   1.4. Set msg_avp parameter
+   1.5. append_branch usage
+   1.6. send usage
+   1.7. send_data usage
+   1.8. is_incoming usage
+   1.9. event_route[network:msg] use cases
+   1.10. Sample PERL code for do_compress and do_uncompress
 
 Chapter 1. Admin Guide
 
@@ -61,6 +83,9 @@ Chapter 1. Admin Guide
    3. Parameters
 
         3.1. alias_subdomains (string)
+        3.2. network_io_intercept (int)
+        3.3. msg_min_len (int)
+        3.4. msg_avp (string)
 
    4. Functions
 
@@ -68,6 +93,7 @@ Chapter 1. Admin Guide
         4.2. send([ host [ :port ] ])
         4.3. send_tcp([ host [ :port ] ])
         4.4. send_data(uri, data)
+        4.5. is_incoming()
 
    5. RPC Commands
 
@@ -76,6 +102,12 @@ Chapter 1. Admin Guide
         5.3. corex.shm_status
         5.4. corex.shm_summary
 
+   6. Event Routes
+
+        6.1. event_route[network:msg]
+
+   7. Examples of Usage
+
 1. Overview
 
    This module provides reimplementation of a few very old functions that
@@ -89,6 +121,15 @@ Chapter 1. Admin Guide
    Contributions to this module must be done under the BSD license, to
    follow the requirements of the core contributions.
 
+   This module now also provides access to network input / output data
+   through event_route[network:msg]. The raw data received from a remote
+   host or about to be sent to a remote host is available in variable $mb.
+   The script writer may manipulate this data and save the final result in
+   an AVP defined by msg_avp module parameter. The content of this AVP
+   will then be processed by SIP worker as normal, i.e. a received message
+   will be parsed and sent to appropriate route block while a sent message
+   is forwarded to remote host.
+
 2. Dependencies
 
    2.1. Kamailio Modules
@@ -108,15 +149,18 @@ Chapter 1. Admin Guide
 3. Parameters
 
    3.1. alias_subdomains (string)
+   3.2. network_io_intercept (int)
+   3.3. msg_min_len (int)
+   3.4. msg_avp (string)
 
 3.1. alias_subdomains (string)
 
-   Register a domain and all its sub-domains to match the "myself"
+   Register a domain and all its sub-domains to match the “myself�
    condition. It can be set many times. Its full format is:
    'proto:domain:port', allowing to set restrictions on protocol and port
    as well. Protocol and port are optional.
 
-   Default value is "NULL".
+   Default value is “NULL�.
 
    Example 1.1. Set alias_subdomains parameter
 ...
@@ -124,14 +168,56 @@ modparam("corex", "alias_subdomains", "kamailio.org")
 modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 ...
 
+3.2. network_io_intercept (int)
+
+   If set to non-zero then raw data received from a remote host or about
+   to be sent to a remote host is made available in
+   event_route[network:msg]. The script writer may modify this and save to
+   msg_avp, which will then be processed by SIP worker as normal.
+
+   Default value is 0, i.e. do not allow access to network io data.
+
+   Example 1.2. Set network_io_intercept parameter
+...
+modparam("corex", "network_io_intercept", 1)
+...
+
+3.3. msg_min_len (int)
+
+   Minimum content length of the packet to execute the
+   event_route[network:msg]. This only works if network_io_intercept
+   parameter is set to non-zero.
+
+   Default value is 0.
+
+   Example 1.3. Set msg_min_len parameter
+...
+modparam("corex", "msg_min_len", 32)
+...
+
+3.4. msg_avp (string)
+
+   AVP name to store modified content to be set in the packet. If not set
+   in event_route[network:msg], then all changes are lost and original
+   contents are used. This only works if network_io_intercept parameter is
+   to set non-zero.
+
+   Default value is empty.
+
+   Example 1.4. Set msg_avp parameter
+...
+modparam("corex", "msg_avp", "$avp(msg)")
+...
+
 4. Functions
 
    4.1. append_branch([ uri, [ q ] ])
    4.2. send([ host [ :port ] ])
    4.3. send_tcp([ host [ :port ] ])
    4.4. send_data(uri, data)
+   4.5. is_incoming()
 
-4.1. append_branch([ uri, [ q ] ])
+4.1.  append_branch([ uri, [ q ] ])
 
    Append a new branch to the destination set, useful to build the set of
    destination addresses for parallel forking or redirect replies.
@@ -147,13 +233,13 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 
    This function can be used from REQUEST_ROUTE or FAILURE_ROUTE.
 
-   Example 1.2. append_branch usage
+   Example 1.5. append_branch usage
 ...
     append_branch();
     append_branch("$avp(uri)", "0.5");
 ...
 
-4.2. send([ host [ :port ] ])
+4.2.  send([ host [ :port ] ])
 
    Send the original SIP message to a specific destination in stateless
    mode. No changes are applied to received message, no Via header is
@@ -167,7 +253,7 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 
    This function can be used from REQUEST_ROUTE or FAILURE_ROUTE.
 
-   Example 1.3. send usage
+   Example 1.6. send usage
 ...
         send();
         send("10.20.15.10");
@@ -175,12 +261,12 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
         send("$var(res)");
 ...
 
-4.3. send_tcp([ host [ :port ] ])
+4.3.  send_tcp([ host [ :port ] ])
 
    This function is identical to send() described above, except that it
    sends the SIP message using the TCP protocol instead of UDP.
 
-4.4. send_data(uri, data)
+4.4.  send_data(uri, data)
 
    Send the data to address specified by uri. Both parameters can contain
    pseudo-variables. The uri parameter has to be a valid SIP URI. The data
@@ -188,11 +274,33 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 
    This function can be used from ANY_ROUTE.
 
-   Example 1.4. send_data usage
+   Example 1.7. send_data usage
 ...
         send("sip:example.com:5070;transport=sctp", "Message at $Ts");
 ...
 
+4.5.  is_incoming()
+
+   Returns true if contents of message buffer $mb are the data received
+   from remote host, otherwise false indicating that the contents of $mb
+   are data that is about to be sent out to remote host. This only works
+   if network_io_intercept parameter is set to non-zero.
+
+   This function can be used from event_route[network:msg].
+
+   Example 1.8. is_incoming usage
+...
+event_route[network:msg] {
+    if (is_incoming()) {
+        xlog("L_INFO", "Received message '$mb' \n");
+        $avp(msg) = $mb;
+    } else {
+        xlog("L_INFO", "Sending message '$mb' \n");
+        $avp(msg) = $mb;
+    };
+}
+...
+
 5. RPC Commands
 
    5.1. corex.list_sockets
@@ -200,30 +308,169 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
    5.3. corex.shm_status
    5.4. corex.shm_summary
 
-5.1. corex.list_sockets
+5.1.  corex.list_sockets
 
    Print the list of sockets the application is listening on.
 
    Example:
                 kamcmd corex.list_sockets
 
-5.2. corex.list_aliases
+5.2.  corex.list_aliases
 
    Print the list of hostname aliases used to match the myself condition.
 
    Example:
                 kamcmd corex.list_aliases
 
-5.3. corex.shm_status
+5.3.  corex.shm_status
 
    Trigger shm status dump to syslog.
 
    Example:
                 kamcmd corex.shm_status
 
-5.4. corex.shm_summary
+5.4.  corex.shm_summary
 
    Trigger shm summary dump to syslog.
 
    Example:
                 kamcmd corex.shm_summary
+
+6. Event Routes
+
+   6.1. event_route[network:msg]
+
+6.1.  event_route[network:msg]
+
+   Event route block to be executed when new data is received from network
+   or the data that is about to be sent to a remote host by a SIP worker
+   process.
+
+   The kamailio script writer can check which type of data triggered this
+   event route using is_incoming method.
+
+   After executing of this event route, if msg_avp was defined and set
+   then its value is used for further processing, otherwise, original
+   value of $mb is used. If message was received from remote host, then it
+   is parsed and proceeds to appropriate route. Oterhwise if message set
+   to send out, then is sent to remote host per configured SIP timers in
+   config script.
+
+   Please note this event route is meant to prepare the message for
+   on-wire communication, e.g. to do custom encryption or decryption,
+   compression/decompression etc. of the message sent to or received from
+   remote host. Therefore, except text operations, no module fucntions or
+   pseudo variables are available in this event route.
+
+7. Examples of Usage
+
+   To use network event_route[network:msg] the remote SIP UA must also
+   implement and understand the encoding / decoding done in this event
+   route. It is up to Kamailio config script writer to define and
+   implement how encoding and decoding is done. Any language module such
+   as app_perl or app_lua can be called in in event_route[network:msg] to
+   implement desired logic.
+
+   The most simple use case is to compress the SIP packet on-wire. As SIP
+   is a text based protocol, so it is highly compressable. Using this
+   module, one can compress entire SIP message, including headers and
+   message body before sending it to remote host using any compression
+   algorithm of choice, thus saving significant bandwidth on mobile data
+   networks.
+
+   A useful case is to use this function between SIP edge proxy and SIP
+   application server. The SIP messages received from end-user at SIP edge
+   proxy may be decrypted and sent to SIP application server at remote
+   location unencrypted, where they are processed as normal. One the way
+   back, the messages received from SIP application server at edge proxy
+   can be encrpyted before being sent to actual destination. The edge
+   proxy can check whether received message came from end-user or SIP
+   application server by using simple regular expressions.
+
+   Another use case is to implement a virtual HTTP tunnel for SIP
+   messages. The SIP client app can convert SIP message to binary e.g. by
+   doing XOR, Base64 etc., then prepend some fake HTTP headers to make it
+   look like an HTTP request before sending it to kamailio over SIP TCP
+   socket. At kamailio, the fake headers are removed and data is decoded
+   back to normal SIP and processed per config script logic. For the data
+   that is to be sent to SIP client app, one can prepend fake HTTP reply
+   headers to encoded data before sending it to client app.
+
+   More advance use cases may involve custom encryption algorithms such as
+   ITV encryption algorithm, https://github.com/mshary/itv
+
+   For example, the client app running on Android or iPhone, may send
+   device UUID along with ITV key, encrypted using RSA or AES256 with
+   pre-shared secret, as first packet, which is set as cookie by server in
+   e.g. memcache. This cookie is referred by client app in each next
+   packet, so server can retrive encyption key from cache and use that for
+   decryption. Same can be done for server at client app side, so messages
+   encrypted by server can be decrypted at client app.
+
+   Next is a basic usage example where encoding and decoding is done using
+   PERL,
+
+   Example 1.9. event_route[network:msg] use cases
+...
+loadmodule "app_perl.so"
+loadmodule "corex.so"
+...
+# ----- app_perl params -----
+modparam("app_perl", "filename", "/usr/local/etc/kamailio/custom_compress.pl")
+modparam("app_perl", "modpath", "/usr/local/lib64/kamailio/perl")
+
+# ----- corex params -----
+modparam("corex", "network_io_intercept", 32)
+modparam("corex", "min_msg_len", 32)
+modparam("corex", "msg_avp", "$avp(msg)")
+...
+event_route[network:msg] {
+        if (is_incoming()) {
+                if (perl_exec_simple("do_uncompress", "" + $mb + "")) {
+                        xlog("L_INFO", "Received message '$avp(msg)' \n");
+                } else {
+                        xlog("L_INFO", "Received message '$mb' \n");
+                        $avp(msg) = $mb;
+                };
+        } else {
+                xlog("L_INFO", "Sending message '$mb' \n");
+                if (!perl_exec_simple("do_compress", "" + $mb + "")) {
+                        $avp(msg) = $mb;
+                };
+        };
+}
+...
+
+   Example 1.10. Sample PERL code for do_compress and do_uncompress
+...
+use strict;
+use warnings;
+use IO::Compress::Gzip qw(gzip $GzipError) ;
+use IO::Uncompress::Gunzip qw(gunzip $GunzipError) ;
+
+sub do_compress() {
+        my $input = shift;
+        my $output;
+
+        gzip \$input => \$output
+                or eval {
+                        Kamailio::log(L_WARN, "GZIP failed: $GzipError\n");
+                        $output = $input;
+                };
+
+        Kamailio::AVP::add("msg", $output);
+}
+
+sub do_uncompress() {
+        my $input = shift;
+        my $output;
+
+        gunzip \$input => \$output
+                or eval {
+                        Kamailio::log(L_WARN, "GUNZIP failed: $GzipError\n");
+                        $output = $input;
+                };
+
+        Kamailio::AVP::add("msg", $output);
+}
+...

+ 15 - 2
modules/corex/corex_mod.c

@@ -31,9 +31,11 @@
 #include "corex_lib.h"
 #include "corex_rpc.h"
 #include "corex_var.h"
+#include "corex_nio.h"
 
 MODULE_VERSION
 
+static int nio_intercept = 0;
 static int w_append_branch(sip_msg_t *msg, char *su, char *sq);
 static int w_send(sip_msg_t *msg, char *su, char *sq);
 static int w_send_tcp(sip_msg_t *msg, char *su, char *sq);
@@ -69,14 +71,19 @@ static cmd_export_t cmds[]={
 			0, REQUEST_ROUTE | FAILURE_ROUTE },
 	{"send_data", (cmd_function)w_send_data, 2, fixup_spve_spve,
 			0, ANY_ROUTE },
-
+	{"is_incoming",    (cmd_function)nio_check_incoming, 0, 0,
+    	    0, ANY_ROUTE },
 
 	{0, 0, 0, 0, 0, 0}
 };
 
 static param_export_t params[]={
-	{"alias_subdomains",  PARAM_STRING|USE_FUNC_PARAM,
+	{"alias_subdomains",		STR_PARAM|USE_FUNC_PARAM,
 								(void*)corex_alias_subdomains_param},
+    {"network_io_intercept",	INT_PARAM, &nio_intercept},
+    {"min_msg_len",				INT_PARAM, &nio_min_msg_len},
+    {"msg_avp",			  		PARAM_STR, &nio_msg_avp_param},
+
 	{0, 0, 0}
 };
 
@@ -114,6 +121,12 @@ static int mod_init(void)
 		return -1;
 	}
 
+	if((nio_intercept > 0) && (nio_intercept_init() < 0))
+	{
+		LM_ERR("failed to register network io intercept callback\n");
+		return -1;
+	}
+
 	return 0;
 }
 

+ 199 - 0
modules/corex/corex_nio.c

@@ -0,0 +1,199 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 SIP-Router.org
+ *
+ * This file is part of Extensible SIP Router, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "corex_nio.h"
+
+/**
+ * init nio function
+ */
+int nio_intercept_init(void)
+{
+	int route_no;
+	pv_spec_t avp_spec;
+
+	route_no=route_get(&event_rt, "network:msg");
+
+	if (route_no==-1)
+	{
+		LM_ERR("failed to find event_route[network:msg]\n");
+		return -1;
+	}
+
+	if (event_rt.rlist[route_no]==0)
+	{
+		LM_ERR("event_route[network:msg] is empty\n");
+		return -1;
+	}
+
+	nio_route_no=route_no;
+	
+	if (nio_min_msg_len < 0)
+	{
+		LM_WARN("min_msg_len is less then zero, setting it to zero");
+		nio_min_msg_len = 0;
+	}
+
+	if (nio_msg_avp_param.s && nio_msg_avp_param.len > 0) 
+	{
+		if (pv_parse_spec(&nio_msg_avp_param, &avp_spec)==0
+				|| avp_spec.type!=PVT_AVP)
+		{
+			LM_ERR("malformed or non AVP %.*s AVP definition\n",
+					nio_msg_avp_param.len, nio_msg_avp_param.s);
+			return -1;
+		}
+
+		if(pv_get_avp_name(0, &(avp_spec.pvp), &nio_msg_avp_name,
+					&nio_msg_avp_type)!=0)
+		{
+			LM_ERR("[%.*s]- invalid AVP definition\n",
+					nio_msg_avp_param.len, nio_msg_avp_param.s);
+			return -1;
+		}
+	} else {
+		LM_WARN("no AVP defined to store modified message\n");
+	}
+
+    /* register network hooks */
+    sr_event_register_cb(SREV_NET_DATA_IN, nio_msg_received);
+    sr_event_register_cb(SREV_NET_DATA_OUT, nio_msg_sent);
+#ifdef USE_TCP
+    tcp_set_clone_rcvbuf(1);
+#endif
+    return 0;
+}
+
+/**
+ *
+ */
+int nio_msg_received(void *data)
+{
+    sip_msg_t msg;
+    str *obuf;
+    char *nbuf = NULL;
+    int_str avp_value;
+    struct usr_avp *avp;
+    struct run_act_ctx ra_ctx;
+
+    obuf = (str*)data;
+
+    if (obuf->len < nio_min_msg_len) {
+        return -1;
+    }
+
+    memset(&msg, 0, sizeof(sip_msg_t));
+    msg.buf = obuf->s;
+    msg.len = obuf->len;
+
+    nio_is_incoming = 1;
+    init_run_actions_ctx(&ra_ctx);
+    run_actions(&ra_ctx, event_rt.rlist[nio_route_no], &msg);
+
+    if(nio_msg_avp_name.n!=0) {
+        avp = NULL;
+        avp=search_first_avp(nio_msg_avp_type, nio_msg_avp_name,
+            &avp_value, 0);
+        if(avp!=NULL && is_avp_str_val(avp)) {
+            msg.buf = avp_value.s.s;
+            msg.len = avp_value.s.len;
+            nbuf = nio_msg_update(&msg, (unsigned int*)&obuf->len);
+            if(obuf->len>=BUF_SIZE) {
+                LM_ERR("new buffer overflow (%d)\n", obuf->len);
+                pkg_free(nbuf);
+                return -1;
+            }
+            memcpy(obuf->s, nbuf, obuf->len);
+            obuf->s[obuf->len] = '\0';
+        } else {
+            LM_WARN("no value set for AVP %.*s, using unmodified message\n",
+                nio_msg_avp_param.len, nio_msg_avp_param.s);
+        }
+    }
+
+    if(nbuf!=NULL)
+        pkg_free(nbuf);
+    free_sip_msg(&msg);
+    return 0;
+}
+
+/**
+ *
+ */
+int nio_msg_sent(void *data)
+{
+    sip_msg_t msg;
+    str *obuf;
+    int_str avp_value;
+    struct usr_avp *avp;
+    struct run_act_ctx ra_ctx;
+
+    obuf = (str*)data;
+
+    if (obuf->len < nio_min_msg_len) {
+        return -1;
+    }
+
+    memset(&msg, 0, sizeof(sip_msg_t));
+    msg.buf = obuf->s;
+    msg.len = obuf->len;
+
+    nio_is_incoming = 0;
+    init_run_actions_ctx(&ra_ctx);
+    run_actions(&ra_ctx, event_rt.rlist[nio_route_no], &msg);
+
+    if(nio_msg_avp_name.n!=0) {
+        avp = NULL;
+        avp=search_first_avp(nio_msg_avp_type, nio_msg_avp_name,
+                &avp_value, 0);
+        if(avp!=NULL && is_avp_str_val(avp)) {
+            msg.buf = avp_value.s.s;
+            msg.len = avp_value.s.len;
+            obuf->s = nio_msg_update(&msg, (unsigned int*)&obuf->len);
+        } else {
+            LM_WARN("no value set for AVP %.*s, using unmodified message\n",
+                nio_msg_avp_param.len, nio_msg_avp_param.s);
+        }
+    }
+
+    free_sip_msg(&msg);
+    return 0;
+}
+
+/**
+ *
+ */
+int nio_check_incoming(void)
+{
+    return (nio_is_incoming) ? 1 : -1;
+}
+
+/**
+ *
+ */
+char* nio_msg_update(sip_msg_t *msg, unsigned int *olen)
+{
+    struct dest_info dst;
+
+    init_dest_info(&dst);
+    dst.proto = PROTO_UDP;
+    return build_req_buf_from_sip_req(msg,
+            olen, &dst, BUILD_NO_LOCAL_VIA|BUILD_NO_VIA1_UPDATE);
+}
+

+ 53 - 0
modules/corex/corex_nio.h

@@ -0,0 +1,53 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009 SIP-Router.org
+ *
+ * This file is part of Extensible SIP Router, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _COREX_NIO_H_
+#define _COREX_NIO_H_
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../events.h"
+#include "../../ut.h"
+
+#include "../../tcp_options.h"
+#include "../../msg_translator.h"
+
+int nio_route_no;
+int nio_min_msg_len;
+int nio_is_incoming;
+
+str nio_msg_avp_param;
+int_str nio_msg_avp_name;
+unsigned short nio_msg_avp_type;
+
+int nio_msg_received(void *data);
+int nio_msg_sent(void *data);
+
+int nio_check_incoming(void);
+char* nio_msg_update(sip_msg_t *msg, unsigned int *olen);
+
+int nio_intercept_init(void);
+
+#endif

+ 5 - 0
modules/corex/doc/corex.xml

@@ -23,6 +23,11 @@
 		<surname>Mierla</surname>
 		<email>[email protected]</email>
 	    </editor>
+	    <editor>
+		<firstname>Muhammad Shahzad</firstname>
+		<surname>Shafi</surname>
+		<email>[email protected]</email>
+	    </editor>
 	</authorgroup>
 	<copyright>
 	    <year>2012</year>

+ 257 - 0
modules/corex/doc/corex_admin.xml

@@ -29,6 +29,15 @@
 		Contributions to this module must be done under the BSD license, to
 		follow the requirements of the core contributions.
 	</para>
+	<para>
+		This module now also provides access to network input / output data through
+		event_route[network:msg]. The raw data received from a remote host or about to
+		be sent to a remote host is available in variable $mb. The script writer may
+		manipulate this data and save the final result in an AVP defined by msg_avp
+		module parameter. The content of this AVP will then be processed by SIP worker
+		as normal, i.e. a received message will be parsed and sent to appropriate
+		route block while a sent message is forwarded to remote host.
+	</para>
 	</section>
 
 	<section>
@@ -87,6 +96,69 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 </programlisting>
 	    </example>
 	</section>
+    <section>
+        <title><varname>network_io_intercept</varname> (int)</title>
+        <para>
+            If set to non-zero then raw data received from a remote host or about to
+			be sent to a remote host is made available in event_route[network:msg].
+			The script writer may modify this and save to msg_avp, which will then
+			be processed by SIP worker as normal.
+        </para>
+        <para>
+        <emphasis>
+            Default value is 0, i.e. do not allow access to network io data.
+        </emphasis>
+        </para>
+        <example>
+        <title>Set <varname>network_io_intercept</varname> parameter</title>
+        <programlisting format="linespecific">
+...
+modparam("corex", "network_io_intercept", 1)
+...
+</programlisting>
+        </example>
+    </section>
+    <section>
+        <title><varname>msg_min_len</varname> (int)</title>
+        <para>
+            Minimum content length of the packet to execute the event_route[network:msg].
+			This only works if network_io_intercept parameter is set to non-zero.
+        </para>
+        <para>
+        <emphasis>
+            Default value is 0.
+        </emphasis>
+        </para>
+        <example>
+        <title>Set <varname>msg_min_len</varname> parameter</title>
+        <programlisting format="linespecific">
+...
+modparam("corex", "msg_min_len", 32)
+...
+</programlisting>
+        </example>
+    </section>
+    <section>
+        <title><varname>msg_avp</varname> (string)</title>
+        <para>
+            AVP name to store modified content to be set in the packet. If not set in
+            event_route[network:msg], then all changes are lost and original contents
+            are used. This only works if network_io_intercept parameter is to set non-zero.
+        </para>
+        <para>
+        <emphasis>
+            Default value is empty.
+        </emphasis>
+        </para>
+        <example>
+        <title>Set <varname>msg_avp</varname> parameter</title>
+        <programlisting format="linespecific">
+...
+modparam("corex", "msg_avp", "$avp(msg)")
+...
+</programlisting>
+        </example>
+    </section>
 	</section>
 
 	<section>
@@ -200,6 +272,36 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 </programlisting>
 		</example>
 	</section>
+    <section>
+		<title>
+			<function moreinfo="none">is_incoming()</function>
+		</title>
+        <para>
+            Returns true if contents of message buffer $mb are the data received from
+            remote host, otherwise false indicating that the contents of $mb are data
+            that is about to be sent out to remote host. This only works if 
+			network_io_intercept parameter is set to non-zero.
+        </para>
+        <para>
+        This function can be used from event_route[network:msg].
+        </para>
+        <example>
+        <title><function>is_incoming</function> usage</title>
+        <programlisting format="linespecific">
+...
+event_route[network:msg] {
+    if (is_incoming()) {
+        xlog("L_INFO", "Received message '$mb' \n");
+        $avp(msg) = $mb;
+    } else {
+        xlog("L_INFO", "Sending message '$mb' \n");
+        $avp(msg) = $mb;
+    };
+}
+...
+</programlisting>
+        </example>
+    </section>
 
 	</section>
 
@@ -263,5 +365,160 @@ modparam("corex", "alias_subdomains", "udp:sip-router.org:5060")
 		</programlisting>
     </section>
     </section>
+
+    <section>
+    <title>Event Routes</title>
+    <section id="async.evr.network_io">
+        <title>
+        <function moreinfo="none">event_route[network:msg]</function>
+        </title>
+        <para>
+            Event route block to be executed when new data is received from network
+            or the data that is about to be sent to a remote host by a SIP worker
+            process.
+        </para>
+        <para>
+            The kamailio script writer can check which type of data triggered this
+            event route using is_incoming method.
+        </para>
+        <para>
+            After executing of this event route, if msg_avp was defined and set then
+            its value is used for further processing, otherwise, original value of
+            $mb is used. If message was received from remote host, then it is parsed
+            and proceeds to appropriate route. Oterhwise if message set to send out,
+            then is sent to remote host per configured SIP timers in config script.
+        </para>
+        <para>
+            Please note this event route is meant to <emphasis>prepare</emphasis>
+            the message for on-wire communication, e.g. to do custom encryption or
+            decryption, compression/decompression etc. of the message sent to or 
+			received from remote host. Therefore, except text operations,
+			no module fucntions or pseudo variables are available in this event route.
+        </para>
+    </section>
+    </section>
+
+    <section>
+    <title>Examples of Usage</title>
+        <para>
+			To use network event_route[network:msg] the remote SIP UA must also implement and 
+			understand the encoding / decoding done in this event route. It is up to  &kamailio;
+			config script writer to define and implement how encoding and decoding is done. 
+			Any language module such as app_perl or app_lua can be called in
+			in event_route[network:msg] to implement desired logic.
+        </para>
+        <para>
+			The most simple use case is to compress the SIP packet on-wire. As SIP is a
+			text based protocol, so it is highly compressable. Using this module, one can
+			compress entire SIP message, including headers and message body before sending
+			it to remote host using any compression algorithm of choice, thus saving
+			significant bandwidth on mobile data networks.
+        </para>
+        <para>
+			A useful case is to use this function between SIP edge proxy and SIP application
+			server. The SIP messages received from end-user at SIP edge proxy may be decrypted
+			and sent to SIP application server at remote location unencrypted, where they are
+			processed as normal. One the way back, the messages received from SIP application
+			server at edge proxy can be encrpyted before being sent to actual destination.
+			The edge proxy can check whether received message came from end-user or SIP 
+			application server by using simple regular expressions.
+        </para>
+        <para>
+			Another use case is to implement a virtual HTTP tunnel for SIP messages. The SIP
+			client app can convert SIP message to binary e.g. by doing XOR, Base64 etc., then
+			prepend some fake HTTP headers to make it look like an HTTP request before sending
+			it to kamailio over SIP TCP socket. At kamailio, the fake headers are removed and data
+			is decoded back to normal SIP and processed per config script logic. For the data that
+			is to be sent to SIP client app, one can prepend fake HTTP reply headers to encoded
+			data before sending it to client app.
+        </para>
+        <para>
+			More advance use cases may involve custom encryption algorithms such as
+			ITV encryption algorithm,
+			<ulink url="https://github.com/mshary/itv"></ulink>
+        </para>
+        <para>
+			For example, the client app running on Android or iPhone, may send device UUID along
+			with ITV key, encrypted using RSA or AES256 with pre-shared secret, as first packet,
+			which is set as cookie by server in e.g. memcache. This cookie is referred by client
+			app in each next packet, so server can retrive encyption key from cache and use that
+			for decryption. Same can be done for server at client app side, so messages encrypted
+			by server can be decrypted at client app.
+        </para>
+        <para>
+			Next is a basic usage example where encoding and decoding is done using PERL,
+        </para>
+        <example>
+        <title><function>event_route[network:msg]</function> use cases</title>
+        <programlisting format="linespecific">
+...
+loadmodule "app_perl.so"
+loadmodule "corex.so"
+...
+# ----- app_perl params -----
+modparam("app_perl", "filename", "/usr/local/etc/kamailio/custom_compress.pl")
+modparam("app_perl", "modpath", "/usr/local/lib64/kamailio/perl")
+
+# ----- corex params -----
+modparam("corex", "network_io_intercept", 32)
+modparam("corex", "min_msg_len", 32)
+modparam("corex", "msg_avp", "$avp(msg)")
+...
+event_route[network:msg] {
+	if (is_incoming()) {
+		if (perl_exec_simple("do_uncompress", "" + $mb + "")) {
+			xlog("L_INFO", "Received message '$avp(msg)' \n");
+		} else {
+			xlog("L_INFO", "Received message '$mb' \n");
+			$avp(msg) = $mb;
+		};
+	} else {
+		xlog("L_INFO", "Sending message '$mb' \n");
+		if (!perl_exec_simple("do_compress", "" + $mb + "")) {
+			$avp(msg) = $mb;
+		};
+	};
+}
+...
+</programlisting>
+        </example>
+        <example>
+        <title>Sample PERL code for do_compress and do_uncompress</title>
+        <programlisting format="linespecific">
+...
+use strict;
+use warnings;
+use IO::Compress::Gzip qw(gzip $GzipError) ;
+use IO::Uncompress::Gunzip qw(gunzip $GunzipError) ;
+
+sub do_compress() {
+	my $input = shift;
+	my $output;
+
+	gzip \$input => \$output
+		or eval {
+			Kamailio::log(L_WARN, "GZIP failed: $GzipError\n");
+			$output = $input;
+		};
+
+	Kamailio::AVP::add("msg", $output);
+}
+
+sub do_uncompress() {
+	my $input = shift;
+	my $output;
+
+	gunzip \$input => \$output
+		or eval {
+			Kamailio::log(L_WARN, "GUNZIP failed: $GzipError\n");
+			$output = $input;
+		};
+
+	Kamailio::AVP::add("msg", $output);
+}
+...
+</programlisting>
+        </example>
+    </section>
 </chapter>