#!/usr/bin/perl -w

# iptables -I OUTPUT -p udp --dport 5070 -j QUEUE
# iptables -I INPUT -p udp --sport 5070 -j QUEUE

use strict;
use IPTables::IPv4::IPQueue qw(:constants);
use NetPacket::IP;
use NetPacket::UDP;




sub nonce_sum($) 
{
    my $val = shift;
    if($val > 57) {
	if($val > 102) {
	    if($val > 70) {
		return -1;
	    } else {
		return $val - 55;
	    }
	} else {
	    return $val - 87;
	}
    } else {
	return $val - 48;
    }
}



sub fix_nonce($$)
{
    my $nonce=$_[0];
    my $callid=$_[1];
    my ($val,$nonce_buff,$sum,$nonce_offset,$j,@nonce, @callid);
    
    if(length $nonce<10) {
	printf("nonce is too short\n");
	return;
    }
    
    $nonce_offset=0;
    $sum=0;
    @nonce=split(//,$nonce);
    @callid=split(//,$callid);
    
    while(1) {
	$val=nonce_sum(ord($nonce[$nonce_offset++]));
	if($val == -1) {
	    last;
	}
	$sum+=$val;
	if($nonce_offset>=9) {
	    if(($sum&0xF)==1) {
		$nonce_buff=$nonce."_";
		if((ord($callid[0])-12)>0) {
		    for($nonce_offset=0;$nonce_offset<8;$nonce_offset++) {
			$val=nonce_sum(ord($nonce[$nonce_offset]));
			if($val==-1) {
			    last;
			}
			$j=$val % length $callid;
			if($j<0 || $j > length $callid) {
			    printf "Something bad happened\n";
			    printf "callid[0] = ".ord($callid[0])."\n";
			    printf "callid[0] = ".(ord($callid[0])-12)."\n";
			    printf "j = $j\n";
			    return;
			}
			$nonce_buff.=$callid[$j];
		    }
		}
	    }
	    last;
	} # if(nonce_offset >= 9)
    } # while
    return $nonce_buff;
}










my $queue = new IPTables::IPv4::IPQueue(copy_mode => IPQ_COPY_PACKET, copy_range => 2048)
    or die IPTables::IPv4::IPQueue->errstr;

my $udp_obj;
my $ip_obj;
while(1){
    my $msg = $queue->get_message(0);
    if (!defined $msg) {
	next if IPTables::IPv4::IPQueue->errstr eq 'Timeout';
	die IPTables::IPv4::IPQueue->errstr;
    }

    my $nonce = undef;
    my $callid = undef;
    my $modified=0;
    $ip_obj = NetPacket::IP->decode($msg->payload());
    if($ip_obj->{proto} == NetPacket::IP::IP_PROTO_UDP) {
	$udp_obj = NetPacket::UDP->decode($ip_obj->{data});
	if (defined $udp_obj && $udp_obj->{data} ne '') {
	    if($udp_obj->{data} =~ m/From: .*\<sip:(.*?)[\>\;]/) {
		# from server
		if ($udp_obj->{data} =~ m/WWW-Authenticate: .*nonce=\"(.*?)\"/i) {
		    # add the nonce tail
		    $nonce = $1;
		    if ($udp_obj->{data} =~ m/Call-ID: (.*)/i) {
			$callid = $1;
			my $new_nonce = fix_nonce($nonce,$callid);
			if($new_nonce) {
			    $udp_obj->{data} =~ s/\"$nonce\"/\"$new_nonce\"/;
			}
			$modified=1;
		    }
		}
		# from client
		if ($udp_obj->{data} =~ m/Authorization: .*nonce=\"(.*?)\"/i) {
		    # remove the nonce tail
		    $nonce = $1;
		    $nonce =~ m/(.*)_.*?/;
		    my $new_nonce = $1;
		    $udp_obj->{data} =~ s/\"$nonce\"/\"$new_nonce\"/;
		    $modified=1;
		}
	    }
	}
    }

    if($modified) {
	#Attempt to reencode
	$ip_obj->{data} = $udp_obj->encode($ip_obj);    #re-encode the udp packet and store inside the IP object
	my $ip_packet = $ip_obj->encode;       #re-encode the resulting IP packet(checksums hopefully recalculated)
	my $size;
	{
	    use bytes;
	    $size = length $ip_packet;
	}
	#try and flush the IP packet down the queue again, the error is probably here...
	$queue->set_verdict($msg->packet_id, NF_ACCEPT, $size, $ip_packet);
    } else {
	$queue->set_verdict($msg->packet_id, NF_ACCEPT);
    }
}
