ruby-nsca/lib/nsca/server.rb

87 lines
2.5 KiB
Ruby

module NSCA
class Server
include Enumerable
attr_reader :iv_key, :server, :packet_version, :password, :xor_iv_key, :xor_password
def initialize *args
opts = {}
opts = args.pop.dup if args.last.is_a? Hash
opts[:host] ||= opts[:hostname]
opts[:serv] ||= opts[:server]
opts[:pass] ||= opts[:password]
case args[0]
when Integer
opts[:port] = args[0]
opts[:host] ||= args[1]
when IO
opts[:serv] = args[0]
end
@packet_version = opts[:packet_version] || PacketV3
@iv_key = (opts[:iv_key] || SecureRandom.random_bytes( 128)).to_s
raise ArgumentError, "Key must be 128 bytes long" unless 128 == @iv_key.length
@password = opts[:pass].to_s
@server = if opts[:serv].is_a?( TCPServer) or opts[:serv].is_a?( UNIXServer)
opts[:serv]
elsif opts[:port].is_a? Integer
TCPServer.new *[opts[:host], opts[:port]].compact
else
raise ArgumentError, "Server or port-number expected"
end
end
def accept() Connection.new @server.accept, self end
def close() @server.close end
def each &block
return Enumerator.new( self) unless block_given?
while conn = accept
yield conn
end
end
class Connection
include Enumerable
def initialize socket, server
@socket, @server = socket, server
@iv_key, @password = server.iv_key, server.password
@packet_version = server.packet_version
@packet_length = @packet_version::PACK_LENGTH
@socket.write [@iv_key, Time.now.to_i].pack( 'a* L>')
@xor_password = NSCA::Helper.xor_stream @password
@xor_iv_key = NSCA::Helper.xor_stream @iv_key
end
def fetch
iv_key = NSCA::Helper.xor_stream @iv_key
password = NSCA::Helper.xor_stream @password
packet_version = iv_key[ password[ read PacketV3::PACKET_VERSION]]
v = packet_version.unpack( 's>').first
case v
when 3
data = packet_version + iv_key[ password[ read( PacketV3::PACK_LENGTH - PacketV3::PACKET_VERSION)]]
begin
return PacketV3.parse( data)
rescue NSCA::Packet::CSC32CheckFailed
x = read( PacketV3__2_9::PACK_LENGTH - data.length)
raise if x.nil?
return PacketV3__2_9.parse( data + iv_key[ password[ x]])
end
else raise "Unknown Version #{v.inspect}"
end
end
def each &block
return Enumerator.new( self) unless block_given?
yield fetch until eof?
end
def eof?() @socket.eof? end
def read( len = nil) @socket.read( len || @packet_length) end
def close() @socket.close end
end
end
end