rcon 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. data/bin/rcontool +296 -0
  2. data/lib/rcon.rb +24 -17
  3. metadata +27 -13
data/bin/rcontool ADDED
@@ -0,0 +1,296 @@
1
+ #!/usr/bin/env ruby
2
+ ################################################################
3
+ #
4
+ # rcontool - shell interface to rcon commands
5
+ #
6
+ # (C) 2006 Erik Hollensbe, License details below
7
+ #
8
+ # Use 'rcontool -h' for usage instructions.
9
+ #
10
+ # The compilation of software known as rcontool is distributed under the
11
+ # following terms:
12
+ # Copyright (C) 2005-2006 Erik Hollensbe. All rights reserved.
13
+ #
14
+ # Redistribution and use in source form, with or without
15
+ # modification, are permitted provided that the following conditions
16
+ # are met:
17
+ # 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
23
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
+ # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
+ # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
+ # SUCH DAMAGE.
30
+ #
31
+ #
32
+ ################################################################
33
+
34
+ #
35
+ # rubygems hack
36
+ #
37
+
38
+ begin
39
+ require 'rubygems'
40
+ rescue LoadError => e
41
+ end
42
+ begin
43
+ require 'rcon'
44
+ require 'ip'
45
+ rescue LoadError => e
46
+ $stderr.puts "rcontool requires the rcon and ip libraries be installed."
47
+ $stderr.puts "You can find them both via rubygems or at http://rubyforge.org."
48
+ exit -1
49
+ end
50
+
51
+ RCONTOOL_VERSION = '0.1.0'
52
+
53
+ require 'optparse'
54
+ require 'ostruct'
55
+
56
+ #
57
+ # Manages our options
58
+ #
59
+
60
+ def get_options
61
+ options = OpenStruct.new
62
+ # ip address (IP::Address object)
63
+ options.ip_address = nil
64
+ # port (integer)
65
+ options.port = nil
66
+ # password
67
+ options.password = nil
68
+ # protocol type (one of :hlds, :source, :oldquake, :newquake)
69
+ options.protocol_type = nil
70
+ # verbose, spit out extra information
71
+ options.verbose = false
72
+ # command to execute on the server
73
+ options.command = nil
74
+
75
+ optparse = OptionParser.new do |opts|
76
+ opts.banner = "Usage: #{File.basename $0} <ip_address:port> <command> [options]"
77
+ opts.separator ""
78
+ opts.separator "Options:"
79
+
80
+ opts.on("--ip-address [ADDRESS]",
81
+ "Provide an IP address to connect to. Does not take a port.") do |ip_address|
82
+ if ! options.ip_address.nil?
83
+ $stderr.puts "Error: you have already provided an IP Address."
84
+ $stderr.puts opts
85
+ exit -1
86
+ end
87
+
88
+ options.ip_address = IP::Address.new(ip_address)
89
+ end
90
+
91
+ opts.on("-r", "--port [PORT]",
92
+ "Port to connect to.") do |port|
93
+ if ! options.port.nil?
94
+ $stderr.puts "Error: you have already provided a port."
95
+ $stderr.puts opts
96
+ exit -1
97
+ end
98
+
99
+ options.port = port.to_i
100
+ end
101
+
102
+ opts.on("-c", "--command [COMMAND]",
103
+ "Command to run on the server.") do |command|
104
+ if ! options.command.nil?
105
+ $stderr.puts "Error: you have already provided a command."
106
+ $stderr.puts opts
107
+ exit -1
108
+ end
109
+
110
+ options.command = command
111
+ end
112
+
113
+ opts.on("-p", "--password [PASSWORD]",
114
+ "Provide a password on the command line.") do |password|
115
+ options.password = password
116
+ end
117
+
118
+ opts.on("-f", "--password-from [FILENAME]",
119
+ "Get the password from a file (use '/dev/fd/0' or '/dev/stdin' to read from Standard Input).") do |filename|
120
+ if !filename.nil?
121
+ f = File.open(filename)
122
+ options.password = f.gets.chomp
123
+ f.close
124
+ else
125
+ $stderr.puts "Error: filename (from -f) is not valid."
126
+ $stderr.puts opts
127
+ exit -1
128
+ end
129
+ end
130
+
131
+ opts.on("-t", "--protocol-type [TYPE]", [:hlds, :source, :oldquake, :newquake],
132
+ "Type of rcon connection to make: (hlds, source, oldquake, newquake).",
133
+ " Note: oldquake is quake1/quakeworld, newquake is quake2/3.") do |protocol_type|
134
+ options.protocol_type = protocol_type
135
+ end
136
+
137
+ opts.on("-v", "--[no-]verbose",
138
+ "Run verbosely, print information about each packet recieved and turnaround times.") do |verbose|
139
+ options.verbose = verbose
140
+ end
141
+
142
+ opts.on("-h", "--help",
143
+ "This help message.") do
144
+ $stderr.puts opts
145
+ exit -1
146
+ end
147
+
148
+ opts.on("--version", "Print the version information.") do
149
+ $stderr.puts "This is rcontool version #{RCONTOOL_VERSION},"
150
+ $stderr.puts "it is located at #{File.expand_path $0}."
151
+ exit -1
152
+ end
153
+
154
+ opts.separator ""
155
+ opts.separator "Note: IP, port, protocol type, password and command are required to function."
156
+ opts.separator ""
157
+ opts.separator "Examples (all are equivalent):"
158
+ opts.separator "\t#{File.basename($0)} 10.0.0.11 status -t hlds -r 27015 -p foobar"
159
+ opts.separator "\techo 'foobar' | #{File.basename($0)} 10.0.0.11:27015 status -t hlds -f /dev/stdin"
160
+ opts.separator "\t#{File.basename($0)} --ip-address 10.0.0.11 --port 27015 -c status -t hlds -f file_with_password"
161
+ opts.separator ""
162
+
163
+ end
164
+
165
+ ################################################################
166
+ #
167
+ # This hackery is to help facilitate the bareword options if
168
+ # they exist, while still allowing for the option parser
169
+ # to work properly.
170
+ #
171
+ ################################################################
172
+
173
+ s1 = ARGV.shift
174
+ s2 = ARGV.shift
175
+
176
+ begin
177
+ options.ip_address = IP::Address.new(s1)
178
+ options.command = s2
179
+ rescue IP::AddressException => e
180
+ # attempt to split it first... not sure how to best handle this situation
181
+ begin
182
+ ip,port = s1.split(/:/, 2)
183
+ options.ip_address = IP::Address.new(ip)
184
+ options.port = port.to_i
185
+ options.command = s2
186
+ rescue Exception => e
187
+ end
188
+
189
+ if [options.ip_address, options.port].include? nil
190
+ ARGV.unshift(s2)
191
+ ARGV.unshift(s1)
192
+ end
193
+ end
194
+
195
+ optparse.parse!
196
+
197
+ if [options.ip_address, options.protocol_type, options.port, options.password, options.command].include? nil
198
+ $stderr.puts optparse
199
+ exit -1
200
+ end
201
+
202
+ return options
203
+ end
204
+
205
+ def verbose(string)
206
+ $stderr.puts string if $options.verbose
207
+ end
208
+
209
+ def dump_source_packet(packet)
210
+ if $options.verbose
211
+ verbose "Request ID: #{packet.request_id}"
212
+ verbose "Packet Size: #{packet.packet_size}"
213
+ verbose "Response Type: #{packet.command_type}"
214
+ end
215
+ end
216
+
217
+ ################################################################
218
+ #
219
+ # start main block
220
+ #
221
+ ################################################################
222
+
223
+ $options = get_options
224
+
225
+ ################################################################
226
+ #
227
+ # Source query
228
+ #
229
+ ################################################################
230
+
231
+ if $options.protocol_type == :source
232
+ verbose "Protocol type 'SOURCE' selected."
233
+
234
+ rcon = RCon::Query::Source.new($options.ip_address.ip_address, $options.port)
235
+
236
+ # if we have a verbose request, give all the information we can about
237
+ # the query, including the packet information.
238
+ rcon.return_packets = $options.verbose
239
+
240
+ verbose "Attempting authentication to #{$options.ip_address.ip_address}:#{$options.port} with password '#{$options.password}'"
241
+
242
+ value = rcon.auth $options.password
243
+
244
+ dump_source_packet value
245
+
246
+ if ($options.verbose && value.command_type == RCon::Packet::Source::RESPONSE_AUTH) || value
247
+ verbose "Authentication succeeded. Sending command: '#{$options.command}'"
248
+
249
+ value = rcon.command $options.command
250
+
251
+ dump_source_packet value
252
+ verbose ""
253
+
254
+ if $options.verbose
255
+ puts value.string1
256
+ else
257
+ puts value
258
+ end
259
+
260
+ exit 0
261
+ else
262
+ $stderr.puts "Authentication failed."
263
+ exit 1
264
+ end
265
+
266
+ ################################################################
267
+ #
268
+ # Original Query
269
+ #
270
+ ################################################################
271
+
272
+ else
273
+ rcon = nil
274
+ case $options.protocol_type
275
+ when :hlds
276
+ verbose "Protocol type 'HLDS' selected"
277
+ rcon = RCon::Query::Original.new($options.ip_address.ip_address, $options.port, $options.password,
278
+ RCon::Query::Original::HLDS)
279
+ when :oldquake
280
+ verbose "Protocol type 'OLDQUAKE' selected"
281
+ rcon = RCon::Query::Original.new($options.ip_address.ip_address, $options.port, $options.password,
282
+ RCon::Query::Original::QUAKEWORLD)
283
+ when :newquake
284
+ verbose "Protocol type 'NEWQUAKE' selected"
285
+ rcon = RCon::Query::Original.new($options.ip_address.ip_address, $options.port, $options.password,
286
+ RCon::Query::Original::NEWQUAKE)
287
+ end
288
+ verbose "Attempting transmission to #{$options.ip_address.ip_address}:#{$options.port}"
289
+ verbose "Using password: '#{$options.password}' and sending command: '#{$options.command}'"
290
+ verbose ""
291
+ string = rcon.command($options.command)
292
+
293
+ puts string
294
+ exit 0
295
+ end
296
+
data/lib/rcon.rb CHANGED
@@ -4,7 +4,7 @@ require 'socket'
4
4
  # RCon is a module to work with Quake 1/2/3, Half-Life, and Half-Life
