#!/usr/bin/perl

use strict;
use warnings;
use Socket;
use Socket6;
use Digest::SHA qw(hmac_sha1);
use Digest::CRC qw(crc32);

my ($prio, $ip, $port, $username, $pwd) = @ARGV;

my $fd;
my @dests = getaddrinfo($ip, $port, AF_UNSPEC, SOCK_DGRAM);
while (@dests >= 5) {
	my ($fam, $type, $prot, $addr, $canon, @dests) = @dests;
	if (!socket($fd, $fam, $type, $prot)) {
		undef($fd);
		next;
	}
	if (!connect($fd, $addr)) {
		undef($fd);
		next;
	}
	last;
}
$fd or die($!);

my @rand = ('A' .. 'Z', 'a' .. 'z');
my $ufrag = join('', (map {$rand[rand($#rand)]} (1 .. 10)));
my $tract = join('', (map {$rand[rand($#rand)]} (1 .. 12)));
my $control = rand() < .5;
my $tbreak = int(rand(0xffffffff)) * int(rand(0xffffffff));

print("transaction: $tract\n");
print("my username fragment: $ufrag\n");
print(($control?'controlling':'controlled')."\n");
print("tie breaker: $tbreak\n");


my $packet = '';
$packet .= attr(6, "$username:$ufrag");
$packet .= attr($control ? 0x802a : 0x8029, pack('Q', $tbreak));
$packet .= attr(0x24, pack('N', $prio));
$packet .= integrity();
$packet .= fingerprint();
$packet = header() . $packet;

send($fd, $packet, 0) or die $!;
my $buf;
recv($fd, $buf, 200, 0) or die;

my ($code, $length, $cookie, $tract2, $attrs) = unpack('nnN a12 a*', $buf);

if ($cookie == 0x2112A442 || $tract2 ne $tract) {
	printf("code: \%x\n", $code);
	while ($attrs ne '') {
		my ($type, $len, $cont);
		($type, $len, $attrs) = unpack('nn a*', $attrs);
		my $pad = 0;
		while ((($len + $pad) % 4) != 0) {
			$pad++;
		}
		($cont, $pad, $attrs) = unpack("a$len a$pad a*", $attrs);
		printf("  attr type: \%x\n", $type);
		print("  content: $cont\n");
	}
}
else {
	print("not stun: ".unpack('H*', $buf)."\n");
}

exit;


sub attr {
	my ($type, $data) = @_;
	my $len = length($data);
	while ((length($data) % 4) != 0) {
		$data .= "\0";
	}
	return pack('nn a*', $type, $len, $data);
}
sub header {
	my ($add_length) = @_;
	$add_length ||= 0;
	return pack('nnN a12', 1, length($packet) + $add_length, 0x2112A442, $tract);
}
sub integrity {
	my $h = header(24);
	my $hmac = hmac_sha1($h.$packet, $pwd);
	return attr(8, $hmac);
}
sub fingerprint {
	my $h = header(8);
	my $crc = crc32($h.$packet);
	return attr(0x8028, pack('N', ($crc ^ 0x5354554e)));
}
