#!/usr/bin/perl

# ====================================================================
# The Vovida Software License, Version 1.0 
# 
# Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. The names "VOCAL", "Vovida Open Communication Application Library",
#    and "Vovida Open Communication Application Library (VOCAL)" must
#    not be used to endorse or promote products derived from this
#    software without prior written permission. For written
#    permission, please contact vocal\@vovida.org.
# 
# 4. Products derived from this software may not be called "VOCAL", nor
#    may "VOCAL" appear in their name, without prior written
#    permission of Vovida Networks, Inc.
# 
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
# NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
# NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
# IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
# 
# ====================================================================
# 
# This software consists of voluntary contributions made by Vovida
# Networks, Inc. and many individuals on behalf of Vovida Networks,
# Inc.  For more information on Vovida Networks, Inc., please see
# <http://www.vovida.org/>.

# $Id: proxyudp-telogy,v 1.13 2000/08/12 01:25:04 bogawa Exp $

# this version of proxyudp proxies correctly for telogy MGCP gateway v 6.1

BEGIN {
push(@INC, "../contrib/perl/Time-HiRes-01.20/blib/lib", "../contrib/perl/Time-HiRes-01.20/blib/arch/auto/Time/HiRes");
}

use Socket;
require "getopts.pl";
#use Time::HiRes qw(gettimeofday tv_interval);

&Getopts("n:d");

if($opt_d) { 
    $debug = 1;
}
$number = 1;
if($opt_n) {
    $number = $opt_n;
}

# if($#ARGV < 1) {
#     print "Usage:\n";
#     print "$0 host1:<local_port1>:<remote_port1> host2:<local_port2>:<remote_port2> host3:<local_port3>:<remote_port3>\n";
#     exit(-1);
# }

if($ARGV[0]) {
   $agent_host = $ARGV[0];
} else {
   chomp($agent_host = `hostname`);
}
$unique_internal = 0;

$flash_time = .5;

print "Agent is at $agent_host\n";

$dest{"o_agent1"} = "$agent_host:5011";
$dest{"o_agent2"} = "$agent_host:5012";
$dest{"o_agent3"} = "$agent_host:5013";
$dest{"o_agent4"} = "$agent_host:5014";

$dest{"o_gw1"} = "192.168.5.180:2427";
$dest{"o_gw2"} = "192.168.5.182:2427";


$src{"i_agent1"} = "5051";
$src{"i_agent2"} = "5052";
$src{"i_agent3"} = "5053";
$src{"i_agent4"} = "5054";

$src{"i_gw"} = "2427";

$map{"i_gw"} = "";
$map{"i_agent1"} = "o_gw1";
$map{"i_agent2"} = "o_gw1";
$map{"i_agent3"} = "o_gw2";
$map{"i_agent4"} = "o_gw2";

$rewrite{"i_gw"} = \&rewrite_S2;
$rewrite{"i_agent1"} = \&rewrite_S1;
$rewrite{"i_agent2"} = \&rewrite_S1;
$rewrite{"i_agent3"} = \&rewrite_S1;
$rewrite{"i_agent4"} = \&rewrite_S1;

$rewrite{"rtp1"} = &rewrite_rtp;
$rewrite{"rtp2"} = &rewrite_rtp;
$rewrite{"rtp3"} = &rewrite_rtp;
$rewrite{"rtp4"} = &rewrite_rtp;
$rewrite{"rtp5"} = &rewrite_rtp;

$fake{"rtp1"} = "rtp2";
$fake{"rtp2"} = "rtp1";

$fake{"i_agent1"} = "i_gw";
$fake{"i_agent2"} = "i_gw";
$fake{"i_agent3"} = "i_gw";
$fake{"i_agent4"} = "i_gw";
$fake{"i_gw"} = "i_agent1";
#$fake{"junk_1"} = "i_agent1";
#$fake{"junk_2"} = "i_agent2";
#$fake{"junk_3"} = "i_agent3";
#$fake{"junk_4"} = "i_agent4";