5
5
  # 2 (Source Engine) RCon (Remote Console) protocols.
6
6
  #
7
- # Version:: 0.1.0
7
+ # Version:: 0.2.0
8
8
  #
9
9
  # Author:: Erik Hollensbe <erik@hollensbe.org>
10
10
  #
@@ -20,9 +20,13 @@ require 'socket'
20
20
  # Usage is fairly simple:
21
21
  #
22
22
  # # Note: Other classes have different constructors
23
+ #
23
24
  # rcon = RCon::Query::Source.new("10.0.0.1", 27015)
25
+ #
24
26
  # rcon.auth("foobar") # source only
27
+ #
25
28
  # rcon.command("mp_friendlyfire") => "mp_friendlyfire = 1"
29
+ #
26
30
  # rcon.cvar("mp_friendlyfire") => 1
27
31
  #
28
32
  # ================================================================
@@ -34,8 +38,7 @@ require 'socket'
34
38
  # Redistribution and use in source form, with or without
35
39
  # modification, are permitted provided that the following conditions
36
40
  # are met:
37
- # 1. Redistributions of source code must retain the above copyright
38
- # notice, this list of conditions and the following disclaimer.
41
+ # 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
39
42
  #
40
43
  # THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
41
44
  # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -104,8 +107,8 @@ class RCon::Packet::Source
