mirror of
https://github.com/ENSL/ensl.org.git
synced 2024-12-27 04:51:14 +00:00
Remove rcon
This commit is contained in:
parent
ec3498ec70
commit
cf9f073f05
4 changed files with 0 additions and 2168 deletions
296
vendor/plugins/rcon/bin/rcontool
vendored
296
vendor/plugins/rcon/bin/rcontool
vendored
|
@ -1,296 +0,0 @@
|
||||||
#!/usr/bin/env ruby
|
|
||||||
################################################################
|
|
||||||
#
|
|
||||||
# rcontool - shell interface to rcon commands
|
|
||||||
#
|
|
||||||
# (C) 2006 Erik Hollensbe, License details below
|
|
||||||
#
|
|
||||||
# Use 'rcontool -h' for usage instructions.
|
|
||||||
#
|
|
||||||
# The compilation of software known as rcontool is distributed under the
|
|
||||||
# following terms:
|
|
||||||
# Copyright (C) 2005-2006 Erik Hollensbe. All rights reserved.
|
|
||||||
#
|
|
||||||
# Redistribution and use in source form, with or without
|
|
||||||
# modification, are permitted provided that the following conditions
|
|
||||||
# are met:
|
|
||||||
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
################################################################
|
|
||||||
|
|
||||||
#
|
|
||||||
# rubygems hack
|
|
||||||
#
|
|
||||||
|
|
||||||
begin
|
|
||||||
require 'rubygems'
|
|
||||||
rescue LoadError => e
|
|
||||||
end
|
|
||||||
begin
|
|
||||||
require 'rcon'
|
|
||||||
require 'ip'
|
|
||||||
rescue LoadError => e
|
|
||||||
$stderr.puts "rcontool requires the rcon and ip libraries be installed."
|
|
||||||
$stderr.puts "You can find them both via rubygems or at http://rubyforge.org."
|
|
||||||
exit -1
|
|
||||||
end
|
|
||||||
|
|
||||||
RCONTOOL_VERSION = '0.1.0'
|
|
||||||
|
|
||||||
require 'optparse'
|
|
||||||
require 'ostruct'
|
|
||||||
|
|
||||||
#
|
|
||||||
# Manages our options
|
|
||||||
#
|
|
||||||
|
|
||||||
def get_options
|
|
||||||
options = OpenStruct.new
|
|
||||||
# ip address (IP::Address object)
|
|
||||||
options.ip_address = nil
|
|
||||||
# port (integer)
|
|
||||||
options.port = nil
|
|
||||||
# password
|
|
||||||
options.password = nil
|
|
||||||
# protocol type (one of :hlds, :source, :oldquake, :newquake)
|
|
||||||
options.protocol_type = nil
|
|
||||||
# verbose, spit out extra information
|
|
||||||
options.verbose = false
|
|
||||||
# command to execute on the server
|
|
||||||
options.command = nil
|
|
||||||
|
|
||||||
optparse = OptionParser.new do |opts|
|
|
||||||
opts.banner = "Usage: #{File.basename $0} <ip_address:port> <command> [options]"
|
|
||||||
opts.separator ""
|
|
||||||
opts.separator "Options:"
|
|
||||||
|
|
||||||
opts.on("--ip-address [ADDRESS]",
|
|
||||||
"Provide an IP address to connect to. Does not take a port.") do |ip_address|
|
|
||||||
if ! options.ip_address.nil?
|
|
||||||
$stderr.puts "Error: you have already provided an IP Address."
|
|
||||||
$stderr.puts opts
|
|
||||||
exit -1
|
|
||||||
end
|
|
||||||
|
|
||||||
options.ip_address = IP::Address.new(ip_address)
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.on("-r", "--port [PORT]",
|
|
||||||
"Port to connect to.") do |port|
|
|
||||||
if ! options.port.nil?
|
|
||||||
$stderr.puts "Error: you have already provided a port."
|
|
||||||
$stderr.puts opts
|
|
||||||
exit -1
|
|
||||||
end
|
|
||||||
|
|
||||||
options.port = port.to_i
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.on("-c", "--command [COMMAND]",
|
|
||||||
"Command to run on the server.") do |command|
|
|
||||||
if ! options.command.nil?
|
|
||||||
$stderr.puts "Error: you have already provided a command."
|
|
||||||
$stderr.puts opts
|
|
||||||
exit -1
|
|
||||||
end
|
|
||||||
|
|
||||||
options.command = command
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.on("-p", "--password [PASSWORD]",
|
|
||||||
"Provide a password on the command line.") do |password|
|
|
||||||
options.password = password
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.on("-f", "--password-from [FILENAME]",
|
|
||||||
"Get the password from a file (use '/dev/fd/0' or '/dev/stdin' to read from Standard Input).") do |filename|
|
|
||||||
if !filename.nil?
|
|
||||||
f = File.open(filename)
|
|
||||||
options.password = f.gets.chomp
|
|
||||||
f.close
|
|
||||||
else
|
|
||||||
$stderr.puts "Error: filename (from -f) is not valid."
|
|
||||||
$stderr.puts opts
|
|
||||||
exit -1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.on("-t", "--protocol-type [TYPE]", [:hlds, :source, :oldquake, :newquake],
|
|
||||||
"Type of rcon connection to make: (hlds, source, oldquake, newquake).",
|
|
||||||
" Note: oldquake is quake1/quakeworld, newquake is quake2/3.") do |protocol_type|
|
|
||||||
options.protocol_type = protocol_type
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.on("-v", "--[no-]verbose",
|
|
||||||
"Run verbosely, print information about each packet recieved and turnaround times.") do |verbose|
|
|
||||||
options.verbose = verbose
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.on("-h", "--help",
|
|
||||||
"This help message.") do
|
|
||||||
$stderr.puts opts
|
|
||||||
exit -1
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.on("--version", "Print the version information.") do
|
|
||||||
$stderr.puts "This is rcontool version #{RCONTOOL_VERSION},"
|
|
||||||
$stderr.puts "it is located at #{File.expand_path $0}."
|
|
||||||
exit -1
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.separator ""
|
|
||||||
opts.separator "Note: IP, port, protocol type, password and command are required to function."
|
|
||||||
opts.separator ""
|
|
||||||
opts.separator "Examples (all are equivalent):"
|
|
||||||
opts.separator "\t#{File.basename($0)} 10.0.0.11 status -t hlds -r 27015 -p foobar"
|
|
||||||
opts.separator "\techo 'foobar' | #{File.basename($0)} 10.0.0.11:27015 status -t hlds -f /dev/stdin"
|
|
||||||
opts.separator "\t#{File.basename($0)} --ip-address 10.0.0.11 --port 27015 -c status -t hlds -f file_with_password"
|
|
||||||
opts.separator ""
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
################################################################
|
|
||||||
#
|
|
||||||
# This hackery is to help facilitate the bareword options if
|
|
||||||
# they exist, while still allowing for the option parser
|
|
||||||
# to work properly.
|
|
||||||
#
|
|
||||||
################################################################
|
|
||||||
|
|
||||||
s1 = ARGV.shift
|
|
||||||
s2 = ARGV.shift
|
|
||||||
|
|
||||||
begin
|
|
||||||
options.ip_address = IP::Address::IPv4.new(s1)
|
|
||||||
options.command = s2
|
|
||||||
rescue IP::AddressException => e
|
|
||||||
# attempt to split it first... not sure how to best handle this situation
|
|
||||||
begin
|
|
||||||
ip,port = s1.split(/:/, 2)
|
|
||||||
options.ip_address = IP::Address::IPv4.new(ip)
|
|
||||||
options.port = port.to_i
|
|
||||||
options.command = s2
|
|
||||||
rescue Exception => e
|
|
||||||
end
|
|
||||||
|
|
||||||
if [options.ip_address, options.port].include? nil
|
|
||||||
ARGV.unshift(s2)
|
|
||||||
ARGV.unshift(s1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
optparse.parse!
|
|
||||||
|
|
||||||
if [options.ip_address, options.protocol_type, options.port, options.password, options.command].include? nil
|
|
||||||
$stderr.puts optparse
|
|
||||||
exit -1
|
|
||||||
end
|
|
||||||
|
|
||||||
return options
|
|
||||||
end
|
|
||||||
|
|
||||||
def verbose(string)
|
|
||||||
$stderr.puts string if $options.verbose
|
|
||||||
end
|
|
||||||
|
|
||||||
def dump_source_packet(packet)
|
|
||||||
if $options.verbose
|
|
||||||
verbose "Request ID: #{packet.request_id}"
|
|
||||||
verbose "Packet Size: #{packet.packet_size}"
|
|
||||||
verbose "Response Type: #{packet.command_type}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
################################################################
|
|
||||||
#
|
|
||||||
# start main block
|
|
||||||
#
|
|
||||||
################################################################
|
|
||||||
|
|
||||||
$options = get_options
|
|
||||||
|
|
||||||
################################################################
|
|
||||||
#
|
|
||||||
# Source query
|
|
||||||
#
|
|
||||||
################################################################
|
|
||||||
|
|
||||||
if $options.protocol_type == :source
|
|
||||||
verbose "Protocol type 'SOURCE' selected."
|
|
||||||
|
|
||||||
rcon = RCon::Query::Source.new($options.ip_address.ip_address, $options.port)
|
|
||||||
|
|
||||||
# if we have a verbose request, give all the information we can about
|
|
||||||
# the query, including the packet information.
|
|
||||||
rcon.return_packets = $options.verbose
|
|
||||||
|
|
||||||
verbose "Attempting authentication to #{$options.ip_address.ip_address}:#{$options.port} with password '#{$options.password}'"
|
|
||||||
|
|
||||||
value = rcon.auth $options.password
|
|
||||||
|
|
||||||
dump_source_packet value
|
|
||||||
|
|
||||||
if ($options.verbose && value.command_type == RCon::Packet::Source::RESPONSE_AUTH) || value
|
|
||||||
verbose "Authentication succeeded. Sending command: '#{$options.command}'"
|
|
||||||
|
|
||||||
value = rcon.command $options.command
|
|
||||||
|
|
||||||
dump_source_packet value
|
|
||||||
verbose ""
|
|
||||||
|
|
||||||
if $options.verbose
|
|
||||||
puts value.string1
|
|
||||||
else
|
|
||||||
puts value
|
|
||||||
end
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
$stderr.puts "Authentication failed."
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
################################################################
|
|
||||||
#
|
|
||||||
# Original Query
|
|
||||||
#
|
|
||||||
################################################################
|
|
||||||
|
|
||||||
else
|
|
||||||
rcon = nil
|
|
||||||
case $options.protocol_type
|
|
||||||
when :hlds
|
|
||||||
verbose "Protocol type 'HLDS' selected"
|
|
||||||
rcon = RCon::Query::Original.new($options.ip_address.ip_address, $options.port, $options.password,
|
|
||||||
RCon::Query::Original::HLDS)
|
|
||||||
when :oldquake
|
|
||||||
verbose "Protocol type 'OLDQUAKE' selected"
|
|
||||||
rcon = RCon::Query::Original.new($options.ip_address.ip_address, $options.port, $options.password,
|
|
||||||
RCon::Query::Original::QUAKEWORLD)
|
|
||||||
when :newquake
|
|
||||||
verbose "Protocol type 'NEWQUAKE' selected"
|
|
||||||
rcon = RCon::Query::Original.new($options.ip_address.ip_address, $options.port, $options.password,
|
|
||||||
RCon::Query::Original::NEWQUAKE)
|
|
||||||
end
|
|
||||||
verbose "Attempting transmission to #{$options.ip_address.ip_address}:#{$options.port}"
|
|
||||||
verbose "Using password: '#{$options.password}' and sending command: '#{$options.command}'"
|
|
||||||
verbose ""
|
|
||||||
string = rcon.command($options.command)
|
|
||||||
|
|
||||||
puts string
|
|
||||||
exit 0
|
|
||||||
end
|
|
||||||
|
|
499
vendor/plugins/rcon/lib/rcon.rb
vendored
499
vendor/plugins/rcon/lib/rcon.rb
vendored
|
@ -1,499 +0,0 @@
|
||||||
# encoding: US-ASCII
|
|
||||||
|
|
||||||
require 'socket'
|
|
||||||
|
|
||||||
#
|
|
||||||
# RCon is a module to work with Quake 1/2/3, Half-Life, and Half-Life
|
|
||||||
# 2 (Source Engine) RCon (Remote Console) protocols.
|
|
||||||
#
|
|
||||||
# Version:: 0.2.0
|
|
||||||
# Author:: Erik Hollensbe <erik@hollensbe.org>
|
|
||||||
# License:: BSD
|
|
||||||
# Contact:: erik@hollensbe.org
|
|
||||||
# Copyright:: Copyright (c) 2005-2006 Erik Hollensbe
|
|
||||||
#
|
|
||||||
# The relevant modules to query RCon are in the RCon::Query namespace,
|
|
||||||
# under RCon::Query::Original (for Quake 1/2/3 and Half-Life), and
|
|
||||||
# RCon::Query::Source (for HL2 and CS: Source, and other Source Engine
|
|
||||||
# games). The RCon::Packet namespace is used to manage complex packet
|
|
||||||
# structures if required. The Original protocol does not require
|
|
||||||
# this, but Source does.
|
|
||||||
#
|
|
||||||
# Usage is fairly simple:
|
|
||||||
#
|
|
||||||
# # Note: Other classes have different constructors
|
|
||||||
#
|
|
||||||
# rcon = RCon::Query::Source.new("10.0.0.1", 27015)
|
|
||||||
#
|
|
||||||
# rcon.auth("foobar") # source only
|
|
||||||
#
|
|
||||||
# rcon.command("mp_friendlyfire") => "mp_friendlyfire = 1"
|
|
||||||
#
|
|
||||||
# rcon.cvar("mp_friendlyfire") => 1
|
|
||||||
#
|
|
||||||
#--
|
|
||||||
#
|
|
||||||
# The compilation of software known as rcon.rb is distributed under the
|
|
||||||
# following terms:
|
|
||||||
# Copyright (C) 2005-2006 Erik Hollensbe. All rights reserved.
|
|
||||||
#
|
|
||||||
# Redistribution and use in source form, with or without
|
|
||||||
# modification, are permitted provided that the following conditions
|
|
||||||
# are met:
|
|
||||||
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
# SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
#++
|
|
||||||
|
|
||||||
|
|
||||||
class RCon
|
|
||||||
class Packet
|
|
||||||
# placeholder so ruby doesn't bitch
|
|
||||||
end
|
|
||||||
class Query
|
|
||||||
|
|
||||||
#
|
|
||||||
# Convenience method to scrape input from cvar output and return that data.
|
|
||||||
# Returns integers as a numeric type if possible.
|
|
||||||
#
|
|
||||||
# ex: rcon.cvar("mp_friendlyfire") => 1
|
|
||||||
#
|
|
||||||
|
|
||||||
def cvar(cvar_name)
|
|
||||||
response = command(cvar_name)
|
|
||||||
match = /^.+?\s(?:is|=)\s"([^"]+)".*$/.match response
|
|
||||||
match = match[1]
|
|
||||||
if /\D/.match match
|
|
||||||
return match
|
|
||||||
else
|
|
||||||
return match.to_i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# RCon::Packet::Source generates a packet structure useful for
|
|
||||||
# RCon::Query::Source protocol queries.
|
|
||||||
#
|
|
||||||
# This class is primarily used internally, but is available if you
|
|
||||||
# want to do something more advanced with the Source RCon
|
|
||||||
# protocol.
|
|
||||||
#
|
|
||||||
# Use at your own risk.
|
|
||||||
#
|
|
||||||
|
|
||||||
class RCon::Packet::Source
|
|
||||||
# execution command
|
|
||||||
COMMAND_EXEC = 2
|
|
||||||
# auth command
|
|
||||||
COMMAND_AUTH = 3
|
|
||||||
# auth response
|
|
||||||
RESPONSE_AUTH = 2
|
|
||||||
# normal response
|
|
||||||
RESPONSE_NORM = 0
|
|
||||||
# packet trailer
|
|
||||||
TRAILER = "\x00\x00"
|
|
||||||
|
|
||||||
# size of the packet (10 bytes for header + string1 length)
|
|
||||||
attr_accessor :packet_size
|
|
||||||
# Request Identifier, used in managing multiple requests at once
|
|
||||||
attr_accessor :request_id
|
|
||||||
# Type of command, normally COMMAND_AUTH or COMMAND_EXEC. In response packets, RESPONSE_AUTH or RESPONSE_NORM
|
|
||||||
attr_accessor :command_type
|
|
||||||
# First string, the only used one in the protocol, contains
|
|
||||||
# commands and responses. Null terminated.
|
|
||||||
attr_accessor :string1
|
|
||||||
# Second string, unused by the protocol. Null terminated.
|
|
||||||
attr_accessor :string2
|
|
||||||
|
|
||||||
#
|
|
||||||
# Generate a command packet to be sent to an already
|
|
||||||
# authenticated RCon connection. Takes the command as an
|
|
||||||
# argument.
|
|
||||||
#
|
|
||||||
def command(string)
|
|
||||||
@request_id = rand(1000)
|
|
||||||
@string1 = string
|
|
||||||
@string2 = TRAILER
|
|
||||||
@command_type = COMMAND_EXEC
|
|
||||||
|
|
||||||
@packet_size = build_packet.length
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Generate an authentication packet to be sent to a newly
|
|
||||||
# started RCon connection. Takes the RCon password as an
|
|
||||||
# argument.
|
|
||||||
#
|
|
||||||
def auth(string)
|
|
||||||
@request_id = rand(1000)
|
|
||||||
@string1 = string
|
|
||||||
@string2 = TRAILER
|
|
||||||
@command_type = COMMAND_AUTH
|
|
||||||
|
|
||||||
@packet_size = build_packet.length
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Builds a packet ready to deliver, without the size prepended.
|
|
||||||
# Used to calculate the packet size, use #to_s to get the packet
|
|
||||||
# that srcds actually needs.
|
|
||||||
#
|
|
||||||
def build_packet
|
|
||||||
return [@request_id, @command_type, @string1, @string2].pack("VVa#{@string1.length}a2")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns a string representation of the packet, useful for
|
|
||||||
# sending and debugging. This include the packet size.
|
|
||||||
def to_s
|
|
||||||
packet = build_packet
|
|
||||||
@packet_size = packet.length
|
|
||||||
return [@packet_size].pack("V") + packet
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# RCon::Query::Original queries Quake 1/2/3 and Half-Life servers
|
|
||||||
# with the rcon protocol. This protocol travels over UDP to the
|
|
||||||
# game server port, and requires an initial authentication step,
|
|
||||||
# the information of which is provided at construction time.
|
|
||||||
#
|
|
||||||
# Some of the work here (namely the RCon packet structure) was taken
|
|
||||||
# from the KKRcon code, which is written in perl.
|
|
||||||
#
|
|
||||||
# One query per authentication is allowed.
|
|
||||||
#
|
|
||||||
|
|
||||||
class RCon::Query::Original < RCon::Query
|
|
||||||
# HLDS-Based Servers
|
|
||||||
HLDS = "l"
|
|
||||||
# QuakeWorld/Quake 1 Servers
|
|
||||||
QUAKEWORLD = "n"
|
|
||||||
# Quake 2/3 Servers
|
|
||||||
NEWQUAKE = ""
|
|
||||||
|
|
||||||
# Request to be sent to server
|
|
||||||
attr_reader :request
|
|
||||||
# Response from server
|
|
||||||
attr_reader :response
|
|
||||||
# Challenge ID (served by server-side of connection)
|
|
||||||
attr_reader :challenge_id
|
|
||||||
# UDPSocket object
|
|
||||||
attr_reader :socket
|
|
||||||
# Host of connection
|
|
||||||
attr_reader :host
|
|
||||||
# Port of connection
|
|
||||||
attr_reader :port
|
|
||||||
# RCon password
|
|
||||||
attr_reader :password
|
|
||||||
# type of server
|
|
||||||
attr_reader :server_type
|
|
||||||
|
|
||||||
#
|
|
||||||
# Creates a RCon::Query::Original object for use.
|
|
||||||
#
|
|
||||||
# The type (the default of which is HLDS), has multiple possible
|
|
||||||
# values:
|
|
||||||
#
|
|
||||||
# HLDS - Half Life 1 (will not work with older versions of HLDS)
|
|
||||||
#
|
|
||||||
# QUAKEWORLD - QuakeWorld/Quake 1
|
|
||||||
#
|
|
||||||
# NEWQUAKE - Quake 2/3 (and many derivatives)
|
|
||||||
#
|
|
||||||
|
|
||||||
def initialize(host, port, password, type=HLDS)
|
|
||||||
@host = host
|
|
||||||
@port = port
|
|
||||||
@password = password
|
|
||||||
@server_type = type
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Sends a request given as the argument, and returns the
|
|
||||||
# response as a string.
|
|
||||||
#
|
|
||||||
def command(request)
|
|
||||||
@request = request
|
|
||||||
@challenge_id = nil
|
|
||||||
|
|
||||||
establish_connection
|
|
||||||
|
|
||||||
@socket.print "\xFF" * 4 + "challenge rcon\n\x00"
|
|
||||||
|
|
||||||
tmp = retrieve_socket_data
|
|
||||||
challenge_id = /challenge rcon (\d+)/.match tmp
|
|
||||||
if challenge_id
|
|
||||||
@challenge_id = challenge_id[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
if @challenge_id.nil?
|
|
||||||
raise RCon::NetworkException.new("RCon challenge ID never returned: wrong rcon password?")
|
|
||||||
end
|
|
||||||
|
|
||||||
@socket.print "\xFF" * 4 + "rcon #{@challenge_id} \"#{@password}\" #{@request}\n\x00"
|
|
||||||
@response = retrieve_socket_data
|
|
||||||
|
|
||||||
@response.sub!(/^\xFF\xFF\xFF\xFF#{@server_type}/, "")
|
|
||||||
@response.sub!(/\x00+$/, "")
|
|
||||||
|
|
||||||
return @response
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Disconnects the RCon connection.
|
|
||||||
#
|
|
||||||
def disconnect
|
|
||||||
if @socket
|
|
||||||
@socket.close
|
|
||||||
@socket = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
#
|
|
||||||
# Establishes the connection.
|
|
||||||
#
|
|
||||||
def establish_connection
|
|
||||||
if @socket.nil?
|
|
||||||
@socket = UDPSocket.new
|
|
||||||
@socket.connect(@host, @port)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Generic method to pull data from the socket.
|
|
||||||
#
|
|
||||||
|
|
||||||
def retrieve_socket_data
|
|
||||||
return "" if @socket.nil?
|
|
||||||
|
|
||||||
retval = ""
|
|
||||||
loop do
|
|
||||||
break unless IO.select([@socket], nil, nil, 10)
|
|
||||||
packet = @socket.recv(8192)
|
|
||||||
retval << packet
|
|
||||||
break if packet.length < 8192
|
|
||||||
end
|
|
||||||
|
|
||||||
return retval
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# RCon::Query::Source sends queries to a "Source" Engine server,
|
|
||||||
# such as Half-Life 2: Deathmatch, Counter-Strike: Source, or Day
|
|
||||||
# of Defeat: Source.
|
|
||||||
#
|
|
||||||
# Note that one authentication packet needs to be sent to send
|
|
||||||
# multiple commands. Sending multiple authentication packets may
|
|
||||||
# damage the current connection and require it to be reset.
|
|
||||||
#
|
|
||||||
# Note: If the attribute 'return_packets' is set to true, the full
|
|
||||||
# RCon::Packet::Source object is returned, instead of just a string
|
|
||||||
# with the headers stripped. Useful for debugging.
|
|
||||||
#
|
|
||||||
|
|
||||||
class RCon::Query::Source < RCon::Query
|
|
||||||
# RCon::Packet::Source object that was sent as a result of the last query
|
|
||||||
attr_reader :packet
|
|
||||||
# TCPSocket object
|
|
||||||
attr_reader :socket
|
|
||||||
# Host of connection
|
|
||||||
attr_reader :host
|
|
||||||
# Port of connection
|
|
||||||
attr_reader :port
|
|
||||||
# Authentication Status
|
|
||||||
attr_reader :authed
|
|
||||||
# return full packet, or just data?
|
|
||||||
attr_accessor :return_packets
|
|
||||||
|
|
||||||
#
|
|
||||||
# Given a host and a port (dotted-quad or hostname OK), creates
|
|
||||||
# a RCon::Query::Source object. Note that this will still
|
|
||||||
# require an authentication packet (see the auth() method)
|
|
||||||
# before commands can be sent.
|
|
||||||
#
|
|
||||||
|
|
||||||
def initialize(host, port)
|
|
||||||
@host = host
|
|
||||||
@port = port
|
|
||||||
@socket = nil
|
|
||||||
@packet = nil
|
|
||||||
@authed = false
|
|
||||||
@return_packets = false
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# See RCon::Query#cvar.
|
|
||||||
#
|
|
||||||
|
|
||||||
def cvar(cvar_name)
|
|
||||||
return_packets = @return_packets
|
|
||||||
@return_packets = false
|
|
||||||
response = super
|
|
||||||
@return_packets = return_packets
|
|
||||||
return response
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Sends a RCon command to the server. May be used multiple times
|
|
||||||
# after an authentication is successful.
|
|
||||||
#
|
|
||||||
# See the class-level documentation on the 'return_packet' attribute
|
|
||||||
# for return values. The default is to return a string containing
|
|
||||||
# the response.
|
|
||||||
#
|
|
||||||
|
|
||||||
def command(command)
|
|
||||||
|
|
||||||
if ! @authed
|
|
||||||
raise RCon::NetworkException.new("You must authenticate the connection successfully before sending commands.")
|
|
||||||
end
|
|
||||||
|
|
||||||
@packet = RCon::Packet::Source.new
|
|
||||||
@packet.command(command)
|
|
||||||
|
|
||||||
@socket.print @packet.to_s
|
|
||||||
rpacket = build_response_packet
|
|
||||||
|
|
||||||
if rpacket.command_type != RCon::Packet::Source::RESPONSE_NORM
|
|
||||||
raise RCon::NetworkException.new("error sending command: #{rpacket.command_type}")
|
|
||||||
end
|
|
||||||
|
|
||||||
if @return_packets
|
|
||||||
return rpacket
|
|
||||||
else
|
|
||||||
return rpacket.string1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Requests authentication from the RCon server, given a
|
|
||||||
# password. Is only expected to be used once.
|
|
||||||
#
|
|
||||||
# See the class-level documentation on the 'return_packet' attribute
|
|
||||||
# for return values. The default is to return a true value if auth
|
|
||||||
# succeeded.
|
|
||||||
#
|
|
||||||
|
|
||||||
def auth(password)
|
|
||||||
establish_connection
|
|
||||||
|
|
||||||
@packet = RCon::Packet::Source.new
|
|
||||||
@packet.auth(password)
|
|
||||||
|
|
||||||
@socket.print @packet.to_s
|
|
||||||
# on auth, one junk packet is sent
|
|
||||||
rpacket = nil
|
|
||||||
2.times { rpacket = build_response_packet }
|
|
||||||
|
|
||||||
if rpacket.command_type != RCon::Packet::Source::RESPONSE_AUTH
|
|
||||||
raise RCon::NetworkException.new("error authenticating: #{rpacket.command_type}")
|
|
||||||
end
|
|
||||||
|
|
||||||
@authed = true
|
|
||||||
if @return_packets
|
|
||||||
return rpacket
|
|
||||||
else
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
alias_method :authenticate, :auth
|
|
||||||
|
|
||||||
#
|
|
||||||
# Disconnects from the Source server.
|
|
||||||
#
|
|
||||||
|
|
||||||
def disconnect
|
|
||||||
if @socket
|
|
||||||
@socket.close
|
|
||||||
@socket = nil
|
|
||||||
@authed = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
#
|
|
||||||
# Builds a RCon::Packet::Source packet based on the response
|
|
||||||
# given by the server.
|
|
||||||
#
|
|
||||||
def build_response_packet
|
|
||||||
rpacket = RCon::Packet::Source.new
|
|
||||||
total_size = 0
|
|
||||||
request_id = 0
|
|
||||||
type = 0
|
|
||||||
response = ""
|
|
||||||
message = ""
|
|
||||||
|
|
||||||
|
|
||||||
loop do
|
|
||||||
break unless IO.select([@socket], nil, nil, 10)
|
|
||||||
|
|
||||||
#
|
|
||||||
# TODO: clean this up - read everything and then unpack.
|
|
||||||
#
|
|
||||||
|
|
||||||
tmp = @socket.recv(14)
|
|
||||||
if tmp.nil?
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
size, request_id, type, message = tmp.unpack("VVVa*")
|
|
||||||
total_size += size
|
|
||||||
|
|
||||||
# special case for authentication
|
|
||||||
break if message.sub!(/\x00\x00$/, "")
|
|
||||||
|
|
||||||
response << message
|
|
||||||
|
|
||||||
# the 'size - 10' here accounts for the fact that we've snarfed 14 bytes,
|
|
||||||
# the size (which is 4 bytes) is not counted, yet represents the rest
|
|
||||||
# of the packet (which we have already taken 10 bytes from)
|
|
||||||
|
|
||||||
tmp = @socket.recv(size - 10)
|
|
||||||
response << tmp
|
|
||||||
response.sub!(/\x00\x00$/, "")
|
|
||||||
end
|
|
||||||
|
|
||||||
rpacket.packet_size = total_size
|
|
||||||
rpacket.request_id = request_id
|
|
||||||
rpacket.command_type = type
|
|
||||||
|
|
||||||
# strip nulls (this is actually the end of string1 and string2)
|
|
||||||
rpacket.string1 = response.sub(/\x00\x00$/, "")
|
|
||||||
return rpacket
|
|
||||||
end
|
|
||||||
|
|
||||||
# establishes a connection to the server.
|
|
||||||
def establish_connection
|
|
||||||
if @socket.nil?
|
|
||||||
@socket = TCPSocket.new(@host, @port)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# Exception class for network errors
|
|
||||||
class RCon::NetworkException < Exception
|
|
||||||
end
|
|
13
vendor/plugins/rcon/rcon.gemspec
vendored
13
vendor/plugins/rcon/rcon.gemspec
vendored
|
@ -1,13 +0,0 @@
|
||||||
spec = Gem::Specification.new
|
|
||||||
spec.name = "rcon"
|
|
||||||
spec.version = "0.2.1"
|
|
||||||
spec.author = "Erik Hollensbe"
|
|
||||||
spec.email = "erik@hollensbe.org"
|
|
||||||
spec.summary = "Ruby class to work with Quake 1/2/3, Half-Life and Source Engine rcon (remote console)"
|
|
||||||
spec.has_rdoc = true
|
|
||||||
spec.autorequire = "rcon"
|
|
||||||
spec.bindir = 'bin'
|
|
||||||
spec.executables << 'rcontool'
|
|
||||||
spec.add_dependency('ip', '>= 0.2.1')
|
|
||||||
spec.files = Dir['lib/rcon.rb'] + Dir['bin/rcontool']
|
|
||||||
spec.rubyforge_project = 'rcon'
|
|
1360
vendor/plugins/rcon/setup.rb
vendored
1360
vendor/plugins/rcon/setup.rb
vendored
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue