lib/steam-condenser/servers/packets/steam_packet_factory.rb
# This code is free software; you can redistribute it and/or modify it under
# the terms of the new BSD License.
#
# Copyright (c) 2008-2013, Sebastian Staudt
require 'zlib'
require 'steam-condenser/servers/packets/s2a_info_detailed_packet'
require 'steam-condenser/servers/packets/a2s_info_packet'
require 'steam-condenser/servers/packets/s2a_info2_packet'
require 'steam-condenser/servers/packets/a2s_player_packet'
require 'steam-condenser/servers/packets/s2a_player_packet'
require 'steam-condenser/servers/packets/a2s_rules_packet'
require 'steam-condenser/servers/packets/s2a_rules_packet'
require 'steam-condenser/servers/packets/a2s_serverquery_getchallenge_packet'
require 'steam-condenser/servers/packets/s2c_challenge_packet'
require 'steam-condenser/servers/packets/a2m_get_servers_batch2_packet'
require 'steam-condenser/servers/packets/m2a_server_batch_packet'
require 'steam-condenser/servers/packets/s2a_logstring_packet'
require 'steam-condenser/servers/packets/rcon/rcon_goldsrc_response'
require 'steam-condenser/error/packet_format'
module SteamCondenser::Servers::Packets
# This module provides functionality to handle raw packet data, including
# data split into several UDP / TCP packets and BZIP2 compressed data. It's
# the main utility to transform data bytes into packet objects.
#
# @author Sebastian Staudt
# @see BasePacket
module SteamPacketFactory
# Creates a new packet object based on the header byte of the given raw
# data
#
# @param [String] raw_data The raw data of the packet
# @raise [Error::PacketFormat] if the packet header is not recognized
# @return [BasePacket] The packet object generated from the packet data
def self.packet_from_data(raw_data)
header = raw_data[0].ord
data = raw_data[1..-1]
case header
when BasePacket::S2A_INFO_DETAILED_HEADER
return S2A_INFO_DETAILED_Packet.new(data)
when BasePacket::A2S_INFO_HEADER
return A2S_INFO_Packet.new
when BasePacket::S2A_INFO2_HEADER
return S2A_INFO2_Packet.new(data)
when BasePacket::A2S_PLAYER_HEADER
return A2S_PLAYER_Packet.new
when BasePacket::S2A_PLAYER_HEADER
return S2A_PLAYER_Packet.new(data)
when BasePacket::A2S_RULES_HEADER
return A2S_RULES_Packet
when BasePacket::S2A_RULES_HEADER
return S2A_RULES_Packet.new(data)
when BasePacket::A2S_SERVERQUERY_GETCHALLENGE_HEADER
return A2S_SERVERQUERY_GETCHALLENGE_Packet.new
when BasePacket::S2C_CHALLENGE_HEADER
return S2C_CHALLENGE_Packet.new(data)
when BasePacket::A2M_GET_SERVERS_BATCH2_HEADER
return A2M_GET_SERVERS_BATCH2_Packet.new(data)
when BasePacket::M2A_SERVER_BATCH_HEADER
return M2A_SERVER_BATCH_Packet.new(data)
when BasePacket::RCON_GOLDSRC_CHALLENGE_HEADER,
BasePacket::RCON_GOLDSRC_NO_CHALLENGE_HEADER,
BasePacket::RCON_GOLDSRC_RESPONSE_HEADER
return RCON::RCONGoldSrcResponse.new(data)
else
raise SteamCondenser::Error::PacketFormat, "Unknown packet with header 0x#{header.to_s(16)} received."
end
end
# Reassembles the data of a split and/or compressed packet into a single
# packet object
#
# @param [Array<String>] split_packets An array of packet data
# @param [Boolean] is_compressed whether the data of this packet is
# compressed
# @param [Fixnum] packet_checksum The CRC32 checksum of the decompressed
# packet data
# @raise [Error::PacketFormat] if the calculated CRC32 checksum does not
# match the expected value
# @return [BasePacket] The reassembled packet
# @see packet_from_data
def self.reassemble_packet(split_packets, is_compressed = false, packet_checksum = 0)
packet_data = split_packets.join ''
if is_compressed
begin
require 'bzip2-ruby'
rescue LoadError
raise SteamCondenser::Error, 'The "bzip2-ruby" gem is not installed. Please install it, if you want to query Source servers sending compressed packets.'
end
packet_data = Bzip2.decompress packet_data
unless Zlib.crc32(packet_data) == packet_checksum
raise SteamCondenser::Error::PacketFormat, 'CRC32 checksum mismatch of uncompressed packet data.'
end
end
packet_from_data packet_data[4..-1]
end
end
end