Panel/Agent-Windows/OGP64/OGP/ArmaBE/ArmaBE.pm
2026-06-06 18:46:40 -04:00

198 lines
3.6 KiB
Perl

# 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__