#!/usr/bin/perl -w
use strict;
use warnings;
use IO::Socket::INET;
use Switch;

### Configure the remote host here.
my $forward_to_host = "my.sip.provider";	# Put your SIP providers hostname here.
my $listen_on_ip = "203.1.2.3.4";		# The IP to listen for requests from the SIP provider. If you are behind
						# NAT, this is your public IP (and forward the port!)
my $listen_on_port = "15060";			# What port shall we listen on?	
my $try_to_recover_callerid = "1";              # Try a few methods to get around hidden caller ID. Enable = 1, Disable = 0. Not currently used.
my @blacklisted_callers = qw( );		# Not currently used.

### You shouldn't need to modify anything below here.
my $packet_count;

## Start up the listening socket.
my $sock = IO::Socket::INET->new(LocalAddr => $listen_on_ip, LocalPort => $listen_on_port, Proto => "udp", ReuseAddr => 1) or die "Cant bind: $@\n";
$sock->autoflush(1);

while ( $sock->recv(my $packet, 2048) ) {
        $packet_count++;
	
	## Define some variables that we need here...
	my $output_packet;
	my $source_ip;
	my $source_port;
	my $destination_ip;
	my $destination_port;
	my $filter_ip;
	my $filter_port;
	my $sip_client_ip;	## We'll pick these up in the REGISTER and store them to make sure we relay things
	my $sip_client_port;	## correctly. Yes, we're limited to 1 SIP client. Patches accepted to solve this!

	if ( $sock->peerhost() eq $sock->sockaddr() or $sock->peeraddr() eq inet_aton($forward_to_host)) {
		## We get in here if the packet comes from an address (FQDN) that matches the reverse DNS of the host
		## defined in $sock->sockaddr() or the dotted decimal address matches what the host in $sock->sockaddr()
		## resolves to. This could get ugly if you use DNS SRV records - but hopefully that's not many!
		## This translates server -> filter -> client.

		$destination_ip = $sip_client_ip;
		$destination_port = $sip_client_port;
		$source_ip = $filter_ip;
		$source_port = $listen_on_port;
	} else {
		## We get in here if a packet comes from anywhere BUT the host defined in $forward_to_host.
		## Generally, this is from the SIP client. This translates client -> filter -> server.

		$destination_ip = $forward_to_host;
		$destination_port = "5060";
		$source_ip = $listen_on_ip;
		$source_port = $listen_on_port;
	}

	## Start working through the packet and switching values around. Yes, this is the verbose way, but it allows
	## to instantly see what lines we alter on what packets. It also allows us to easily expand the munging easily.
	foreach ( split /\n/, $packet ) {
		switch ($_) {
			case /^REGISTER .+$/ {
				$_ =~ /^REGISTER sip:(.+):(.+) (.*)$/;
				$filter_ip = $1;
				$filter_port = $2;
				$output_packet .= "REGISTER sip:$forward_to_host:5060 $3\n";
			}

			case /^Via: .+$/ {
				$_ =~ /^Via: (.+) (\d.+):(\d+);(.+)$/;
				$sip_client_ip= $2;
				$sip_client_port = $3;
				$output_packet .= "Via: $1 $filter_ip:$filter_port;$4\n";
			}

			case /^From: .+$/ {
				$_ =~ /^From: <sip:(.+)@.+>;(.+)$/;
				$output_packet .= "From: <sip:$1\@$forward_to_host>;$2\n";
			}
			case /^To: .+$/ {
				$_ =~ /^To: <sip:(.+)@.+>/;
				$output_packet .= "To: <sip:$1\@$forward_to_host>\n";
			}

			case /^Call-ID: .+$/ {
				$_ =~ /^Call-ID: (.+)\@.+$/;
				$output_packet .= "Call-ID: $1\@$filter_ip\n";
			}

			case /^Contact: .+$/ {
				$_ =~ /^Contact: <sip:(.+)\@.+$/;
				$output_packet .= "Contact: <sip:$1\@$filter_ip>\n";
			}

			else {
				$output_packet .= "$_\n";
			}
		}
	}
	## Open up the sender socket, and spew the packet out.
	my $dst = sockaddr_in($destination_port, inet_aton($destination_ip));
	$sock->send($output_packet, undef, $dst) or die "Error: Sending packet failed - $@\n";

        print "------------ Original Packet $packet_count -------------\n$packet\n--------------------------------------------------------\n\n";
        print "------------ Munged Packet $packet_count -------------\n$output_packet\n------------------------------------------------------\n";
}
close($sock);

