rcon 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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: