| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- package KKrcon;
- #
- # KKrcon Perl Module - execute commands on a remote Half-Life server using Rcon.
- # http://kkrcon.sourceforge.net
- #
- # Synopsis:
- #
- # use KKrcon;
- # $rcon = new KKrcon(Password=>PASSWORD, [Host=>HOST], [Port=>PORT], [Type=>"new"|"old"]);
- # $result = $rcon->execute(COMMAND);
- # %players = $rcon->getPlayers();
- # $player = $rcon->getPlayer(USERID);
- #
- # Copyright (C) 2000, 2001 Rod May
- #
- # This program is free software; you can redistribute it and/or
- # modify it under the terms of the GNU General Public License
- # as published by the Free Software Foundation; either version 2
- # of the License, or (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- #
- use Socket;
- use Sys::Hostname;
- # Release version number
- $VERSION = "2.11";
- ##
- ## Main
- ##
- #
- # Constructor
- #
- sub new
- {
- my $class_name = shift;
- my %params = @_;
-
- my $self = {};
- bless($self, $class_name);
-
- my %server_types = (new=>1, old=>2);
-
- # Check parameters
- $params{"Host"} = "127.0.0.1" unless($params{"Host"});
- $params{"Port"} = 27015 unless($params{"Port"});
- $params{"Type"} = "new" unless($params{"Type"});
-
- # Initialise properties
- $self->{"rcon_password"} = $params{"Password"}
- or die("KKrcon: a Password is required\n");
- $self->{"server_host"} = $params{"Host"};
- $self->{"server_port"} = int($params{"Port"})
- or die("KKrcon: invalid Port \"" . $params{"Port"} . "\"\n");
- $self->{"server_type"} = ($server_types{$params{"Type"}} || 1);
-
- $self->{"error"} = "";
-
- # Set up socket parameters
- $self->{"_ipaddr"} = gethostbyname($self->{"server_host"})
- or die("KKrcon: could not resolve Host \"" . $self->{"server_host"} . "\"\n");
-
- return $self;
- }
- #
- # Execute an Rcon command and return the response
- #
- sub execute
- {
- my ($self, $command) = @_;
-
- my $msg;
- my $ans;
- if ($self->{"server_type"} == 1)
- {
- # version x.1.0.6+ HL server
- $msg = "\xFF\xFF\xFF\xFFchallenge rcon\n\0";
- $ans = $self->_sendrecv($msg);
-
- if ($ans =~ /challenge +rcon +(\d+)/)
- {
- $msg = "\xFF\xFF\xFF\xFFrcon $1 \"" . $self->{"rcon_password"} . "\" $command\0";
- $ans = $self->_sendrecv($msg);
- }
- elsif (!$self->error())
- {
- $ans = "";
- $self->{"error"} = "No challenge response";
- }
- }
- else
- {
- # QW/Q2/Q3 or old HL server
- $msg = "\xFF\xFF\xFF\xFFrcon " . $self->{"rcon_password"} . " $command\n\0";
- $ans = $self->_sendrecv($msg);
- }
-
- if ($ans =~ /bad rcon_password/i)
- {
- $self->{"error"} = "Bad Password";
- }
-
- return $ans;
- }
- sub _sendrecv
- {
- my ($self, $msg) = @_;
-
- my $host = $self->{"server_host"};
- my $port = $self->{"server_port"};
- my $ipaddr = $self->{"_ipaddr"};
-
- # Open socket
- socket(RCON, PF_INET, SOCK_DGRAM, getprotobyname("udp")) or die("KKrcon: socket: $!\n");
- my $hispaddr = sockaddr_in($port, $ipaddr);
-
- unless(defined(send(RCON, $msg, 0, $hispaddr)))
- {
- die("KKrcon: send $ip:$port : $!");
- }
- my $rin;
- vec($rin, fileno(RCON), 1) = 1;
- my $ans;
- if (select($rin, undef, undef, 10.0)) {
- $hispaddr = recv(RCON, $ans, 8192, 0);
- if (defined($ans)) {
- $ans =~ s/^\xFF\xFF\xFF\xFFprint\n//; # CoD2 response
- $ans =~ s/\x00+$//; # trailing crap
- $ans =~ s/^\xFF\xFF\xFF\xFFl//; # HL response
- $ans =~ s/^\xFF\xFF\xFF\xFFn//; # QW response
- $ans =~ s/^\xFF\xFF\xFF\xFF//; # Q2/Q3 response
- $ans =~ s/^\xFE\xFF\xFF\xFF.....//; # old HL bug/feature
- if (length($ans) > 512) {
- my $tmp;
- my @explode;
- while (select($rin, undef, undef, 0.05)) {
- @explode = split(/\n/, $ans);
- $explode[$#explode] =~ s/^ //;
- $explode[$#explode] = 'X' . $explode[$#explode];
- $ans = join("\n", @explode);
- $hispaddr = recv(RCON, $tmp, 8192, 0);
- if (defined($tmp)) {
- $tmp =~ s/^\xFF\xFF\xFF\xFFprint\n//; # CoD2 response
- $tmp =~ s/\x00+$//; # trailing crap
- $tmp =~ s/^\xFF\xFF\xFF\xFFl//; # HL response
- $tmp =~ s/^\xFF\xFF\xFF\xFFn//; # QW response
- $tmp =~ s/^\xFF\xFF\xFF\xFF//; # Q2/Q3 response
- $tmp =~ s/^\xFE\xFF\xFF\xFF.....//; # old HL bug/feature
- $ans .= $tmp;
- }
- }
- }
- }
- }
-
- # Close socket
- close(RCON);
-
- if (!defined($ans)) {
- $ans = "";
- $self->{"error"} = "Rcon timeout";
- }
-
- return $ans;
- }
- #
- # Get error message
- #
- sub error
- {
- my ($self) = @_;
-
- return $self->{"error"};
- }
- #
- # Parse "status" command output into player information
- #
- sub getPlayers
- {
- my ($self) = @_;
-
- my $status = $self->execute("status");
- my @lines = split(/[\r\n]+/, $status);
-
- my %players;
-
- foreach $line (@lines)
- {
- if ($line =~ /^\#[\s\d]\d\s+
- (.+)\s+ # name
- (\d+)\s+ # userid
- (\d+)\s+ # wonid
- ([\d-]+)\s+ # frags
- ([\d:]+)\s+ # time
- (\d+)\s+ # ping
- (\d+)\s+ # loss
- (\S+) # addr
- $/x)
- {
- my $name = $1;
- my $userid = $2;
- my $wonid = $3;
- my $frags = $4;
- my $time = $5;
- my $ping = $6;
- my $loss = $7;
- my $address = $8;
-
- $players{$userid} = {
- "Name" => $name,
- "UserID" => $userid,
- "WONID" => $wonid,
- "Frags" => $frags,
- "Time" => $time,
- "Ping" => $ping,
- "Loss" => $loss,
- "Address" => $address
- };
- }
- }
-
- return %players;
- }
- #
- # Get information about a player by userID
- #
- sub getPlayer
- {
- my ($self, $userid) = @_;
-
- my %players = $self->getPlayers();
-
- if (defined($players{$userid}))
- {
- return $players{$userid};
- }
- else
- {
- $self->{"error"} = "No such player # $userid";
- return 0;
- }
- }
- 1;
- # end
|