ArmaBE.pm 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. # ArmaBE - Perl extension BattlEye ARMA Rcon interface
  2. # Original Source for BattlEye source - https://github.com/Jaegerhaus/BE-RCon-Tools
  3. #
  4. # $Id:$
  5. #
  6. package ArmaBE;
  7. use strict;
  8. use warnings;
  9. use IO::Socket::INET;
  10. # release version
  11. our $VERSION = "0.01";
  12. # create class
  13. sub new {
  14. my $class = shift;
  15. # create object with defaults
  16. my $self = {
  17. hostname => undef,
  18. port => 27015,
  19. password => undef,
  20. timeout => 5,
  21. connected => 0,
  22. authenticated => 0,
  23. socket => undef,
  24. sequence => 0,
  25. };
  26. # create object
  27. bless($self, $class);
  28. # initialize class instances
  29. $self->init();
  30. # parse constructor args
  31. while (my ($key, $val) = splice(@_, 0, 2)) {
  32. $key = lc($key);
  33. if ($key eq "hostname") { $self->hostname($val) }
  34. elsif ($key eq "port") { $self->port($val) }
  35. elsif ($key eq "password") { $self->password($val) }
  36. elsif ($key eq "timeout") { $self->timeout($val) }
  37. else { print STDERR "Unknown attribute: $key\n" }
  38. }
  39. return $self;
  40. }
  41. # initialize class instances
  42. sub init {
  43. my $self = shift;
  44. my $class = ref($self);
  45. # manipulate symbol table.. gotta love perl
  46. no strict "refs";
  47. no warnings;
  48. foreach my $instance (keys %$self) {
  49. *{"${class}::${instance}"} = sub {
  50. my $self = shift;
  51. my $value = shift;
  52. my $ref = \$self->{$instance};
  53. if (defined $value) {
  54. $$ref = $value;
  55. return $self;
  56. } else {
  57. return $$ref;
  58. }
  59. };
  60. }
  61. }
  62. # run a command and return its response
  63. sub run {
  64. my $self = shift;
  65. my $command = shift;
  66. if (!$self->connected()) {
  67. $self->connect();
  68. }
  69. if (!$self->authenticated()) {
  70. $self->authenticate();
  71. }
  72. if ($self->authenticated()) {
  73. my $socket = $self->socket();
  74. print $socket $self->packet("\1\0".$command);
  75. return 1;
  76. } else {
  77. return 0;
  78. }
  79. }
  80. # create tcp socket
  81. sub connect {
  82. my $self = shift;
  83. my $socket = IO::Socket::INET->new(
  84. PeerAddr => $self->hostname(),
  85. PeerPort => $self->port(),
  86. Timeout => $self->timeout(),
  87. Proto => "udp",
  88. ) || die "Failed to connect: $!\n";
  89. $self->socket($socket);
  90. $self->connected(1);
  91. }
  92. # authenticate rcon session
  93. sub authenticate {
  94. my $self = shift;
  95. # send authentication packet to server
  96. my $socket = $self->socket();
  97. print $socket $self->packet("\0".$self->password());
  98. my $response = $self->response();
  99. my $authenticated = int(substr($response, -1));
  100. $self->authenticated($authenticated);
  101. }
  102. ######################
  103. # PROTOCOL FUNCTIONS #
  104. ######################
  105. # rcon command protocol:
  106. # https://www.battleye.com/downloads/BERConProtocol.txt
  107. sub crc32 {
  108. my ($self,$input,$init_value,$polynomial) = @_;
  109. $init_value = 0 unless (defined $init_value);
  110. $polynomial = 0xedb88320 unless (defined $polynomial);
  111. my @lookup_table;
  112. for (my $i=0; $i<256; $i++) {
  113. my $x = $i;
  114. for (my $j=0; $j<8; $j++) {
  115. if ($x & 1) {
  116. $x = ($x >> 1) ^ $polynomial;
  117. } else {
  118. $x = $x >> 1;
  119. }
  120. }
  121. push @lookup_table, $x;
  122. }
  123. my $crc = $init_value ^ 0xffffffff;
  124. foreach my $x (unpack ('C*', $input)) {
  125. $crc = (($crc >> 8) & 0xffffff) ^ $lookup_table[ ($crc ^ $x) & 0xff ];
  126. }
  127. $crc = $crc ^ 0xffffffff;
  128. return $crc;
  129. }
  130. # create a packet of type (AUTH or CMD)
  131. sub packet {
  132. my $self = shift;
  133. my $payload = shift;
  134. my $break = pack('C', 0xff);
  135. my $packet = "BE"
  136. . pack('V', $self->crc32($break . $payload))
  137. . $break
  138. . $payload;
  139. return $packet;
  140. }
  141. # receive packet
  142. sub response {
  143. my $self = shift;
  144. my $payload = $self->read();
  145. return $payload;
  146. }
  147. # read length of bytes from socket with timeout
  148. sub read {
  149. my $self = shift;
  150. my $received;
  151. my $socket = $self->socket();
  152. $socket->recv($received, 9);
  153. return unpack('H*', $received);
  154. }
  155. 1;
  156. __END__