$terminal_name[1] = 'aaln/1@gw1.vovida.com';
$terminal_name[2] = 'aaln/2@gw1.vovida.com';
$terminal_name[3] = 'aaln/1@gw2.vovida.com';
$terminal_name[4] = 'aaln/2@gw2.vovida.com';



foreach(sort keys %src) {
    $FROM{$_} = $_;
    &mksocket($src{$_}, $FROM{$_});
}

sub rewrite_rtp {
    return $_[0];
}

foreach(sort keys %dest) {
    &make_dest($_);
}

sub make_dest {
    local($x) = $_[0];
    local($host, $port, $name, $aliasese, $type, $len, $host_addr, $sockaddr);

    ($host, $port) = split(/:/, $dest{$x});
    ($name,$aliases,$type,$len,$host_addr) = gethostbyname($host);
    $sockaddr = "S n a4 x8";
    $TO{$x} = pack($sockaddr, PF_INET, $port, $host_addr);
    &debug( "TO: $x : $TO{$x}\n");
}


sub mksocket {
    local($port, $socket) = @_;
    
    $sockaddr = "S n a4 x8";
    ($name,$aliases,$proto) = getprotobyname('udp');
    chomp($hostname  = `hostname`);
    ($name,$aliases,$type,$len,$myaddr) = gethostbyname($hostname);
    $me = pack($sockaddr,PF_INET, $port, $myaddr);
    
    &debug( "myaddr: $hostname\n");
    &debug( "port: $port\n");
    
    socket($socket, PF_INET, SOCK_DGRAM, $proto) || die $!;
    bind($socket , $me) || die "died with port $port $!";
    
}


