# ArmaBE - Perl extension BattlEye ARMA Rcon interface # Original Source for BattlEye source - https://github.com/Jaegerhaus/BE-RCon-Tools # # $Id:$ # package ArmaBE; use strict; use warnings; use IO::Socket::INET; # release version our $VERSION = "0.01"; # create class sub new { my $class = shift; # create object with defaults my $self = { hostname => undef, port => 27015, password => undef, timeout => 5, connected => 0, authenticated => 0, socket => undef, sequence => 0, }; # create object bless($self, $class); # initialize class instances $self->init(); # parse constructor args while (my ($key, $val) = splice(@_, 0, 2)) { $key = lc($key); if ($key eq "hostname") { $self->hostname($val) } elsif ($key eq "port") { $self->port($val) } elsif ($key eq "password") { $self->password($val) } elsif ($key eq "timeout") { $self->timeout($val) } else { print STDERR "Unknown attribute: $key\n" } } return $self; } # initialize class instances sub init { my $self = shift; my $class = ref($self); # manipulate symbol table.. gotta love perl no strict "refs"; no warnings; foreach my $instance (keys %$self) { *{"${class}::${instance}"} = sub { my $self = shift; my $value = shift; my $ref = \$self->{$instance}; if (defined $value) { $$ref = $value; return $self; } else { return $$ref; } }; } } # run a command and return its response sub run { my $self = shift; my $command = shift; if (!$self->connected()) { $self->connect(); } if (!$self->authenticated()) { $self->authenticate(); } if ($self->authenticated()) { my $socket = $self->socket(); print $socket $self->packet("\1\0".$command); return 1; } else { return 0; } } # create tcp socket sub connect { my $self = shift; my $socket = IO::Socket::INET->new( PeerAddr => $self->hostname(), PeerPort => $self->port(), Timeout => $self->timeout(), Proto => "udp", ) || die "Failed to connect: $!\n"; $self->socket($socket); $self->connected(1); } # authenticate rcon session sub authenticate { my $self = shift; # send authentication packet to server my $socket = $self->socket(); print $socket $self->packet("\0".$self->password()); my $response = $self->response(); my $authenticated = int(substr($response, -1)); $self->authenticated($authenticated); } ###################### # PROTOCOL FUNCTIONS # ###################### # rcon command protocol: # https://www.battleye.com/downloads/BERConProtocol.txt sub crc32 { my ($self,$input,$init_value,$polynomial) = @_; $init_value = 0 unless (defined $init_value); $polynomial = 0xedb88320 unless (defined $polynomial); my @lookup_table; for (my $i=0; $i<256; $i++) { my $x = $i; for (my $j=0; $j<8; $j++) { if ($x & 1) { $x = ($x >> 1) ^ $polynomial; } else { $x = $x >> 1; } } push @lookup_table, $x; } my $crc = $init_value ^ 0xffffffff; foreach my $x (unpack ('C*', $input)) { $crc = (($crc >> 8) & 0xffffff) ^ $lookup_table[ ($crc ^ $x) & 0xff ]; } $crc = $crc ^ 0xffffffff; return $crc; } # create a packet of type (AUTH or CMD) sub packet { my $self = shift; my $payload = shift; my $break = pack('C', 0xff); my $packet = "BE" . pack('V', $self->crc32($break . $payload)) . $break . $payload; return $packet; } # receive packet sub response { my $self = shift; my $payload = $self->read(); return $payload; } # read length of bytes from socket with timeout sub read { my $self = shift; my $received; my $socket = $self->socket(); $socket->recv($received, 9); return unpack('H*', $received); } 1; __END__