104
107
  attr_accessor :packet_size
105
108
  # Request Identifier, used in managing multiple requests at once
106
109
  attr_accessor :request_id
107
- # Type of command, normally COMMAND_AUTH or COMMAND_EXEC
108
- attr_accessor :type
110
+ # Type of command, normally COMMAND_AUTH or COMMAND_EXEC. In response packets, RESPONSE_AUTH or RESPONSE_NORM
111
+ attr_accessor :command_type
109
112
  # First string, the only used one in the protocol, contains
110
113
  # commands and responses. Null terminated.
111
114
  attr_accessor :string1
@@ -121,7 +124,7 @@ class RCon::Packet::Source
121
124
  @request_id = rand(1000)
122
125
  @string1 = string
123
126
  @string2 = TRAILER
124
- @type = COMMAND_EXEC
127
+ @command_type = COMMAND_EXEC
125
128
 
126
129
  @packet_size = build_packet.length
127
130
 
@@ -137,7 +140,7 @@ class RCon::Packet::Source
137
140
  @request_id = rand(1000)
138
141
  @string1 = string
139
142
  @string2 = TRAILER
140
- @type = COMMAND_AUTH
143
+ @command_type = COMMAND_AUTH
141
144
 
142
145
  @packet_size = build_packet.length
143
146
 
@@ -150,7 +153,7 @@ class RCon::Packet::Source
150
153
  # that srcds actually needs.
151
154
  #
152
155
  def build_packet
153
- return [@request_id, @type, @string1, @string2].pack("VVa#{@string1.length}a2")
156
+ return [@request_id, @command_type, @string1, @string2].pack("VVa#{@string1.length}a2")
154
157
  end
155
158
 
156
159
  # Returns a string representation of the packet, useful for
@@ -197,6 +200,8 @@ class RCon::Query::Original < RCon::Query
197
200
  attr_reader :port
198
201
  # RCon password
199
202
  attr_reader :password
203
+ # type of server
204
+ attr_reader :server_type
200
205
 
201
206
  #
202
207
  # Creates a RCon::Query::Original object for use.
@@ -205,7 +210,9 @@ class RCon::Query::Original < RCon::Query
205
210
  # values:
206
211
  #
207
212
  # HLDS - Half Life 1 (will not work with older versions of HLDS)
213
+ #
208
214
  # QUAKEWORLD - QuakeWorld/Quake 1