while(1) {
    $rin = '';
    foreach(sort keys %FROM) {
	vec($rin, fileno($FROM{$_}), 1) = 1;
    }

    if(select($rin, undef, undef, 0.25)) {
	@bits = split(//, unpack("b*", $rin));
	undef($data);
	undef($new_data);
	$flag = 0;
	foreach(sort keys %FROM) {
	    if($flag == 0 && $bits[fileno($FROM{$_})]) {
#		&debug( "got $FROM{$_}, target $target\n");
		$data = "";
		$his_address = recv($FROM{$_}, $data, 60000, 0);
		&send_msg($_, $data);
		&dequeue();
		$flag = 1;
	    }
	}
    }
    &handle_timeouts();
}


sub handle_timeouts {

    for($i = 1; $i <= 4; $i++) {
	if($hookflash_timestamp[$i]) {
#	    print "a: $hookflash_timestamp[$i]\n";
	    $b = gettimeofday;
#	    print "b: $b\n";
	    $elapsed = $b - $hookflash_timestamp[$i];
	    print "e: $elapsed\n";
	    if($elapsed > $flash_time) {
		# longer than a second
		&enqueue("i_gw", "fake $in_hookflash[$i]");
		$in_hookflash[$i]  = "";
		$hookflash_timestamp[$i] = "";
	    }
	}
	
    }
    &dequeue();
}

sub renumber_S2 {
    local($_) = $_[0];
    
    s/^([0-9]*)\s([0-9]*)\s/$1 $map{$2} /;
    $map{$number} = $2;
    
    
    return $_;
}

sub renumber_S1 {
    local($_) = $_[0];
    if(s/^([a-zA-Z]*)\s([0-9]*)\s/$1 $number /) {
	$map{$number} = $2;
	$number++;
    }
    return $_;
}

sub send_msg {
    local($_, $data) = @_;

    local($target, $myfake, $new_data);

    $target = $map{$FROM{$_}};
    $myfake = $fake{$_};
    if($data) {
	&debug( "======== ORIGINAL ===========\n");
	&debug( "$data");
	&debug( "-------- REWRITING ----------\n");
	$new_data = &{$rewrite{$_}}($data);
	if($new_data) {
	    &debug( "----------- NEW -------------\n");
	    &debug( "$new_data");
	    &debug( "--------- SENDING -----------\n");
	    &debug( "RECV:     $_ ($src{$_})\n");
            &debug( "SENDFROM: $FROM{$myfake} $_ ($src{$FROM{$myfake}})\n");
            &debug( "SENDTO:   $target ($dest{$target})\n");
	    if($target) {
		defined(send($FROM{$myfake},$new_data,0,$TO{$target}))  or die "errno= $? $!";
	    }
	    &debug( "=============================\n");
        
	}
    }
}

sub enqueue {
    local($_, $data) = @_;
    &debug( "enqueuing $_\n");
    push(@my_target, $_);
    push(@my_data, $data);
};

sub dequeue {
    local($x, $y);
    while($x = shift(@my_target)) {
	$y = shift(@my_data);
	&debug( "dequeuing $x\n");

	&send_msg($x, $y);
    }
}

sub mini_send {
    local($new_data, $myfake, $target);
    send($FROM{$myfake},$new_data,0,$TO{$target});
}

sub rewrite_S1 {
    # convert from the agent to the gateway

    local($_) = $_[0];

    s/aaln_/aaln\//;

    $is_fake = 0;

    if(/^fake [a-zA-Z]+ ([0-9]+)/i) {
	$fake{$1} = 1;
	$is_fake = 1;
	s/^fake //;
    }
    
    if(/^200 ([0-9]+)/i) {
	if($fake1{$1}) {
	    $fake1{$1} = 0;
	    $_ = "";
	    &debug( "deletia\n");
	}
    }
    

    if(/^([a-z]{4,4})\s+([0-9]+)/i) {
	$x = $2;
	&debug("agent -> gw  [TID: $x]\n");
       if(/aaln\/1\@gw1\.vovida\.com/) {
	    $terminal = "1";
	    $host1{$x} = "o_agent1";
	    $target = "o_gw1";
	}
    	if(/aaln\/2\@gw1\.vovida\.com/) {
	    $terminal = "2";

	    $host1{$x} = "o_agent2";
	    $target = "o_gw1";
	}
    	if(/aaln\/1\@gw2\.vovida\.com/) {
	    $terminal = "3";

	    $host1{$x} = "o_agent3";
	    $target = "o_gw2";
	}
    	if(/aaln\/2\@gw2\.vovida\.com/) {
	    $terminal = "4";

	    $host1{$x} = "o_agent4";
	    $target = "o_gw2";
	}
    }
    
    s/auep/AUEP/;
    s/rqnt/RQNT/;
    s/crcx/CRCX/;
    s/mdcx/MDCX/;
    s/dlcx/DLCX/;
    s/mgcp/MGCP/;

    s/\nr:/\nR:/;
    s/\nx:/\nX:/;
    
  #  s/MGCP 0\.1/MGCP 0\.1 NCS 1\.0/;
    
#    s/testID/aaln\/1\@vovida\.com/;
    s/d\/x\(d\)/\[0-9\*\#T\]\(D\), \*(D), \#(D), T\(N\)/;
    s/d\/x\(n\)/\[0-9\*\#\]\(N\)/;
    s/\nd:.*/\nD: (xxxx)/;
    s/\,/, /g;
#    s/l\/hf\(n\)\, //;
#    s/l\/hu\(n\), l\/hf\(n\)\, /l\/hf\(n\), [T]\(n\), T\(n\), l\/hu\(n\), /;
#    s/l\/hu\(n\), l\/hf\(n\)\, /hf\(n\), /;

#    s/l\/hu\(n\), l\/hf\(n\).*/l\/hf\(n\)/;

    if($use_hookflash) 
  {
    #hookflash implementation

    if(!$is_fake) {
	if(/l\/hf\(n\)/) {
	    &debug("hookflash active\n"); 
	    $hookflash[$terminal] = 1;
	    $in_hookflash[$terminal] = "";
	    $hookflash_timestamp[$terminal] = "";

	} elsif (/^rqnt/i) {
	    &debug("hookflash off\n"); 
	    $hookflash[$terminal] = 0;
	    $in_hookflash[$terminal] = "";
	    $hookflash_timestamp[$terminal] = "";

	}
    }
  }


    s/\nL:.*/\nL: p:20, a:G.711u, e:on, s:off/;

    if(s/(\nv=0[\000-\377]*)$//) {
        $sdp = $1;
        $sdp =~ s/demo5/192.168.5.105/g;
	$sdp =~ s/lab3.private.vovida.com/192.168.5.131/g;
	$sdp =~ s/lab4.private.vovida.com/192.168.5.132/g;
	$_ .= $sdp;
    }

    if(!/\nv\=0/) {
	s/\nM: sendrecv/\nM: recvonly/;
    }

    if(/\nv=0/ && !/\na=/ && !/^crcx/i && !/^mdcx/i) {
	s/\nv=0/\n\nv=0/;
	s/=in ip4/=IN IP4/g;
	s/(\nm=.*\n)/$1a=ptime:20\na=recvonly\n/;
    } elsif(/\nv=0/ && !/\na=/ && /^crcx/i) {
	s/\nv=0/\n\nv=0/;
    	s/(\nm=.*\n)/$1a=rtpmap:96 G.711u\n/;
	s/=in ip4/=IN IP4/g;
    } elsif(/\nv=0/ && !/\na=/ && /^mdcx/i) {
	s/\nv=0/\n\nv=0/;
    	s/(\nm=.*\n)/$1a=rtpmap:96 G.711u\n/;
	s/=in ip4/=IN IP4/g;
	s/\ns=.*//;
	s/\no=.*//;
    }



    if(/^([0-9]*) ([0-9]*)/) {
	if($fake{$2}) {
	    &debug( " xx fake\n");
	    $_ = "";
	}
	$target = $host{$2};
    }

    $_ = &renumber_S1($_);
    return $_;
}


sub rewrite_S2 {
    # convert from the gateway to the agent

    local($_) = $_[0];
    local($nosend) = 0;
    
    $_ = &renumber_S2($_);

    $is_fake = 0;
    
    if(/^fake [a-zA-Z]+ ([0-9]+)/i) {
	$fake1{$1} = 1;
 	$is_fake = 1;
	s/^fake //;
    }

    if(/call per endpoint/i) {
        print "Telogy box requires refresh\n";
    }

    if(/^200 ([0-9]+)/i) {
	if($fake{$1}) {
	    $fake{$1} = 0;
	    $_ = "";
	}
    }
    
    if(s/^nosend //) {
	$nosend = 1;
    }
    

   if(s/aaln\/\*\@gw1.vovida\.com/aaln\/1\@gw1.vovida.com/i) {
	&debug( "matched\n");
    }

   if(s/aaln\/\*\@gw2.vovida\.com/aaln\/1\@gw2.vovida.com/i) {
	&debug( "matched\n");
    }
    s/\no:.*\*/\no: d\/*/i;
    s/\no:.*\#/\no: d\/\#/i;

    if(/^([a-z]{4,4})\s+([0-9]+)/i) {
	$x = $2;
	$transaction_id = $x;
	&debug("gw -> agent  [TID: $x]\n");
        if(/aaln\/1\@gw1\.vovida\.com/) {
	    &debug( "RECEIVED for 1\@gw1: $x\n");
	    $terminal = 1;
	    $host{$x} = "o_gw1";
	    $target = "o_agent1";
	    $myfake = "i_agent1";
	}
	if(/aaln\/2\@gw1\.vovida\.com/) {
	    &debug( "RECEIVED for 2\@gw1: $x\n");
	    $terminal = 2;
 	    $host{$x} = "o_gw1";
	    $target = "o_agent2";
	    $myfake = "i_agent2";
	}
	if(/aaln\/1\@gw2\.vovida\.com/) {
	    &debug( "RECEIVED for 1\@gw2: $x\n");
	    $terminal = 3;
	    $host{$x} = "o_gw2";
	    $target = "o_agent3";
	    $myfake = "i_agent3";
	}
	if(/aaln\/2\@gw2\.vovida\.com/) {
	    &debug( "RECEIVED for 2\@gw2: $x\n");
	    $terminal = 4;
	    $host{$x} = "o_gw2";
	    $target = "o_agent4";
	    $myfake = "i_agent4";
	}
    }

#    s/ NCS 1\.0//;
    s/\ni:(.*)/\nI: $1/i;

    if(/^([0-9]*) ([0-9]*)/) {
	if($fake{$2}) {
	    &debug( " xx fake\n");
	    $_ = "";
	}
	$target = $host1{$2};
        $myfake = $target;
        $myfake =~ s/^o/i/;
    }


    if($use_hookflash)
    # hookflash implementation
 { 
    if(!$is_fake) {
	if(/^ntfy /i) {
	    if($in_hookflash[$terminal]) {
		&debug("in the middle of hookflash\n");
		if(/\no:.*hd/i) {
		    &debug("REPLACING hd with hf\n");
		    # then this was a flash, so eat it
		    $in_hookflash[$terminal] =~ s/hu/l\/hf/i;
		    $_ = $in_hookflash[$terminal];
		    $in_hookflash[$terminal] = "";		
		    $hookflash_timestamp[$terminal] = "";
		    &enqueue("i_gw", $_);
		    &enqueue("i_agent1", &renumber_S1("200 $transaction_id ok\n"));
		    $_ = "";
		    $hookflash[$terminal] = 0;
		}
	    } elsif(/\no:.*hu/i) {
		# start of hookflash
		&debug("starting hookflash\n");
		if($hookflash[$terminal]) {
		    # send pseudo-message to gateway
		    &debug("got hookflash start\n");
		    &enqueue("i_agent1", &renumber_S1("200 $transaction_id ok\n"));
		    $unique = &unique();
		    &enqueue("i_agent1", "fake rqnt 65000 $terminal_name[$terminal] mgcp 0.1\nR: l/hd(n)\nx: $unique\n");
		    $in_hookflash[$terminal] = $_;
		    $_ = "";
		    $hookflash_timestamp[$terminal] = gettimeofday();
		}
	    }
	}
    }
  }

    
    s/auep/AUEP/;
    s/rqnt/RQNT/;
    s/crcx/CRCX/;
    s/mdcx/MDCX/;
    s/dlcx/DLCX/;
    s/mgcp/MGCP/;
#    s/\*/ep1/;
    
    s/\nO:/\nO: /i;
    s/\nX:/\nX: /i;
    
    s/hd/l\/hd/;
    s/hu/l\/hu/;
    
#    s/\,l\/hf\(n\)//ig;
    if(y/\0//d) {
	&debug( "killed \\0!\n");
    }
    
    s/\n\s+\n/\n\n/g;
    y/\r//d;
    s/\n+$/\n/;
    s/\n\n$/\n/g;
    #    s/\n+/\n/g; 
    s/\n/\r\n/g;
    if(s/([^\r\n])(\r\n)+$/$1\r\n/g) {
	&debug( "2 ate \\r\\n\n");
    }
    
    if(/^rqnt/i) {
	s/,d\/x\(n\)//i;
    }

#    if (/^NTFY/)
#    {
#        s/O: 93831003/0: L\/9\,L\/3\,L\/8\,L\/3\,L\/1\,L\/0\,L\/0\,L\/3/;
#    }
    
    
    if(/^crcx/i) {
	if(!/v\=0/i) {
	    s/sendrecv/recvonly/;
	}
    }
    
    s/RM:restart//;
    
    s/aaln\//aaln_/;

    if($nosend) {
	&debug( "nosend:\n$_----------\n");
	$_ = "";
    }

    return $_;
}

sub debug {
    if($debug) {
	print @_;
    }
}

sub unique {
    $unique_internal++;
}
