require 'socket' require 'ipaddr' class RawSocket # :nodoc: @@id_arr = [] def initialize(src_addr, dest_addr) # Define socket begin @socket = Socket.new PF_INET, SOCK_RAW, IPPROTO_RAW rescue SystemCallError => e raise SystemCallError, "You must be root to use raw sockets! #{e}" end @socket.setsockopt IPPROTO_IP, IP_HDRINCL, 1 # Checks addresses @src_addr = check_addr src_addr @dest_addr = check_addr dest_addr # Source and destination port are zero @src_port = 0 @dest_port = 0 # Set correct protocol version in the header @version = @dest_addr.ipv4? ? "0100" : "0110" # Total lenght: must be overridden by subclasses @tot_lenght = 20 # Protocol: must be overridden by subclasses @protocol = 1 # ICMP by default # Generate a new id # @id = genID @id = 1234 # Generate peer sockaddr @to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s end def send(payload = '') packet = make_ip_header([ [@version + '0101', 'B8'], # version, hlen [0, 'C'], # tos [@tot_lenght + payload.size, 'n'], # total len [@id, 'n'], # id [0, 'n'], # flags, offset [64, 'C'], # ttl [@protocol, 'C'], # protocol [0, 'n'], # checksum [@src_addr.to_i, 'N'], # source [@dest_addr.to_i, 'N'], # destination ]) packet << make_transport_header(payload.size) packet << [payload].pack("a*") @socket.send(packet, 0, @to) end private def check_addr(addr) case addr when String IPAddr.new(addr) when IPAddr addr else raise ArgumentError, "Wrong address format: #{addr}" end end def check_port(port) if (1..65_535).cover?(port) && port.is_a?(Integer) port else raise ArgumentError, "Port #{port} not valid" end end def genID while @@id_arr.include?(q = rand(65_535)) end @@id_arr.push(q) q end def ipchecksum(data) checksum = data.unpack("n*").inject(0) { |s, x| s + x } ((checksum >> 16) + (checksum & 0xffff)) ^ 0xffff end def make_ip_header(parts) template = '' data = [] parts.each do |part| data += part[0..-2] template << part[-1] end data_str = data.pack(template) checksum = ipchecksum(data_str) data[-3] = checksum data.pack(template) end def make_transport_header "" end end class UdpRawSocket < RawSocket # :nodoc: def initialize(src_addr, src_port, dest_addr, dest_port) super(src_addr, dest_addr) # Check ports @src_port = check_port src_port @dest_port = check_port dest_port # Total lenght: must be overridden by subclasses @tot_lenght = 20 + 8 # 8 bytes => UDP Header # Protocol: must be overridden by subclasses @protocol = 17 # UDP protocol @to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s end private def make_udp_header(parts) template = '' data = [] parts.each do |part| data += part[0..-2] template << part[-1] end data.pack(template) end def make_transport_header(pay_size) make_udp_header([ [@src_port, 'n'], # source port [@dest_port, 'n'], # destination port [8 + pay_size, 'n'], # len [0, 'n'] # checksum (mandatory) ]) end end