215
+ #
209
216
  # NEWQUAKE - Quake 2/3 (and many derivatives)
210
217
  #
211
218
 
@@ -213,7 +220,7 @@ class RCon::Query::Original < RCon::Query
213
220
  @host = host
214
221
  @port = port
215
222
  @password = password
216
- @type = type
223
+ @server_type = type
217
224
  end
218
225
 
219
226
  #
@@ -241,7 +248,7 @@ class RCon::Query::Original < RCon::Query
241
248
  @socket.print "\xFF" * 4 + "rcon #{@challenge_id} \"#{@password}\" #{@request}\n\x00"
242
249
  @response = retrieve_socket_data
243
250
 
244
- @response.sub! /^\xFF\xFF\xFF\xFF#{@type}/, ""
251
+ @response.sub! /^\xFF\xFF\xFF\xFF#{@server_type}/, ""
245
252
  @response.sub! /\x00+$/, ""
246
253
 
247
254
  return @response
@@ -316,7 +323,7 @@ class RCon::Query::Source < RCon::Query
316
323
  # Authentication Status
317
324
  attr_reader :authed
318
325
  # return full packet, or just data?
319
- attr :return_packets
326
+ attr_accessor :return_packets
320
327
 
321
328
  #
322
329
  # Given a host and a port (dotted-quad or hostname OK), creates
@@ -367,8 +374,8 @@ class RCon::Query::Source < RCon::Query
367
374
  @socket.print @packet.to_s
368
375
  rpacket = build_response_packet
369
376
 
370
- if rpacket.type != RCon::Packet::Source::RESPONSE_NORM
371
- raise RCon::NetworkException.new("error sending command: #{rpacket.type}")
377
+ if rpacket.command_type != RCon::Packet::Source::RESPONSE_NORM
378
+ raise RCon::NetworkException.new("error sending command: #{rpacket.command_type}")
372
379
  end
373
380
 
374
381
  if @return_packets
@@ -398,12 +405,12 @@ class RCon::Query::Source < RCon::Query
398
405
  rpacket = nil
399
406
  2.times { rpacket = build_response_packet }
400
407
 
401
- if rpacket.type != RCon::Packet::Source::RESPONSE_AUTH
402
- raise RCon::NetworkException.new("error authenticating: #{rpacket.type}")
408
+ if rpacket.command_type != RCon::Packet::Source::RESPONSE_AUTH
409
+ raise RCon::NetworkException.new("error authenticating: #{rpacket.command_type}")
403
410
  end
404
411
 
405
412
  @authed = true
406
- if @return_packet
413
+ if @return_packets
407
414
  return rpacket
408
415
  else
409
416
  return true
@@ -463,7 +470,7 @@ class RCon::Query::Source < RCon::Query
463
470
 
464
471
  rpacket.packet_size = total_size
465
472
  rpacket.request_id = request_id
466
- rpacket.type = type
473
+ rpacket.command_type = type
467
474
 
468
475
  # strip nulls (this is actually the end of string1 and string2)
469
476
  rpacket.string1 = response.sub /\x00\x00$/, ""
metadata CHANGED
@@ -3,12 +3,11 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: rcon
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.0
7
- date: 2006-01-28 00:00:00 -08:00
8
- summary: "Ruby class to work with Quake 1/2/3, Half-Life and Source Engine rcon (remote
9
- console)"
6
+ version: 0.2.0
7
+ date: 2006-02-08 00:00:00 -08:00
8
+ summary: Ruby class to work with Quake 1/2/3, Half-Life and Source Engine rcon (remote console)
10
9
  require_paths:
11
- - lib
10
+ - lib
12
11
  email: erik@hollensbe.org
13
12
  homepage:
14
13
  rubyforge_project:
@@ -19,22 +18,37 @@ bindir: bin
19
18
  has_rdoc: true
20
19
  required_ruby_version: !ruby/object:Gem::Version::Requirement
21
20
  requirements:
22
- -
23
- - ">"
24
- - !ruby/object:Gem::Version
25
- version: 0.0.0
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
26
24
  version:
27
25
  platform: ruby
28
26
  signing_key:
29
27
  cert_chain:
30
28
  authors:
31
- - Erik Hollensbe
29
+ - Erik Hollensbe
32
30
  files:
33
- - lib/rcon.rb
31
+ - lib/rcon.rb
32
+ - bin/rcontool
34
33
  test_files: []
34
+
35
35
  rdoc_options: []
36
+
36
37
  extra_rdoc_files: []
37
- executables: []
38
+
39
+ executables:
40
+ - rcontool
38
41
  extensions: []
42
+
39
43
  requirements: []
40
- dependencies: []
44
+
45
+ dependencies:
46
+ - !ruby/object:Gem::Dependency
47
+ name: ip
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Version::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.1.1
54
+ version: